OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.serialization_model_test; | 5 library dart2js.serialization_model_test; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 import 'package:async_helper/async_helper.dart'; | 9 import 'package:async_helper/async_helper.dart'; |
10 import 'package:expect/expect.dart'; | 10 import 'package:expect/expect.dart'; |
| 11 import 'package:compiler/src/closure.dart'; |
11 import 'package:compiler/src/commandline_options.dart'; | 12 import 'package:compiler/src/commandline_options.dart'; |
12 import 'package:compiler/src/common/backend_api.dart'; | 13 import 'package:compiler/src/common/backend_api.dart'; |
13 import 'package:compiler/src/common/names.dart'; | 14 import 'package:compiler/src/common/names.dart'; |
14 import 'package:compiler/src/common/resolution.dart'; | 15 import 'package:compiler/src/common/resolution.dart'; |
15 import 'package:compiler/src/compiler.dart'; | 16 import 'package:compiler/src/compiler.dart'; |
16 import 'package:compiler/src/dart_types.dart'; | 17 import 'package:compiler/src/dart_types.dart'; |
17 import 'package:compiler/src/elements/elements.dart'; | 18 import 'package:compiler/src/elements/elements.dart'; |
18 import 'package:compiler/src/filenames.dart'; | 19 import 'package:compiler/src/filenames.dart'; |
19 import 'package:compiler/src/serialization/element_serialization.dart'; | 20 import 'package:compiler/src/serialization/element_serialization.dart'; |
20 import 'package:compiler/src/serialization/impact_serialization.dart'; | 21 import 'package:compiler/src/serialization/impact_serialization.dart'; |
21 import 'package:compiler/src/serialization/json_serializer.dart'; | 22 import 'package:compiler/src/serialization/json_serializer.dart'; |
22 import 'package:compiler/src/serialization/serialization.dart'; | 23 import 'package:compiler/src/serialization/serialization.dart'; |
23 import 'package:compiler/src/serialization/equivalence.dart'; | 24 import 'package:compiler/src/serialization/equivalence.dart'; |
24 import 'package:compiler/src/serialization/task.dart'; | 25 import 'package:compiler/src/serialization/task.dart'; |
| 26 import 'package:compiler/src/tree/nodes.dart'; |
25 import 'package:compiler/src/universe/world_impact.dart'; | 27 import 'package:compiler/src/universe/world_impact.dart'; |
26 import 'package:compiler/src/universe/class_set.dart'; | 28 import 'package:compiler/src/universe/class_set.dart'; |
27 import 'package:compiler/src/universe/use.dart'; | 29 import 'package:compiler/src/universe/use.dart'; |
28 import '../memory_compiler.dart'; | 30 import '../memory_compiler.dart'; |
29 import 'helper.dart'; | 31 import 'helper.dart'; |
30 import 'test_data.dart'; | 32 import 'test_data.dart'; |
31 import 'test_helper.dart'; | 33 import 'test_helper.dart'; |
32 | 34 |
33 main(List<String> args) { | 35 main(List<String> args) { |
34 asyncTest(() async { | 36 asyncTest(() async { |
35 Arguments arguments = new Arguments.from(args); | 37 Arguments arguments = new Arguments.from(args); |
36 String serializedData = await serializeDartCore(arguments: arguments); | 38 String serializedData = await serializeDartCore(arguments: arguments); |
37 if (arguments.filename != null) { | 39 if (arguments.filename != null) { |
38 Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.filename)); | 40 Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.filename)); |
39 await check(serializedData, entryPoint); | 41 await checkModels(serializedData, entryPoint); |
40 } else { | 42 } else { |
41 Uri entryPoint = Uri.parse('memory:main.dart'); | 43 Uri entryPoint = Uri.parse('memory:main.dart'); |
42 for (Test test in TESTS) { | 44 for (Test test in TESTS) { |
43 print('=============================================================='); | 45 print('=============================================================='); |
44 print(test.sourceFiles); | 46 print(test.sourceFiles); |
45 await check( | 47 await checkModels( |
46 serializedData, | 48 serializedData, |
47 entryPoint, | 49 entryPoint, |
48 sourceFiles: test.sourceFiles, | 50 sourceFiles: test.sourceFiles, |
49 verbose: arguments.verbose); | 51 verbose: arguments.verbose); |
50 } | 52 } |
51 } | 53 } |
52 }); | 54 }); |
53 } | 55 } |
54 | 56 |
55 Future check( | 57 Future checkModels( |
56 String serializedData, | 58 String serializedData, |
57 Uri entryPoint, | 59 Uri entryPoint, |
58 {Map<String, String> sourceFiles: const <String, String>{}, | 60 {Map<String, String> sourceFiles: const <String, String>{}, |
59 bool verbose: false}) async { | 61 bool verbose: false}) async { |
60 | 62 |
61 print('------------------------------------------------------------------'); | 63 print('------------------------------------------------------------------'); |
62 print('compile normal'); | 64 print('compile normal'); |
63 print('------------------------------------------------------------------'); | 65 print('------------------------------------------------------------------'); |
64 Compiler compilerNormal = compilerFor( | 66 Compiler compilerNormal = compilerFor( |
65 memorySourceFiles: sourceFiles, | 67 memorySourceFiles: sourceFiles, |
66 options: [Flags.analyzeOnly]); | 68 options: [Flags.analyzeOnly]); |
67 compilerNormal.resolution.retainCachesForTesting = true; | 69 compilerNormal.resolution.retainCachesForTesting = true; |
68 await compilerNormal.run(entryPoint); | 70 await compilerNormal.run(entryPoint); |
69 compilerNormal.phase = Compiler.PHASE_DONE_RESOLVING; | 71 compilerNormal.phase = Compiler.PHASE_DONE_RESOLVING; |
70 compilerNormal.world.populate(); | 72 compilerNormal.world.populate(); |
| 73 compilerNormal.backend.onResolutionComplete(); |
71 | 74 |
72 print('------------------------------------------------------------------'); | 75 print('------------------------------------------------------------------'); |
73 print('compile deserialized'); | 76 print('compile deserialized'); |
74 print('------------------------------------------------------------------'); | 77 print('------------------------------------------------------------------'); |
75 Compiler compilerDeserialized = compilerFor( | 78 Compiler compilerDeserialized = compilerFor( |
76 memorySourceFiles: sourceFiles, | 79 memorySourceFiles: sourceFiles, |
77 options: [Flags.analyzeOnly]); | 80 options: [Flags.analyzeOnly]); |
78 compilerDeserialized.resolution.retainCachesForTesting = true; | 81 compilerDeserialized.resolution.retainCachesForTesting = true; |
79 deserialize(compilerDeserialized, serializedData); | 82 deserialize(compilerDeserialized, serializedData); |
80 await compilerDeserialized.run(entryPoint); | 83 await compilerDeserialized.run(entryPoint); |
81 compilerDeserialized.phase = Compiler.PHASE_DONE_RESOLVING; | 84 compilerDeserialized.phase = Compiler.PHASE_DONE_RESOLVING; |
82 compilerDeserialized.world.populate(); | 85 compilerDeserialized.world.populate(); |
| 86 compilerDeserialized.backend.onResolutionComplete(); |
83 | 87 |
84 checkAllImpacts( | 88 checkAllImpacts( |
85 compilerNormal, compilerDeserialized, | 89 compilerNormal, compilerDeserialized, |
86 verbose: verbose); | 90 verbose: verbose); |
87 | 91 |
88 checkSets( | 92 checkSets( |
89 compilerNormal.resolverWorld.directlyInstantiatedClasses, | 93 compilerNormal.resolverWorld.directlyInstantiatedClasses, |
90 compilerDeserialized.resolverWorld.directlyInstantiatedClasses, | 94 compilerDeserialized.resolverWorld.directlyInstantiatedClasses, |
91 "Directly instantiated classes mismatch", | 95 "Directly instantiated classes mismatch", |
92 areElementsEquivalent, | 96 areElementsEquivalent, |
(...skipping 11 matching lines...) Expand all Loading... |
104 compilerDeserialized.resolverWorld.isChecks, | 108 compilerDeserialized.resolverWorld.isChecks, |
105 "Is-check mismatch", | 109 "Is-check mismatch", |
106 areTypesEquivalent, | 110 areTypesEquivalent, |
107 verbose: verbose); | 111 verbose: verbose); |
108 | 112 |
109 checkSets( | 113 checkSets( |
110 compilerNormal.enqueuer.resolution.processedElements, | 114 compilerNormal.enqueuer.resolution.processedElements, |
111 compilerDeserialized.enqueuer.resolution.processedElements, | 115 compilerDeserialized.enqueuer.resolution.processedElements, |
112 "Processed element mismatch", | 116 "Processed element mismatch", |
113 areElementsEquivalent, | 117 areElementsEquivalent, |
| 118 onSameElement: (a, b) { |
| 119 checkElements( |
| 120 compilerNormal, compilerDeserialized, a, b, verbose: verbose); |
| 121 }, |
114 verbose: verbose); | 122 verbose: verbose); |
115 | 123 |
116 checkClassHierarchyNodes( | 124 checkClassHierarchyNodes( |
117 compilerNormal, | 125 compilerNormal, |
118 compilerDeserialized, | 126 compilerDeserialized, |
119 compilerNormal.world.getClassHierarchyNode( | 127 compilerNormal.world.getClassHierarchyNode( |
120 compilerNormal.coreClasses.objectClass), | 128 compilerNormal.coreClasses.objectClass), |
121 compilerDeserialized.world.getClassHierarchyNode( | 129 compilerDeserialized.world.getClassHierarchyNode( |
122 compilerDeserialized.coreClasses.objectClass), | 130 compilerDeserialized.coreClasses.objectClass), |
123 verbose: verbose); | 131 verbose: verbose); |
124 } | 132 } |
125 | 133 |
| 134 void checkElements( |
| 135 Compiler compiler1, Compiler compiler2, |
| 136 Element element1, Element element2, |
| 137 {bool verbose: false}) { |
| 138 if (element1.isFunction || |
| 139 element1.isConstructor || |
| 140 (element1.isField && element1.isInstanceMember)) { |
| 141 ClosureClassMap closureData1 = |
| 142 compiler1.closureToClassMapper.computeClosureToClassMapping( |
| 143 compiler1.backend.frontend.getResolvedAst(element1.declaration)); |
| 144 ClosureClassMap closureData2 = |
| 145 compiler2.closureToClassMapper.computeClosureToClassMapping( |
| 146 compiler2.backend.frontend.getResolvedAst(element2.declaration)); |
| 147 |
| 148 checkElementIdentities(closureData1, closureData2, |
| 149 '$element1.closureElement', |
| 150 closureData1.closureElement, closureData2.closureElement); |
| 151 checkElementIdentities(closureData1, closureData2, |
| 152 '$element1.closureClassElement', |
| 153 closureData1.closureClassElement, closureData2.closureClassElement); |
| 154 checkElementIdentities(closureData1, closureData2, |
| 155 '$element1.callElement', |
| 156 closureData1.callElement, closureData2.callElement); |
| 157 check(closureData1, closureData2, |
| 158 '$element1.thisLocal', |
| 159 closureData1.thisLocal, closureData2.thisLocal, |
| 160 areLocalsEquivalent); |
| 161 checkMaps( |
| 162 closureData1.freeVariableMap, |
| 163 closureData2.freeVariableMap, |
| 164 "$element1.freeVariableMap", |
| 165 areLocalsEquivalent, |
| 166 areCapturedVariablesEquivalent, |
| 167 verbose: verbose); |
| 168 checkMaps( |
| 169 closureData1.capturingScopes, |
| 170 closureData2.capturingScopes, |
| 171 "$element1.capturingScopes", |
| 172 areNodesEquivalent, |
| 173 areClosureScopesEquivalent, |
| 174 verbose: verbose, |
| 175 keyToString: nodeToString); |
| 176 checkSets( |
| 177 closureData1.variablesUsedInTryOrGenerator, |
| 178 closureData2.variablesUsedInTryOrGenerator, |
| 179 "$element1.variablesUsedInTryOrGenerator", |
| 180 areLocalsEquivalent, |
| 181 verbose: verbose); |
| 182 } |
| 183 } |
| 184 |
126 void checkMixinUses( | 185 void checkMixinUses( |
127 Compiler compiler1, Compiler compiler2, | 186 Compiler compiler1, Compiler compiler2, |
128 ClassElement class1, ClassElement class2, | 187 ClassElement class1, ClassElement class2, |
129 {bool verbose: false}) { | 188 {bool verbose: false}) { |
130 | 189 |
131 checkSets( | 190 checkSets( |
132 compiler1.world.mixinUsesOf(class1), | 191 compiler1.world.mixinUsesOf(class1), |
133 compiler2.world.mixinUsesOf(class2), | 192 compiler2.world.mixinUsesOf(class2), |
134 "Mixin uses of $class1 vs $class2", | 193 "Mixin uses of $class1 vs $class2", |
135 areElementsEquivalent, | 194 areElementsEquivalent, |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 'Missing subclass ${child.cls} of ${node1.cls}'); | 234 'Missing subclass ${child.cls} of ${node1.cls}'); |
176 } | 235 } |
177 } | 236 } |
178 checkMixinUses(compiler1, compiler2, node1.cls, node2.cls, verbose: verbose); | 237 checkMixinUses(compiler1, compiler2, node1.cls, node2.cls, verbose: verbose); |
179 } | 238 } |
180 | 239 |
181 void checkSets( | 240 void checkSets( |
182 Iterable set1, | 241 Iterable set1, |
183 Iterable set2, | 242 Iterable set2, |
184 String messagePrefix, | 243 String messagePrefix, |
185 bool areEquivalent(a, b), | 244 bool sameElement(a, b), |
186 {bool failOnUnfound: true, | 245 {bool failOnUnfound: true, |
187 bool verbose: false}) { | 246 bool verbose: false, |
| 247 void onSameElement(a, b)}) { |
188 List common = []; | 248 List common = []; |
189 List unfound = []; | 249 List unfound = []; |
190 Set remaining = computeSetDifference( | 250 Set remaining = computeSetDifference( |
191 set1, set2, common, unfound, areEquivalent); | 251 set1, set2, common, unfound, |
| 252 sameElement: sameElement, |
| 253 checkElements: onSameElement); |
192 StringBuffer sb = new StringBuffer(); | 254 StringBuffer sb = new StringBuffer(); |
193 sb.write("$messagePrefix:"); | 255 sb.write("$messagePrefix:"); |
194 if (verbose) { | 256 if (verbose) { |
195 sb.write("\n Common:\n ${common.join('\n ')}"); | 257 sb.write("\n Common:\n ${common.join('\n ')}"); |
196 } | 258 } |
197 if (unfound.isNotEmpty || verbose) { | 259 if (unfound.isNotEmpty || verbose) { |
198 sb.write("\n Unfound:\n ${unfound.join('\n ')}"); | 260 sb.write("\n Unfound:\n ${unfound.join('\n ')}"); |
199 } | 261 } |
200 if (remaining.isNotEmpty || verbose) { | 262 if (remaining.isNotEmpty || verbose) { |
201 sb.write("\n Extra: \n ${remaining.join('\n ')}"); | 263 sb.write("\n Extra: \n ${remaining.join('\n ')}"); |
202 } | 264 } |
203 String message = sb.toString(); | 265 String message = sb.toString(); |
204 if (unfound.isNotEmpty || remaining.isNotEmpty) { | 266 if (unfound.isNotEmpty || remaining.isNotEmpty) { |
205 | 267 |
206 if (failOnUnfound || remaining.isNotEmpty) { | 268 if (failOnUnfound || remaining.isNotEmpty) { |
207 Expect.fail(message); | 269 Expect.fail(message); |
208 } else { | 270 } else { |
209 print(message); | 271 print(message); |
210 } | 272 } |
211 } else if (verbose) { | 273 } else if (verbose) { |
212 print(message); | 274 print(message); |
213 } | 275 } |
214 } | 276 } |
| 277 |
| 278 String defaultToString(obj) => '$obj'; |
| 279 |
| 280 void checkMaps( |
| 281 Map map1, |
| 282 Map map2, |
| 283 String messagePrefix, |
| 284 bool sameKey(a, b), |
| 285 bool sameValue(a, b), |
| 286 {bool failOnUnfound: true, |
| 287 bool failOnMismatch: true, |
| 288 bool verbose: false, |
| 289 String keyToString(key): defaultToString, |
| 290 String valueToString(key): defaultToString}) { |
| 291 List common = []; |
| 292 List unfound = []; |
| 293 List<List> mismatch = <List>[]; |
| 294 Set remaining = computeSetDifference( |
| 295 map1.keys, map2.keys, common, unfound, |
| 296 sameElement: sameKey, |
| 297 checkElements: (k1, k2) { |
| 298 var v1 = map1[k1]; |
| 299 var v2 = map2[k2]; |
| 300 if (!sameValue(v1, v2)) { |
| 301 mismatch.add([k1, k2]); |
| 302 } |
| 303 }); |
| 304 StringBuffer sb = new StringBuffer(); |
| 305 sb.write("$messagePrefix:"); |
| 306 if (verbose) { |
| 307 sb.write("\n Common: \n"); |
| 308 for (List pair in common) { |
| 309 var k1 = pair[0]; |
| 310 var k2 = pair[1]; |
| 311 var v1 = map1[k1]; |
| 312 var v2 = map2[k2]; |
| 313 sb.write(" key1 =${keyToString(k1)}\n"); |
| 314 sb.write(" key2 =${keyToString(k2)}\n"); |
| 315 sb.write(" value1=${valueToString(v1)}\n"); |
| 316 sb.write(" value2=${valueToString(v2)}\n"); |
| 317 } |
| 318 } |
| 319 if (unfound.isNotEmpty || verbose) { |
| 320 sb.write("\n Unfound: \n"); |
| 321 for (var k1 in unfound) { |
| 322 var v1 = map1[k1]; |
| 323 sb.write(" key1 =${keyToString(k1)}\n"); |
| 324 sb.write(" value1=${valueToString(v1)}\n"); |
| 325 } |
| 326 } |
| 327 if (remaining.isNotEmpty || verbose) { |
| 328 sb.write("\n Extra: \n"); |
| 329 for (var k2 in remaining) { |
| 330 var v2 = map2[k2]; |
| 331 sb.write(" key2 =${keyToString(k2)}\n"); |
| 332 sb.write(" value2=${valueToString(v2)}\n"); |
| 333 } |
| 334 } |
| 335 if (mismatch.isNotEmpty || verbose) { |
| 336 sb.write("\n Mismatch: \n"); |
| 337 for (List pair in mismatch) { |
| 338 var k1 = pair[0]; |
| 339 var k2 = pair[1]; |
| 340 var v1 = map1[k1]; |
| 341 var v2 = map2[k2]; |
| 342 sb.write(" key1 =${keyToString(k1)}\n"); |
| 343 sb.write(" key2 =${keyToString(k2)}\n"); |
| 344 sb.write(" value1=${valueToString(v1)}\n"); |
| 345 sb.write(" value2=${valueToString(v2)}\n"); |
| 346 } |
| 347 } |
| 348 String message = sb.toString(); |
| 349 if (unfound.isNotEmpty || mismatch.isNotEmpty || remaining.isNotEmpty) { |
| 350 if ((unfound.isNotEmpty && failOnUnfound) || |
| 351 (mismatch.isNotEmpty && failOnMismatch) || |
| 352 remaining.isNotEmpty) { |
| 353 Expect.fail(message); |
| 354 } else { |
| 355 print(message); |
| 356 } |
| 357 } else if (verbose) { |
| 358 print(message); |
| 359 } |
| 360 } |
| 361 |
| 362 bool areLocalsEquivalent(Local a, Local b) { |
| 363 if (a == b) return true; |
| 364 if (a == null || b == null) return false; |
| 365 |
| 366 if (a is Element) { |
| 367 return b is Element && areElementsEquivalent(a as Element, b as Element); |
| 368 } else { |
| 369 return a.runtimeType == b.runtimeType && |
| 370 areElementsEquivalent(a.executableContext, b.executableContext); |
| 371 } |
| 372 } |
| 373 |
| 374 bool areCapturedVariablesEquivalent(CapturedVariable a, CapturedVariable b) { |
| 375 if (a == b) return true; |
| 376 if (a == null || b == null) return false; |
| 377 if (a is ClosureFieldElement && b is ClosureFieldElement) { |
| 378 return areElementsEquivalent(a.closureClass, b.closureClass) && |
| 379 areLocalsEquivalent(a.local, b.local); |
| 380 } else if (a is BoxFieldElement && b is BoxFieldElement) { |
| 381 return areElementsEquivalent(a.variableElement, b.variableElement) && |
| 382 areLocalsEquivalent(a.box, b.box); |
| 383 } |
| 384 return false; |
| 385 } |
| 386 |
| 387 bool areClosureScopesEquivalent(ClosureScope a, ClosureScope b) { |
| 388 if (a == b) return true; |
| 389 if (a == null || b == null) return false; |
| 390 if (!areLocalsEquivalent(a.boxElement, b.boxElement)) { |
| 391 return false; |
| 392 } |
| 393 checkMaps(a.capturedVariables, b.capturedVariables, |
| 394 'ClosureScope.capturedVariables', |
| 395 areLocalsEquivalent, |
| 396 areElementsEquivalent); |
| 397 checkSets(a.boxedLoopVariables, b.boxedLoopVariables, |
| 398 'ClosureScope.boxedLoopVariables', |
| 399 areElementsEquivalent); |
| 400 return true; |
| 401 } |
| 402 |
| 403 String nodeToString(Node node) { |
| 404 String text = '$node'; |
| 405 if (text.length > 40) { |
| 406 return '(${node.runtimeType}) ${text.substring(0, 37)}...'; |
| 407 } |
| 408 return '(${node.runtimeType}) $text'; |
| 409 } |
OLD | NEW |