| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.serialization_test; |
| |
| import 'dart:io'; |
| import 'memory_compiler.dart'; |
| import 'package:async_helper/async_helper.dart'; |
| import 'package:compiler/src/commandline_options.dart'; |
| import 'package:compiler/src/constants/constructors.dart'; |
| import 'package:compiler/src/constants/expressions.dart'; |
| import 'package:compiler/src/dart_types.dart'; |
| import 'package:compiler/src/compiler.dart'; |
| import 'package:compiler/src/diagnostics/invariant.dart'; |
| import 'package:compiler/src/elements/elements.dart'; |
| import 'package:compiler/src/elements/visitor.dart'; |
| import 'package:compiler/src/ordered_typeset.dart'; |
| import 'package:compiler/src/serialization/element_serialization.dart'; |
| import 'package:compiler/src/serialization/equivalence.dart'; |
| import 'package:compiler/src/serialization/json_serializer.dart'; |
| import 'package:compiler/src/serialization/serialization.dart'; |
| |
| main(List<String> arguments) { |
| // Ensure that we can print out constant expressions. |
| DEBUG_MODE = true; |
| |
| Uri entryPoint; |
| String outPath; |
| bool prettyPrint = false; |
| for (String arg in arguments) { |
| if (arg.startsWith('--')) { |
| if (arg.startsWith('--out=')) { |
| outPath = arg.substring('--out='.length); |
| } else if (arg == '--pretty-print') { |
| prettyPrint = true; |
| } else { |
| print("Unknown option $arg"); |
| } |
| } else { |
| if (entryPoint != null) { |
| print("Multiple entrypoints is not supported."); |
| } |
| entryPoint = Uri.parse(arg); |
| } |
| } |
| if (entryPoint == null) { |
| entryPoint = Uri.parse('dart:core'); |
| } |
| asyncTest(() async { |
| CompilationResult result = await runCompiler( |
| entryPoint: entryPoint, options: [Flags.analyzeAll]); |
| Compiler compiler = result.compiler; |
| testSerialization(compiler.libraryLoader.libraries, |
| outPath: outPath, |
| prettyPrint: prettyPrint); |
| }); |
| } |
| |
| void testSerialization(Iterable<LibraryElement> libraries1, |
| {String outPath, |
| bool prettyPrint}) { |
| Serializer serializer = new Serializer(); |
| for (LibraryElement library1 in libraries1) { |
| serializer.serialize(library1); |
| } |
| String text = serializer.toText(const JsonSerializationEncoder()); |
| String outText = text; |
| if (prettyPrint) { |
| outText = serializer.prettyPrint(); |
| } |
| if (outPath != null) { |
| new File(outPath).writeAsStringSync(outText); |
| } else if (prettyPrint) { |
| print(outText); |
| } |
| |
| Deserializer deserializer = new Deserializer.fromText( |
| new DeserializationContext(), |
| text, const JsonSerializationDecoder()); |
| List<LibraryElement> libraries2 = <LibraryElement>[]; |
| for (LibraryElement library1 in libraries1) { |
| LibraryElement library2 = |
| deserializer.lookupLibrary(library1.canonicalUri); |
| if (library2 == null) { |
| throw new ArgumentError('No library ${library1.canonicalUri} found.'); |
| } |
| checkLibraryContent('library1', 'library2', 'library', library1, library2); |
| libraries2.add(library2); |
| } |
| |
| Serializer serializer2 = new Serializer(); |
| for (LibraryElement library2 in libraries2) { |
| serializer2.serialize(library2); |
| } |
| String text2 = serializer2.toText(const JsonSerializationEncoder()); |
| |
| Deserializer deserializer3 = new Deserializer.fromText( |
| new DeserializationContext(), |
| text2, const JsonSerializationDecoder()); |
| for (LibraryElement library1 in libraries1) { |
| LibraryElement library2 = |
| deserializer.lookupLibrary(library1.canonicalUri); |
| if (library2 == null) { |
| throw new ArgumentError('No library ${library1.canonicalUri} found.'); |
| } |
| LibraryElement library3 = |
| deserializer3.lookupLibrary(library1.canonicalUri); |
| if (library3 == null) { |
| throw new ArgumentError('No library ${library1.canonicalUri} found.'); |
| } |
| checkLibraryContent('library1', 'library3', 'library', library1, library3); |
| checkLibraryContent('library2', 'library3', 'library', library2, library3); |
| } |
| } |
| |
| /// Check the equivalence of [library1] and [library2] and their content. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| checkLibraryContent( |
| Object object1, object2, String property, |
| LibraryElement library1, LibraryElement library2) { |
| checkElementProperties(object1, object2, property, library1, library2); |
| } |
| |
| /// Check the equivalence of [element1] and [element2] and their properties. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| checkElementProperties( |
| Object object1, object2, String property, |
| Element element1, Element element2) { |
| const ElementPropertyEquivalence().visit(element1, element2); |
| } |
| |
| /// Check the equivalence of the two lists of elements, [list1] and [list2]. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| checkElementLists(Object object1, Object object2, String property, |
| Iterable<Element> list1, Iterable<Element> list2) { |
| checkListEquivalence(object1, object2, property, |
| list1, list2, checkElementProperties); |
| } |
| |
| /// Check equivalence of the two lists, [list1] and [list2], using |
| /// [checkEquivalence] to check the pair-wise equivalence. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkListEquivalence( |
| Object object1, Object object2, String property, |
| Iterable list1, Iterable list2, |
| void checkEquivalence(o1, o2, property, a, b)) { |
| for (int i = 0; i < list1.length && i < list2.length; i++) { |
| checkEquivalence( |
| object1, object2, property, |
| list1.elementAt(i), list2.elementAt(i)); |
| } |
| for (int i = list1.length; i < list2.length; i++) { |
| throw |
| 'Missing equivalent for element ' |
| '#$i ${list2.elementAt(i)} in `${property}` on $object2.\n' |
| '`${property}` on $object1:\n ${list1.join('\n ')}\n' |
| '`${property}` on $object2:\n ${list2.join('\n ')}'; |
| } |
| for (int i = list2.length; i < list1.length; i++) { |
| throw |
| 'Missing equivalent for element ' |
| '#$i ${list1.elementAt(i)} in `${property}` on $object1.\n' |
| '`${property}` on $object1:\n ${list1.join('\n ')}\n' |
| '`${property}` on $object2:\n ${list2.join('\n ')}'; |
| } |
| return true; |
| } |
| |
| /// Computes the set difference between [set1] and [set2] using |
| /// [elementEquivalence] to determine element equivalence. |
| /// |
| /// Elements both in [set1] and [set2] are added to [common], elements in [set1] |
| /// but not in [set2] are added to [unfound], and the set of elements in [set2] |
| /// but not in [set1] are returned. |
| Set computeSetDifference( |
| Iterable set1, |
| Iterable set2, |
| List common, |
| List unfound, |
| [bool sameElement(a, b) = equality]) { |
| // TODO(johnniwinther): Avoid the quadratic cost here. Some ideas: |
| // - convert each set to a list and sort it first, then compare by walking |
| // both lists in parallel |
| // - map each element to a canonical object, create a map containing those |
| // mappings, use the mapped sets to compare (then operations like |
| // set.difference would work) |
| Set remaining = set2.toSet(); |
| for (var element1 in set1) { |
| bool found = false; |
| for (var element2 in remaining) { |
| if (sameElement(element1, element2)) { |
| found = true; |
| remaining.remove(element2); |
| break; |
| } |
| } |
| if (found) { |
| common.add(element1); |
| } else { |
| unfound.add(element1); |
| } |
| } |
| return remaining; |
| } |
| |
| /// Check equivalence of the two iterables, [set1] and [set1], as sets using |
| /// [elementEquivalence] to compute the pair-wise equivalence. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkSetEquivalence( |
| var object1, |
| var object2, |
| String property, |
| Iterable set1, |
| Iterable set2, |
| bool sameElement(a, b)) { |
| List common = []; |
| List unfound = []; |
| Set remaining = |
| computeSetDifference(set1, set2, common, unfound, sameElement); |
| if (unfound.isNotEmpty || remaining.isNotEmpty) { |
| String message = |
| "Set mismatch for `$property` on $object1 vs $object2: \n" |
| "Common:\n ${common.join('\n ')}\n" |
| "Unfound:\n ${unfound.join('\n ')}\n" |
| "Extra: \n ${remaining.join('\n ')}"; |
| throw message; |
| } |
| return true; |
| } |
| |
| /// Checks the equivalence of the identity (but not properties) of [element1] |
| /// and [element2]. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkElementIdentities( |
| Object object1, Object object2, String property, |
| Element element1, Element element2) { |
| if (identical(element1, element2)) return true; |
| if (element1 == null || element2 == null) { |
| return check(object1, object2, property, element1, element2); |
| } else { |
| return const ElementIdentityEquivalence(const CheckStrategy()) |
| .visit(element1, element2); |
| } |
| } |
| |
| /// Checks the pair-wise equivalence of the identity (but not properties) of the |
| /// elements in [list] and [list2]. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkElementListIdentities( |
| Object object1, Object object2, String property, |
| Iterable<Element> list1, Iterable<Element> list2) { |
| return checkListEquivalence( |
| object1, object2, property, |
| list1, list2, checkElementIdentities); |
| } |
| |
| /// Checks the equivalence of [type1] and [type2]. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkTypes( |
| Object object1, Object object2, String property, |
| DartType type1, DartType type2) { |
| if (identical(type1, type2)) return true; |
| if (type1 == null || type2 == null) { |
| return check(object1, object2, property, type1, type2); |
| } else { |
| return const TypeEquivalence(const CheckStrategy()).visit(type1, type2); |
| } |
| } |
| |
| /// Checks the pair-wise equivalence of the types in [list1] and [list2]. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkTypeLists( |
| Object object1, Object object2, String property, |
| List<DartType> list1, List<DartType> list2) { |
| return checkListEquivalence( |
| object1, object2, property, list1, list2, checkTypes); |
| } |
| |
| /// Checks the equivalence of [exp1] and [exp2]. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkConstants( |
| Object object1, Object object2, String property, |
| ConstantExpression exp1, ConstantExpression exp2) { |
| if (identical(exp1, exp2)) return true; |
| if (exp1 == null || exp2 == null) { |
| return check(object1, object2, property, exp1, exp2); |
| } else { |
| return const ConstantEquivalence(const CheckStrategy()).visit(exp1, exp2); |
| } |
| } |
| |
| /// Checks the pair-wise equivalence of the contants in [list1] and [list2]. |
| /// |
| /// Uses [object1], [object2] and [property] to provide context for failures. |
| bool checkConstantLists( |
| Object object1, Object object2, String property, |
| List<ConstantExpression> list1, |
| List<ConstantExpression> list2) { |
| return checkListEquivalence( |
| object1, object2, property, |
| list1, list2, checkConstants); |
| } |
| |
| |
| /// Strategy for checking equivalence. |
| /// |
| /// Use this strategy to fail early with contextual information in the event of |
| /// inequivalence. |
| class CheckStrategy implements TestStrategy { |
| const CheckStrategy(); |
| |
| @override |
| bool test(var object1, var object2, String property, var value1, var value2) { |
| return check(object1, object2, property, value1, value2); |
| } |
| |
| @override |
| bool testLists( |
| Object object1, Object object2, String property, |
| List list1, List list2, |
| [bool elementEquivalence(a, b) = equality]) { |
| return checkListEquivalence( |
| object1, object2, property, list1, list2, |
| (o1, o2, p, v1, v2) { |
| if (!elementEquivalence(v1, v2)) { |
| throw "$o1.$p = '${v1}' <> " |
| "$o2.$p = '${v2}'"; |
| } |
| return true; |
| }); |
| } |
| |
| @override |
| bool testSets( |
| var object1, var object2, String property, |
| Iterable set1, Iterable set2, |
| [bool elementEquivalence(a, b) = equality]) { |
| return checkSetEquivalence( |
| object1, object2,property, set1, set2, elementEquivalence); |
| } |
| |
| @override |
| bool testElements( |
| Object object1, Object object2, String property, |
| Element element1, Element element2) { |
| return checkElementIdentities( |
| object1, object2, property, element1, element2); |
| } |
| |
| @override |
| bool testTypes( |
| Object object1, Object object2, String property, |
| DartType type1, DartType type2) { |
| return checkTypes(object1, object2, property, type1, type2); |
| } |
| |
| @override |
| bool testConstants( |
| Object object1, Object object2, String property, |
| ConstantExpression exp1, ConstantExpression exp2) { |
| return checkConstants(object1, object2, property, exp1, exp2); |
| } |
| |
| @override |
| bool testTypeLists( |
| Object object1, Object object2, String property, |
| List<DartType> list1, List<DartType> list2) { |
| return checkTypeLists(object1, object2, property, list1, list2); |
| } |
| |
| @override |
| bool testConstantLists( |
| Object object1, Object object2, String property, |
| List<ConstantExpression> list1, |
| List<ConstantExpression> list2) { |
| return checkConstantLists(object1, object2, property, list1, list2); |
| } |
| } |
| |
| /// Checks the equivalence of [constructor1] and [constructor2]. |
| void constantConstructorEquivalence(ConstantConstructor constructor1, |
| ConstantConstructor constructor2) { |
| const ConstantConstructorEquivalence().visit(constructor1, constructor2); |
| } |
| |
| /// Visitor that checks the equivalence of [ConstantConstructor]s. |
| class ConstantConstructorEquivalence |
| extends ConstantConstructorVisitor<dynamic, ConstantConstructor> { |
| const ConstantConstructorEquivalence(); |
| |
| @override |
| void visit(ConstantConstructor constructor1, |
| ConstantConstructor constructor2) { |
| if (identical(constructor1, constructor2)) return; |
| check(constructor1, constructor2, 'kind', |
| constructor1.kind, constructor2.kind); |
| constructor1.accept(this, constructor2); |
| } |
| |
| @override |
| visitGenerative( |
| GenerativeConstantConstructor constructor1, |
| GenerativeConstantConstructor constructor2) { |
| checkTypes( |
| constructor1, constructor2, 'type', |
| constructor1.type, constructor2.type); |
| check(constructor1, constructor2, 'defaultValues.length', |
| constructor1.defaultValues.length, |
| constructor2.defaultValues.length); |
| constructor1.defaultValues.forEach((k, v) { |
| checkConstants( |
| constructor1, constructor2, 'defaultValue[$k]', |
| v, constructor2.defaultValues[k]); |
| }); |
| check(constructor1, constructor2, 'fieldMap.length', |
| constructor1.fieldMap.length, |
| constructor2.fieldMap.length); |
| constructor1.fieldMap.forEach((k1, v1) { |
| bool matched = false; |
| constructor2.fieldMap.forEach((k2, v2) { |
| if (k1.name == k2.name && |
| k1.library.canonicalUri == k2.library.canonicalUri) { |
| checkElementIdentities( |
| constructor1, constructor2, 'fieldMap[${k1.name}].key', k1, k2); |
| checkConstants( |
| constructor1, constructor2, 'fieldMap[${k1.name}].value', v1, v2); |
| matched = true; |
| } |
| }); |
| if (!matched) { |
| throw 'Unmatched field $k1 = $v1'; |
| } |
| }); |
| checkConstants( |
| constructor1, constructor2, 'superConstructorInvocation', |
| constructor1.superConstructorInvocation, |
| constructor2.superConstructorInvocation); |
| } |
| |
| @override |
| visitRedirectingFactory( |
| RedirectingFactoryConstantConstructor constructor1, |
| RedirectingFactoryConstantConstructor constructor2) { |
| checkConstants( |
| constructor1, constructor2, 'targetConstructorInvocation', |
| constructor1.targetConstructorInvocation, |
| constructor2.targetConstructorInvocation); |
| } |
| |
| @override |
| visitRedirectingGenerative( |
| RedirectingGenerativeConstantConstructor constructor1, |
| RedirectingGenerativeConstantConstructor constructor2) { |
| check(constructor1, constructor2, 'defaultValues.length', |
| constructor1.defaultValues.length, |
| constructor2.defaultValues.length); |
| constructor1.defaultValues.forEach((k, v) { |
| checkConstants( |
| constructor1, constructor2, 'defaultValue[$k]', |
| v, constructor2.defaultValues[k]); |
| }); |
| checkConstants( |
| constructor1, constructor2, 'thisConstructorInvocation', |
| constructor1.thisConstructorInvocation, |
| constructor2.thisConstructorInvocation); |
| } |
| } |
| |
| /// Check that the values [property] of [object1] and [object2], [value1] and |
| /// [value2] respectively, are equal and throw otherwise. |
| bool check(var object1, var object2, String property, var value1, value2) { |
| if (value1 != value2) { |
| throw "$object1.$property = '${value1}' <> " |
| "$object2.$property = '${value2}'"; |
| } |
| return true; |
| } |
| |
| /// Visitor that checks for equivalence of [Element] properties. |
| class ElementPropertyEquivalence extends BaseElementVisitor<dynamic, Element> { |
| const ElementPropertyEquivalence(); |
| |
| void visit(Element element1, Element element2) { |
| if (element1 == null && element2 == null) return; |
| element1 = element1.declaration; |
| element2 = element2.declaration; |
| if (element1 == element2) return; |
| check(element1, element2, 'kind', element1.kind, element2.kind); |
| element1.accept(this, element2); |
| } |
| |
| @override |
| void visitElement(Element e, Element arg) { |
| throw new UnsupportedError("Unsupported element $e"); |
| } |
| |
| @override |
| void visitLibraryElement(LibraryElement element1, LibraryElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| check(element1, element2, 'name', element1.name, element2.name); |
| check(element1, element2, 'libraryName', |
| element1.libraryName, element2.libraryName); |
| visitMembers(element1, element2); |
| visit(element1.entryCompilationUnit, element2.entryCompilationUnit); |
| |
| checkElementLists( |
| element1, element2, 'compilationUnits', |
| LibrarySerializer.getCompilationUnits(element1), |
| LibrarySerializer.getCompilationUnits(element2)); |
| |
| checkElementListIdentities( |
| element1, element2, 'imports', |
| LibrarySerializer.getImports(element1), |
| LibrarySerializer.getImports(element2)); |
| checkElementListIdentities( |
| element1, element2, 'exports', element1.exports, element2.exports); |
| |
| checkElementListIdentities( |
| element1, element2, 'importScope', |
| LibrarySerializer.getImportedElements(element1), |
| LibrarySerializer.getImportedElements(element2)); |
| |
| checkElementListIdentities( |
| element1, element2, 'exportScope', |
| LibrarySerializer.getExportedElements(element1), |
| LibrarySerializer.getExportedElements(element2)); |
| } |
| |
| @override |
| void visitCompilationUnitElement(CompilationUnitElement element1, |
| CompilationUnitElement element2) { |
| check(element1, element2, |
| 'name', |
| element1.name, element2.name); |
| checkElementIdentities( |
| element1, element2, 'library', |
| element1.library, element2.library); |
| check(element1, element2, |
| 'script.resourceUri', |
| element1.script.resourceUri, element2.script.resourceUri); |
| List<Element> members1 = <Element>[]; |
| List<Element> members2 = <Element>[]; |
| element1.forEachLocalMember((Element member) { |
| members1.add(member); |
| }); |
| element2.forEachLocalMember((Element member) { |
| members2.add(member); |
| }); |
| checkElementListIdentities( |
| element1, element2, 'localMembers', members1, members2); |
| } |
| |
| void visitMembers(ScopeContainerElement element1, |
| ScopeContainerElement element2) { |
| Set<String> names = new Set<String>(); |
| Iterable<Element> members1 = element1.isLibrary |
| ? LibrarySerializer.getMembers(element1) |
| : ClassSerializer.getMembers(element1); |
| Iterable<Element> members2 = element2.isLibrary |
| ? LibrarySerializer.getMembers(element2) |
| : ClassSerializer.getMembers(element2); |
| for (Element member in members1) { |
| names.add(member.name); |
| } |
| for (Element member in members2) { |
| names.add(member.name); |
| } |
| element1 = element1.implementation; |
| element2 = element2.implementation; |
| for (String name in names) { |
| Element member1 = element1.localLookup(name); |
| Element member2 = element2.localLookup(name); |
| if (member1 == null) { |
| String message = |
| 'Missing member for $member2 in\n ${members1.join('\n ')}'; |
| if (member2.isAbstractField) { |
| // TODO(johnniwinther): Ensure abstract fields are handled correctly. |
| //print(message); |
| continue; |
| } else { |
| throw message; |
| } |
| } |
| if (member2 == null) { |
| String message = |
| 'Missing member for $member1 in\n ${members2.join('\n ')}'; |
| if (member1.isAbstractField) { |
| // TODO(johnniwinther): Ensure abstract fields are handled correctly. |
| //print(message); |
| continue; |
| } else { |
| throw message; |
| } |
| } |
| //print('Checking member ${member1} against ${member2}'); |
| visit(member1, member2); |
| } |
| } |
| |
| @override |
| void visitClassElement(ClassElement element1, ClassElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| check(element1, element2, 'name', |
| element1.name, element2.name); |
| check(element1, element2, 'sourcePosition', |
| element1.sourcePosition, element2.sourcePosition); |
| checkElementIdentities( |
| element1, element2, 'library', |
| element1.library, element2.library); |
| checkElementIdentities( |
| element1, element2, 'compilationUnit', |
| element1.compilationUnit, element2.compilationUnit); |
| check(element1, element2, 'isObject', |
| element1.isObject, element2.isObject); |
| checkTypeLists(element1, element2, 'typeVariables', |
| element1.typeVariables, element2.typeVariables); |
| check(element1, element2, 'isAbstract', |
| element1.isAbstract, element2.isAbstract); |
| check(element1, element2, 'isUnnamedMixinApplication', |
| element1.isUnnamedMixinApplication, element2.isUnnamedMixinApplication); |
| check(element1, element2, 'isEnumClass', |
| element1.isEnumClass, element2.isEnumClass); |
| if (element1.isEnumClass) { |
| EnumClassElement enum1 = element1; |
| EnumClassElement enum2 = element2; |
| checkElementLists(enum1, enum2, 'enumValues', |
| enum1.enumValues, enum2.enumValues); |
| } |
| if (!element1.isObject) { |
| checkTypes(element1, element2, 'supertype', |
| element1.supertype, element2.supertype); |
| } |
| check(element1, element2, 'hierarchyDepth', |
| element1.hierarchyDepth, element2.hierarchyDepth); |
| checkTypeLists( |
| element1, element2, 'allSupertypes', |
| element1.allSupertypes.toList(), |
| element2.allSupertypes.toList()); |
| OrderedTypeSet typeSet1 = element1.allSupertypesAndSelf; |
| OrderedTypeSet typeSet2 = element1.allSupertypesAndSelf; |
| checkListEquivalence( |
| element1, element2, 'allSupertypes', |
| typeSet1.levelOffsets, |
| typeSet2.levelOffsets, |
| check); |
| check(element1, element2, 'allSupertypesAndSelf.levels', |
| typeSet1.levels, typeSet2.levels); |
| checkTypeLists( |
| element1, element2, 'supertypes', |
| typeSet1.supertypes.toList(), |
| typeSet2.supertypes.toList()); |
| checkTypeLists( |
| element1, element2, 'types', |
| typeSet1.types.toList(), |
| typeSet2.types.toList()); |
| |
| checkTypeLists( |
| element1, element2, 'interfaces', |
| element1.interfaces.toList(), |
| element2.interfaces.toList()); |
| |
| List<ConstructorElement> getConstructors(ClassElement cls) { |
| return cls.implementation.constructors.map((c) => c.declaration).toList(); |
| } |
| |
| checkElementLists( |
| element1, element2, 'constructors', |
| getConstructors(element1), |
| getConstructors(element2)); |
| |
| visitMembers(element1, element2); |
| } |
| |
| @override |
| void visitFieldElement(FieldElement element1, FieldElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| check(element1, element2, 'name', |
| element1.name, element2.name); |
| check(element1, element2, 'sourcePosition', |
| element1.sourcePosition, element2.sourcePosition); |
| checkTypes( |
| element1, element2, 'type', |
| element1.type, element2.type); |
| check(element1, element2, 'isConst', |
| element1.isConst, element2.isConst); |
| check(element1, element2, 'isFinal', |
| element1.isFinal, element2.isFinal); |
| if (element1.isConst) { |
| checkConstants( |
| element1, element2, 'constant', |
| element1.constant, element2.constant); |
| } |
| check(element1, element2, 'isTopLevel', |
| element1.isTopLevel, element2.isTopLevel); |
| check(element1, element2, 'isStatic', |
| element1.isStatic, element2.isStatic); |
| check(element1, element2, 'isInstanceMember', |
| element1.isInstanceMember, element2.isInstanceMember); |
| |
| checkElementIdentities( |
| element1, element2, 'library', |
| element1.library, element2.library); |
| checkElementIdentities( |
| element1, element2, 'compilationUnit', |
| element1.compilationUnit, element2.compilationUnit); |
| checkElementIdentities( |
| element1, element2, 'enclosingClass', |
| element1.enclosingClass, element2.enclosingClass); |
| } |
| |
| @override |
| void visitFunctionElement(FunctionElement element1, |
| FunctionElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| check(element1, element2, 'name', |
| element1.name, element2.name); |
| check(element1, element2, 'sourcePosition', |
| element1.sourcePosition, element2.sourcePosition); |
| checkTypes( |
| element1, element2, 'type', |
| element1.type, element2.type); |
| checkListEquivalence( |
| element1, element2, 'parameters', |
| element1.parameters, element2.parameters, |
| checkElementProperties); |
| check(element1, element2, 'isOperator', |
| element1.isOperator, element2.isOperator); |
| |
| checkElementIdentities( |
| element1, element2, 'library', |
| element1.library, element2.library); |
| checkElementIdentities( |
| element1, element2, 'compilationUnit', |
| element1.compilationUnit, element2.compilationUnit); |
| checkElementIdentities( |
| element1, element2, 'enclosingClass', |
| element1.enclosingClass, element2.enclosingClass); |
| } |
| |
| @override |
| void visitConstructorElement(ConstructorElement element1, |
| ConstructorElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| checkElementIdentities( |
| element1, element2, 'enclosingClass', |
| element1.enclosingClass, element2.enclosingClass); |
| check( |
| element1, element2, 'name', |
| element1.name, element2.name); |
| check(element1, element2, 'sourcePosition', |
| element1.sourcePosition, element2.sourcePosition); |
| checkListEquivalence( |
| element1, element2, 'parameters', |
| element1.parameters, element2.parameters, |
| checkElementProperties); |
| checkTypes( |
| element1, element2, 'type', |
| element1.type, element2.type); |
| check(element1, element2, 'isConst', |
| element1.isConst, element2.isConst); |
| check(element1, element2, 'isExternal', |
| element1.isExternal, element2.isExternal); |
| if (element1.isConst && !element1.isExternal) { |
| constantConstructorEquivalence( |
| element1.constantConstructor, |
| element2.constantConstructor); |
| } |
| } |
| |
| @override |
| void visitAbstractFieldElement(AbstractFieldElement element1, |
| AbstractFieldElement element2) { |
| visit(element1.getter, element2.getter); |
| visit(element1.setter, element2.setter); |
| } |
| |
| @override |
| void visitTypeVariableElement(TypeVariableElement element1, |
| TypeVariableElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| check(element1, element2, 'name', element1.name, element2.name); |
| check(element1, element2, 'sourcePosition', |
| element1.sourcePosition, element2.sourcePosition); |
| check(element1, element2, 'index', element1.index, element2.index); |
| checkTypes( |
| element1, element2, 'type', |
| element1.type, element2.type); |
| checkTypes( |
| element1, element2, 'bound', |
| element1.bound, element2.bound); |
| } |
| |
| @override |
| void visitTypedefElement(TypedefElement element1, |
| TypedefElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| check(element1, element2, 'name', element1.name, element2.name); |
| check(element1, element2, 'sourcePosition', |
| element1.sourcePosition, element2.sourcePosition); |
| checkTypes( |
| element1, element2, 'alias', |
| element1.alias, element2.alias); |
| checkTypeLists( |
| element1, element2, 'typeVariables', |
| element1.typeVariables, element2.typeVariables); |
| checkElementIdentities( |
| element1, element2, 'library', |
| element1.library, element2.library); |
| checkElementIdentities( |
| element1, element2, 'compilationUnit', |
| element1.compilationUnit, element2.compilationUnit); |
| // TODO(johnniwinther): Check the equivalence of typedef parameters. |
| } |
| |
| @override |
| void visitParameterElement(ParameterElement element1, |
| ParameterElement element2) { |
| checkElementIdentities(null, null, null, element1, element2); |
| checkElementIdentities( |
| element1, element2, 'functionDeclaration', |
| element1.functionDeclaration, element2.functionDeclaration); |
| check(element1, element2, 'name', element1.name, element2.name); |
| check(element1, element2, 'sourcePosition', |
| element1.sourcePosition, element2.sourcePosition); |
| checkTypes( |
| element1, element2, 'type', |
| element1.type, element2.type); |
| check( |
| element1, element2, 'isOptional', |
| element1.isOptional, element2.isOptional); |
| check( |
| element1, element2, 'isNamed', |
| element1.isNamed, element2.isNamed); |
| check(element1, element2, 'name', element1.name, element2.name); |
| if (element1.isOptional) { |
| checkConstants( |
| element1, element2, 'constant', |
| element1.constant, element2.constant); |
| } |
| checkElementIdentities( |
| element1, element2, 'compilationUnit', |
| element1.compilationUnit, element2.compilationUnit); |
| } |
| |
| @override |
| void visitFieldParameterElement(InitializingFormalElement element1, |
| InitializingFormalElement element2) { |
| visitParameterElement(element1, element2); |
| checkElementIdentities( |
| element1, element2, 'fieldElement', |
| element1.fieldElement, element2.fieldElement); |
| } |
| |
| @override |
| void visitImportElement(ImportElement element1, ImportElement element2) { |
| check(element1, element2, 'uri', element1.uri, element2.uri); |
| check( |
| element1, element2, 'isDeferred', |
| element1.isDeferred, element2.isDeferred); |
| checkElementProperties( |
| element1, element2, 'prefix', |
| element1.prefix, element2.prefix); |
| checkElementIdentities( |
| element1, element2, 'importedLibrary', |
| element1.importedLibrary, element2.importedLibrary); |
| } |
| |
| @override |
| void visitExportElement(ExportElement element1, ExportElement element2) { |
| check(element1, element2, 'uri', element1.uri, element2.uri); |
| checkElementIdentities( |
| element1, element2, 'importedLibrary', |
| element1.exportedLibrary, element2.exportedLibrary); |
| } |
| |
| @override |
| void visitPrefixElement(PrefixElement element1, PrefixElement element2) { |
| check( |
| element1, element2, 'isDeferred', |
| element1.isDeferred, element2.isDeferred); |
| checkElementIdentities( |
| element1, element2, 'importedLibrary', |
| element1.deferredImport, element2.deferredImport); |
| // TODO(johnniwinther): Check members. |
| } |
| } |