| // Copyright (c) 2016, 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. |
| |
| /// Functions for asserting equivalence across serialization. |
| |
| library dart2js.serialization.equivalence; |
| |
| import '../closure.dart'; |
| import '../common/resolution.dart'; |
| import '../constants/expressions.dart'; |
| import '../constants/values.dart'; |
| import '../elements/resolution_types.dart'; |
| import '../elements/elements.dart'; |
| import '../elements/entities.dart'; |
| import '../elements/modelx.dart'; |
| import '../elements/jumps.dart'; |
| import '../elements/names.dart'; |
| import '../elements/types.dart'; |
| import '../elements/visitor.dart'; |
| import '../js_backend/backend_serialization.dart' |
| show NativeBehaviorSerialization; |
| import '../native/native.dart' show NativeBehavior; |
| import '../resolution/access_semantics.dart'; |
| import '../resolution/send_structure.dart'; |
| import '../resolution/tree_elements.dart'; |
| import 'package:front_end/src/fasta/scanner.dart'; |
| import '../tree/nodes.dart'; |
| import '../universe/selector.dart'; |
| import '../universe/feature.dart'; |
| import '../universe/use.dart'; |
| import '../util/util.dart'; |
| import 'resolved_ast_serialization.dart'; |
| |
| typedef bool Equivalence<E>(E a, E b, {TestStrategy strategy}); |
| |
| /// Equality based equivalence function. |
| bool equality(a, b) => a == b; |
| |
| /// Returns `true` if the elements in [a] and [b] are pair-wise equivalent |
| /// according to [elementEquivalence]. |
| bool areListsEquivalent<T>(List<T> a, List<T> b, |
| [bool elementEquivalence(T a, T b) = equality]) { |
| if (a.length != b.length) return false; |
| for (int i = 0; i < a.length && i < b.length; i++) { |
| if (!elementEquivalence(a[i], b[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /// Returns `true` if the elements in [a] and [b] are equivalent as sets using |
| /// [elementEquivalence] to determine element equivalence. |
| bool areSetsEquivalent<E>(Iterable<E> set1, Iterable<E> set2, |
| [bool elementEquivalence(E a, E b) = equality]) { |
| Set<E> remaining = set2.toSet(); |
| for (dynamic element1 in set1) { |
| bool found = false; |
| for (dynamic element2 in set2) { |
| if (elementEquivalence(element1, element2)) { |
| found = true; |
| remaining.remove(element2); |
| break; |
| } |
| } |
| if (!found) { |
| return false; |
| } |
| } |
| 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<K, V>(Map<K, V> map1, Map<K, V> map2, |
| [bool keyEquivalence(K a, K b) = equality, |
| bool valueEquivalence(V a, V b) = equality]) { |
| Set remaining = map2.keys.toSet(); |
| for (dynamic key1 in map1.keys) { |
| bool found = false; |
| for (dynamic key2 in map2.keys) { |
| if (keyEquivalence(key1, 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, {TestStrategy strategy}) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| return new ElementIdentityEquivalence(strategy ?? const TestStrategy()) |
| .visit(a, b); |
| } |
| |
| bool areEntitiesEquivalent(Entity a, Entity b, {TestStrategy strategy}) { |
| return areElementsEquivalent(a, b, strategy: strategy); |
| } |
| |
| /// Returns `true` if types [a] and [b] are equivalent. |
| bool areTypesEquivalent(DartType a, DartType b, {TestStrategy strategy}) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| return new TypeEquivalence(strategy ?? const TestStrategy()).visit(a, b); |
| } |
| |
| /// Returns `true` if constants [exp1] and [exp2] are equivalent. |
| bool areConstantsEquivalent(ConstantExpression exp1, ConstantExpression exp2, |
| {TestStrategy strategy}) { |
| if (identical(exp1, exp2)) return true; |
| if (exp1 == null || exp2 == null) return false; |
| return new ConstantEquivalence(strategy ?? const TestStrategy()) |
| .visit(exp1, exp2); |
| } |
| |
| /// Returns `true` if constant values [value1] and [value2] are equivalent. |
| bool areConstantValuesEquivalent(ConstantValue value1, ConstantValue value2, |
| {TestStrategy strategy}) { |
| if (identical(value1, value2)) return true; |
| if (value1 == null || value2 == null) return false; |
| return new ConstantValueEquivalence(strategy ?? const TestStrategy()) |
| .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); |
| } |
| |
| /// Returns `true` if the lists of types, [a] and [b], are equivalent. |
| bool areTypeListsEquivalent( |
| List<ResolutionDartType> a, List<ResolutionDartType> b) { |
| return areListsEquivalent(a, b, areTypesEquivalent); |
| } |
| |
| /// Returns `true` if the lists of constants, [a] and [b], are equivalent. |
| bool areConstantListsEquivalent( |
| List<ConstantExpression> a, List<ConstantExpression> b) { |
| 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, |
| {TestStrategy strategy: const TestStrategy()}) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| return a.kind == b.kind && |
| a.callStructure == b.callStructure && |
| areNamesEquivalent(a.memberName, b.memberName, strategy: strategy); |
| } |
| |
| /// Returns `true` if the names [a] and [b] are equivalent. |
| bool areNamesEquivalent(Name a, Name b, |
| {TestStrategy strategy: const TestStrategy()}) { |
| return a.text == b.text && |
| a.isSetter == b.isSetter && |
| strategy.testElements(a, b, 'library', a.library, b.library); |
| } |
| |
| /// Returns `true` if the dynamic uses [a] and [b] are equivalent. |
| bool areDynamicUsesEquivalent(DynamicUse a, DynamicUse b, |
| {TestStrategy strategy: const TestStrategy()}) { |
| return areSelectorsEquivalent(a.selector, b.selector, strategy: strategy); |
| } |
| |
| /// Returns `true` if the static uses [a] and [b] are equivalent. |
| bool areStaticUsesEquivalent(StaticUse a, StaticUse b, |
| {TestStrategy strategy: const TestStrategy()}) { |
| return a.kind == b.kind && |
| strategy.testElements(a, b, 'element', a.element, b.element); |
| } |
| |
| /// Returns `true` if the type uses [a] and [b] are equivalent. |
| bool areTypeUsesEquivalent(TypeUse a, TypeUse b, |
| {TestStrategy strategy: const TestStrategy()}) { |
| return a.kind == b.kind && strategy.testTypes(a, b, 'type', a.type, b.type); |
| } |
| |
| /// Returns `true` if the list literal uses [a] and [b] are equivalent. |
| bool areListLiteralUsesEquivalent(ListLiteralUse a, ListLiteralUse b, |
| {TestStrategy strategy: const TestStrategy()}) { |
| return strategy.testTypes(a, b, 'type', a.type, b.type) && |
| a.isConstant == b.isConstant && |
| a.isEmpty == b.isEmpty; |
| } |
| |
| /// Returns `true` if the map literal uses [a] and [b] are equivalent. |
| bool areMapLiteralUsesEquivalent(MapLiteralUse a, MapLiteralUse b, |
| {TestStrategy strategy: const TestStrategy()}) { |
| return strategy.testTypes(a, b, 'type', a.type, b.type) && |
| a.isConstant == b.isConstant && |
| a.isEmpty == b.isEmpty; |
| } |
| |
| /// Returns `true` if the access semantics [a] and [b] are equivalent. |
| bool areAccessSemanticsEquivalent(AccessSemantics a, AccessSemantics b) { |
| if (a.kind != b.kind) return false; |
| switch (a.kind) { |
| case AccessKind.EXPRESSION: |
| case AccessKind.THIS: |
| // No additional properties. |
| return true; |
| case AccessKind.THIS_PROPERTY: |
| case AccessKind.DYNAMIC_PROPERTY: |
| case AccessKind.CONDITIONAL_DYNAMIC_PROPERTY: |
| return areNamesEquivalent(a.name, b.name); |
| case AccessKind.CLASS_TYPE_LITERAL: |
| case AccessKind.TYPEDEF_TYPE_LITERAL: |
| case AccessKind.DYNAMIC_TYPE_LITERAL: |
| return areConstantsEquivalent(a.constant, b.constant); |
| case AccessKind.LOCAL_FUNCTION: |
| case AccessKind.LOCAL_VARIABLE: |
| case AccessKind.FINAL_LOCAL_VARIABLE: |
| case AccessKind.PARAMETER: |
| case AccessKind.FINAL_PARAMETER: |
| case AccessKind.STATIC_FIELD: |
| case AccessKind.FINAL_STATIC_FIELD: |
| case AccessKind.STATIC_METHOD: |
| case AccessKind.STATIC_GETTER: |
| case AccessKind.STATIC_SETTER: |
| case AccessKind.TOPLEVEL_FIELD: |
| case AccessKind.FINAL_TOPLEVEL_FIELD: |
| case AccessKind.TOPLEVEL_METHOD: |
| case AccessKind.TOPLEVEL_GETTER: |
| case AccessKind.TOPLEVEL_SETTER: |
| case AccessKind.SUPER_FIELD: |
| case AccessKind.SUPER_FINAL_FIELD: |
| case AccessKind.SUPER_METHOD: |
| case AccessKind.SUPER_GETTER: |
| case AccessKind.SUPER_SETTER: |
| case AccessKind.TYPE_PARAMETER_TYPE_LITERAL: |
| case AccessKind.UNRESOLVED: |
| case AccessKind.UNRESOLVED_SUPER: |
| case AccessKind.INVALID: |
| return areElementsEquivalent(a.element, b.element); |
| case AccessKind.COMPOUND: |
| CompoundAccessSemantics compoundAccess1 = a; |
| CompoundAccessSemantics compoundAccess2 = b; |
| return compoundAccess1.compoundAccessKind == |
| compoundAccess2.compoundAccessKind && |
| areElementsEquivalent( |
| compoundAccess1.getter, compoundAccess2.getter) && |
| areElementsEquivalent(compoundAccess1.setter, compoundAccess2.setter); |
| case AccessKind.CONSTANT: |
| default: |
| throw new UnsupportedError('Unsupported access kind: ${a.kind}'); |
| } |
| } |
| |
| /// Returns `true` if the send structures [a] and [b] are equivalent. |
| bool areSendStructuresEquivalent(SendStructure a, SendStructure b) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| if (a.kind != b.kind) return false; |
| |
| dynamic ad = a; |
| dynamic bd = b; |
| switch (a.kind) { |
| case SendStructureKind.IF_NULL: |
| case SendStructureKind.LOGICAL_AND: |
| case SendStructureKind.LOGICAL_OR: |
| case SendStructureKind.NOT: |
| case SendStructureKind.INVALID_UNARY: |
| case SendStructureKind.INVALID_BINARY: |
| // No additional properties. |
| return true; |
| |
| case SendStructureKind.IS: |
| case SendStructureKind.IS_NOT: |
| case SendStructureKind.AS: |
| return areTypesEquivalent(ad.type, bd.type); |
| |
| case SendStructureKind.INVOKE: |
| case SendStructureKind.INCOMPATIBLE_INVOKE: |
| if (!areSelectorsEquivalent(ad.selector, bd.selector)) return false; |
| continue semantics; |
| |
| case SendStructureKind.UNARY: |
| case SendStructureKind.BINARY: |
| case SendStructureKind.PREFIX: |
| case SendStructureKind.POSTFIX: |
| case SendStructureKind.INDEX_PREFIX: |
| case SendStructureKind.INDEX_POSTFIX: |
| case SendStructureKind.COMPOUND: |
| case SendStructureKind.COMPOUND_INDEX_SET: |
| if (ad.operator != bd.operator) return false; |
| continue semantics; |
| |
| case SendStructureKind.DEFERRED_PREFIX: |
| return areElementsEquivalent(ad.prefix, bd.prefix) && |
| areSendStructuresEquivalent(ad.sendStructure, bd.sendStructure); |
| |
| semantics: |
| case SendStructureKind.GET: |
| case SendStructureKind.SET: |
| case SendStructureKind.INDEX: |
| case SendStructureKind.INDEX_SET: |
| case SendStructureKind.EQUALS: |
| case SendStructureKind.NOT_EQUALS: |
| case SendStructureKind.SET_IF_NULL: |
| case SendStructureKind.INDEX_SET_IF_NULL: |
| return areAccessSemanticsEquivalent(ad.semantics, bd.semantics); |
| } |
| throw new UnsupportedError('Unexpected send structures $a vs $b'); |
| } |
| |
| /// Returns `true` if the new structures [a] and [b] are equivalent. |
| bool areNewStructuresEquivalent(NewStructure a, NewStructure b) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| if (a.kind != b.kind) return false; |
| |
| dynamic ad = a; |
| dynamic bd = b; |
| switch (a.kind) { |
| case NewStructureKind.NEW_INVOKE: |
| return ad.semantics.kind == bd.semantics.kind && |
| areElementsEquivalent(ad.semantics.element, bd.semantics.element) && |
| areTypesEquivalent(ad.semantics.type, bd.semantics.type) && |
| areSelectorsEquivalent(ad.selector, bd.selector); |
| case NewStructureKind.CONST_INVOKE: |
| return ad.constantInvokeKind == bd.constantInvokeKind && |
| areConstantsEquivalent(ad.constant, bd.constant); |
| case NewStructureKind.LATE_CONST: |
| default: |
| throw new UnsupportedError('Unsupported NewStructure kind ${a.kind}.'); |
| } |
| } |
| |
| /// Returns `true` if nodes [a] and [b] are equivalent. |
| bool areNodesEquivalent(Node node1, Node node2) { |
| if (identical(node1, node2)) return true; |
| if (node1 == null || node2 == null) return false; |
| return node1.accept1(const NodeEquivalenceVisitor(), node2); |
| } |
| |
| /// Strategy for testing equivalence. |
| /// |
| /// Use this strategy to determine equivalence without failing on inequivalence. |
| class TestStrategy { |
| final Equivalence<Entity> elementEquivalence; |
| final Equivalence<DartType> typeEquivalence; |
| final Equivalence<ConstantExpression> constantEquivalence; |
| final Equivalence<ConstantValue> constantValueEquivalence; |
| |
| const TestStrategy( |
| {this.elementEquivalence: areEntitiesEquivalent, |
| this.typeEquivalence: areTypesEquivalent, |
| this.constantEquivalence: areConstantsEquivalent, |
| this.constantValueEquivalence: areConstantValuesEquivalent}); |
| |
| /// An equivalence [TestStrategy] that doesn't throw on inequivalence. |
| TestStrategy get testOnly => this; |
| |
| bool test<T>( |
| dynamic object1, dynamic object2, String property, T value1, T value2, |
| [bool equivalence(T a, T b) = equality]) { |
| return equivalence(value1, value2); |
| } |
| |
| bool testLists( |
| Object object1, Object object2, String property, List list1, List list2, |
| [bool elementEquivalence(a, b) = equality]) { |
| return areListsEquivalent(list1, list2, elementEquivalence); |
| } |
| |
| bool testSets<E>(dynamic object1, dynamic object2, String property, |
| Iterable<E> set1, Iterable<E> set2, |
| [bool elementEquivalence(E a, E b) = equality]) { |
| return areSetsEquivalent(set1, set2, elementEquivalence); |
| } |
| |
| bool testMaps<K, V>(dynamic object1, dynamic object2, String property, |
| Map<K, V> map1, Map<K, V> map2, |
| [bool keyEquivalence(K a, K b) = equality, |
| bool valueEquivalence(V a, V b) = equality]) { |
| return areMapsEquivalent(map1, map2, keyEquivalence, valueEquivalence); |
| } |
| |
| bool testElements(Object object1, Object object2, String property, |
| Entity element1, Entity element2) { |
| return test(object1, object2, property, element1, element2, |
| (a, b) => elementEquivalence(a, b, strategy: this)); |
| } |
| |
| bool testTypes(Object object1, Object object2, String property, |
| DartType type1, DartType type2) { |
| return test(object1, object2, property, type1, type2, |
| (a, b) => typeEquivalence(a, b, strategy: this)); |
| } |
| |
| bool testConstants(Object object1, Object object2, String property, |
| ConstantExpression exp1, ConstantExpression exp2) { |
| return test(object1, object2, property, exp1, exp2, |
| (a, b) => constantEquivalence(a, b, strategy: this)); |
| } |
| |
| bool testConstantValues(Object object1, Object object2, String property, |
| ConstantValue value1, ConstantValue value2) { |
| return test(object1, object2, property, value1, value2, |
| (a, b) => constantValueEquivalence(a, b, strategy: this)); |
| } |
| |
| bool testTypeLists(Object object1, Object object2, String property, |
| List<DartType> list1, List<DartType> list2) { |
| return testLists(object1, object2, property, list1, list2, |
| (a, b) => typeEquivalence(a, b, strategy: this)); |
| } |
| |
| bool testConstantLists(Object object1, Object object2, String property, |
| List<ConstantExpression> list1, List<ConstantExpression> list2) { |
| return testLists(object1, object2, property, list1, list2, |
| (a, b) => constantEquivalence(a, b, strategy: this)); |
| } |
| |
| bool testConstantValueLists(Object object1, Object object2, String property, |
| List<ConstantValue> list1, List<ConstantValue> list2) { |
| return testLists(object1, object2, property, list1, list2, |
| (a, b) => constantValueEquivalence(a, b, strategy: this)); |
| } |
| |
| bool testNodes( |
| Object object1, Object object2, String property, Node node1, Node node2) { |
| return areNodesEquivalent(node1, node2); |
| } |
| } |
| |
| /// Visitor that checks for equivalence of [Element]s. |
| class ElementIdentityEquivalence extends BaseElementVisitor<bool, Element> { |
| final TestStrategy strategy; |
| |
| const ElementIdentityEquivalence([this.strategy = const TestStrategy()]); |
| |
| bool visit(Element element1, Element element2) { |
| if (element1 == null && element2 == null) { |
| return true; |
| } else if (element1 == null || element2 == null) { |
| return false; |
| } |
| element1 = element1.declaration; |
| element2 = element2.declaration; |
| if (element1 == element2) { |
| return true; |
| } |
| return strategy.test( |
| element1, element2, 'kind', element1.kind, element2.kind) && |
| element1.accept(this, element2); |
| } |
| |
| @override |
| bool visitElement(Element e, Element arg) { |
| throw new UnsupportedError("Unsupported element $e"); |
| } |
| |
| @override |
| bool visitLibraryElement( |
| LibraryElement element1, covariant LibraryElement element2) { |
| return strategy.test(element1, element2, 'canonicalUri', |
| element1.canonicalUri, element2.canonicalUri); |
| } |
| |
| @override |
| bool visitCompilationUnitElement(CompilationUnitElement element1, |
| covariant CompilationUnitElement element2) { |
| return strategy.test(element1, element2, 'script.resourceUri', |
| element1.script.resourceUri, element2.script.resourceUri) && |
| visit(element1.library, element2.library); |
| } |
| |
| @override |
| bool visitClassElement( |
| ClassElement element1, covariant ClassElement element2) { |
| if (!strategy.test( |
| element1, |
| element2, |
| 'isUnnamedMixinApplication', |
| element1.isUnnamedMixinApplication, |
| element2.isUnnamedMixinApplication)) { |
| return false; |
| } |
| if (element1.isUnnamedMixinApplication) { |
| MixinApplicationElement mixin1 = element1; |
| MixinApplicationElement mixin2 = element2; |
| return strategy.testElements( |
| mixin1, mixin2, 'subclass', mixin1.subclass, mixin2.subclass) && |
| // Using the [mixinType] is more precise but requires the test to |
| // handle self references: The identity of a type variable is based on |
| // its type declaration and if [mixin1] is generic the [mixinType] |
| // will contain the type variables declared by [mixin1], i.e. |
| // `abstract class Mixin<T> implements MixinType<T> {}` |
| strategy.testElements( |
| mixin1, mixin2, 'mixin', mixin1.mixin, mixin2.mixin); |
| } else { |
| return strategy.test( |
| element1, element2, 'name', element1.name, element2.name) && |
| visit(element1.library, element2.library); |
| } |
| } |
| |
| bool checkMembers(Element element1, covariant Element element2) { |
| if (!strategy.test( |
| element1, element2, 'name', element1.name, element2.name)) { |
| return false; |
| } |
| if (element1.enclosingClass != null || element2.enclosingClass != null) { |
| return visit(element1.enclosingClass, element2.enclosingClass); |
| } else { |
| return visit(element1.library, element2.library); |
| } |
| } |
| |
| @override |
| bool visitFieldElement( |
| FieldElement element1, covariant FieldElement element2) { |
| return checkMembers(element1, element2); |
| } |
| |
| @override |
| bool visitBoxFieldElement( |
| BoxFieldElement element1, covariant BoxFieldElement element2) { |
| return element1.box.name == element2.box.name && |
| visit(element1.box.executableContext, element2.box.executableContext) && |
| visit(element1.variableElement, element2.variableElement); |
| } |
| |
| @override |
| bool visitConstructorElement( |
| ConstructorElement element1, covariant ConstructorElement element2) { |
| return checkMembers(element1, element2); |
| } |
| |
| @override |
| bool visitMethodElement( |
| covariant MethodElement element1, covariant MethodElement element2) { |
| return checkMembers(element1, element2); |
| } |
| |
| @override |
| bool visitGetterElement( |
| GetterElement element1, covariant GetterElement element2) { |
| return checkMembers(element1, element2); |
| } |
| |
| @override |
| bool visitSetterElement( |
| SetterElement element1, covariant SetterElement element2) { |
| return checkMembers(element1, element2); |
| } |
| |
| @override |
| bool visitLocalFunctionElement( |
| LocalFunctionElement element1, covariant LocalFunctionElement element2) { |
| // TODO(johnniwinther): Define an equivalence on locals. |
| MemberElement member1 = element1.memberContext; |
| MemberElement member2 = element2.memberContext; |
| return strategy.test( |
| element1, element2, 'name', element1.name, element2.name) && |
| checkMembers(member1, member2); |
| } |
| |
| @override |
| bool visitLocalVariableElement( |
| LocalVariableElement element1, covariant LocalVariableElement element2) { |
| // TODO(johnniwinther): Define an equivalence on locals. |
| return strategy.test( |
| element1, element2, 'name', element1.name, element2.name) && |
| checkMembers(element1.memberContext, element2.memberContext); |
| } |
| |
| bool visitAbstractFieldElement( |
| AbstractFieldElement element1, covariant AbstractFieldElement element2) { |
| return checkMembers(element1, element2); |
| } |
| |
| @override |
| bool visitTypeVariableElement( |
| TypeVariableElement element1, covariant TypeVariableElement element2) { |
| return strategy.test( |
| element1, element2, 'name', element1.name, element2.name) && |
| visit(element1.typeDeclaration, element2.typeDeclaration); |
| } |
| |
| @override |
| bool visitTypedefElement( |
| TypedefElement element1, covariant TypedefElement element2) { |
| return strategy.test( |
| element1, element2, 'name', element1.name, element2.name) && |
| visit(element1.library, element2.library); |
| } |
| |
| @override |
| bool visitParameterElement( |
| ParameterElement element1, covariant ParameterElement element2) { |
| return strategy.test( |
| element1, element2, 'name', element1.name, element2.name) && |
| visit(element1.functionDeclaration, element2.functionDeclaration); |
| } |
| |
| @override |
| bool visitImportElement( |
| ImportElement element1, covariant ImportElement element2) { |
| return visit(element1.importedLibrary, element2.importedLibrary) && |
| visit(element1.library, element2.library); |
| } |
| |
| @override |
| bool visitExportElement( |
| ExportElement element1, covariant ExportElement element2) { |
| return visit(element1.exportedLibrary, element2.exportedLibrary) && |
| visit(element1.library, element2.library); |
| } |
| |
| @override |
| bool visitPrefixElement( |
| PrefixElement element1, covariant PrefixElement element2) { |
| return strategy.test( |
| element1, element2, 'name', element1.name, element2.name) && |
| visit(element1.library, element2.library); |
| } |
| |
| @override |
| bool visitErroneousElement( |
| ErroneousElement element1, covariant ErroneousElement element2) { |
| return strategy.test(element1, element2, 'messageKind', |
| element1.messageKind, element2.messageKind); |
| } |
| |
| @override |
| bool visitWarnOnUseElement( |
| WarnOnUseElement element1, covariant WarnOnUseElement element2) { |
| return strategy.testElements(element1, element2, 'wrappedElement', |
| element1.wrappedElement, element2.wrappedElement); |
| } |
| } |
| |
| /// Visitor that checks for equivalence of [ResolutionDartType]s. |
| class TypeEquivalence |
| implements ResolutionDartTypeVisitor<bool, ResolutionDartType> { |
| final TestStrategy strategy; |
| |
| const TypeEquivalence([this.strategy = const TestStrategy()]); |
| |
| bool visit( |
| covariant ResolutionDartType type1, covariant ResolutionDartType type2) { |
| return strategy.test(type1, type2, 'kind', type1.kind, type2.kind) && |
| type1.accept(this, type2); |
| } |
| |
| @override |
| bool visitDynamicType(covariant ResolutionDynamicType type, |
| covariant ResolutionDynamicType other) => |
| true; |
| |
| @override |
| bool visitFunctionType(covariant ResolutionFunctionType type, |
| covariant ResolutionFunctionType other) { |
| return strategy.testTypeLists(type, other, 'parameterTypes', |
| type.parameterTypes, other.parameterTypes) && |
| strategy.testTypeLists(type, other, 'optionalParameterTypes', |
| type.optionalParameterTypes, other.optionalParameterTypes) && |
| strategy.testTypeLists(type, other, 'namedParameterTypes', |
| type.namedParameterTypes, other.namedParameterTypes) && |
| strategy.testLists(type, other, 'namedParameters', type.namedParameters, |
| other.namedParameters); |
| } |
| |
| bool visitGenericType(GenericType type, GenericType other) { |
| return strategy.testElements( |
| type, other, 'element', type.element, other.element) && |
| strategy.testTypeLists(type, other, 'typeArguments', type.typeArguments, |
| other.typeArguments); |
| } |
| |
| @override |
| bool visitMalformedType(MalformedType type, covariant MalformedType other) => |
| true; |
| |
| @override |
| bool visitTypeVariableType(covariant ResolutionTypeVariableType type, |
| covariant ResolutionTypeVariableType other) { |
| return strategy.testElements( |
| type, other, 'element', type.element, other.element) && |
| strategy.test(type, other, 'is MethodTypeVariableType', |
| type is MethodTypeVariableType, other is MethodTypeVariableType); |
| } |
| |
| @override |
| bool visitVoidType(covariant ResolutionVoidType type, |
| covariant ResolutionVoidType argument) => |
| true; |
| |
| @override |
| bool visitInterfaceType(covariant ResolutionInterfaceType type, |
| covariant ResolutionInterfaceType other) { |
| return visitGenericType(type, other); |
| } |
| |
| @override |
| bool visitTypedefType(covariant ResolutionTypedefType type, |
| covariant ResolutionTypedefType other) { |
| return visitGenericType(type, other); |
| } |
| |
| @override |
| bool visitFunctionTypeVariable( |
| FunctionTypeVariable type, ResolutionDartType other) { |
| throw new UnsupportedError("Function type variables are not supported."); |
| } |
| |
| @override |
| bool visitFutureOrType( |
| covariant FutureOrType type, covariant ResolutionDartType other) { |
| throw new UnsupportedError("FutureOr is not supported."); |
| } |
| } |
| |
| /// Visitor that checks for structural equivalence of [ConstantExpression]s. |
| class ConstantEquivalence |
| implements ConstantExpressionVisitor<bool, ConstantExpression> { |
| final TestStrategy strategy; |
| |
| const ConstantEquivalence([this.strategy = const TestStrategy()]); |
| |
| @override |
| bool visit(ConstantExpression exp1, covariant ConstantExpression exp2) { |
| if (identical(exp1, exp2)) return true; |
| return strategy.test(exp1, exp2, 'kind', exp1.kind, exp2.kind) && |
| exp1.accept(this, exp2); |
| } |
| |
| @override |
| bool visitAs(AsConstantExpression exp1, covariant AsConstantExpression exp2) { |
| return strategy.test( |
| exp1, exp2, 'expression', exp1.expression, exp2.expression) && |
| strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type); |
| } |
| |
| @override |
| bool visitBinary( |
| BinaryConstantExpression exp1, covariant BinaryConstantExpression exp2) { |
| return strategy.test( |
| exp1, exp2, 'operator', exp1.operator, exp2.operator) && |
| strategy.testConstants(exp1, exp2, 'left', exp1.left, exp2.left) && |
| strategy.testConstants(exp1, exp2, 'right', exp1.right, exp2.right); |
| } |
| |
| @override |
| bool visitConcatenate(ConcatenateConstantExpression exp1, |
| covariant ConcatenateConstantExpression exp2) { |
| return strategy.testConstantLists( |
| exp1, exp2, 'expressions', exp1.expressions, exp2.expressions); |
| } |
| |
| @override |
| bool visitConditional(ConditionalConstantExpression exp1, |
| covariant ConditionalConstantExpression exp2) { |
| return strategy.testConstants( |
| exp1, exp2, 'condition', exp1.condition, exp2.condition) && |
| strategy.testConstants( |
| exp1, exp2, 'trueExp', exp1.trueExp, exp2.trueExp) && |
| strategy.testConstants( |
| exp1, exp2, 'falseExp', exp1.falseExp, exp2.falseExp); |
| } |
| |
| @override |
| bool visitConstructed(ConstructedConstantExpression exp1, |
| covariant ConstructedConstantExpression exp2) { |
| return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type) && |
| strategy.testElements(exp1, exp2, 'target', exp1.target, exp2.target) && |
| strategy.testConstantLists( |
| exp1, exp2, 'arguments', exp1.arguments, exp2.arguments) && |
| strategy.test(exp1, exp2, 'callStructure', exp1.callStructure, |
| exp2.callStructure); |
| } |
| |
| @override |
| bool visitFunction(FunctionConstantExpression exp1, |
| covariant FunctionConstantExpression exp2) { |
| return strategy.testElements( |
| exp1, exp2, 'element', exp1.element, exp2.element); |
| } |
| |
| @override |
| bool visitIdentical(IdenticalConstantExpression exp1, |
| covariant IdenticalConstantExpression exp2) { |
| return strategy.testConstants(exp1, exp2, 'left', exp1.left, exp2.left) && |
| strategy.testConstants(exp1, exp2, 'right', exp1.right, exp2.right); |
| } |
| |
| @override |
| bool visitList( |
| ListConstantExpression exp1, covariant ListConstantExpression exp2) { |
| return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type) && |
| strategy.testConstantLists( |
| exp1, exp2, 'values', exp1.values, exp2.values); |
| } |
| |
| @override |
| bool visitMap( |
| MapConstantExpression exp1, covariant MapConstantExpression exp2) { |
| return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type) && |
| strategy.testConstantLists(exp1, exp2, 'keys', exp1.keys, exp2.keys) && |
| strategy.testConstantLists( |
| exp1, exp2, 'values', exp1.values, exp2.values); |
| } |
| |
| @override |
| bool visitNamed( |
| NamedArgumentReference exp1, covariant NamedArgumentReference exp2) { |
| return strategy.test(exp1, exp2, 'name', exp1.name, exp2.name); |
| } |
| |
| @override |
| bool visitPositional(PositionalArgumentReference exp1, |
| covariant PositionalArgumentReference exp2) { |
| return strategy.test(exp1, exp2, 'index', exp1.index, exp2.index); |
| } |
| |
| @override |
| bool visitSymbol( |
| SymbolConstantExpression exp1, covariant SymbolConstantExpression exp2) { |
| // TODO(johnniwinther): Handle private names. Currently not even supported |
| // in resolution. |
| return strategy.test(exp1, exp2, 'name', exp1.name, exp2.name); |
| } |
| |
| @override |
| bool visitType( |
| TypeConstantExpression exp1, covariant TypeConstantExpression exp2) { |
| return strategy.testTypes(exp1, exp2, 'type', exp1.type, exp2.type); |
| } |
| |
| @override |
| bool visitUnary( |
| UnaryConstantExpression exp1, covariant UnaryConstantExpression exp2) { |
| return strategy.test( |
| exp1, exp2, 'operator', exp1.operator, exp2.operator) && |
| strategy.testConstants( |
| exp1, exp2, 'expression', exp1.expression, exp2.expression); |
| } |
| |
| @override |
| bool visitField( |
| FieldConstantExpression exp1, covariant FieldConstantExpression exp2) { |
| return strategy.testElements( |
| exp1, exp2, 'element', exp1.element, exp2.element); |
| } |
| |
| @override |
| bool visitLocalVariable(LocalVariableConstantExpression exp1, |
| covariant LocalVariableConstantExpression exp2) { |
| return strategy.testElements( |
| exp1, exp2, 'element', exp1.element, exp2.element); |
| } |
| |
| @override |
| bool visitBool( |
| BoolConstantExpression exp1, covariant BoolConstantExpression exp2) { |
| return strategy.test( |
| exp1, exp2, 'boolValue', exp1.boolValue, exp2.boolValue); |
| } |
| |
| @override |
| bool visitDouble( |
| DoubleConstantExpression exp1, covariant DoubleConstantExpression exp2) { |
| return strategy.test( |
| exp1, exp2, 'doubleValue', exp1.doubleValue, exp2.doubleValue); |
| } |
| |
| @override |
| bool visitInt( |
| IntConstantExpression exp1, covariant IntConstantExpression exp2) { |
| return strategy.test(exp1, exp2, 'intValue', exp1.intValue, exp2.intValue); |
| } |
| |
| @override |
| bool visitNull( |
| NullConstantExpression exp1, covariant NullConstantExpression exp2) { |
| return true; |
| } |
| |
| @override |
| bool visitString( |
| StringConstantExpression exp1, covariant StringConstantExpression exp2) { |
| return strategy.test( |
| exp1, exp2, 'stringValue', exp1.stringValue, exp2.stringValue); |
| } |
| |
| @override |
| bool visitBoolFromEnvironment(BoolFromEnvironmentConstantExpression exp1, |
| covariant BoolFromEnvironmentConstantExpression exp2) { |
| return strategy.testConstants(exp1, exp2, 'name', exp1.name, exp2.name) && |
| strategy.testConstants( |
| exp1, exp2, 'defaultValue', exp1.defaultValue, exp2.defaultValue); |
| } |
| |
| @override |
| bool visitIntFromEnvironment(IntFromEnvironmentConstantExpression exp1, |
| covariant IntFromEnvironmentConstantExpression exp2) { |
| return strategy.testConstants(exp1, exp2, 'name', exp1.name, exp2.name) && |
| strategy.testConstants( |
| exp1, exp2, 'defaultValue', exp1.defaultValue, exp2.defaultValue); |
| } |
| |
| @override |
| bool visitStringFromEnvironment(StringFromEnvironmentConstantExpression exp1, |
| covariant StringFromEnvironmentConstantExpression exp2) { |
| return strategy.testConstants(exp1, exp2, 'name', exp1.name, exp2.name) && |
| strategy.testConstants( |
| exp1, exp2, 'defaultValue', exp1.defaultValue, exp2.defaultValue); |
| } |
| |
| @override |
| bool visitStringLength(StringLengthConstantExpression exp1, |
| covariant StringLengthConstantExpression exp2) { |
| return strategy.testConstants( |
| exp1, exp2, 'expression', exp1.expression, exp2.expression); |
| } |
| |
| @override |
| bool visitDeferred(DeferredConstantExpression exp1, |
| covariant DeferredConstantExpression exp2) { |
| return strategy.testElements( |
| exp1, exp2, 'import', exp1.import, exp2.import) && |
| strategy.testConstants( |
| exp1, exp2, 'expression', exp1.expression, exp2.expression); |
| } |
| |
| @override |
| bool visitAssert( |
| AssertConstantExpression exp1, covariant AssertConstantExpression exp2) { |
| return strategy.testConstants( |
| exp1, exp2, 'condition', exp1.condition, exp2.condition) && |
| strategy.testConstants( |
| exp1, exp2, 'message', exp1.message, exp2.message); |
| } |
| } |
| |
| /// 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, covariant 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, |
| covariant ConstructedConstantValue value2) { |
| return strategy.testTypes( |
| value1, value2, 'type', value1.type, value2.type) && |
| strategy.testMaps( |
| value1, |
| value2, |
| 'fields', |
| value1.fields, |
| value2.fields, |
| strategy.elementEquivalence, |
| (a, b) => strategy.testConstantValues( |
| value1, value2, 'fields.values', a, b)); |
| } |
| |
| @override |
| bool visitFunction( |
| FunctionConstantValue value1, covariant FunctionConstantValue value2) { |
| return strategy.testElements( |
| value1, value2, 'element', value1.element, value2.element); |
| } |
| |
| @override |
| bool visitList(ListConstantValue value1, covariant 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, covariant 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, covariant TypeConstantValue value2) { |
| return strategy.testTypes(value1, value2, 'type', value1.type, value2.type); |
| } |
| |
| @override |
| bool visitBool(BoolConstantValue value1, covariant BoolConstantValue value2) { |
| return strategy.test( |
| value1, value2, 'boolValue', value1.boolValue, value2.boolValue); |
| } |
| |
| @override |
| bool visitDouble( |
| DoubleConstantValue value1, covariant DoubleConstantValue value2) { |
| return strategy.test( |
| value1, value2, 'doubleValue', value1.doubleValue, value2.doubleValue); |
| } |
| |
| @override |
| bool visitInt(IntConstantValue value1, covariant IntConstantValue value2) { |
| return strategy.test( |
| value1, value2, 'intValue', value1.intValue, value2.intValue); |
| } |
| |
| @override |
| bool visitNull(NullConstantValue value1, covariant NullConstantValue value2) { |
| return true; |
| } |
| |
| @override |
| bool visitString( |
| StringConstantValue value1, covariant StringConstantValue value2) { |
| return strategy.test( |
| value1, value2, 'stringValue', value1.stringValue, value2.stringValue); |
| } |
| |
| @override |
| bool visitDeferred( |
| DeferredConstantValue value1, covariant DeferredConstantValue value2) { |
| return strategy.testElements( |
| value1, value2, 'prefix', value1.import, value2.import) && |
| strategy.testConstantValues( |
| value1, value2, 'referenced', value1.referenced, value2.referenced); |
| } |
| |
| @override |
| bool visitDeferredGlobal(DeferredGlobalConstantValue value1, |
| covariant DeferredGlobalConstantValue value2) { |
| return strategy.testSets( |
| value1, |
| value2, |
| 'imports', |
| value1.unit.importsForTesting, |
| value2.unit.importsForTesting, |
| strategy.elementEquivalence) && |
| strategy.testConstantValues( |
| value1, value2, 'referenced', value1.referenced, value2.referenced); |
| } |
| |
| @override |
| bool visitNonConstant( |
| NonConstantValue value1, covariant NonConstantValue value2) { |
| return true; |
| } |
| |
| @override |
| bool visitSynthetic( |
| SyntheticConstantValue value1, covariant 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, |
| covariant InterceptorConstantValue value2) { |
| return strategy.testElements(value1, value2, 'cls', value1.cls, value2.cls); |
| } |
| } |
| |
| /// Tests the equivalence of [impact1] and [impact2] using [strategy]. |
| bool testResolutionImpactEquivalence( |
| ResolutionImpact impact1, ResolutionImpact impact2, |
| {TestStrategy strategy = const TestStrategy(), |
| Iterable<ConstantExpression> filterConstantLiterals( |
| Iterable<ConstantExpression> constants, |
| {bool fromFirstImpact})}) { |
| return strategy.testSets(impact1, impact2, 'constSymbolNames', |
| impact1.constSymbolNames, impact2.constSymbolNames) && |
| strategy.testSets( |
| impact1, |
| impact2, |
| 'constantLiterals', |
| filterConstantLiterals != null |
| ? filterConstantLiterals(impact1.constantLiterals, |
| fromFirstImpact: true) |
| : impact1.constantLiterals, |
| filterConstantLiterals != null |
| ? filterConstantLiterals(impact2.constantLiterals, |
| fromFirstImpact: false) |
| : impact2.constantLiterals, |
| areConstantsEquivalent) && |
| strategy.testSets( |
| impact1, |
| impact2, |
| 'dynamicUses', |
| impact1.dynamicUses, |
| impact2.dynamicUses, |
| (a, b) => |
| areDynamicUsesEquivalent(a, b, strategy: strategy.testOnly)) && |
| strategy.testSets( |
| impact1, impact2, 'features', impact1.features, impact2.features) && |
| strategy.testSets( |
| impact1, |
| impact2, |
| 'listLiterals', |
| impact1.listLiterals, |
| impact2.listLiterals, |
| (a, b) => areListLiteralUsesEquivalent(a, b, |
| strategy: strategy.testOnly)) && |
| strategy.testSets( |
| impact1, |
| impact2, |
| 'mapLiterals', |
| impact1.mapLiterals, |
| impact2.mapLiterals, |
| (a, b) => |
| areMapLiteralUsesEquivalent(a, b, strategy: strategy.testOnly)) && |
| strategy.testSets( |
| impact1, |
| impact2, |
| 'staticUses', |
| impact1.staticUses, |
| impact2.staticUses, |
| (a, b) => |
| areStaticUsesEquivalent(a, b, strategy: strategy.testOnly)) && |
| strategy.testSets( |
| impact1, |
| impact2, |
| 'typeUses', |
| impact1.typeUses, |
| impact2.typeUses, |
| (a, b) => areTypeUsesEquivalent(a, b, strategy: strategy.testOnly)) && |
| strategy.testSets( |
| impact1, |
| impact2, |
| 'nativeData', |
| impact1.nativeData, |
| impact2.nativeData, |
| (a, b) => testNativeBehavior(a, b, strategy: strategy)); |
| } |
| |
| /// Tests the equivalence of [resolvedAst1] and [resolvedAst2] using [strategy]. |
| bool testResolvedAstEquivalence( |
| ResolvedAst resolvedAst1, ResolvedAst resolvedAst2, |
| [TestStrategy strategy = const TestStrategy()]) { |
| if (!strategy.test(resolvedAst1, resolvedAst1, 'kind', resolvedAst1.kind, |
| resolvedAst2.kind)) { |
| return false; |
| } |
| if (resolvedAst1.kind != ResolvedAstKind.PARSED) { |
| // Nothing more to check. |
| return true; |
| } |
| bool result = strategy.testElements(resolvedAst1, resolvedAst2, 'element', |
| resolvedAst1.element, resolvedAst2.element) && |
| strategy.testNodes(resolvedAst1, resolvedAst2, 'node', resolvedAst1.node, |
| resolvedAst2.node) && |
| strategy.testNodes(resolvedAst1, resolvedAst2, 'body', resolvedAst1.body, |
| resolvedAst2.body) && |
| testTreeElementsEquivalence(resolvedAst1, resolvedAst2, strategy) && |
| strategy.test(resolvedAst1, resolvedAst2, 'sourceUri', |
| resolvedAst1.sourceUri, resolvedAst2.sourceUri); |
| if (resolvedAst1.element is FunctionElement) { |
| FunctionElement element1 = resolvedAst1.element; |
| FunctionElement element2 = resolvedAst2.element; |
| for (int index = 0; index < element1.parameters.length; index++) { |
| dynamic parameter1 = element1.parameters[index]; |
| dynamic parameter2 = element2.parameters[index]; |
| result = result && |
| strategy.testNodes(parameter1, parameter2, 'node', |
| parameter1.implementation.node, parameter2.implementation.node) && |
| strategy.testNodes( |
| parameter1, |
| parameter2, |
| 'initializer', |
| parameter1.implementation.initializer, |
| parameter2.implementation.initializer); |
| } |
| } |
| return result; |
| } |
| |
| /// Tests the equivalence of the data stored in the [TreeElements] of |
| /// [resolvedAst1] and [resolvedAst2] using [strategy]. |
| bool testTreeElementsEquivalence( |
| ResolvedAst resolvedAst1, ResolvedAst resolvedAst2, |
| [TestStrategy strategy = const TestStrategy()]) { |
| AstIndexComputer indices1 = new AstIndexComputer(); |
| resolvedAst1.node.accept(indices1); |
| AstIndexComputer indices2 = new AstIndexComputer(); |
| resolvedAst2.node.accept(indices2); |
| |
| TreeElements elements1 = resolvedAst1.elements; |
| TreeElements elements2 = resolvedAst2.elements; |
| |
| TreeElementsEquivalenceVisitor visitor = new TreeElementsEquivalenceVisitor( |
| indices1, indices2, elements1, elements2, strategy); |
| resolvedAst1.node.accept(visitor); |
| if (visitor.success) { |
| return strategy.test(elements1, elements2, 'containsTryStatement', |
| elements1.containsTryStatement, elements2.containsTryStatement); |
| } |
| return false; |
| } |
| |
| bool testNativeBehavior(NativeBehavior a, NativeBehavior b, |
| {TestStrategy strategy = const TestStrategy()}) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| return strategy.test( |
| a, b, 'codeTemplateText', a.codeTemplateText, b.codeTemplateText) && |
| strategy.test(a, b, 'isAllocation', a.isAllocation, b.isAllocation) && |
| strategy.test(a, b, 'sideEffects', a.sideEffects, b.sideEffects) && |
| strategy.test(a, b, 'throwBehavior', a.throwBehavior, b.throwBehavior) && |
| strategy.testTypeLists( |
| a, |
| b, |
| 'dartTypesReturned', |
| NativeBehaviorSerialization.filterDartTypes(a.typesReturned), |
| NativeBehaviorSerialization.filterDartTypes(b.typesReturned)) && |
| strategy.testLists( |
| a, |
| b, |
| 'specialTypesReturned', |
| NativeBehaviorSerialization.filterSpecialTypes(a.typesReturned), |
| NativeBehaviorSerialization.filterSpecialTypes(b.typesReturned)) && |
| strategy.testTypeLists( |
| a, |
| b, |
| 'dartTypesInstantiated', |
| NativeBehaviorSerialization.filterDartTypes(a.typesInstantiated), |
| NativeBehaviorSerialization.filterDartTypes(b.typesInstantiated)) && |
| strategy.testLists( |
| a, |
| b, |
| 'specialTypesInstantiated', |
| NativeBehaviorSerialization.filterSpecialTypes(a.typesInstantiated), |
| NativeBehaviorSerialization |
| .filterSpecialTypes(b.typesInstantiated)) && |
| strategy.test(a, b, 'useGvn', a.useGvn, b.useGvn); |
| } |
| |
| /// Visitor that checks the equivalence of [TreeElements] data. |
| class TreeElementsEquivalenceVisitor extends Visitor { |
| final TestStrategy strategy; |
| final AstIndexComputer indices1; |
| final AstIndexComputer indices2; |
| final TreeElements elements1; |
| final TreeElements elements2; |
| bool success = true; |
| |
| TreeElementsEquivalenceVisitor( |
| this.indices1, this.indices2, this.elements1, this.elements2, |
| [this.strategy = const TestStrategy()]); |
| |
| bool testJumpTargets( |
| Node node1, Node node2, String property, JumpTarget a, JumpTarget b) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| return strategy.test( |
| a, b, 'nestingLevel', a.nestingLevel, b.nestingLevel) && |
| strategy.test(a, b, 'statement', indices1.nodeIndices[a.statement], |
| indices2.nodeIndices[b.statement]) && |
| strategy.test( |
| a, b, 'isBreakTarget', a.isBreakTarget, b.isBreakTarget) && |
| strategy.test( |
| a, b, 'isContinueTarget', a.isContinueTarget, b.isContinueTarget) && |
| strategy.testLists(a, b, 'labels', a.labels.toList(), b.labels.toList(), |
| (a, b) { |
| return indices1.nodeIndices[a.label] == indices2.nodeIndices[b.label]; |
| }); |
| } |
| |
| bool testLabelDefinitions(Node node1, Node node2, String property, |
| LabelDefinitionX a, LabelDefinitionX b) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| return strategy.test(a, b, 'label', indices1.nodeIndices[a.label], |
| indices2.nodeIndices[b.label]) && |
| strategy.test(a, b, 'labelName', a.labelName, b.labelName) && |
| strategy.test(a, b, 'target', indices1.nodeIndices[a.target.statement], |
| indices2.nodeIndices[b.target.statement]) && |
| strategy.test( |
| a, b, 'isBreakTarget', a.isBreakTarget, b.isBreakTarget) && |
| strategy.test( |
| a, b, 'isContinueTarget', a.isContinueTarget, b.isContinueTarget); |
| } |
| |
| bool testNativeData(Node node1, Node node2, String property, a, b) { |
| if (identical(a, b)) return true; |
| if (a == null || b == null) return false; |
| if (a is NativeBehavior && b is NativeBehavior) { |
| return testNativeBehavior(a, b, strategy: strategy); |
| } |
| return true; |
| } |
| |
| visitNode(Node node1) { |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| Node node2 = indices2.nodeList[index]; |
| success = strategy.testElements( |
| node1, node2, '[$index]', elements1[node1], elements2[node2]) && |
| strategy.testTypes(node1, node2, 'getType($index)', |
| elements1.getType(node1), elements2.getType(node2)) && |
| strategy.test( |
| node1, |
| node2, |
| 'getSelector($index)', |
| elements1.getSelector(node1), |
| elements2.getSelector(node2), |
| areSelectorsEquivalent) && |
| strategy.testConstants(node1, node2, 'getConstant($index)', |
| elements1.getConstant(node1), elements2.getConstant(node2)) && |
| strategy.testTypes(node1, node2, 'typesCache[$index]', |
| elements1.typesCache[node1], elements2.typesCache[node2]) && |
| testJumpTargets( |
| node1, |
| node2, |
| 'getTargetDefinition($index)', |
| elements1.getTargetDefinition(node1), |
| elements2.getTargetDefinition(node2)) && |
| testNativeData(node1, node2, 'getNativeData($index)', |
| elements1.getNativeData(node1), elements2.getNativeData(node2)); |
| |
| node1.visitChildren(this); |
| } |
| |
| @override |
| visitSend(Send node1) { |
| visitExpression(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| Send node2 = indices2.nodeList[index]; |
| success = strategy.test(node1, node2, 'isTypeLiteral($index)', |
| elements1.isTypeLiteral(node1), elements2.isTypeLiteral(node2)) && |
| strategy.testTypes( |
| node1, |
| node2, |
| 'getTypeLiteralType($index)', |
| elements1.getTypeLiteralType(node1), |
| elements2.getTypeLiteralType(node2)) && |
| strategy.test( |
| node1, |
| node2, |
| 'getSendStructure($index)', |
| elements1.getSendStructure(node1), |
| elements2.getSendStructure(node2), |
| areSendStructuresEquivalent); |
| } |
| |
| @override |
| visitNewExpression(NewExpression node1) { |
| visitExpression(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| NewExpression node2 = indices2.nodeList[index]; |
| success = strategy.test( |
| node1, |
| node2, |
| 'getNewStructure($index)', |
| elements1.getNewStructure(node1), |
| elements2.getNewStructure(node2), |
| areNewStructuresEquivalent); |
| } |
| |
| @override |
| visitSendSet(SendSet node1) { |
| visitSend(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| SendSet node2 = indices2.nodeList[index]; |
| success = strategy.test( |
| node1, |
| node2, |
| 'getGetterSelectorInComplexSendSet($index)', |
| elements1.getGetterSelectorInComplexSendSet(node1), |
| elements2.getGetterSelectorInComplexSendSet(node2), |
| areSelectorsEquivalent) && |
| strategy.test( |
| node1, |
| node2, |
| 'getOperatorSelectorInComplexSendSet($index)', |
| elements1.getOperatorSelectorInComplexSendSet(node1), |
| elements2.getOperatorSelectorInComplexSendSet(node2), |
| areSelectorsEquivalent); |
| } |
| |
| @override |
| visitFunctionExpression(FunctionExpression node1) { |
| visitNode(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| FunctionExpression node2 = indices2.nodeList[index]; |
| if (elements1[node1] is! FunctionElement) { |
| // [getFunctionDefinition] is currently stored in [] which doesn't always |
| // contain a [FunctionElement]. |
| return; |
| } |
| success = strategy.testElements( |
| node1, |
| node2, |
| 'getFunctionDefinition($index)', |
| elements1.getFunctionDefinition(node1), |
| elements2.getFunctionDefinition(node2)); |
| } |
| |
| @override |
| visitForIn(ForIn node1) { |
| visitLoop(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| ForIn node2 = indices2.nodeList[index]; |
| success = strategy.testElements(node1, node2, 'getForInVariable($index)', |
| elements1.getForInVariable(node1), elements2.getForInVariable(node2)); |
| } |
| |
| @override |
| visitRedirectingFactoryBody(RedirectingFactoryBody node1) { |
| visitStatement(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| RedirectingFactoryBody node2 = indices2.nodeList[index]; |
| success = strategy.testElements( |
| node1, |
| node2, |
| 'getRedirectingTargetConstructor($index)', |
| elements1.getRedirectingTargetConstructor(node1), |
| elements2.getRedirectingTargetConstructor(node2)); |
| } |
| |
| @override |
| visitGotoStatement(GotoStatement node1) { |
| visitStatement(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| GotoStatement node2 = indices2.nodeList[index]; |
| success = testJumpTargets(node1, node2, 'getTargetOf($index)', |
| elements1.getTargetOf(node1), elements2.getTargetOf(node2)); |
| if (!success) return; |
| if (node1.target == null && node2.target == null) { |
| return; |
| } |
| success = testLabelDefinitions(node1, node2, 'getTarget($index)', |
| elements1.getTargetLabel(node1), elements2.getTargetLabel(node2)); |
| } |
| |
| @override |
| visitLabel(Label node1) { |
| visitNode(node1); |
| if (!success) return; |
| int index = indices1.nodeIndices[node1]; |
| Label node2 = indices2.nodeList[index]; |
| success = testLabelDefinitions( |
| node1, |
| node2, |
| 'getLabelDefinition($index)', |
| elements1.getLabelDefinition(node1), |
| elements2.getLabelDefinition(node2)); |
| } |
| } |
| |
| class NodeEquivalenceVisitor implements Visitor1<bool, Node> { |
| final TestStrategy strategy; |
| |
| const NodeEquivalenceVisitor([this.strategy = const TestStrategy()]); |
| |
| bool testNodes(dynamic object1, dynamic object2, String property, Node node1, |
| Node node2) { |
| return strategy.test(object1, object2, property, node1, node2, |
| (Node n1, Node n2) { |
| if (n1 == n2) return true; |
| if (n1 == null || n2 == null) return false; |
| return n1.accept1(this, n2); |
| }); |
| } |
| |
| bool testNodeLists(dynamic object1, dynamic object2, String property, |
| Link<Node> list1, Link<Node> list2) { |
| return strategy.test(object1, object2, property, list1, list2, |
| (Link<Node> l1, Link<Node> l2) { |
| if (l1 == l2) return true; |
| if (l1 == null || l2 == null) return false; |
| while (l1.isNotEmpty && l2.isNotEmpty) { |
| if (!l1.head.accept1(this, l2.head)) { |
| return false; |
| } |
| l1 = l1.tail; |
| l2 = l2.tail; |
| } |
| return l1.isEmpty && l2.isEmpty; |
| }); |
| } |
| |
| bool testTokens(dynamic object1, dynamic object2, String property, |
| Token token1, Token token2) { |
| return strategy.test(object1, object2, property, token1, token2, |
| (Token t1, Token t2) { |
| if (t1 == t2) return true; |
| if (t1 == null || t2 == null) return false; |
| return strategy.test( |
| t1, t2, 'charOffset', t1.charOffset, t2.charOffset) && |
| strategy.test(t1, t2, 'info', t1.type, t2.type) && |
| strategy.test(t1, t2, 'value', t1.lexeme, t2.lexeme); |
| }); |
| } |
| |
| @override |
| bool visitAssert(Assert node1, covariant Assert node2) { |
| return testTokens(node1, node2, 'assertToken', node1.assertToken, |
| node2.assertToken) && |
| testNodes( |
| node1, node2, 'condition', node1.condition, node2.condition) && |
| testNodes(node1, node2, 'message', node1.message, node2.message); |
| } |
| |
| @override |
| bool visitAsyncForIn(AsyncForIn node1, covariant AsyncForIn node2) { |
| return visitForIn(node1, node2) && |
| testTokens( |
| node1, node2, 'awaitToken', node1.awaitToken, node2.awaitToken); |
| } |
| |
| @override |
| bool visitAsyncModifier(AsyncModifier node1, covariant AsyncModifier node2) { |
| return testTokens( |
| node1, node2, 'asyncToken', node1.asyncToken, node2.asyncToken) && |
| testTokens(node1, node2, 'starToken', node1.starToken, node2.starToken); |
| } |
| |
| @override |
| bool visitAwait(Await node1, covariant Await node2) { |
| return testTokens( |
| node1, node2, 'awaitToken', node1.awaitToken, node2.awaitToken) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitBlock(Block node1, covariant Block node2) { |
| return testNodes( |
| node1, node2, 'statements', node1.statements, node2.statements); |
| } |
| |
| @override |
| bool visitBreakStatement( |
| BreakStatement node1, covariant BreakStatement node2) { |
| return testTokens(node1, node2, 'keywordToken', node1.keywordToken, |
| node2.keywordToken) && |
| testNodes(node1, node2, 'target', node1.target, node2.target); |
| } |
| |
| @override |
| bool visitCascade(Cascade node1, covariant Cascade node2) { |
| return testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitCascadeReceiver( |
| CascadeReceiver node1, covariant CascadeReceiver node2) { |
| return testTokens(node1, node2, 'cascadeOperator', node1.cascadeOperator, |
| node2.cascadeOperator) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitCaseMatch(CaseMatch node1, covariant CaseMatch node2) { |
| return testTokens(node1, node2, 'caseKeyword', node1.caseKeyword, |
| node2.caseKeyword) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitCatchBlock(CatchBlock node1, covariant CatchBlock node2) { |
| return testTokens(node1, node2, 'catchKeyword', node1.catchKeyword, |
| node2.catchKeyword) && |
| testTokens( |
| node1, node2, 'onKeyword', node1.onKeyword, node2.onKeyword) && |
| testNodes(node1, node2, 'type', node1.type, node2.type) && |
| testNodes(node1, node2, 'formals', node1.formals, node2.formals) && |
| testNodes(node1, node2, 'block', node1.block, node2.block); |
| } |
| |
| @override |
| bool visitClassNode(ClassNode node1, covariant ClassNode node2) { |
| return testTokens( |
| node1, node2, 'beginToken', node1.beginToken, node2.beginToken) && |
| testTokens(node1, node2, 'extendsKeyword', node1.extendsKeyword, |
| node2.extendsKeyword) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes( |
| node1, node2, 'modifiers', node1.modifiers, node2.modifiers) && |
| testNodes(node1, node2, 'name', node1.name, node2.name) && |
| testNodes( |
| node1, node2, 'superclass', node1.superclass, node2.superclass) && |
| testNodes( |
| node1, node2, 'interfaces', node1.interfaces, node2.interfaces) && |
| testNodes(node1, node2, 'typeParameters', node1.typeParameters, |
| node2.typeParameters) && |
| testNodes(node1, node2, 'body', node1.body, node2.body); |
| } |
| |
| @override |
| bool visitCombinator(Combinator node1, covariant Combinator node2) { |
| return testTokens(node1, node2, 'keywordToken', node1.keywordToken, |
| node2.keywordToken) && |
| testNodes( |
| node1, node2, 'identifiers', node1.identifiers, node2.identifiers); |
| } |
| |
| @override |
| bool visitConditional(Conditional node1, covariant Conditional node2) { |
| return testTokens(node1, node2, 'questionToken', node1.questionToken, |
| node2.questionToken) && |
| testTokens( |
| node1, node2, 'colonToken', node1.colonToken, node2.colonToken) && |
| testNodes( |
| node1, node2, 'condition', node1.condition, node2.condition) && |
| testNodes(node1, node2, 'thenExpression', node1.thenExpression, |
| node2.thenExpression) && |
| testNodes(node1, node2, 'elseExpression', node1.elseExpression, |
| node2.elseExpression); |
| } |
| |
| @override |
| bool visitConditionalUri( |
| ConditionalUri node1, covariant ConditionalUri node2) { |
| return testTokens(node1, node2, 'ifToken', node1.ifToken, node2.ifToken) && |
| testNodes(node1, node2, 'key', node1.key, node2.key) && |
| testNodes(node1, node2, 'value', node1.value, node2.value) && |
| testNodes(node1, node2, 'uri', node1.uri, node2.uri); |
| } |
| |
| @override |
| bool visitContinueStatement( |
| ContinueStatement node1, covariant ContinueStatement node2) { |
| return testTokens(node1, node2, 'keywordToken', node1.keywordToken, |
| node2.keywordToken) && |
| testNodes(node1, node2, 'target', node1.target, node2.target); |
| } |
| |
| @override |
| bool visitDoWhile(DoWhile node1, covariant DoWhile node2) { |
| return testTokens( |
| node1, node2, 'doKeyword', node1.doKeyword, node2.doKeyword) && |
| testTokens(node1, node2, 'whileKeyword', node1.whileKeyword, |
| node2.whileKeyword) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes( |
| node1, node2, 'condition', node1.condition, node2.condition) && |
| testNodes(node1, node2, 'body', node1.body, node2.body); |
| } |
| |
| @override |
| bool visitDottedName(DottedName node1, covariant DottedName node2) { |
| return testTokens(node1, node2, 'token', node1.token, node2.token) && |
| testNodes( |
| node1, node2, 'identifiers', node1.identifiers, node2.identifiers); |
| } |
| |
| @override |
| bool visitEmptyStatement( |
| EmptyStatement node1, covariant EmptyStatement node2) { |
| return testTokens(node1, node2, 'semicolonToken', node1.semicolonToken, |
| node2.semicolonToken); |
| } |
| |
| @override |
| bool visitEnum(Enum node1, covariant Enum node2) { |
| return testTokens( |
| node1, node2, 'enumToken', node1.enumToken, node2.enumToken) && |
| testNodes(node1, node2, 'name', node1.name, node2.name) && |
| testNodes(node1, node2, 'names', node1.names, node2.names); |
| } |
| |
| @override |
| bool visitExport(Export node1, covariant Export node2) { |
| return visitLibraryDependency(node1, node2) && |
| testTokens(node1, node2, 'exportKeyword', node1.exportKeyword, |
| node2.exportKeyword); |
| } |
| |
| @override |
| bool visitExpressionStatement( |
| ExpressionStatement node1, covariant ExpressionStatement node2) { |
| return testTokens( |
| node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitFor(For node1, covariant For node2) { |
| return testTokens( |
| node1, node2, 'forToken', node1.forToken, node2.forToken) && |
| testNodes(node1, node2, 'initializer', node1.initializer, |
| node2.initializer) && |
| testNodes(node1, node2, 'conditionStatement', node1.conditionStatement, |
| node2.conditionStatement) && |
| testNodes(node1, node2, 'update', node1.update, node2.update) && |
| testNodes(node1, node2, 'body', node1.body, node2.body); |
| } |
| |
| @override |
| bool visitForIn(ForIn node1, covariant ForIn node2) { |
| return testNodes( |
| node1, node2, 'condition', node1.condition, node2.condition) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression) && |
| testNodes(node1, node2, 'body', node1.body, node2.body) && |
| testNodes(node1, node2, 'declaredIdentifier', node1.declaredIdentifier, |
| node2.declaredIdentifier); |
| } |
| |
| @override |
| bool visitFunctionDeclaration( |
| FunctionDeclaration node1, covariant FunctionDeclaration node2) { |
| return testNodes(node1, node2, 'function', node1.function, node2.function); |
| } |
| |
| @override |
| bool visitFunctionExpression( |
| FunctionExpression node1, covariant FunctionExpression node2) { |
| return testTokens( |
| node1, node2, 'getOrSet', node1.getOrSet, node2.getOrSet) && |
| testNodes(node1, node2, 'name', node1.name, node2.name) && |
| testNodes( |
| node1, node2, 'parameters', node1.parameters, node2.parameters) && |
| testNodes(node1, node2, 'body', node1.body, node2.body) && |
| testNodes( |
| node1, node2, 'returnType', node1.returnType, node2.returnType) && |
| testNodes( |
| node1, node2, 'modifiers', node1.modifiers, node2.modifiers) && |
| testNodes(node1, node2, 'initializers', node1.initializers, |
| node2.initializers) && |
| testNodes(node1, node2, 'asyncModifier', node1.asyncModifier, |
| node2.asyncModifier); |
| } |
| |
| @override |
| bool visitGotoStatement(GotoStatement node1, covariant GotoStatement node2) { |
| return testTokens(node1, node2, 'keywordToken', node1.keywordToken, |
| node2.keywordToken) && |
| testTokens(node1, node2, 'semicolonToken', node1.semicolonToken, |
| node2.semicolonToken) && |
| testNodes(node1, node2, 'target', node1.target, node2.target); |
| } |
| |
| @override |
| bool visitIdentifier(Identifier node1, covariant Identifier node2) { |
| return testTokens(node1, node2, 'token', node1.token, node2.token); |
| } |
| |
| @override |
| bool visitIf(If node1, covariant If node2) { |
| return testTokens(node1, node2, 'ifToken', node1.ifToken, node2.ifToken) && |
| testTokens( |
| node1, node2, 'elseToken', node1.elseToken, node2.elseToken) && |
| testNodes( |
| node1, node2, 'condition', node1.condition, node2.condition) && |
| testNodes(node1, node2, 'thenPart', node1.thenPart, node2.thenPart) && |
| testNodes(node1, node2, 'elsePart', node1.elsePart, node2.elsePart); |
| } |
| |
| @override |
| bool visitImport(Import node1, covariant Import node2) { |
| return visitLibraryDependency(node1, node2) && |
| testTokens(node1, node2, 'importKeyword', node1.importKeyword, |
| node2.importKeyword) && |
| testNodes(node1, node2, 'prefix', node1.prefix, node2.prefix) && |
| strategy.test( |
| node1, node2, 'isDeferred', node1.isDeferred, node2.isDeferred); |
| } |
| |
| @override |
| bool visitLabel(Label node1, covariant Label node2) { |
| return testTokens( |
| node1, node2, 'colonToken', node1.colonToken, node2.colonToken) && |
| testNodes( |
| node1, node2, 'identifier', node1.identifier, node2.identifier); |
| } |
| |
| @override |
| bool visitLabeledStatement( |
| LabeledStatement node1, covariant LabeledStatement node2) { |
| return testNodes(node1, node2, 'labels', node1.labels, node2.labels) && |
| testNodes(node1, node2, 'statement', node1.statement, node2.statement); |
| } |
| |
| @override |
| bool visitLibraryDependency( |
| LibraryDependency node1, covariant LibraryDependency node2) { |
| return visitLibraryTag(node1, node2) && |
| testNodes(node1, node2, 'uri', node1.uri, node2.uri) && |
| testNodes(node1, node2, 'conditionalUris', node1.conditionalUris, |
| node2.conditionalUris) && |
| testNodes( |
| node1, node2, 'combinators', node1.combinators, node2.combinators); |
| } |
| |
| @override |
| bool visitLibraryName(LibraryName node1, covariant LibraryName node2) { |
| return visitLibraryTag(node1, node2) && |
| testTokens(node1, node2, 'libraryKeyword', node1.libraryKeyword, |
| node2.libraryKeyword) && |
| testNodes(node1, node2, 'name', node1.name, node2.name); |
| } |
| |
| @override |
| bool visitLibraryTag(LibraryTag node1, covariant LibraryTag node2) { |
| // TODO(johnniwinther): Check metadata? |
| return true; |
| } |
| |
| @override |
| bool visitLiteral(Literal node1, covariant Literal node2) { |
| return testTokens(node1, node2, 'token', node1.token, node2.token); |
| } |
| |
| @override |
| bool visitLiteralBool(LiteralBool node1, covariant LiteralBool node2) { |
| return visitLiteral(node1, node2); |
| } |
| |
| @override |
| bool visitLiteralDouble(LiteralDouble node1, covariant LiteralDouble node2) { |
| return visitLiteral(node1, node2); |
| } |
| |
| @override |
| bool visitLiteralInt(LiteralInt node1, covariant LiteralInt node2) { |
| return visitLiteral(node1, node2); |
| } |
| |
| @override |
| bool visitLiteralList(LiteralList node1, covariant LiteralList node2) { |
| return testTokens(node1, node2, 'constKeyword', node1.constKeyword, |
| node2.constKeyword) && |
| testNodes(node1, node2, 'typeArguments', node1.typeArguments, |
| node2.typeArguments) && |
| testNodes(node1, node2, 'elements', node1.elements, node2.elements); |
| } |
| |
| @override |
| bool visitLiteralMap(LiteralMap node1, covariant LiteralMap node2) { |
| return testTokens(node1, node2, 'constKeyword', node1.constKeyword, |
| node2.constKeyword) && |
| testNodes(node1, node2, 'typeArguments', node1.typeArguments, |
| node2.typeArguments) && |
| testNodes(node1, node2, 'entries', node1.entries, node2.entries); |
| } |
| |
| @override |
| bool visitLiteralMapEntry( |
| LiteralMapEntry node1, covariant LiteralMapEntry node2) { |
| return testTokens( |
| node1, node2, 'colonToken', node1.colonToken, node2.colonToken) && |
| testNodes(node1, node2, 'key', node1.key, node2.key) && |
| testNodes(node1, node2, 'value', node1.value, node2.value); |
| } |
| |
| @override |
| bool visitLiteralNull(LiteralNull node1, covariant LiteralNull node2) { |
| return visitLiteral(node1, node2); |
| } |
| |
| @override |
| bool visitLiteralString(LiteralString node1, covariant LiteralString node2) { |
| return testTokens(node1, node2, 'token', node1.token, node2.token) && |
| strategy.test( |
| node1, node2, 'dartString', node1.dartString, node2.dartString); |
| } |
| |
| @override |
| bool visitLiteralSymbol(LiteralSymbol node1, covariant LiteralSymbol node2) { |
| return testTokens( |
| node1, node2, 'hashToken', node1.hashToken, node2.hashToken) && |
| testNodes( |
| node1, node2, 'identifiers', node1.identifiers, node2.identifiers); |
| } |
| |
| @override |
| bool visitLoop(Loop node1, covariant Loop node2) { |
| return testNodes( |
| node1, node2, 'condition', node1.condition, node2.condition) && |
| testNodes(node1, node2, 'body', node1.body, node2.body); |
| } |
| |
| @override |
| bool visitMetadata(Metadata node1, covariant Metadata node2) { |
| return testTokens(node1, node2, 'token', node1.token, node2.token) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitMixinApplication( |
| MixinApplication node1, covariant MixinApplication node2) { |
| return testNodes( |
| node1, node2, 'superclass', node1.superclass, node2.superclass) && |
| testNodes(node1, node2, 'mixins', node1.mixins, node2.mixins); |
| } |
| |
| @override |
| bool visitModifiers(Modifiers node1, covariant Modifiers node2) { |
| return strategy.test(node1, node2, 'flags', node1.flags, node2.flags) && |
| testNodes(node1, node2, 'nodes', node1.nodes, node2.nodes); |
| } |
| |
| @override |
| bool visitNamedArgument(NamedArgument node1, covariant NamedArgument node2) { |
| return testTokens( |
| node1, node2, 'colonToken', node1.colonToken, node2.colonToken) && |
| testNodes(node1, node2, 'name', node1.name, node2.name) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitNamedMixinApplication( |
| NamedMixinApplication node1, covariant NamedMixinApplication node2) { |
| return testTokens(node1, node2, 'classKeyword', node1.classKeyword, |
| node2.classKeyword) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes(node1, node2, 'name', node1.name, node2.name) && |
| testNodes(node1, node2, 'typeParameters', node1.typeParameters, |
| node2.typeParameters) && |
| testNodes( |
| node1, node2, 'modifiers', node1.modifiers, node2.modifiers) && |
| testNodes(node1, node2, 'mixinApplication', node1.mixinApplication, |
| node2.mixinApplication) && |
| testNodes( |
| node1, node2, 'interfaces', node1.interfaces, node2.interfaces); |
| } |
| |
| @override |
| bool visitNewExpression(NewExpression node1, covariant NewExpression node2) { |
| return testTokens( |
| node1, node2, 'newToken', node1.newToken, node2.newToken) && |
| testNodes(node1, node2, 'send', node1.send, node2.send); |
| } |
| |
| @override |
| bool visitNodeList(NodeList node1, covariant NodeList node2) { |
| return testTokens( |
| node1, node2, 'beginToken', node1.beginToken, node2.beginToken) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| strategy.test( |
| node1, node2, 'delimiter', node1.delimiter, node2.delimiter) && |
| testNodeLists(node1, node2, 'nodes', node1.nodes, node2.nodes); |
| } |
| |
| @override |
| bool visitOperator(Operator node1, covariant Operator node2) { |
| return visitIdentifier(node1, node2); |
| } |
| |
| @override |
| bool visitParenthesizedExpression( |
| ParenthesizedExpression node1, covariant ParenthesizedExpression node2) { |
| return testTokens( |
| node1, node2, 'beginToken', node1.beginToken, node2.beginToken) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitPart(Part node1, covariant Part node2) { |
| return visitLibraryTag(node1, node2) && |
| testTokens(node1, node2, 'partKeyword', node1.partKeyword, |
| node2.partKeyword) && |
| testNodes(node1, node2, 'uri', node1.uri, node2.uri); |
| } |
| |
| @override |
| bool visitPartOf(PartOf node1, covariant PartOf node2) { |
| // TODO(johnniwinther): Check metadata? |
| return testTokens(node1, node2, 'partKeyword', node1.partKeyword, |
| node2.partKeyword) && |
| testNodes(node1, node2, 'name', node1.name, node2.name); |
| } |
| |
| @override |
| bool visitPostfix(Postfix node1, covariant Postfix node2) { |
| return visitNodeList(node1, node2); |
| } |
| |
| @override |
| bool visitPrefix(Prefix node1, covariant Prefix node2) { |
| return visitNodeList(node1, node2); |
| } |
| |
| @override |
| bool visitRedirectingFactoryBody( |
| RedirectingFactoryBody node1, covariant RedirectingFactoryBody node2) { |
| return testTokens( |
| node1, node2, 'beginToken', node1.beginToken, node2.beginToken) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes(node1, node2, 'constructorReference', |
| node1.constructorReference, node2.constructorReference); |
| } |
| |
| @override |
| bool visitRethrow(Rethrow node1, covariant Rethrow node2) { |
| return testTokens( |
| node1, node2, 'throwToken', node1.throwToken, node2.throwToken) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken); |
| } |
| |
| @override |
| bool visitReturn(Return node1, covariant Return node2) { |
| return testTokens( |
| node1, node2, 'beginToken', node1.beginToken, node2.beginToken) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitSend(Send node1, covariant Send node2) { |
| return strategy.test(node1, node2, 'isConditional', node1.isConditional, |
| node2.isConditional) && |
| testNodes(node1, node2, 'receiver', node1.receiver, node2.receiver) && |
| testNodes(node1, node2, 'selector', node1.selector, node2.selector) && |
| testNodes(node1, node2, 'argumentsNode', node1.argumentsNode, |
| node2.argumentsNode); |
| } |
| |
| @override |
| bool visitSendSet(SendSet node1, covariant SendSet node2) { |
| return visitSend(node1, node2) && |
| testNodes(node1, node2, 'assignmentOperator', node1.assignmentOperator, |
| node2.assignmentOperator); |
| } |
| |
| @override |
| bool visitStringInterpolation( |
| StringInterpolation node1, covariant StringInterpolation node2) { |
| return testNodes(node1, node2, 'string', node1.string, node2.string) && |
| testNodes(node1, node2, 'parts', node1.parts, node2.parts); |
| } |
| |
| @override |
| bool visitStringInterpolationPart( |
| StringInterpolationPart node1, covariant StringInterpolationPart node2) { |
| return testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitStringJuxtaposition( |
| StringJuxtaposition node1, covariant StringJuxtaposition node2) { |
| return testNodes(node1, node2, 'first', node1.first, node2.first) && |
| testNodes(node1, node2, 'second', node1.second, node2.second); |
| } |
| |
| @override |
| bool visitSwitchCase(SwitchCase node1, covariant SwitchCase node2) { |
| return testTokens(node1, node2, 'defaultKeyword', node1.defaultKeyword, |
| node2.defaultKeyword) && |
| testTokens( |
| node1, node2, 'startToken', node1.startToken, node2.startToken) && |
| testNodes(node1, node2, 'labelsAndCases', node1.labelsAndCases, |
| node2.labelsAndCases) && |
| testNodes( |
| node1, node2, 'statements', node1.statements, node2.statements); |
| } |
| |
| @override |
| bool visitSwitchStatement( |
| SwitchStatement node1, covariant SwitchStatement node2) { |
| return testTokens(node1, node2, 'switchKeyword', node1.switchKeyword, |
| node2.switchKeyword) && |
| testNodes(node1, node2, 'parenthesizedExpression', |
| node1.parenthesizedExpression, node2.parenthesizedExpression) && |
| testNodes(node1, node2, 'cases', node1.cases, node2.cases); |
| } |
| |
| @override |
| bool visitSyncForIn(SyncForIn node1, covariant SyncForIn node2) { |
| return visitForIn(node1, node2); |
| } |
| |
| @override |
| bool visitThrow(Throw node1, covariant Throw node2) { |
| return testTokens( |
| node1, node2, 'throwToken', node1.throwToken, node2.throwToken) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitTryStatement(TryStatement node1, covariant TryStatement node2) { |
| return testTokens( |
| node1, node2, 'tryKeyword', node1.tryKeyword, node2.tryKeyword) && |
| testTokens(node1, node2, 'finallyKeyword', node1.finallyKeyword, |
| node2.finallyKeyword) && |
| testNodes(node1, node2, 'tryBlock', node1.tryBlock, node2.tryBlock) && |
| testNodes(node1, node2, 'catchBlocks', node1.catchBlocks, |
| node2.catchBlocks) && |
| testNodes(node1, node2, 'finallyBlock', node1.finallyBlock, |
| node2.finallyBlock); |
| } |
| |
| @override |
| bool visitNominalTypeAnnotation( |
| NominalTypeAnnotation node1, covariant NominalTypeAnnotation node2) { |
| return testNodes( |
| node1, node2, 'typeName', node1.typeName, node2.typeName) && |
| testNodes(node1, node2, 'typeArguments', node1.typeArguments, |
| node2.typeArguments); |
| } |
| |
| @override |
| bool visitFunctionTypeAnnotation( |
| FunctionTypeAnnotation node1, covariant FunctionTypeAnnotation node2) { |
| return testNodes( |
| node1, node2, 'returnType', node1.returnType, node2.returnType) && |
| testNodes(node1, node2, 'formals', node1.formals, node2.formals) && |
| testNodes(node1, node2, 'typeParameters', node1.typeParameters, |
| node2.typeParameters); |
| } |
| |
| @override |
| bool visitTypeVariable(TypeVariable node1, covariant TypeVariable node2) { |
| return testNodes(node1, node2, 'name', node1.name, node2.name) && |
| testNodes(node1, node2, 'bound', node1.bound, node2.bound); |
| } |
| |
| @override |
| bool visitTypedef(Typedef node1, covariant Typedef node2) { |
| return testTokens(node1, node2, 'typedefKeyword', node1.typedefKeyword, |
| node2.typedefKeyword) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes( |
| node1, node2, 'returnType', node1.returnType, node2.returnType) && |
| testNodes(node1, node2, 'name', node1.name, node2.name) && |
| testNodes(node1, node2, 'typeParameters', node1.templateParameters, |
| node2.templateParameters) && |
| testNodes(node1, node2, 'formals', node1.formals, node2.formals); |
| } |
| |
| @override |
| bool visitVariableDefinitions( |
| VariableDefinitions node1, covariant VariableDefinitions node2) { |
| return testNodes( |
| node1, node2, 'metadata', node1.metadata, node2.metadata) && |
| testNodes(node1, node2, 'type', node1.type, node2.type) && |
| testNodes( |
| node1, node2, 'modifiers', node1.modifiers, node2.modifiers) && |
| testNodes( |
| node1, node2, 'definitions', node1.definitions, node2.definitions); |
| } |
| |
| @override |
| bool visitWhile(While node1, covariant While node2) { |
| return testTokens(node1, node2, 'whileKeyword', node1.whileKeyword, |
| node2.whileKeyword) && |
| testNodes( |
| node1, node2, 'condition', node1.condition, node2.condition) && |
| testNodes(node1, node2, 'body', node1.body, node2.body); |
| } |
| |
| @override |
| bool visitYield(Yield node1, covariant Yield node2) { |
| return testTokens( |
| node1, node2, 'yieldToken', node1.yieldToken, node2.yieldToken) && |
| testTokens( |
| node1, node2, 'starToken', node1.starToken, node2.starToken) && |
| testTokens(node1, node2, 'endToken', node1.endToken, node2.endToken) && |
| testNodes( |
| node1, node2, 'expression', node1.expression, node2.expression); |
| } |
| |
| @override |
| bool visitNode(Node node1, covariant Node node2) { |
| throw new UnsupportedError('Unexpected nodes: $node1 <> $node2'); |
| } |
| |
| @override |
| bool visitExpression(Expression node1, covariant Expression node2) { |
| throw new UnsupportedError('Unexpected nodes: $node1 <> $node2'); |
| } |
| |
| @override |
| bool visitStatement(Statement node1, covariant Statement node2) { |
| throw new UnsupportedError('Unexpected nodes: $node1 <> $node2'); |
| } |
| |
| @override |
| bool visitStringNode(StringNode node1, covariant StringNode node2) { |
| throw new UnsupportedError('Unexpected nodes: $node1 <> $node2'); |
| } |
| |
| @override |
| bool visitTypeAnnotation( |
| TypeAnnotation node1, covariant TypeAnnotation node2) { |
| throw new UnsupportedError('Unexpected nodes: $node1 <> $node2'); |
| } |
| } |
| |
| bool areMetadataAnnotationsEquivalent( |
| MetadataAnnotation metadata1, MetadataAnnotation metadata2) { |
| if (metadata1 == metadata2) return true; |
| if (metadata1 == null || metadata2 == null) return false; |
| return areElementsEquivalent( |
| metadata1.annotatedElement, metadata2.annotatedElement) && |
| areConstantsEquivalent(metadata1.constant, metadata2.constant); |
| } |