blob: 3103cb2e04da853af282de82497ebb8436beb335 [file] [log] [blame]
// 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;
late _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<T>(int compare(T a, T b), List<T> 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;
// TODO(48820): Should unit be non-nullable?
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);
}
}