| // 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_model_test; |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| import 'package:async_helper/async_helper.dart'; |
| import 'package:expect/expect.dart'; |
| import 'package:compiler/src/commandline_options.dart'; |
| import 'package:compiler/src/common/backend_api.dart'; |
| import 'package:compiler/src/common/names.dart'; |
| import 'package:compiler/src/common/resolution.dart'; |
| import 'package:compiler/src/compiler.dart'; |
| import 'package:compiler/src/dart_types.dart'; |
| import 'package:compiler/src/elements/elements.dart'; |
| import 'package:compiler/src/filenames.dart'; |
| import 'package:compiler/src/serialization/element_serialization.dart'; |
| import 'package:compiler/src/serialization/impact_serialization.dart'; |
| import 'package:compiler/src/serialization/json_serializer.dart'; |
| import 'package:compiler/src/serialization/serialization.dart'; |
| import 'package:compiler/src/serialization/equivalence.dart'; |
| import 'package:compiler/src/serialization/task.dart'; |
| import 'package:compiler/src/universe/world_impact.dart'; |
| import 'package:compiler/src/universe/class_set.dart'; |
| import 'package:compiler/src/universe/use.dart'; |
| import 'memory_compiler.dart'; |
| import 'serialization_helper.dart'; |
| import 'serialization_test_data.dart'; |
| import 'serialization_test_helper.dart'; |
| |
| main(List<String> arguments) { |
| String filename; |
| for (String arg in arguments) { |
| if (!arg.startsWith('-')) { |
| filename = arg; |
| } |
| } |
| bool verbose = arguments.contains('-v'); |
| |
| asyncTest(() async { |
| print('------------------------------------------------------------------'); |
| print('serialize dart:core'); |
| print('------------------------------------------------------------------'); |
| String serializedData; |
| File file = new File('out.data'); |
| if (arguments.contains('-l')) { |
| if (file.existsSync()) { |
| print('Loading data from $file'); |
| serializedData = file.readAsStringSync(); |
| } |
| } |
| if (serializedData == null) { |
| serializedData = await serializeDartCore(); |
| if (arguments.contains('-s')) { |
| print('Saving data to $file'); |
| file.writeAsStringSync(serializedData); |
| } |
| } |
| if (filename != null) { |
| Uri entryPoint = Uri.base.resolve(nativeToUriPath(filename)); |
| await check(serializedData, entryPoint); |
| } else { |
| Uri entryPoint = Uri.parse('memory:main.dart'); |
| for (Test test in TESTS) { |
| if (test.sourceFiles['main.dart'] |
| .contains('main(List<String> arguments)')) { |
| // TODO(johnniwinther): Check this test. |
| continue; |
| } |
| print('=============================================================='); |
| print(test.sourceFiles); |
| await check( |
| serializedData, |
| entryPoint, |
| sourceFiles: test.sourceFiles, |
| verbose: verbose); |
| } |
| } |
| }); |
| } |
| |
| Future check( |
| String serializedData, |
| Uri entryPoint, |
| {Map<String, String> sourceFiles: const <String, String>{}, |
| bool verbose: false}) async { |
| |
| print('------------------------------------------------------------------'); |
| print('compile normal'); |
| print('------------------------------------------------------------------'); |
| Compiler compilerNormal = compilerFor( |
| memorySourceFiles: sourceFiles, |
| options: [Flags.analyzeOnly]); |
| compilerNormal.resolution.retainCachesForTesting = true; |
| await compilerNormal.run(entryPoint); |
| compilerNormal.world.populate(); |
| |
| print('------------------------------------------------------------------'); |
| print('compile deserialized'); |
| print('------------------------------------------------------------------'); |
| Compiler compilerDeserialized = compilerFor( |
| memorySourceFiles: sourceFiles, |
| options: [Flags.analyzeOnly]); |
| compilerDeserialized.resolution.retainCachesForTesting = true; |
| deserialize(compilerDeserialized, serializedData); |
| await compilerDeserialized.run(entryPoint); |
| compilerDeserialized.world.populate(); |
| |
| checkAllImpacts( |
| compilerNormal, compilerDeserialized, |
| verbose: verbose); |
| |
| checkSets( |
| compilerNormal.resolverWorld.directlyInstantiatedClasses, |
| compilerDeserialized.resolverWorld.directlyInstantiatedClasses, |
| "Directly instantiated classes mismatch", |
| areElementsEquivalent, |
| verbose: verbose); |
| |
| checkSets( |
| compilerNormal.resolverWorld.instantiatedTypes, |
| compilerDeserialized.resolverWorld.instantiatedTypes, |
| "Instantiated types mismatch", |
| areTypesEquivalent, |
| // TODO(johnniwinther): Ensure that all instantiated types are tracked. |
| failOnUnfound: false, |
| verbose: verbose); |
| |
| checkSets( |
| compilerNormal.resolverWorld.isChecks, |
| compilerDeserialized.resolverWorld.isChecks, |
| "Is-check mismatch", |
| areTypesEquivalent, |
| verbose: verbose); |
| |
| checkSets( |
| compilerNormal.enqueuer.resolution.processedElements, |
| compilerDeserialized.enqueuer.resolution.processedElements, |
| "Processed element mismatch", |
| areElementsEquivalent, |
| verbose: verbose); |
| |
| checkClassHierarchyNodes( |
| compilerNormal.world.getClassHierarchyNode( |
| compilerNormal.coreClasses.objectClass), |
| compilerDeserialized.world.getClassHierarchyNode( |
| compilerDeserialized.coreClasses.objectClass), |
| verbose: verbose); |
| } |
| |
| void checkClassHierarchyNodes( |
| ClassHierarchyNode a, ClassHierarchyNode b, |
| {bool verbose: false}) { |
| if (verbose) { |
| print('Checking $a vs $b'); |
| } |
| Expect.isTrue( |
| areElementsEquivalent(a.cls, b.cls), |
| "Element identity mismatch for ${a.cls} vs ${b.cls}."); |
| Expect.equals( |
| a.isDirectlyInstantiated, |
| b.isDirectlyInstantiated, |
| "Value mismatch for 'isDirectlyInstantiated' for ${a.cls} vs ${b.cls}."); |
| Expect.equals( |
| a.isIndirectlyInstantiated, |
| b.isIndirectlyInstantiated, |
| "Value mismatch for 'isIndirectlyInstantiated' " |
| "for ${a.cls} vs ${b.cls}."); |
| // TODO(johnniwinther): Enforce a canonical and stable order on direct |
| // subclasses. |
| for (ClassHierarchyNode child in a.directSubclasses) { |
| bool found = false; |
| for (ClassHierarchyNode other in b.directSubclasses) { |
| if (areElementsEquivalent(child.cls, other.cls)) { |
| checkClassHierarchyNodes(child, other, |
| verbose: verbose); |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| Expect.isFalse( |
| child.isInstantiated, 'Missing subclass ${child.cls} of ${a.cls}'); |
| } |
| } |
| } |
| |
| void checkSets( |
| Iterable set1, |
| Iterable set2, |
| String messagePrefix, |
| bool areEquivalent(a, b), |
| {bool failOnUnfound: true, |
| bool verbose: false}) { |
| List common = []; |
| List unfound = []; |
| Set remaining = computeSetDifference( |
| set1, set2, common, unfound, areEquivalent); |
| StringBuffer sb = new StringBuffer(); |
| sb.write("$messagePrefix:"); |
| if (verbose) { |
| sb.write("\n Common:\n ${common.join('\n ')}"); |
| } |
| if (unfound.isNotEmpty || verbose) { |
| sb.write("\n Unfound:\n ${unfound.join('\n ')}"); |
| } |
| if (remaining.isNotEmpty || verbose) { |
| sb.write("\n Extra: \n ${remaining.join('\n ')}"); |
| } |
| String message = sb.toString(); |
| if (unfound.isNotEmpty || remaining.isNotEmpty) { |
| |
| if (failOnUnfound || remaining.isNotEmpty) { |
| Expect.fail(message); |
| } else { |
| print(message); |
| } |
| } else if (verbose) { |
| print(message); |
| } |
| } |