| Index: pkg/compiler/lib/src/serialization/equivalence.dart
|
| diff --git a/pkg/compiler/lib/src/serialization/equivalence.dart b/pkg/compiler/lib/src/serialization/equivalence.dart
|
| index 6cb175cc6d9227bdce9430d57531fb3b4ca226c4..a99b984d911077e73e868aa3ee188c7940f22673 100644
|
| --- a/pkg/compiler/lib/src/serialization/equivalence.dart
|
| +++ b/pkg/compiler/lib/src/serialization/equivalence.dart
|
| @@ -10,6 +10,7 @@ import '../closure.dart';
|
| import '../common.dart';
|
| import '../common/resolution.dart';
|
| import '../constants/expressions.dart';
|
| +import '../constants/values.dart';
|
| import '../dart_types.dart';
|
| import '../elements/elements.dart';
|
| import '../elements/visitor.dart';
|
| @@ -63,6 +64,31 @@ bool areSetsEquivalent(Iterable set1, Iterable set2,
|
| return remaining.isEmpty;
|
| }
|
|
|
| +/// Returns `true` if the content of [map1] and [map2] is equivalent using
|
| +/// [keyEquivalence] and [valueEquivalence] to determine key/value equivalence.
|
| +bool areMapsEquivalent(Map map1, Map map2,
|
| + [bool keyEquivalence(a, b) = equality,
|
| + bool valueEquivalence(a, b) = equality]) {
|
| + Set remaining = map2.keys.toSet();
|
| + for (var key1 in map1.keys) {
|
| + bool found = false;
|
| + for (var key2 in map2.keys) {
|
| + if (keyEquivalence(key2, key2)) {
|
| + found = true;
|
| + remaining.remove(key2);
|
| + if (!valueEquivalence(map1[key1], map2[key2])) {
|
| + return false;
|
| + }
|
| + break;
|
| + }
|
| + }
|
| + if (!found) {
|
| + return false;
|
| + }
|
| + }
|
| + return remaining.isEmpty;
|
| +}
|
| +
|
| /// Returns `true` if elements [a] and [b] are equivalent.
|
| bool areElementsEquivalent(Element a, Element b) {
|
| if (identical(a, b)) return true;
|
| @@ -77,13 +103,20 @@ bool areTypesEquivalent(DartType a, DartType b) {
|
| return const TypeEquivalence().visit(a, b);
|
| }
|
|
|
| -/// Returns `true` if constants [a] and [b] are equivalent.
|
| +/// Returns `true` if constants [exp1] and [exp2] are equivalent.
|
| bool areConstantsEquivalent(ConstantExpression exp1, ConstantExpression exp2) {
|
| if (identical(exp1, exp2)) return true;
|
| if (exp1 == null || exp2 == null) return false;
|
| return const ConstantEquivalence().visit(exp1, exp2);
|
| }
|
|
|
| +/// Returns `true` if constant values [value1] and [value2] are equivalent.
|
| +bool areConstantValuesEquivalent(ConstantValue value1, ConstantValue value2) {
|
| + if (identical(value1, value2)) return true;
|
| + if (value1 == null || value2 == null) return false;
|
| + return const ConstantValueEquivalence().visit(value1, value2);
|
| +}
|
| +
|
| /// Returns `true` if the lists of elements, [a] and [b], are equivalent.
|
| bool areElementListsEquivalent(List<Element> a, List<Element> b) {
|
| return areListsEquivalent(a, b, areElementsEquivalent);
|
| @@ -100,6 +133,12 @@ bool areConstantListsEquivalent(
|
| return areListsEquivalent(a, b, areConstantsEquivalent);
|
| }
|
|
|
| +/// Returns `true` if the lists of constant values, [a] and [b], are equivalent.
|
| +bool areConstantValueListsEquivalent(
|
| + List<ConstantValue> a, List<ConstantValue> b) {
|
| + return areListsEquivalent(a, b, areConstantValuesEquivalent);
|
| +}
|
| +
|
| /// Returns `true` if the selectors [a] and [b] are equivalent.
|
| bool areSelectorsEquivalent(Selector a, Selector b) {
|
| if (identical(a, b)) return true;
|
| @@ -307,6 +346,12 @@ class TestStrategy {
|
| return areSetsEquivalent(set1, set2, elementEquivalence);
|
| }
|
|
|
| + bool testMaps(var object1, var object2, String property, Map map1, Map map2,
|
| + [bool keyEquivalence(a, b) = equality,
|
| + bool valueEquivalence(a, b) = equality]) {
|
| + return areMapsEquivalent(map1, map2, keyEquivalence, valueEquivalence);
|
| + }
|
| +
|
| bool testElements(Object object1, Object object2, String property,
|
| Element element1, Element element2) {
|
| return areElementsEquivalent(element1, element2);
|
| @@ -322,6 +367,11 @@ class TestStrategy {
|
| return areConstantsEquivalent(exp1, exp2);
|
| }
|
|
|
| + bool testConstantValues(Object object1, Object object2, String property,
|
| + ConstantValue value1, ConstantValue value2) {
|
| + return areConstantValuesEquivalent(value1, value2);
|
| + }
|
| +
|
| bool testTypeLists(Object object1, Object object2, String property,
|
| List<DartType> list1, List<DartType> list2) {
|
| return areTypeListsEquivalent(list1, list2);
|
| @@ -332,6 +382,11 @@ class TestStrategy {
|
| return areConstantListsEquivalent(list1, list2);
|
| }
|
|
|
| + bool testConstantValueLists(Object object1, Object object2, String property,
|
| + List<ConstantValue> list1, List<ConstantValue> list2) {
|
| + return areConstantValueListsEquivalent(list1, list2);
|
| + }
|
| +
|
| bool testNodes(
|
| Object object1, Object object2, String property, Node node1, Node node2) {
|
| return areNodesEquivalent(node1, node2);
|
| @@ -780,9 +835,130 @@ class ConstantEquivalence
|
| @override
|
| bool visitDeferred(
|
| DeferredConstantExpression exp1, DeferredConstantExpression exp2) {
|
| - // TODO(johnniwinther): Implement this.
|
| + return strategy.testElements(
|
| + exp1, exp2, 'prefix', exp1.prefix, exp2.prefix) &&
|
| + strategy.testConstants(
|
| + exp1, exp2, 'expression', exp1.expression, exp2.expression);
|
| + }
|
| +}
|
| +
|
| +/// Visitor that checks for structural equivalence of [ConstantValue]s.
|
| +class ConstantValueEquivalence
|
| + implements ConstantValueVisitor<bool, ConstantValue> {
|
| + final TestStrategy strategy;
|
| +
|
| + const ConstantValueEquivalence([this.strategy = const TestStrategy()]);
|
| +
|
| + bool visit(ConstantValue value1, ConstantValue value2) {
|
| + if (identical(value1, value2)) return true;
|
| + return strategy.test(value1, value2, 'kind', value1.kind, value2.kind) &&
|
| + value1.accept(this, value2);
|
| + }
|
| +
|
| + @override
|
| + bool visitConstructed(
|
| + ConstructedConstantValue value1, ConstructedConstantValue value2) {
|
| + return strategy.testTypes(
|
| + value1, value2, 'type', value1.type, value2.type) &&
|
| + strategy.testMaps(
|
| + value1,
|
| + value2,
|
| + 'fields',
|
| + value1.fields,
|
| + value2.fields,
|
| + areElementsEquivalent,
|
| + (a, b) => strategy.testConstantValues(
|
| + value1, value2, 'fields.values', a, b));
|
| + }
|
| +
|
| + @override
|
| + bool visitFunction(
|
| + FunctionConstantValue value1, FunctionConstantValue value2) {
|
| + return strategy.testElements(
|
| + value1, value2, 'element', value1.element, value2.element);
|
| + }
|
| +
|
| + @override
|
| + bool visitList(ListConstantValue value1, ListConstantValue value2) {
|
| + return strategy.testTypes(
|
| + value1, value2, 'type', value1.type, value2.type) &&
|
| + strategy.testConstantValueLists(
|
| + value1, value2, 'entries', value1.entries, value2.entries);
|
| + }
|
| +
|
| + @override
|
| + bool visitMap(MapConstantValue value1, MapConstantValue value2) {
|
| + return strategy.testTypes(
|
| + value1, value2, 'type', value1.type, value2.type) &&
|
| + strategy.testConstantValueLists(
|
| + value1, value2, 'keys', value1.keys, value2.keys) &&
|
| + strategy.testConstantValueLists(
|
| + value1, value2, 'values', value1.values, value2.values);
|
| + }
|
| +
|
| + @override
|
| + bool visitType(TypeConstantValue value1, TypeConstantValue value2) {
|
| + return strategy.testTypes(value1, value2, 'type', value1.type, value2.type);
|
| + }
|
| +
|
| + @override
|
| + bool visitBool(BoolConstantValue value1, BoolConstantValue value2) {
|
| + return strategy.test(value1, value2, 'primitiveValue',
|
| + value1.primitiveValue, value2.primitiveValue);
|
| + }
|
| +
|
| + @override
|
| + bool visitDouble(DoubleConstantValue value1, DoubleConstantValue value2) {
|
| + return strategy.test(value1, value2, 'primitiveValue',
|
| + value1.primitiveValue, value2.primitiveValue);
|
| + }
|
| +
|
| + @override
|
| + bool visitInt(IntConstantValue value1, IntConstantValue value2) {
|
| + return strategy.test(value1, value2, 'primitiveValue',
|
| + value1.primitiveValue, value2.primitiveValue);
|
| + }
|
| +
|
| + @override
|
| + bool visitNull(NullConstantValue value1, NullConstantValue value2) {
|
| return true;
|
| }
|
| +
|
| + @override
|
| + bool visitString(StringConstantValue value1, StringConstantValue value2) {
|
| + return strategy.test(value1, value2, 'primitiveValue',
|
| + value1.primitiveValue, value2.primitiveValue);
|
| + }
|
| +
|
| + @override
|
| + bool visitDeferred(
|
| + DeferredConstantValue value1, DeferredConstantValue value2) {
|
| + return strategy.testElements(
|
| + value1, value2, 'prefix', value1.prefix, value2.prefix) &&
|
| + strategy.testConstantValues(
|
| + value1, value2, 'referenced', value1.referenced, value2.referenced);
|
| + }
|
| +
|
| + @override
|
| + bool visitNonConstant(NonConstantValue value1, NonConstantValue value2) {
|
| + return true;
|
| + }
|
| +
|
| + @override
|
| + bool visitSynthetic(
|
| + SyntheticConstantValue value1, SyntheticConstantValue value2) {
|
| + return strategy.test(
|
| + value1, value2, 'payload', value1.payload, value2.payload) &&
|
| + strategy.test(
|
| + value1, value2, 'valueKind', value1.valueKind, value2.valueKind);
|
| + }
|
| +
|
| + @override
|
| + bool visitInterceptor(
|
| + InterceptorConstantValue value1, InterceptorConstantValue value2) {
|
| + return strategy.testTypes(value1, value2, 'dispatchedType',
|
| + value1.dispatchedType, value2.dispatchedType);
|
| + }
|
| }
|
|
|
| /// Tests the equivalence of [impact1] and [impact2] using [strategy].
|
|
|