|  | // 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/common.dart'; | 
|  | import 'package:compiler/src/common/resolution.dart'; | 
|  | import 'package:compiler/src/constants/constructors.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/resolution_types.dart'; | 
|  | import 'package:compiler/src/elements/visitor.dart'; | 
|  | import 'package:compiler/src/filenames.dart'; | 
|  | import 'package:compiler/src/library_loader.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'; | 
|  | import 'package:expect/expect.dart'; | 
|  | import 'test_helper.dart'; | 
|  |  | 
|  | const TEST_SOURCES = const <String, String>{ | 
|  | 'main.dart': ''' | 
|  | import 'library.dart'; | 
|  | import 'deferred_library.dart' deferred as prefix; | 
|  |  | 
|  | asyncMethod() async {} | 
|  | asyncStarMethod() async* {} | 
|  | syncStarMethod() sync* {} | 
|  | get asyncGetter async {} | 
|  | get asyncStarGetter async* {} | 
|  | get syncStarGetter sync* {} | 
|  |  | 
|  | genericMethod<T>() {} | 
|  |  | 
|  | class Class1 { | 
|  | factory Class1.deferred() = prefix.DeferredClass; | 
|  | factory Class1.unresolved() = Unresolved; | 
|  | } | 
|  | ''', | 
|  | 'deferred_library.dart': ''' | 
|  | class DeferredClass { | 
|  | } | 
|  |  | 
|  | get getter => 0; | 
|  | set setter(_) {} | 
|  | get property => 0; | 
|  | set property(_) {} | 
|  | ''', | 
|  | 'library.dart': ''' | 
|  | class Type {} | 
|  | ''', | 
|  | }; | 
|  |  | 
|  | 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.base.resolve(nativeToUriPath(arg)); | 
|  | } | 
|  | } | 
|  | Map<String, String> sourceFiles = const <String, String>{}; | 
|  | if (entryPoint == null) { | 
|  | entryPoint = Uri.parse('memory:main.dart'); | 
|  | sourceFiles = TEST_SOURCES; | 
|  | } | 
|  | asyncTest(() async { | 
|  | CompilationResult result = await runCompiler( | 
|  | memorySourceFiles: sourceFiles, | 
|  | entryPoint: entryPoint, | 
|  | options: [Flags.analyzeAll, Flags.genericMethodSyntax]); | 
|  | Compiler compiler = result.compiler; | 
|  | testSerialization(compiler.libraryLoader.libraries, compiler.reporter, | 
|  | compiler.resolution, compiler.libraryLoader, | 
|  | outPath: outPath, prettyPrint: prettyPrint); | 
|  | Expect.isFalse( | 
|  | compiler.reporter.hasReportedError, "Unexpected errors occured."); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testSerialization( | 
|  | Iterable<LibraryElement> libraries1, | 
|  | DiagnosticReporter reporter, | 
|  | Resolution resolution, | 
|  | LibraryProvider libraryProvider, | 
|  | {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(reporter, resolution, libraryProvider), | 
|  | Uri.parse('out1.data'), | 
|  | 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(reporter, resolution, libraryProvider), | 
|  | Uri.parse('out2.data'), | 
|  | 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) { | 
|  | currentCheck = | 
|  | new Check(currentCheck, object1, object2, property, element1, element2); | 
|  | const ElementPropertyEquivalence().visit(element1, element2); | 
|  | currentCheck = currentCheck.parent; | 
|  | } | 
|  |  | 
|  | /// 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) { | 
|  | ResolutionInterfaceType type1 = constructor1.type; | 
|  | ResolutionInterfaceType type2 = constructor2.type; | 
|  | checkTypes(constructor1, constructor2, 'type', type1, type2); | 
|  | 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 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 the equivalence of the two metadata annotations, [metadata1] and | 
|  | /// [metadata2]. | 
|  | /// | 
|  | /// Uses [object1], [object2] and [property] to provide context for failures. | 
|  | checkMetadata(Object object1, Object object2, String property, | 
|  | MetadataAnnotation metadata1, MetadataAnnotation metadata2) { | 
|  | check(object1, object2, property, metadata1, metadata2, | 
|  | areMetadataAnnotationsEquivalent); | 
|  | } | 
|  |  | 
|  | /// 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; | 
|  | if (element1 == null || element2 == null) { | 
|  | throw currentCheck; | 
|  | } | 
|  | element1 = element1.declaration; | 
|  | element2 = element2.declaration; | 
|  | if (element1 == element2) return; | 
|  | check(element1, element2, 'kind', element1.kind, element2.kind); | 
|  | element1.accept(this, element2); | 
|  | check(element1, element2, 'isSynthesized', element1.isSynthesized, | 
|  | element2.isSynthesized); | 
|  | check(element1, element2, 'isLocal', element1.isLocal, element2.isLocal); | 
|  | check(element1, element2, 'isFinal', element1.isFinal, element2.isFinal); | 
|  | check(element1, element2, 'isConst', element1.isConst, element2.isConst); | 
|  | check(element1, element2, 'isAbstract', element1.isAbstract, | 
|  | element2.isAbstract); | 
|  | check(element1, element2, 'isStatic', element1.isStatic, element2.isStatic); | 
|  | check(element1, element2, 'isTopLevel', element1.isTopLevel, | 
|  | element2.isTopLevel); | 
|  | check(element1, element2, 'isClassMember', element1.isClassMember, | 
|  | element2.isClassMember); | 
|  | check(element1, element2, 'isInstanceMember', element1.isInstanceMember, | 
|  | element2.isInstanceMember); | 
|  | List<MetadataAnnotation> metadata1 = <MetadataAnnotation>[]; | 
|  | metadata1.addAll(element1.metadata); | 
|  | if (element1.isPatched) { | 
|  | metadata1.addAll(element1.implementation.metadata); | 
|  | } | 
|  | List<MetadataAnnotation> metadata2 = <MetadataAnnotation>[]; | 
|  | metadata2.addAll(element2.metadata); | 
|  | if (element2.isPatched) { | 
|  | metadata2.addAll(element2.implementation.metadata); | 
|  | } | 
|  | checkListEquivalence( | 
|  | element1, element2, 'metadata', metadata1, metadata2, checkMetadata); | 
|  | } | 
|  |  | 
|  | @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)); | 
|  |  | 
|  | checkElementLists( | 
|  | element1, | 
|  | element2, | 
|  | 'imports', | 
|  | LibrarySerializer.getImports(element1), | 
|  | LibrarySerializer.getImports(element2)); | 
|  | checkElementLists( | 
|  | element1, element2, 'exports', element1.exports, element2.exports); | 
|  |  | 
|  | List<Element> imported1 = LibrarySerializer.getImportedElements(element1); | 
|  | List<Element> imported2 = LibrarySerializer.getImportedElements(element2); | 
|  | checkElementListIdentities( | 
|  | element1, element2, 'importScope', imported1, imported2); | 
|  |  | 
|  | checkElementListIdentities( | 
|  | element1, | 
|  | element2, | 
|  | 'exportScope', | 
|  | LibrarySerializer.getExportedElements(element1), | 
|  | LibrarySerializer.getExportedElements(element2)); | 
|  |  | 
|  | for (int index = 0; index < imported1.length; index++) { | 
|  | checkImportsFor(element1, element2, imported1[index], imported2[index]); | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkImportsFor( | 
|  | Element element1, Element element2, Element import1, Element import2) { | 
|  | List<ImportElement> imports1 = element1.library.getImportsFor(import1); | 
|  | List<ImportElement> imports2 = element2.library.getImportsFor(import2); | 
|  | checkElementListIdentities(element1, element2, | 
|  | 'importsFor($import1/$import2)', imports1, imports2); | 
|  | } | 
|  |  | 
|  | @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; | 
|  | } | 
|  | } | 
|  | currentCheck = new Check( | 
|  | currentCheck, element1, element2, 'member:$name', member1, member2); | 
|  | visit(member1, member2); | 
|  | currentCheck = currentCheck.parent; | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitClassElement(ClassElement element1, ClassElement element2) { | 
|  | checkElementIdentities(null, null, null, element1, element2); | 
|  | check(element1, element2, 'name', element1.name, element2.name); | 
|  | if (!element1.isUnnamedMixinApplication) { | 
|  | check(element1, element2, 'sourcePosition', element1.sourcePosition, | 
|  | element2.sourcePosition); | 
|  | } else { | 
|  | check(element1, element2, 'sourcePosition.uri', | 
|  | element1.sourcePosition.uri, element2.sourcePosition.uri); | 
|  | MixinApplicationElement mixin1 = element1; | 
|  | MixinApplicationElement mixin2 = element2; | 
|  | checkElementIdentities( | 
|  | mixin1, mixin2, 'subclass', mixin1.subclass, mixin2.subclass); | 
|  | checkTypes( | 
|  | mixin1, mixin2, 'mixinType', mixin1.mixinType, mixin2.mixinType); | 
|  | } | 
|  | checkElementIdentities( | 
|  | element1, element2, 'library', element1.library, element2.library); | 
|  | checkElementIdentities(element1, element2, 'compilationUnit', | 
|  | element1.compilationUnit, element2.compilationUnit); | 
|  | checkTypeLists(element1, element2, 'typeVariables', element1.typeVariables, | 
|  | element2.typeVariables); | 
|  | checkTypes( | 
|  | element1, element2, 'thisType', element1.thisType, element2.thisType); | 
|  | checkTypes( | 
|  | element1, element2, 'rawType', element1.rawType, element2.rawType); | 
|  | 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, 'isProxy', element1.isProxy, element2.isProxy); | 
|  | check(element1, element2, 'isInjected', element1.isInjected, | 
|  | element2.isInjected); | 
|  | 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 = element2.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)); | 
|  |  | 
|  | checkElementIdentities( | 
|  | element1, | 
|  | element2, | 
|  | 'defaultConstructor', | 
|  | element1.lookupDefaultConstructor(), | 
|  | element2.lookupDefaultConstructor()); | 
|  |  | 
|  | visitMembers(element1, element2); | 
|  |  | 
|  | ClassElement superclass1 = element1.superclass; | 
|  | ClassElement superclass2 = element2.superclass; | 
|  | while (superclass1 != null && superclass1.isMixinApplication) { | 
|  | checkElementProperties( | 
|  | element1, element2, 'supermixin', superclass1, superclass2); | 
|  | superclass1 = superclass1.superclass; | 
|  | superclass2 = superclass2.superclass; | 
|  | } | 
|  | } | 
|  |  | 
|  | @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); | 
|  | 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); | 
|  | check(element1, element2, 'isInjected', element1.isInjected, | 
|  | element2.isInjected); | 
|  |  | 
|  | 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); | 
|  | check(element1, element2, 'asyncMarker', element1.asyncMarker, | 
|  | element2.asyncMarker); | 
|  | check(element1, element2, 'isInjected', element1.isInjected, | 
|  | element2.isInjected); | 
|  |  | 
|  | checkElementIdentities( | 
|  | element1, element2, 'library', element1.library, element2.library); | 
|  | checkElementIdentities(element1, element2, 'compilationUnit', | 
|  | element1.compilationUnit, element2.compilationUnit); | 
|  | checkElementIdentities(element1, element2, 'enclosingClass', | 
|  | element1.enclosingClass, element2.enclosingClass); | 
|  |  | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.type', | 
|  | element1.functionSignature.type, | 
|  | element2.functionSignature.type, | 
|  | areTypesEquivalent); | 
|  | checkElementLists( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.requiredParameters', | 
|  | element1.functionSignature.requiredParameters, | 
|  | element2.functionSignature.requiredParameters); | 
|  | checkElementLists( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.optionalParameters', | 
|  | element1.functionSignature.optionalParameters, | 
|  | element2.functionSignature.optionalParameters); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.requiredParameterCount', | 
|  | element1.functionSignature.requiredParameterCount, | 
|  | element2.functionSignature.requiredParameterCount); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.optionalParameterCount', | 
|  | element1.functionSignature.optionalParameterCount, | 
|  | element2.functionSignature.optionalParameterCount); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.optionalParametersAreNamed', | 
|  | element1.functionSignature.optionalParametersAreNamed, | 
|  | element2.functionSignature.optionalParametersAreNamed); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.hasOptionalParameters', | 
|  | element1.functionSignature.hasOptionalParameters, | 
|  | element2.functionSignature.hasOptionalParameters); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.parameterCount', | 
|  | element1.functionSignature.parameterCount, | 
|  | element2.functionSignature.parameterCount); | 
|  | checkElementLists( | 
|  | element1, | 
|  | element2, | 
|  | 'functionSignature.orderedOptionalParameters', | 
|  | element1.functionSignature.orderedOptionalParameters, | 
|  | element2.functionSignature.orderedOptionalParameters); | 
|  | checkTypeLists(element1, element2, 'typeVariables', element1.typeVariables, | 
|  | element2.typeVariables); | 
|  | } | 
|  |  | 
|  | @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); | 
|  | if (!element1.isSynthesized) { | 
|  | check(element1, element2, 'sourcePosition', element1.sourcePosition, | 
|  | element2.sourcePosition); | 
|  | } else { | 
|  | check(element1, element2, 'sourcePosition.uri', | 
|  | element1.sourcePosition.uri, element2.sourcePosition.uri); | 
|  | } | 
|  | checkListEquivalence(element1, element2, 'parameters', element1.parameters, | 
|  | element2.parameters, checkElementProperties); | 
|  | checkTypes(element1, element2, 'type', element1.type, element2.type); | 
|  | check(element1, element2, 'isExternal', element1.isExternal, | 
|  | element2.isExternal); | 
|  | if (element1.isConst && !element1.isExternal) { | 
|  | constantConstructorEquivalence( | 
|  | element1.constantConstructor, element2.constantConstructor); | 
|  | } | 
|  | check(element1, element2, 'isRedirectingGenerative', | 
|  | element1.isRedirectingGenerative, element2.isRedirectingGenerative); | 
|  | check(element1, element2, 'isRedirectingFactory', | 
|  | element1.isRedirectingFactory, element2.isRedirectingFactory); | 
|  | checkElementIdentities(element1, element2, 'effectiveTarget', | 
|  | element1.effectiveTarget, element2.effectiveTarget); | 
|  | if (element1.isRedirectingFactory) { | 
|  | checkElementIdentities( | 
|  | element1, | 
|  | element2, | 
|  | 'immediateRedirectionTarget', | 
|  | element1.immediateRedirectionTarget, | 
|  | element2.immediateRedirectionTarget); | 
|  | checkElementIdentities( | 
|  | element1, | 
|  | element2, | 
|  | 'redirectionDeferredPrefix', | 
|  | element1.redirectionDeferredPrefix, | 
|  | element2.redirectionDeferredPrefix); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'isEffectiveTargetMalformed', | 
|  | element1.isEffectiveTargetMalformed, | 
|  | element2.isEffectiveTargetMalformed); | 
|  | } | 
|  | checkElementIdentities(element1, element2, 'definingConstructor', | 
|  | element1.definingConstructor, element2.definingConstructor); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'effectiveTargetType', | 
|  | element1.computeEffectiveTargetType(element1.enclosingClass.thisType), | 
|  | element2.computeEffectiveTargetType(element2.enclosingClass.thisType), | 
|  | areTypesEquivalent); | 
|  | check( | 
|  | element1, | 
|  | element2, | 
|  | 'effectiveTargetType.raw', | 
|  | element1.computeEffectiveTargetType(element1.enclosingClass.rawType), | 
|  | element2.computeEffectiveTargetType(element2.enclosingClass.rawType), | 
|  | areTypesEquivalent); | 
|  | checkElementIdentities( | 
|  | element1, | 
|  | element2, | 
|  | 'immediateRedirectionTarget', | 
|  | element1.immediateRedirectionTarget, | 
|  | element2.immediateRedirectionTarget); | 
|  | checkElementIdentities(element1, element2, 'redirectionDeferredPrefix', | 
|  | element1.redirectionDeferredPrefix, element2.redirectionDeferredPrefix); | 
|  | check(element1, element2, 'isInjected', element1.isInjected, | 
|  | element2.isInjected); | 
|  | } | 
|  |  | 
|  | @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, 'deferredImport', | 
|  | element1.deferredImport, element2.deferredImport); | 
|  | if (element1.isDeferred) { | 
|  | checkElementProperties(element1, element2, 'loadLibrary', | 
|  | element1.loadLibrary, element2.loadLibrary); | 
|  | } | 
|  | element1.forEachLocalMember((Element member1) { | 
|  | String name = member1.name; | 
|  | Element member2 = element2.lookupLocalMember(name); | 
|  | checkElementIdentities( | 
|  | element1, element2, 'lookupLocalMember:$name', member1, member2); | 
|  | checkImportsFor(element1, element2, member1, member2); | 
|  | }); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitErroneousElement( | 
|  | ErroneousElement element1, ErroneousElement element2) { | 
|  | check(element1, element2, 'messageKind', element1.messageKind, | 
|  | element2.messageKind); | 
|  | } | 
|  | } |