blob: 0e5363f3b4b2e26430bf87c94623f1c8e4ccaa37 [file] [log] [blame]
// 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);
}
}