| 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'; | |
| 9 import 'package:async_helper/async_helper.dart'; | 8 import 'package:async_helper/async_helper.dart'; |
| 10 import 'package:expect/expect.dart'; | 9 import 'package:expect/expect.dart'; |
| 11 import 'package:compiler/src/closure.dart'; | 10 import 'package:compiler/src/closure.dart'; |
| 12 import 'package:compiler/src/commandline_options.dart'; | 11 import 'package:compiler/src/commandline_options.dart'; |
| 13 import 'package:compiler/src/common.dart'; | |
| 14 import 'package:compiler/src/constants/values.dart'; | 12 import 'package:compiler/src/constants/values.dart'; |
| 15 import 'package:compiler/src/compiler.dart'; | 13 import 'package:compiler/src/compiler.dart'; |
| 16 import 'package:compiler/src/elements/resolution_types.dart'; | |
| 17 import 'package:compiler/src/elements/types.dart'; | |
| 18 import 'package:compiler/src/deferred_load.dart'; | 14 import 'package:compiler/src/deferred_load.dart'; |
| 19 import 'package:compiler/src/elements/elements.dart'; | 15 import 'package:compiler/src/elements/elements.dart'; |
| 20 import 'package:compiler/src/elements/entities.dart'; | 16 import 'package:compiler/src/elements/entities.dart'; |
| 21 import 'package:compiler/src/enqueue.dart'; | |
| 22 import 'package:compiler/src/filenames.dart'; | |
| 23 import 'package:compiler/src/js_backend/js_backend.dart'; | 17 import 'package:compiler/src/js_backend/js_backend.dart'; |
| 24 import 'package:compiler/src/js_backend/backend_usage.dart'; | |
| 25 import 'package:compiler/src/js_backend/native_data.dart'; | |
| 26 import 'package:compiler/src/js_backend/interceptor_data.dart'; | |
| 27 import 'package:compiler/src/serialization/equivalence.dart'; | 18 import 'package:compiler/src/serialization/equivalence.dart'; |
| 28 import 'package:compiler/src/tree/nodes.dart'; | 19 import 'package:compiler/src/tree/nodes.dart'; |
| 29 import 'package:compiler/src/universe/class_set.dart'; | |
| 30 import 'package:compiler/src/universe/world_builder.dart'; | |
| 31 import 'package:compiler/src/util/enumset.dart'; | |
| 32 import 'package:compiler/src/world.dart' show ClosedWorld; | |
| 33 import '../memory_compiler.dart'; | 20 import '../memory_compiler.dart'; |
| 21 import '../equivalence/check_helpers.dart'; |
| 22 import '../equivalence/check_functions.dart'; |
| 34 import 'helper.dart'; | 23 import 'helper.dart'; |
| 35 import 'test_data.dart'; | 24 import 'test_data.dart'; |
| 36 import 'test_helper.dart'; | |
| 37 | 25 |
| 38 /// Number of tests that are not part of the automatic test grouping. | 26 /// Number of tests that are not part of the automatic test grouping. |
| 39 int SKIP_COUNT = 2; | 27 int SKIP_COUNT = 2; |
| 40 | 28 |
| 41 /// Number of groups that the [TESTS] are split into. | 29 /// Number of groups that the [TESTS] are split into. |
| 42 int SPLIT_COUNT = 5; | 30 int SPLIT_COUNT = 5; |
| 43 | 31 |
| 44 main(List<String> args) { | 32 main(List<String> args) { |
| 45 asyncTest(() async { | 33 asyncTest(() async { |
| 46 Arguments arguments = new Arguments.from(args); | 34 Arguments arguments = new Arguments.from(args); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 checkClosedWorlds( | 97 checkClosedWorlds( |
| 110 compilerNormal.resolutionWorldBuilder.closedWorldForTesting, | 98 compilerNormal.resolutionWorldBuilder.closedWorldForTesting, |
| 111 compilerDeserialized.resolutionWorldBuilder.closedWorldForTesting, | 99 compilerDeserialized.resolutionWorldBuilder.closedWorldForTesting, |
| 112 // Serialized native data include non-live members. | 100 // Serialized native data include non-live members. |
| 113 allowExtra: true, | 101 allowExtra: true, |
| 114 verbose: verbose); | 102 verbose: verbose); |
| 115 checkBackendInfo(compilerNormal, compilerDeserialized, verbose: verbose); | 103 checkBackendInfo(compilerNormal, compilerDeserialized, verbose: verbose); |
| 116 }); | 104 }); |
| 117 } | 105 } |
| 118 | 106 |
| 119 void checkResolutionEnqueuers( | |
| 120 BackendUsage backendUsage1, | |
| 121 BackendUsage backendUsage2, | |
| 122 ResolutionEnqueuer enqueuer1, | |
| 123 ResolutionEnqueuer enqueuer2, | |
| 124 {bool elementEquivalence(Entity a, Entity b): areElementsEquivalent, | |
| 125 bool typeEquivalence(DartType a, DartType b): areTypesEquivalent, | |
| 126 bool elementFilter(Element element), | |
| 127 bool verbose: false}) { | |
| 128 ResolutionWorldBuilderBase worldBuilder1 = enqueuer1.worldBuilder; | |
| 129 ResolutionWorldBuilderBase worldBuilder2 = enqueuer2.worldBuilder; | |
| 130 | |
| 131 checkSets( | |
| 132 enqueuer1.worldBuilder.instantiatedTypes, | |
| 133 enqueuer2.worldBuilder.instantiatedTypes, | |
| 134 "Instantiated types mismatch", | |
| 135 typeEquivalence, | |
| 136 verbose: verbose); | |
| 137 | |
| 138 checkSets( | |
| 139 enqueuer1.worldBuilder.directlyInstantiatedClasses, | |
| 140 enqueuer2.worldBuilder.directlyInstantiatedClasses, | |
| 141 "Directly instantiated classes mismatch", | |
| 142 elementEquivalence, | |
| 143 verbose: verbose); | |
| 144 | |
| 145 checkMaps( | |
| 146 worldBuilder1.getInstantiationMap(), | |
| 147 worldBuilder2.getInstantiationMap(), | |
| 148 "Instantiated classes mismatch", | |
| 149 elementEquivalence, | |
| 150 (a, b) => areInstantiationInfosEquivalent( | |
| 151 a, b, elementEquivalence, typeEquivalence), | |
| 152 verbose: verbose); | |
| 153 | |
| 154 checkSets(enqueuer1.processedEntities, enqueuer2.processedEntities, | |
| 155 "Processed element mismatch", elementEquivalence, elementFilter: (e) { | |
| 156 return elementFilter != null ? elementFilter(e) : true; | |
| 157 }, verbose: verbose); | |
| 158 | |
| 159 checkSets(enqueuer1.worldBuilder.isChecks, enqueuer2.worldBuilder.isChecks, | |
| 160 "Is-check mismatch", typeEquivalence, | |
| 161 verbose: verbose); | |
| 162 | |
| 163 Expect.equals(backendUsage1.isInvokeOnUsed, backendUsage2.isInvokeOnUsed, | |
| 164 "JavaScriptBackend.hasInvokeOnSupport mismatch"); | |
| 165 Expect.equals( | |
| 166 backendUsage1.isFunctionApplyUsed, | |
| 167 backendUsage1.isFunctionApplyUsed, | |
| 168 "JavaScriptBackend.hasFunctionApplySupport mismatch"); | |
| 169 Expect.equals( | |
| 170 backendUsage1.isRuntimeTypeUsed, | |
| 171 backendUsage2.isRuntimeTypeUsed, | |
| 172 "JavaScriptBackend.hasRuntimeTypeSupport mismatch"); | |
| 173 Expect.equals(backendUsage1.isIsolateInUse, backendUsage2.isIsolateInUse, | |
| 174 "JavaScriptBackend.hasIsolateSupport mismatch"); | |
| 175 } | |
| 176 | |
| 177 void checkClosedWorlds(ClosedWorld closedWorld1, ClosedWorld closedWorld2, | |
| 178 {TestStrategy strategy: const TestStrategy(), | |
| 179 bool allowExtra: false, | |
| 180 bool verbose: false}) { | |
| 181 if (verbose) { | |
| 182 print(closedWorld1.dump()); | |
| 183 print(closedWorld2.dump()); | |
| 184 } | |
| 185 checkClassHierarchyNodes( | |
| 186 closedWorld1, | |
| 187 closedWorld2, | |
| 188 closedWorld1 | |
| 189 .getClassHierarchyNode(closedWorld1.commonElements.objectClass), | |
| 190 closedWorld2 | |
| 191 .getClassHierarchyNode(closedWorld2.commonElements.objectClass), | |
| 192 strategy.elementEquivalence, | |
| 193 verbose: verbose); | |
| 194 | |
| 195 checkNativeData(closedWorld1.nativeData, closedWorld2.nativeData, | |
| 196 strategy: strategy, allowExtra: allowExtra, verbose: verbose); | |
| 197 checkInterceptorData(closedWorld1.interceptorData, | |
| 198 closedWorld2.interceptorData, strategy.elementEquivalence, | |
| 199 verbose: verbose); | |
| 200 } | |
| 201 | |
| 202 void checkNativeData(NativeDataImpl data1, NativeDataImpl data2, | |
| 203 {TestStrategy strategy: const TestStrategy(), | |
| 204 bool allowExtra: false, | |
| 205 bool verbose: false}) { | |
| 206 checkMapEquivalence(data1, data2, 'nativeMemberName', data1.nativeMemberName, | |
| 207 data2.nativeMemberName, strategy.elementEquivalence, equality, | |
| 208 allowExtra: allowExtra); | |
| 209 | |
| 210 checkMapEquivalence( | |
| 211 data1, | |
| 212 data2, | |
| 213 'nativeMethodBehavior', | |
| 214 data1.nativeMethodBehavior, | |
| 215 data2.nativeMethodBehavior, | |
| 216 strategy.elementEquivalence, | |
| 217 (a, b) => testNativeBehavior(a, b, strategy: strategy), | |
| 218 allowExtra: allowExtra); | |
| 219 | |
| 220 checkMapEquivalence( | |
| 221 data1, | |
| 222 data2, | |
| 223 'nativeFieldLoadBehavior', | |
| 224 data1.nativeFieldLoadBehavior, | |
| 225 data2.nativeFieldLoadBehavior, | |
| 226 strategy.elementEquivalence, | |
| 227 (a, b) => testNativeBehavior(a, b, strategy: strategy), | |
| 228 allowExtra: allowExtra); | |
| 229 | |
| 230 checkMapEquivalence( | |
| 231 data1, | |
| 232 data2, | |
| 233 'nativeFieldStoreBehavior', | |
| 234 data1.nativeFieldStoreBehavior, | |
| 235 data2.nativeFieldStoreBehavior, | |
| 236 strategy.elementEquivalence, | |
| 237 (a, b) => testNativeBehavior(a, b, strategy: strategy), | |
| 238 allowExtra: allowExtra); | |
| 239 | |
| 240 checkMapEquivalence( | |
| 241 data1, | |
| 242 data2, | |
| 243 'jsInteropLibraryNames', | |
| 244 data1.jsInteropLibraryNames, | |
| 245 data2.jsInteropLibraryNames, | |
| 246 strategy.elementEquivalence, | |
| 247 equality); | |
| 248 | |
| 249 checkSetEquivalence( | |
| 250 data1, | |
| 251 data2, | |
| 252 'anonymousJsInteropClasses', | |
| 253 data1.anonymousJsInteropClasses, | |
| 254 data2.anonymousJsInteropClasses, | |
| 255 strategy.elementEquivalence); | |
| 256 | |
| 257 checkMapEquivalence( | |
| 258 data1, | |
| 259 data2, | |
| 260 'jsInteropClassNames', | |
| 261 data1.jsInteropClassNames, | |
| 262 data2.jsInteropClassNames, | |
| 263 strategy.elementEquivalence, | |
| 264 equality); | |
| 265 | |
| 266 checkMapEquivalence( | |
| 267 data1, | |
| 268 data2, | |
| 269 'jsInteropMemberNames', | |
| 270 data1.jsInteropMemberNames, | |
| 271 data2.jsInteropMemberNames, | |
| 272 strategy.elementEquivalence, | |
| 273 equality); | |
| 274 } | |
| 275 | |
| 276 void checkInterceptorData(InterceptorDataImpl data1, InterceptorDataImpl data2, | |
| 277 bool elementEquivalence(Entity a, Entity b), | |
| 278 {bool verbose: false}) { | |
| 279 checkMapEquivalence( | |
| 280 data1, | |
| 281 data2, | |
| 282 'interceptedElements', | |
| 283 data1.interceptedElementsForTesting, | |
| 284 data2.interceptedElementsForTesting, | |
| 285 equality, | |
| 286 (a, b) => areSetsEquivalent(a, b, elementEquivalence)); | |
| 287 | |
| 288 checkSetEquivalence(data1, data2, 'interceptedClasses', | |
| 289 data1.interceptedClasses, data2.interceptedClasses, elementEquivalence); | |
| 290 | |
| 291 checkSetEquivalence( | |
| 292 data1, | |
| 293 data2, | |
| 294 'classesMixedIntoInterceptedClasses', | |
| 295 data1.classesMixedIntoInterceptedClassesForTesting, | |
| 296 data2.classesMixedIntoInterceptedClassesForTesting, | |
| 297 elementEquivalence); | |
| 298 } | |
| 299 | |
| 300 void checkBackendInfo(Compiler compilerNormal, Compiler compilerDeserialized, | 107 void checkBackendInfo(Compiler compilerNormal, Compiler compilerDeserialized, |
| 301 {bool verbose: false}) { | 108 {bool verbose: false}) { |
| 302 checkSets( | 109 checkSets( |
| 303 compilerNormal.enqueuer.resolution.processedEntities, | 110 compilerNormal.enqueuer.resolution.processedEntities, |
| 304 compilerDeserialized.enqueuer.resolution.processedEntities, | 111 compilerDeserialized.enqueuer.resolution.processedEntities, |
| 305 "Processed element mismatch", | 112 "Processed element mismatch", |
| 306 areElementsEquivalent, onSameElement: (a, b) { | 113 areElementsEquivalent, onSameElement: (a, b) { |
| 307 checkElements(compilerNormal, compilerDeserialized, a, b, verbose: verbose); | 114 checkElements(compilerNormal, compilerDeserialized, a, b, verbose: verbose); |
| 308 }, verbose: verbose); | 115 }, verbose: verbose); |
| 309 Expect.equals( | 116 Expect.equals( |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 414 if (element1 is MethodElement && element2 is MethodElement) { | 221 if (element1 is MethodElement && element2 is MethodElement) { |
| 415 Expect.equals( | 222 Expect.equals( |
| 416 backend1.inlineCache.getCurrentCacheDecisionForTesting(element1), | 223 backend1.inlineCache.getCurrentCacheDecisionForTesting(element1), |
| 417 backend2.inlineCache.getCurrentCacheDecisionForTesting(element2), | 224 backend2.inlineCache.getCurrentCacheDecisionForTesting(element2), |
| 418 "Inline cache decision mismatch for $element1 vs $element2"); | 225 "Inline cache decision mismatch for $element1 vs $element2"); |
| 419 } | 226 } |
| 420 | 227 |
| 421 checkElementOutputUnits(compiler1, compiler2, element1, element2); | 228 checkElementOutputUnits(compiler1, compiler2, element1, element2); |
| 422 } | 229 } |
| 423 | 230 |
| 424 void checkMixinUses( | |
| 425 ClosedWorld closedWorld1, | |
| 426 ClosedWorld closedWorld2, | |
| 427 ClassEntity class1, | |
| 428 ClassEntity class2, | |
| 429 bool elementEquivalence(Entity a, Entity b), | |
| 430 {bool verbose: false}) { | |
| 431 checkSets(closedWorld1.mixinUsesOf(class1), closedWorld2.mixinUsesOf(class2), | |
| 432 "Mixin uses of $class1 vs $class2", elementEquivalence, | |
| 433 verbose: verbose); | |
| 434 } | |
| 435 | |
| 436 void checkClassHierarchyNodes( | |
| 437 ClosedWorld closedWorld1, | |
| 438 ClosedWorld closedWorld2, | |
| 439 ClassHierarchyNode node1, | |
| 440 ClassHierarchyNode node2, | |
| 441 bool elementEquivalence(Entity a, Entity b), | |
| 442 {bool verbose: false}) { | |
| 443 if (verbose) { | |
| 444 print('Checking $node1 vs $node2'); | |
| 445 } | |
| 446 ClassEntity cls1 = node1.cls; | |
| 447 ClassEntity cls2 = node2.cls; | |
| 448 Expect.isTrue(elementEquivalence(cls1, cls2), | |
| 449 "Element identity mismatch for ${cls1} vs ${cls2}."); | |
| 450 Expect.equals( | |
| 451 node1.isDirectlyInstantiated, | |
| 452 node2.isDirectlyInstantiated, | |
| 453 "Value mismatch for 'isDirectlyInstantiated' " | |
| 454 "for ${cls1} vs ${cls2}."); | |
| 455 Expect.equals( | |
| 456 node1.isIndirectlyInstantiated, | |
| 457 node2.isIndirectlyInstantiated, | |
| 458 "Value mismatch for 'isIndirectlyInstantiated' " | |
| 459 "for ${node1.cls} vs ${node2.cls}."); | |
| 460 // TODO(johnniwinther): Enforce a canonical and stable order on direct | |
| 461 // subclasses. | |
| 462 for (ClassHierarchyNode child in node1.directSubclasses) { | |
| 463 bool found = false; | |
| 464 for (ClassHierarchyNode other in node2.directSubclasses) { | |
| 465 ClassEntity child1 = child.cls; | |
| 466 ClassEntity child2 = other.cls; | |
| 467 if (elementEquivalence(child1, child2)) { | |
| 468 checkClassHierarchyNodes( | |
| 469 closedWorld1, closedWorld2, child, other, elementEquivalence, | |
| 470 verbose: verbose); | |
| 471 found = true; | |
| 472 break; | |
| 473 } | |
| 474 } | |
| 475 if (!found) { | |
| 476 if (child.isInstantiated) { | |
| 477 print('Missing subclass ${child.cls} of ${node1.cls} ' | |
| 478 'in ${node2.directSubclasses}'); | |
| 479 print(closedWorld1.dump( | |
| 480 verbose ? closedWorld1.commonElements.objectClass : node1.cls)); | |
| 481 print(closedWorld2.dump( | |
| 482 verbose ? closedWorld2.commonElements.objectClass : node2.cls)); | |
| 483 } | |
| 484 Expect.isFalse( | |
| 485 child.isInstantiated, | |
| 486 'Missing subclass ${child.cls} of ${node1.cls} in ' | |
| 487 '${node2.directSubclasses}'); | |
| 488 } | |
| 489 } | |
| 490 checkMixinUses( | |
| 491 closedWorld1, closedWorld2, node1.cls, node2.cls, elementEquivalence, | |
| 492 verbose: verbose); | |
| 493 } | |
| 494 | |
| 495 bool areLocalsEquivalent(Local a, Local b) { | 231 bool areLocalsEquivalent(Local a, Local b) { |
| 496 if (a == b) return true; | 232 if (a == b) return true; |
| 497 if (a == null || b == null) return false; | 233 if (a == null || b == null) return false; |
| 498 | 234 |
| 499 if (a is Element) { | 235 if (a is Element) { |
| 500 return b is Element && areElementsEquivalent(a as Element, b as Element); | 236 return b is Element && areElementsEquivalent(a as Element, b as Element); |
| 501 } else { | 237 } else { |
| 502 return a.runtimeType == b.runtimeType && | 238 return a.runtimeType == b.runtimeType && |
| 503 areElementsEquivalent(a.executableContext, b.executableContext); | 239 areElementsEquivalent(a.executableContext, b.executableContext); |
| 504 } | 240 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 557 check(outputUnit1, outputUnit2, 'OutputUnit.isMainOutput $message', | 293 check(outputUnit1, outputUnit2, 'OutputUnit.isMainOutput $message', |
| 558 outputUnit1.isMainOutput, outputUnit2.isMainOutput); | 294 outputUnit1.isMainOutput, outputUnit2.isMainOutput); |
| 559 checkSetEquivalence( | 295 checkSetEquivalence( |
| 560 outputUnit1, | 296 outputUnit1, |
| 561 outputUnit2, | 297 outputUnit2, |
| 562 'OutputUnit.imports $message', | 298 'OutputUnit.imports $message', |
| 563 outputUnit1.imports, | 299 outputUnit1.imports, |
| 564 outputUnit2.imports, | 300 outputUnit2.imports, |
| 565 (a, b) => areElementsEquivalent(a.declaration, b.declaration)); | 301 (a, b) => areElementsEquivalent(a.declaration, b.declaration)); |
| 566 } | 302 } |
| 567 | |
| 568 bool areInstantiationInfosEquivalent( | |
| 569 InstantiationInfo info1, | |
| 570 InstantiationInfo info2, | |
| 571 bool elementEquivalence(Entity a, Entity b), | |
| 572 bool typeEquivalence(DartType a, DartType b)) { | |
| 573 checkMaps( | |
| 574 info1.instantiationMap, | |
| 575 info2.instantiationMap, | |
| 576 'instantiationMap of\n ' | |
| 577 '${info1.instantiationMap}\nvs ${info2.instantiationMap}', | |
| 578 elementEquivalence, | |
| 579 (a, b) => areSetsEquivalent( | |
| 580 a, b, (a, b) => areInstancesEquivalent(a, b, typeEquivalence))); | |
| 581 return true; | |
| 582 } | |
| 583 | |
| 584 bool areInstancesEquivalent(Instance instance1, Instance instance2, | |
| 585 bool typeEquivalence(DartType a, DartType b)) { | |
| 586 InterfaceType type1 = instance1.type; | |
| 587 InterfaceType type2 = instance2.type; | |
| 588 return typeEquivalence(type1, type2) && | |
| 589 instance1.kind == instance2.kind && | |
| 590 instance1.isRedirection == instance2.isRedirection; | |
| 591 } | |
| OLD | NEW |