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]. |