| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library dart2js.serialization_model_test; | |
| 6 import 'dart:async'; | |
| 7 import 'dart:io'; | |
| 8 import 'package:async_helper/async_helper.dart'; | |
| 9 import 'package:expect/expect.dart'; | |
| 10 import 'package:compiler/src/closure.dart'; | |
| 11 import 'package:compiler/src/commandline_options.dart'; | |
| 12 import 'package:compiler/src/compiler.dart'; | |
| 13 import 'package:compiler/src/elements/elements.dart'; | |
| 14 import 'package:compiler/src/filenames.dart'; | |
| 15 import 'package:compiler/src/js_backend/js_backend.dart'; | |
| 16 import 'package:compiler/src/serialization/equivalence.dart'; | |
| 17 import 'package:compiler/src/tree/nodes.dart'; | |
| 18 import 'package:compiler/src/universe/class_set.dart'; | |
| 19 import '../memory_compiler.dart'; | |
| 20 import 'helper.dart'; | |
| 21 import 'test_data.dart'; | |
| 22 import 'test_helper.dart'; | |
| 23 | |
| 24 main(List<String> args) { | |
| 25 asyncTest(() async { | |
| 26 Arguments arguments = new Arguments.from(args); | |
| 27 SerializedData serializedData = | |
| 28 await serializeDartCore(arguments: arguments); | |
| 29 if (arguments.filename != null) { | |
| 30 Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.filename)); | |
| 31 await checkModels(entryPoint, | |
| 32 sourceFiles: serializedData.toMemorySourceFiles(), | |
| 33 resolutionInputs: serializedData.toUris()); | |
| 34 } else { | |
| 35 Uri entryPoint = Uri.parse('memory:main.dart'); | |
| 36 await arguments.forEachTest(serializedData, TESTS, checkModels); | |
| 37 } | |
| 38 }); | |
| 39 } | |
| 40 | |
| 41 Future checkModels( | |
| 42 Uri entryPoint, | |
| 43 {Map<String, String> sourceFiles: const <String, String>{}, | |
| 44 List<Uri> resolutionInputs, | |
| 45 int index, | |
| 46 Test test, | |
| 47 bool verbose: false}) async { | |
| 48 String testDescription = test != null ? test.name : '${entryPoint}'; | |
| 49 String id = index != null ? '$index: ' : ''; | |
| 50 print('------------------------------------------------------------------'); | |
| 51 print('compile normal ${id}${testDescription}'); | |
| 52 print('------------------------------------------------------------------'); | |
| 53 Compiler compilerNormal = compilerFor( | |
| 54 memorySourceFiles: sourceFiles, | |
| 55 options: [Flags.analyzeOnly]); | |
| 56 compilerNormal.resolution.retainCachesForTesting = true; | |
| 57 await compilerNormal.run(entryPoint); | |
| 58 compilerNormal.phase = Compiler.PHASE_DONE_RESOLVING; | |
| 59 compilerNormal.world.populate(); | |
| 60 compilerNormal.backend.onResolutionComplete(); | |
| 61 | |
| 62 print('------------------------------------------------------------------'); | |
| 63 print('compile deserialized ${id}${testDescription}'); | |
| 64 print('------------------------------------------------------------------'); | |
| 65 Compiler compilerDeserialized = compilerFor( | |
| 66 memorySourceFiles: sourceFiles, | |
| 67 resolutionInputs: resolutionInputs, | |
| 68 options: [Flags.analyzeOnly]); | |
| 69 compilerDeserialized.resolution.retainCachesForTesting = true; | |
| 70 await compilerDeserialized.run(entryPoint); | |
| 71 compilerDeserialized.phase = Compiler.PHASE_DONE_RESOLVING; | |
| 72 compilerDeserialized.world.populate(); | |
| 73 compilerDeserialized.backend.onResolutionComplete(); | |
| 74 | |
| 75 checkAllImpacts( | |
| 76 compilerNormal, compilerDeserialized, | |
| 77 verbose: verbose); | |
| 78 | |
| 79 checkSets( | |
| 80 compilerNormal.resolverWorld.directlyInstantiatedClasses, | |
| 81 compilerDeserialized.resolverWorld.directlyInstantiatedClasses, | |
| 82 "Directly instantiated classes mismatch", | |
| 83 areElementsEquivalent, | |
| 84 verbose: verbose); | |
| 85 | |
| 86 checkSets( | |
| 87 compilerNormal.resolverWorld.instantiatedTypes, | |
| 88 compilerDeserialized.resolverWorld.instantiatedTypes, | |
| 89 "Instantiated types mismatch", | |
| 90 areTypesEquivalent, | |
| 91 verbose: verbose); | |
| 92 | |
| 93 checkSets( | |
| 94 compilerNormal.resolverWorld.isChecks, | |
| 95 compilerDeserialized.resolverWorld.isChecks, | |
| 96 "Is-check mismatch", | |
| 97 areTypesEquivalent, | |
| 98 verbose: verbose); | |
| 99 | |
| 100 checkSets( | |
| 101 compilerNormal.enqueuer.resolution.processedElements, | |
| 102 compilerDeserialized.enqueuer.resolution.processedElements, | |
| 103 "Processed element mismatch", | |
| 104 areElementsEquivalent, | |
| 105 onSameElement: (a, b) { | |
| 106 checkElements( | |
| 107 compilerNormal, compilerDeserialized, a, b, verbose: verbose); | |
| 108 }, | |
| 109 verbose: verbose); | |
| 110 | |
| 111 checkClassHierarchyNodes( | |
| 112 compilerNormal, | |
| 113 compilerDeserialized, | |
| 114 compilerNormal.world.getClassHierarchyNode( | |
| 115 compilerNormal.coreClasses.objectClass), | |
| 116 compilerDeserialized.world.getClassHierarchyNode( | |
| 117 compilerDeserialized.coreClasses.objectClass), | |
| 118 verbose: verbose); | |
| 119 | |
| 120 Expect.equals(compilerNormal.enabledInvokeOn, | |
| 121 compilerDeserialized.enabledInvokeOn, | |
| 122 "Compiler.enabledInvokeOn mismatch"); | |
| 123 Expect.equals(compilerNormal.enabledFunctionApply, | |
| 124 compilerDeserialized.enabledFunctionApply, | |
| 125 "Compiler.enabledFunctionApply mismatch"); | |
| 126 Expect.equals(compilerNormal.enabledRuntimeType, | |
| 127 compilerDeserialized.enabledRuntimeType, | |
| 128 "Compiler.enabledRuntimeType mismatch"); | |
| 129 Expect.equals(compilerNormal.hasIsolateSupport, | |
| 130 compilerDeserialized.hasIsolateSupport, | |
| 131 "Compiler.hasIsolateSupport mismatch"); | |
| 132 } | |
| 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 AstElement astElement1 = element1; | |
| 142 AstElement astElement2 = element2; | |
| 143 ClosureClassMap closureData1 = | |
| 144 compiler1.closureToClassMapper.computeClosureToClassMapping( | |
| 145 astElement1.resolvedAst); | |
| 146 ClosureClassMap closureData2 = | |
| 147 compiler2.closureToClassMapper.computeClosureToClassMapping( | |
| 148 astElement2.resolvedAst); | |
| 149 | |
| 150 checkElementIdentities(closureData1, closureData2, | |
| 151 '$element1.closureElement', | |
| 152 closureData1.closureElement, closureData2.closureElement); | |
| 153 checkElementIdentities(closureData1, closureData2, | |
| 154 '$element1.closureClassElement', | |
| 155 closureData1.closureClassElement, closureData2.closureClassElement); | |
| 156 checkElementIdentities(closureData1, closureData2, | |
| 157 '$element1.callElement', | |
| 158 closureData1.callElement, closureData2.callElement); | |
| 159 check(closureData1, closureData2, | |
| 160 '$element1.thisLocal', | |
| 161 closureData1.thisLocal, closureData2.thisLocal, | |
| 162 areLocalsEquivalent); | |
| 163 checkMaps( | |
| 164 closureData1.freeVariableMap, | |
| 165 closureData2.freeVariableMap, | |
| 166 "$element1.freeVariableMap", | |
| 167 areLocalsEquivalent, | |
| 168 areCapturedVariablesEquivalent, | |
| 169 verbose: verbose); | |
| 170 checkMaps( | |
| 171 closureData1.capturingScopes, | |
| 172 closureData2.capturingScopes, | |
| 173 "$element1.capturingScopes", | |
| 174 areNodesEquivalent, | |
| 175 areClosureScopesEquivalent, | |
| 176 verbose: verbose, | |
| 177 keyToString: nodeToString); | |
| 178 checkSets( | |
| 179 closureData1.variablesUsedInTryOrGenerator, | |
| 180 closureData2.variablesUsedInTryOrGenerator, | |
| 181 "$element1.variablesUsedInTryOrGenerator", | |
| 182 areLocalsEquivalent, | |
| 183 verbose: verbose); | |
| 184 } | |
| 185 JavaScriptBackend backend1 = compiler1.backend; | |
| 186 JavaScriptBackend backend2 = compiler2.backend; | |
| 187 Expect.equals( | |
| 188 backend1.inlineCache.getCurrentCacheDecisionForTesting(element1), | |
| 189 backend2.inlineCache.getCurrentCacheDecisionForTesting(element2), | |
| 190 "Inline cache decision mismatch for $element1 vs $element2"); | |
| 191 } | |
| 192 | |
| 193 void checkMixinUses( | |
| 194 Compiler compiler1, Compiler compiler2, | |
| 195 ClassElement class1, ClassElement class2, | |
| 196 {bool verbose: false}) { | |
| 197 | |
| 198 checkSets( | |
| 199 compiler1.world.mixinUsesOf(class1), | |
| 200 compiler2.world.mixinUsesOf(class2), | |
| 201 "Mixin uses of $class1 vs $class2", | |
| 202 areElementsEquivalent, | |
| 203 verbose: verbose); | |
| 204 | |
| 205 } | |
| 206 | |
| 207 void checkClassHierarchyNodes( | |
| 208 Compiler compiler1, | |
| 209 Compiler compiler2, | |
| 210 ClassHierarchyNode node1, ClassHierarchyNode node2, | |
| 211 {bool verbose: false}) { | |
| 212 if (verbose) { | |
| 213 print('Checking $node1 vs $node2'); | |
| 214 } | |
| 215 Expect.isTrue( | |
| 216 areElementsEquivalent(node1.cls, node2.cls), | |
| 217 "Element identity mismatch for ${node1.cls} vs ${node2.cls}."); | |
| 218 Expect.equals( | |
| 219 node1.isDirectlyInstantiated, | |
| 220 node2.isDirectlyInstantiated, | |
| 221 "Value mismatch for 'isDirectlyInstantiated' " | |
| 222 "for ${node1.cls} vs ${node2.cls}."); | |
| 223 Expect.equals( | |
| 224 node1.isIndirectlyInstantiated, | |
| 225 node2.isIndirectlyInstantiated, | |
| 226 "Value mismatch for 'isIndirectlyInstantiated' " | |
| 227 "for ${node1.cls} vs ${node2.cls}."); | |
| 228 // TODO(johnniwinther): Enforce a canonical and stable order on direct | |
| 229 // subclasses. | |
| 230 for (ClassHierarchyNode child in node1.directSubclasses) { | |
| 231 bool found = false; | |
| 232 for (ClassHierarchyNode other in node2.directSubclasses) { | |
| 233 if (areElementsEquivalent(child.cls, other.cls)) { | |
| 234 checkClassHierarchyNodes(compiler1, compiler2, | |
| 235 child, other, verbose: verbose); | |
| 236 found = true; | |
| 237 break; | |
| 238 } | |
| 239 } | |
| 240 if (!found) { | |
| 241 if (child.isInstantiated) { | |
| 242 print('Missing subclass ${child.cls} of ${node1.cls} ' | |
| 243 'in ${node2.directSubclasses}'); | |
| 244 print(compiler1.world.dump( | |
| 245 verbose ? compiler1.coreClasses.objectClass : node1.cls)); | |
| 246 print(compiler2.world.dump( | |
| 247 verbose ? compiler2.coreClasses.objectClass : node2.cls)); | |
| 248 } | |
| 249 Expect.isFalse(child.isInstantiated, | |
| 250 'Missing subclass ${child.cls} of ${node1.cls} in ' | |
| 251 '${node2.directSubclasses}'); | |
| 252 } | |
| 253 } | |
| 254 checkMixinUses(compiler1, compiler2, node1.cls, node2.cls, verbose: verbose); | |
| 255 } | |
| 256 | |
| 257 bool areLocalsEquivalent(Local a, Local b) { | |
| 258 if (a == b) return true; | |
| 259 if (a == null || b == null) return false; | |
| 260 | |
| 261 if (a is Element) { | |
| 262 return b is Element && areElementsEquivalent(a as Element, b as Element); | |
| 263 } else { | |
| 264 return a.runtimeType == b.runtimeType && | |
| 265 areElementsEquivalent(a.executableContext, b.executableContext); | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 bool areCapturedVariablesEquivalent(CapturedVariable a, CapturedVariable b) { | |
| 270 if (a == b) return true; | |
| 271 if (a == null || b == null) return false; | |
| 272 if (a is ClosureFieldElement && b is ClosureFieldElement) { | |
| 273 return areElementsEquivalent(a.closureClass, b.closureClass) && | |
| 274 areLocalsEquivalent(a.local, b.local); | |
| 275 } else if (a is BoxFieldElement && b is BoxFieldElement) { | |
| 276 return areElementsEquivalent(a.variableElement, b.variableElement) && | |
| 277 areLocalsEquivalent(a.box, b.box); | |
| 278 } | |
| 279 return false; | |
| 280 } | |
| 281 | |
| 282 bool areClosureScopesEquivalent(ClosureScope a, ClosureScope b) { | |
| 283 if (a == b) return true; | |
| 284 if (a == null || b == null) return false; | |
| 285 if (!areLocalsEquivalent(a.boxElement, b.boxElement)) { | |
| 286 return false; | |
| 287 } | |
| 288 checkMaps(a.capturedVariables, b.capturedVariables, | |
| 289 'ClosureScope.capturedVariables', | |
| 290 areLocalsEquivalent, | |
| 291 areElementsEquivalent); | |
| 292 checkSets(a.boxedLoopVariables, b.boxedLoopVariables, | |
| 293 'ClosureScope.boxedLoopVariables', | |
| 294 areElementsEquivalent); | |
| 295 return true; | |
| 296 } | |
| 297 | |
| 298 String nodeToString(Node node) { | |
| 299 String text = '$node'; | |
| 300 if (text.length > 40) { | |
| 301 return '(${node.runtimeType}) ${text.substring(0, 37)}...'; | |
| 302 } | |
| 303 return '(${node.runtimeType}) $text'; | |
| 304 } | |
| OLD | NEW |