| Index: tests/compiler/dart2js/serialization/model_test.dart
|
| diff --git a/tests/compiler/dart2js/serialization/model_test.dart b/tests/compiler/dart2js/serialization/model_test.dart
|
| index 84a54f7ee67e0d91f9bafcdd98295be7798df490..e8596bbd67ab029abb275f4f48e9c2b927598ca6 100644
|
| --- a/tests/compiler/dart2js/serialization/model_test.dart
|
| +++ b/tests/compiler/dart2js/serialization/model_test.dart
|
| @@ -8,6 +8,7 @@ import 'dart:async';
|
| import 'dart:io';
|
| import 'package:async_helper/async_helper.dart';
|
| import 'package:expect/expect.dart';
|
| +import 'package:compiler/src/closure.dart';
|
| import 'package:compiler/src/commandline_options.dart';
|
| import 'package:compiler/src/common/backend_api.dart';
|
| import 'package:compiler/src/common/names.dart';
|
| @@ -22,6 +23,7 @@ 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/tree/nodes.dart';
|
| import 'package:compiler/src/universe/world_impact.dart';
|
| import 'package:compiler/src/universe/class_set.dart';
|
| import 'package:compiler/src/universe/use.dart';
|
| @@ -36,13 +38,13 @@ main(List<String> args) {
|
| String serializedData = await serializeDartCore(arguments: arguments);
|
| if (arguments.filename != null) {
|
| Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.filename));
|
| - await check(serializedData, entryPoint);
|
| + await checkModels(serializedData, entryPoint);
|
| } else {
|
| Uri entryPoint = Uri.parse('memory:main.dart');
|
| for (Test test in TESTS) {
|
| print('==============================================================');
|
| print(test.sourceFiles);
|
| - await check(
|
| + await checkModels(
|
| serializedData,
|
| entryPoint,
|
| sourceFiles: test.sourceFiles,
|
| @@ -52,7 +54,7 @@ main(List<String> args) {
|
| });
|
| }
|
|
|
| -Future check(
|
| +Future checkModels(
|
| String serializedData,
|
| Uri entryPoint,
|
| {Map<String, String> sourceFiles: const <String, String>{},
|
| @@ -68,6 +70,7 @@ Future check(
|
| await compilerNormal.run(entryPoint);
|
| compilerNormal.phase = Compiler.PHASE_DONE_RESOLVING;
|
| compilerNormal.world.populate();
|
| + compilerNormal.backend.onResolutionComplete();
|
|
|
| print('------------------------------------------------------------------');
|
| print('compile deserialized');
|
| @@ -80,6 +83,7 @@ Future check(
|
| await compilerDeserialized.run(entryPoint);
|
| compilerDeserialized.phase = Compiler.PHASE_DONE_RESOLVING;
|
| compilerDeserialized.world.populate();
|
| + compilerDeserialized.backend.onResolutionComplete();
|
|
|
| checkAllImpacts(
|
| compilerNormal, compilerDeserialized,
|
| @@ -111,6 +115,10 @@ Future check(
|
| compilerDeserialized.enqueuer.resolution.processedElements,
|
| "Processed element mismatch",
|
| areElementsEquivalent,
|
| + onSameElement: (a, b) {
|
| + checkElements(
|
| + compilerNormal, compilerDeserialized, a, b, verbose: verbose);
|
| + },
|
| verbose: verbose);
|
|
|
| checkClassHierarchyNodes(
|
| @@ -123,6 +131,57 @@ Future check(
|
| verbose: verbose);
|
| }
|
|
|
| +void checkElements(
|
| + Compiler compiler1, Compiler compiler2,
|
| + Element element1, Element element2,
|
| + {bool verbose: false}) {
|
| + if (element1.isFunction ||
|
| + element1.isConstructor ||
|
| + (element1.isField && element1.isInstanceMember)) {
|
| + ClosureClassMap closureData1 =
|
| + compiler1.closureToClassMapper.computeClosureToClassMapping(
|
| + compiler1.backend.frontend.getResolvedAst(element1.declaration));
|
| + ClosureClassMap closureData2 =
|
| + compiler2.closureToClassMapper.computeClosureToClassMapping(
|
| + compiler2.backend.frontend.getResolvedAst(element2.declaration));
|
| +
|
| + checkElementIdentities(closureData1, closureData2,
|
| + '$element1.closureElement',
|
| + closureData1.closureElement, closureData2.closureElement);
|
| + checkElementIdentities(closureData1, closureData2,
|
| + '$element1.closureClassElement',
|
| + closureData1.closureClassElement, closureData2.closureClassElement);
|
| + checkElementIdentities(closureData1, closureData2,
|
| + '$element1.callElement',
|
| + closureData1.callElement, closureData2.callElement);
|
| + check(closureData1, closureData2,
|
| + '$element1.thisLocal',
|
| + closureData1.thisLocal, closureData2.thisLocal,
|
| + areLocalsEquivalent);
|
| + checkMaps(
|
| + closureData1.freeVariableMap,
|
| + closureData2.freeVariableMap,
|
| + "$element1.freeVariableMap",
|
| + areLocalsEquivalent,
|
| + areCapturedVariablesEquivalent,
|
| + verbose: verbose);
|
| + checkMaps(
|
| + closureData1.capturingScopes,
|
| + closureData2.capturingScopes,
|
| + "$element1.capturingScopes",
|
| + areNodesEquivalent,
|
| + areClosureScopesEquivalent,
|
| + verbose: verbose,
|
| + keyToString: nodeToString);
|
| + checkSets(
|
| + closureData1.variablesUsedInTryOrGenerator,
|
| + closureData2.variablesUsedInTryOrGenerator,
|
| + "$element1.variablesUsedInTryOrGenerator",
|
| + areLocalsEquivalent,
|
| + verbose: verbose);
|
| + }
|
| +}
|
| +
|
| void checkMixinUses(
|
| Compiler compiler1, Compiler compiler2,
|
| ClassElement class1, ClassElement class2,
|
| @@ -182,13 +241,16 @@ void checkSets(
|
| Iterable set1,
|
| Iterable set2,
|
| String messagePrefix,
|
| - bool areEquivalent(a, b),
|
| + bool sameElement(a, b),
|
| {bool failOnUnfound: true,
|
| - bool verbose: false}) {
|
| + bool verbose: false,
|
| + void onSameElement(a, b)}) {
|
| List common = [];
|
| List unfound = [];
|
| Set remaining = computeSetDifference(
|
| - set1, set2, common, unfound, areEquivalent);
|
| + set1, set2, common, unfound,
|
| + sameElement: sameElement,
|
| + checkElements: onSameElement);
|
| StringBuffer sb = new StringBuffer();
|
| sb.write("$messagePrefix:");
|
| if (verbose) {
|
| @@ -212,3 +274,136 @@ void checkSets(
|
| print(message);
|
| }
|
| }
|
| +
|
| +String defaultToString(obj) => '$obj';
|
| +
|
| +void checkMaps(
|
| + Map map1,
|
| + Map map2,
|
| + String messagePrefix,
|
| + bool sameKey(a, b),
|
| + bool sameValue(a, b),
|
| + {bool failOnUnfound: true,
|
| + bool failOnMismatch: true,
|
| + bool verbose: false,
|
| + String keyToString(key): defaultToString,
|
| + String valueToString(key): defaultToString}) {
|
| + List common = [];
|
| + List unfound = [];
|
| + List<List> mismatch = <List>[];
|
| + Set remaining = computeSetDifference(
|
| + map1.keys, map2.keys, common, unfound,
|
| + sameElement: sameKey,
|
| + checkElements: (k1, k2) {
|
| + var v1 = map1[k1];
|
| + var v2 = map2[k2];
|
| + if (!sameValue(v1, v2)) {
|
| + mismatch.add([k1, k2]);
|
| + }
|
| + });
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write("$messagePrefix:");
|
| + if (verbose) {
|
| + sb.write("\n Common: \n");
|
| + for (List pair in common) {
|
| + var k1 = pair[0];
|
| + var k2 = pair[1];
|
| + var v1 = map1[k1];
|
| + var v2 = map2[k2];
|
| + sb.write(" key1 =${keyToString(k1)}\n");
|
| + sb.write(" key2 =${keyToString(k2)}\n");
|
| + sb.write(" value1=${valueToString(v1)}\n");
|
| + sb.write(" value2=${valueToString(v2)}\n");
|
| + }
|
| + }
|
| + if (unfound.isNotEmpty || verbose) {
|
| + sb.write("\n Unfound: \n");
|
| + for (var k1 in unfound) {
|
| + var v1 = map1[k1];
|
| + sb.write(" key1 =${keyToString(k1)}\n");
|
| + sb.write(" value1=${valueToString(v1)}\n");
|
| + }
|
| + }
|
| + if (remaining.isNotEmpty || verbose) {
|
| + sb.write("\n Extra: \n");
|
| + for (var k2 in remaining) {
|
| + var v2 = map2[k2];
|
| + sb.write(" key2 =${keyToString(k2)}\n");
|
| + sb.write(" value2=${valueToString(v2)}\n");
|
| + }
|
| + }
|
| + if (mismatch.isNotEmpty || verbose) {
|
| + sb.write("\n Mismatch: \n");
|
| + for (List pair in mismatch) {
|
| + var k1 = pair[0];
|
| + var k2 = pair[1];
|
| + var v1 = map1[k1];
|
| + var v2 = map2[k2];
|
| + sb.write(" key1 =${keyToString(k1)}\n");
|
| + sb.write(" key2 =${keyToString(k2)}\n");
|
| + sb.write(" value1=${valueToString(v1)}\n");
|
| + sb.write(" value2=${valueToString(v2)}\n");
|
| + }
|
| + }
|
| + String message = sb.toString();
|
| + if (unfound.isNotEmpty || mismatch.isNotEmpty || remaining.isNotEmpty) {
|
| + if ((unfound.isNotEmpty && failOnUnfound) ||
|
| + (mismatch.isNotEmpty && failOnMismatch) ||
|
| + remaining.isNotEmpty) {
|
| + Expect.fail(message);
|
| + } else {
|
| + print(message);
|
| + }
|
| + } else if (verbose) {
|
| + print(message);
|
| + }
|
| +}
|
| +
|
| +bool areLocalsEquivalent(Local a, Local b) {
|
| + if (a == b) return true;
|
| + if (a == null || b == null) return false;
|
| +
|
| + if (a is Element) {
|
| + return b is Element && areElementsEquivalent(a as Element, b as Element);
|
| + } else {
|
| + return a.runtimeType == b.runtimeType &&
|
| + areElementsEquivalent(a.executableContext, b.executableContext);
|
| + }
|
| +}
|
| +
|
| +bool areCapturedVariablesEquivalent(CapturedVariable a, CapturedVariable b) {
|
| + if (a == b) return true;
|
| + if (a == null || b == null) return false;
|
| + if (a is ClosureFieldElement && b is ClosureFieldElement) {
|
| + return areElementsEquivalent(a.closureClass, b.closureClass) &&
|
| + areLocalsEquivalent(a.local, b.local);
|
| + } else if (a is BoxFieldElement && b is BoxFieldElement) {
|
| + return areElementsEquivalent(a.variableElement, b.variableElement) &&
|
| + areLocalsEquivalent(a.box, b.box);
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool areClosureScopesEquivalent(ClosureScope a, ClosureScope b) {
|
| + if (a == b) return true;
|
| + if (a == null || b == null) return false;
|
| + if (!areLocalsEquivalent(a.boxElement, b.boxElement)) {
|
| + return false;
|
| + }
|
| + checkMaps(a.capturedVariables, b.capturedVariables,
|
| + 'ClosureScope.capturedVariables',
|
| + areLocalsEquivalent,
|
| + areElementsEquivalent);
|
| + checkSets(a.boxedLoopVariables, b.boxedLoopVariables,
|
| + 'ClosureScope.boxedLoopVariables',
|
| + areElementsEquivalent);
|
| + return true;
|
| +}
|
| +
|
| +String nodeToString(Node node) {
|
| + String text = '$node';
|
| + if (text.length > 40) {
|
| + return '(${node.runtimeType}) ${text.substring(0, 37)}...';
|
| + }
|
| + return '(${node.runtimeType}) $text';
|
| +}
|
|
|