| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.js_emitter.constant_ordering; |
| |
| import '../constants/values.dart'; |
| import '../elements/entities.dart' show ClassEntity, FieldEntity, MemberEntity; |
| import '../elements/types.dart'; |
| import 'sorter.dart' show Sorter; |
| |
| /// A canonical but arbitrary ordering of constants. The ordering is 'stable' |
| /// under perturbation of the source. |
| abstract class ConstantOrdering { |
| factory ConstantOrdering(Sorter sorter) = _ConstantOrdering; |
| |
| int compare(ConstantValue a, ConstantValue b); |
| } |
| |
| class _ConstantOrdering |
| implements ConstantOrdering, ConstantValueVisitor<int, ConstantValue> { |
| final Sorter _sorter; |
| _DartTypeOrdering _dartTypeOrdering; |
| _ConstantOrdering(this._sorter) { |
| _dartTypeOrdering = _DartTypeOrdering(this); |
| } |
| |
| @override |
| int compare(ConstantValue a, ConstantValue b) => compareValues(a, b); |
| |
| int compareValues(ConstantValue a, ConstantValue b) { |
| if (identical(a, b)) return 0; |
| int r = a.kind.index.compareTo(b.kind.index); |
| if (r != 0) return r; |
| return a.accept(this, b); |
| } |
| |
| static int compareLists<S, T>(int compare(S a, T b), List<S> a, List<T> b) { |
| int r = a.length.compareTo(b.length); |
| if (r != 0) return r; |
| for (int i = 0; i < a.length; i++) { |
| r = compare(a[i], b[i]); |
| if (r != 0) return r; |
| } |
| return 0; |
| } |
| |
| int compareClasses(ClassEntity a, ClassEntity b) { |
| int r = a.name.compareTo(b.name); |
| if (r != 0) return r; |
| return _sorter.compareClassesByLocation(a, b); |
| } |
| |
| int compareMembers(MemberEntity a, MemberEntity b) { |
| int r = a.name.compareTo(b.name); |
| if (r != 0) return r; |
| return _sorter.compareMembersByLocation(a, b); |
| } |
| |
| int compareDartTypes(DartType a, DartType b) { |
| return _dartTypeOrdering.compare(a, b); |
| } |
| |
| @override |
| int visitFunction(FunctionConstantValue a, FunctionConstantValue b) { |
| return compareMembers(a.element, b.element); |
| } |
| |
| @override |
| int visitNull(NullConstantValue a, NullConstantValue b) { |
| return 0; |
| } |
| |
| @override |
| int visitNonConstant(NonConstantValue a, NonConstantValue b) { |
| return 0; |
| } |
| |
| @override |
| int visitInt(IntConstantValue a, IntConstantValue b) { |
| return a.intValue.compareTo(b.intValue); |
| } |
| |
| @override |
| int visitDouble(DoubleConstantValue a, DoubleConstantValue b) { |
| return a.doubleValue.compareTo(b.doubleValue); |
| } |
| |
| @override |
| int visitBool(BoolConstantValue a, BoolConstantValue b) { |
| int aInt = a.boolValue ? 1 : 0; |
| int bInt = b.boolValue ? 1 : 0; |
| return aInt.compareTo(bInt); |
| } |
| |
| @override |
| int visitString(StringConstantValue a, StringConstantValue b) { |
| String aString = a.stringValue; |
| String bString = b.stringValue; |
| return aString.compareTo(bString); |
| } |
| |
| @override |
| int visitList(ListConstantValue a, ListConstantValue b) { |
| int r = compareLists(compareValues, a.entries, b.entries); |
| if (r != 0) return r; |
| return compareDartTypes(a.type, b.type); |
| } |
| |
| @override |
| int visitSet(SetConstantValue a, SetConstantValue b) { |
| int r = compareLists(compareValues, a.values, b.values); |
| if (r != 0) return r; |
| return compareDartTypes(a.type, b.type); |
| } |
| |
| @override |
| int visitMap(MapConstantValue a, MapConstantValue b) { |
| int r = compareLists(compareValues, a.keys, b.keys); |
| if (r != 0) return r; |
| r = compareLists(compareValues, a.values, b.values); |
| if (r != 0) return r; |
| return compareDartTypes(a.type, b.type); |
| } |
| |
| @override |
| int visitConstructed(ConstructedConstantValue a, ConstructedConstantValue b) { |
| int r = compareDartTypes(a.type, b.type); |
| if (r != 0) return r; |
| |
| // TODO(sra): Avoid all these tear-offs. |
| List<FieldEntity> aFields = a.fields.keys.toList()..sort(compareMembers); |
| List<FieldEntity> bFields = b.fields.keys.toList()..sort(compareMembers); |
| |
| r = compareLists(compareMembers, aFields, bFields); |
| if (r != 0) return r; |
| |
| return compareLists( |
| compareValues, |
| aFields.map((field) => a.fields[field]).toList(), |
| aFields.map((field) => b.fields[field]).toList()); |
| } |
| |
| @override |
| int visitType(TypeConstantValue a, TypeConstantValue b) { |
| int r = compareDartTypes(a.representedType, b.representedType); |
| if (r != 0) return r; |
| return compareDartTypes(a.type, b.type); |
| } |
| |
| @override |
| int visitInterceptor(InterceptorConstantValue a, InterceptorConstantValue b) { |
| return compareClasses(a.cls, b.cls); |
| } |
| |
| @override |
| int visitDummyInterceptor( |
| DummyInterceptorConstantValue a, DummyInterceptorConstantValue b) { |
| // Never emitted. |
| return 0; |
| } |
| |
| @override |
| int visitLateSentinel( |
| LateSentinelConstantValue a, LateSentinelConstantValue b) => |
| 0; |
| |
| @override |
| int visitUnreachable(UnreachableConstantValue a, UnreachableConstantValue b) { |
| // Never emitted. |
| return 0; |
| } |
| |
| @override |
| int visitJsName(JsNameConstantValue a, JsNameConstantValue b) { |
| // An opaque deferred JS AST reference to a name. |
| return 0; |
| } |
| |
| @override |
| int visitDeferredGlobal( |
| DeferredGlobalConstantValue a, DeferredGlobalConstantValue b) { |
| int r = compareValues(a.referenced, b.referenced); |
| if (r != 0) return r; |
| return a.unit.compareTo(b.unit); |
| } |
| |
| @override |
| int visitInstantiation( |
| InstantiationConstantValue a, InstantiationConstantValue b) { |
| int r = compareValues(a.function, b.function); |
| if (r != 0) return r; |
| return compareLists(compareDartTypes, a.typeArguments, b.typeArguments); |
| } |
| } |
| |
| /// Visitor for distinguishing types by kind. |
| class _DartTypeKindVisitor implements DartTypeVisitor<int, Null> { |
| const _DartTypeKindVisitor(); |
| |
| static int kind(DartType type) { |
| return const _DartTypeKindVisitor().visit(type); |
| } |
| |
| @override |
| int visit(DartType type, [_]) => type.accept(this, null); |
| @override |
| int visitFunctionType(FunctionType type, _) => 0; |
| @override |
| int visitInterfaceType(InterfaceType type, _) => 1; |
| @override |
| int visitFunctionTypeVariable(FunctionTypeVariable type, _) => 2; |
| @override |
| int visitTypeVariableType(TypeVariableType type, _) => 3; |
| @override |
| int visitNeverType(NeverType type, _) => 4; |
| @override |
| int visitDynamicType(DynamicType type, _) => 5; |
| @override |
| int visitVoidType(VoidType type, _) => 6; |
| @override |
| int visitAnyType(AnyType type, _) => 7; |
| @override |
| int visitErasedType(ErasedType type, _) => 8; |
| @override |
| int visitFutureOrType(FutureOrType type, _) => 9; |
| @override |
| int visitLegacyType(LegacyType type, _) => 10; |
| @override |
| int visitNullableType(NullableType type, _) => 11; |
| } |
| |
| class _DartTypeOrdering extends DartTypeVisitor<int, DartType> { |
| final _ConstantOrdering _constantOrdering; |
| DartType _root; |
| final List<FunctionTypeVariable> _leftFunctionTypeVariables = []; |
| final List<FunctionTypeVariable> _rightFunctionTypeVariables = []; |
| _DartTypeOrdering(this._constantOrdering); |
| |
| int compare(DartType a, DartType b) { |
| if (a == b) return 0; |
| int r = |
| _DartTypeKindVisitor.kind(a).compareTo(_DartTypeKindVisitor.kind(b)); |
| if (r != 0) return r; |
| _root = a; |
| r = a.accept(this, b); |
| _root = null; |
| return r; |
| } |
| |
| @override |
| int visitLegacyType(covariant LegacyType type, covariant LegacyType other) => |
| compare(type.baseType, other.baseType); |
| |
| @override |
| int visitNullableType( |
| covariant NullableType type, covariant NullableType other) => |
| compare(type.baseType, other.baseType); |
| |
| @override |
| int visitFutureOrType( |
| covariant FutureOrType type, covariant FutureOrType other) => |
| compare(type.typeArgument, other.typeArgument); |
| |
| @override |
| int visitNeverType(covariant NeverType type, covariant NeverType other) { |
| throw UnsupportedError('Unreachable'); |
| } |
| |
| @override |
| int visitVoidType(covariant VoidType type, covariant VoidType other) { |
| throw UnsupportedError('Unreachable'); |
| } |
| |
| @override |
| int visitTypeVariableType( |
| covariant TypeVariableType type, covariant TypeVariableType other) { |
| throw UnsupportedError( |
| "Type variables are not expected in constants: '$type' in '$_root'"); |
| } |
| |
| @override |
| int visitFunctionTypeVariable(covariant FunctionTypeVariable type, |
| covariant FunctionTypeVariable other) { |
| int leftIndex = _leftFunctionTypeVariables.indexOf(type); |
| int rightIndex = _rightFunctionTypeVariables.indexOf(other); |
| assert(leftIndex != -1); |
| assert(rightIndex != -1); |
| int r = leftIndex.compareTo(rightIndex); |
| if (r != 0) return r; |
| return compare(type.bound, other.bound); |
| } |
| |
| @override |
| int visitFunctionType( |
| covariant FunctionType type, covariant FunctionType other) { |
| int oldLeftLength = _leftFunctionTypeVariables.length; |
| int oldRightLength = _rightFunctionTypeVariables.length; |
| _leftFunctionTypeVariables.addAll(type.typeVariables); |
| _rightFunctionTypeVariables.addAll(other.typeVariables); |
| try { |
| int r = _compareTypeArguments(type.parameterTypes, other.parameterTypes); |
| if (r != 0) return r; |
| r = _compareTypeArguments( |
| type.optionalParameterTypes, other.optionalParameterTypes); |
| if (r != 0) return r; |
| r = _ConstantOrdering.compareLists((String a, String b) => a.compareTo(b), |
| type.namedParameters, other.namedParameters); |
| if (r != 0) return r; |
| r = _compareTypeArguments( |
| type.namedParameterTypes, other.namedParameterTypes); |
| if (r != 0) return r; |
| return compare(type.returnType, other.returnType); |
| } finally { |
| _leftFunctionTypeVariables.removeRange( |
| oldLeftLength, _leftFunctionTypeVariables.length); |
| _rightFunctionTypeVariables.removeRange( |
| oldRightLength, _rightFunctionTypeVariables.length); |
| } |
| } |
| |
| @override |
| int visitInterfaceType( |
| covariant InterfaceType type, covariant InterfaceType other) { |
| int r = _constantOrdering.compareClasses(type.element, other.element); |
| if (r != 0) return r; |
| return _compareTypeArguments(type.typeArguments, other.typeArguments); |
| } |
| |
| @override |
| int visitDynamicType( |
| covariant DynamicType type, covariant DynamicType other) { |
| throw UnsupportedError('Unreachable'); |
| } |
| |
| @override |
| int visitErasedType(covariant ErasedType type, covariant ErasedType other) { |
| throw UnsupportedError('Unreachable'); |
| } |
| |
| @override |
| int visitAnyType(covariant AnyType type, covariant AnyType other) { |
| throw UnsupportedError('Unreachable'); |
| } |
| |
| int _compareTypeArguments( |
| List<DartType> aArguments, List<DartType> bArguments) { |
| return _ConstantOrdering.compareLists(compare, aArguments, bArguments); |
| } |
| } |