| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2017, 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 /// Equivalence test functions for data objects. |
| 6 |
| 7 library dart2js.equivalence.functions; |
| 8 |
| 9 import 'package:expect/expect.dart'; |
| 10 import 'package:compiler/src/common/resolution.dart'; |
| 11 import 'package:compiler/src/common_elements.dart'; |
| 12 import 'package:compiler/src/compiler.dart'; |
| 13 import 'package:compiler/src/elements/types.dart'; |
| 14 import 'package:compiler/src/elements/elements.dart'; |
| 15 import 'package:compiler/src/elements/entities.dart'; |
| 16 import 'package:compiler/src/enqueue.dart'; |
| 17 import 'package:compiler/src/js_backend/backend_usage.dart'; |
| 18 import 'package:compiler/src/js_backend/native_data.dart'; |
| 19 import 'package:compiler/src/js_backend/interceptor_data.dart'; |
| 20 import 'package:compiler/src/serialization/equivalence.dart'; |
| 21 import 'package:compiler/src/universe/class_set.dart'; |
| 22 import 'package:compiler/src/universe/world_builder.dart'; |
| 23 import 'package:compiler/src/world.dart'; |
| 24 import 'check_helpers.dart'; |
| 25 |
| 26 void checkClosedWorlds(ClosedWorld closedWorld1, ClosedWorld closedWorld2, |
| 27 {TestStrategy strategy: const TestStrategy(), |
| 28 bool allowExtra: false, |
| 29 bool verbose: false}) { |
| 30 if (verbose) { |
| 31 print(closedWorld1.dump()); |
| 32 print(closedWorld2.dump()); |
| 33 } |
| 34 checkClassHierarchyNodes( |
| 35 closedWorld1, |
| 36 closedWorld2, |
| 37 closedWorld1 |
| 38 .getClassHierarchyNode(closedWorld1.commonElements.objectClass), |
| 39 closedWorld2 |
| 40 .getClassHierarchyNode(closedWorld2.commonElements.objectClass), |
| 41 strategy.elementEquivalence, |
| 42 verbose: verbose); |
| 43 |
| 44 checkNativeData(closedWorld1.nativeData, closedWorld2.nativeData, |
| 45 strategy: strategy, allowExtra: allowExtra, verbose: verbose); |
| 46 checkInterceptorData(closedWorld1.interceptorData, |
| 47 closedWorld2.interceptorData, strategy.elementEquivalence, |
| 48 verbose: verbose); |
| 49 } |
| 50 |
| 51 void checkNativeData(NativeDataImpl data1, NativeDataImpl data2, |
| 52 {TestStrategy strategy: const TestStrategy(), |
| 53 bool allowExtra: false, |
| 54 bool verbose: false}) { |
| 55 checkMapEquivalence(data1, data2, 'nativeMemberName', data1.nativeMemberName, |
| 56 data2.nativeMemberName, strategy.elementEquivalence, equality, |
| 57 allowExtra: allowExtra); |
| 58 |
| 59 checkMapEquivalence( |
| 60 data1, |
| 61 data2, |
| 62 'nativeMethodBehavior', |
| 63 data1.nativeMethodBehavior, |
| 64 data2.nativeMethodBehavior, |
| 65 strategy.elementEquivalence, |
| 66 (a, b) => testNativeBehavior(a, b, strategy: strategy), |
| 67 allowExtra: allowExtra); |
| 68 |
| 69 checkMapEquivalence( |
| 70 data1, |
| 71 data2, |
| 72 'nativeFieldLoadBehavior', |
| 73 data1.nativeFieldLoadBehavior, |
| 74 data2.nativeFieldLoadBehavior, |
| 75 strategy.elementEquivalence, |
| 76 (a, b) => testNativeBehavior(a, b, strategy: strategy), |
| 77 allowExtra: allowExtra); |
| 78 |
| 79 checkMapEquivalence( |
| 80 data1, |
| 81 data2, |
| 82 'nativeFieldStoreBehavior', |
| 83 data1.nativeFieldStoreBehavior, |
| 84 data2.nativeFieldStoreBehavior, |
| 85 strategy.elementEquivalence, |
| 86 (a, b) => testNativeBehavior(a, b, strategy: strategy), |
| 87 allowExtra: allowExtra); |
| 88 |
| 89 checkMapEquivalence( |
| 90 data1, |
| 91 data2, |
| 92 'jsInteropLibraryNames', |
| 93 data1.jsInteropLibraryNames, |
| 94 data2.jsInteropLibraryNames, |
| 95 strategy.elementEquivalence, |
| 96 equality); |
| 97 |
| 98 checkSetEquivalence( |
| 99 data1, |
| 100 data2, |
| 101 'anonymousJsInteropClasses', |
| 102 data1.anonymousJsInteropClasses, |
| 103 data2.anonymousJsInteropClasses, |
| 104 strategy.elementEquivalence); |
| 105 |
| 106 checkMapEquivalence( |
| 107 data1, |
| 108 data2, |
| 109 'jsInteropClassNames', |
| 110 data1.jsInteropClassNames, |
| 111 data2.jsInteropClassNames, |
| 112 strategy.elementEquivalence, |
| 113 equality); |
| 114 |
| 115 checkMapEquivalence( |
| 116 data1, |
| 117 data2, |
| 118 'jsInteropMemberNames', |
| 119 data1.jsInteropMemberNames, |
| 120 data2.jsInteropMemberNames, |
| 121 strategy.elementEquivalence, |
| 122 equality); |
| 123 } |
| 124 |
| 125 void checkInterceptorData(InterceptorDataImpl data1, InterceptorDataImpl data2, |
| 126 bool elementEquivalence(Entity a, Entity b), |
| 127 {bool verbose: false}) { |
| 128 checkMapEquivalence( |
| 129 data1, |
| 130 data2, |
| 131 'interceptedElements', |
| 132 data1.interceptedElementsForTesting, |
| 133 data2.interceptedElementsForTesting, |
| 134 equality, |
| 135 (a, b) => areSetsEquivalent(a, b, elementEquivalence)); |
| 136 |
| 137 checkSetEquivalence(data1, data2, 'interceptedClasses', |
| 138 data1.interceptedClasses, data2.interceptedClasses, elementEquivalence); |
| 139 |
| 140 checkSetEquivalence( |
| 141 data1, |
| 142 data2, |
| 143 'classesMixedIntoInterceptedClasses', |
| 144 data1.classesMixedIntoInterceptedClassesForTesting, |
| 145 data2.classesMixedIntoInterceptedClassesForTesting, |
| 146 elementEquivalence); |
| 147 } |
| 148 |
| 149 void checkClassHierarchyNodes( |
| 150 ClosedWorld closedWorld1, |
| 151 ClosedWorld closedWorld2, |
| 152 ClassHierarchyNode node1, |
| 153 ClassHierarchyNode node2, |
| 154 bool elementEquivalence(Entity a, Entity b), |
| 155 {bool verbose: false}) { |
| 156 if (verbose) { |
| 157 print('Checking $node1 vs $node2'); |
| 158 } |
| 159 ClassEntity cls1 = node1.cls; |
| 160 ClassEntity cls2 = node2.cls; |
| 161 Expect.isTrue(elementEquivalence(cls1, cls2), |
| 162 "Element identity mismatch for ${cls1} vs ${cls2}."); |
| 163 Expect.equals( |
| 164 node1.isDirectlyInstantiated, |
| 165 node2.isDirectlyInstantiated, |
| 166 "Value mismatch for 'isDirectlyInstantiated' " |
| 167 "for ${cls1} vs ${cls2}."); |
| 168 Expect.equals( |
| 169 node1.isIndirectlyInstantiated, |
| 170 node2.isIndirectlyInstantiated, |
| 171 "Value mismatch for 'isIndirectlyInstantiated' " |
| 172 "for ${node1.cls} vs ${node2.cls}."); |
| 173 // TODO(johnniwinther): Enforce a canonical and stable order on direct |
| 174 // subclasses. |
| 175 for (ClassHierarchyNode child in node1.directSubclasses) { |
| 176 bool found = false; |
| 177 for (ClassHierarchyNode other in node2.directSubclasses) { |
| 178 ClassEntity child1 = child.cls; |
| 179 ClassEntity child2 = other.cls; |
| 180 if (elementEquivalence(child1, child2)) { |
| 181 checkClassHierarchyNodes( |
| 182 closedWorld1, closedWorld2, child, other, elementEquivalence, |
| 183 verbose: verbose); |
| 184 found = true; |
| 185 break; |
| 186 } |
| 187 } |
| 188 if (!found) { |
| 189 if (child.isInstantiated) { |
| 190 print('Missing subclass ${child.cls} of ${node1.cls} ' |
| 191 'in ${node2.directSubclasses}'); |
| 192 print(closedWorld1.dump( |
| 193 verbose ? closedWorld1.commonElements.objectClass : node1.cls)); |
| 194 print(closedWorld2.dump( |
| 195 verbose ? closedWorld2.commonElements.objectClass : node2.cls)); |
| 196 } |
| 197 Expect.isFalse( |
| 198 child.isInstantiated, |
| 199 'Missing subclass ${child.cls} of ${node1.cls} in ' |
| 200 '${node2.directSubclasses}'); |
| 201 } |
| 202 } |
| 203 checkMixinUses( |
| 204 closedWorld1, closedWorld2, node1.cls, node2.cls, elementEquivalence, |
| 205 verbose: verbose); |
| 206 } |
| 207 |
| 208 void checkMixinUses( |
| 209 ClosedWorld closedWorld1, |
| 210 ClosedWorld closedWorld2, |
| 211 ClassEntity class1, |
| 212 ClassEntity class2, |
| 213 bool elementEquivalence(Entity a, Entity b), |
| 214 {bool verbose: false}) { |
| 215 checkSets(closedWorld1.mixinUsesOf(class1), closedWorld2.mixinUsesOf(class2), |
| 216 "Mixin uses of $class1 vs $class2", elementEquivalence, |
| 217 verbose: verbose); |
| 218 } |
| 219 |
| 220 /// Check member property equivalence between all members common to [compiler1] |
| 221 /// and [compiler2]. |
| 222 void checkLoadedLibraryMembers( |
| 223 Compiler compiler1, |
| 224 Compiler compiler2, |
| 225 bool hasProperty(Element member1), |
| 226 void checkMemberProperties(Compiler compiler1, Element member1, |
| 227 Compiler compiler2, Element member2, |
| 228 {bool verbose}), |
| 229 {bool verbose: false}) { |
| 230 void checkMembers(Element member1, Element member2) { |
| 231 if (member1.isClass && member2.isClass) { |
| 232 ClassElement class1 = member1; |
| 233 ClassElement class2 = member2; |
| 234 if (!class1.isResolved) return; |
| 235 |
| 236 if (hasProperty(member1)) { |
| 237 if (areElementsEquivalent(member1, member2)) { |
| 238 checkMemberProperties(compiler1, member1, compiler2, member2, |
| 239 verbose: verbose); |
| 240 } |
| 241 } |
| 242 |
| 243 class1.forEachLocalMember((m1) { |
| 244 checkMembers(m1, class2.localLookup(m1.name)); |
| 245 }); |
| 246 ClassElement superclass1 = class1.superclass; |
| 247 ClassElement superclass2 = class2.superclass; |
| 248 while (superclass1 != null && superclass1.isUnnamedMixinApplication) { |
| 249 for (ConstructorElement c1 in superclass1.constructors) { |
| 250 checkMembers(c1, superclass2.lookupConstructor(c1.name)); |
| 251 } |
| 252 superclass1 = superclass1.superclass; |
| 253 superclass2 = superclass2.superclass; |
| 254 } |
| 255 return; |
| 256 } |
| 257 |
| 258 if (!hasProperty(member1)) { |
| 259 return; |
| 260 } |
| 261 |
| 262 if (member2 == null) { |
| 263 throw 'Missing member for ${member1}'; |
| 264 } |
| 265 |
| 266 if (areElementsEquivalent(member1, member2)) { |
| 267 checkMemberProperties(compiler1, member1, compiler2, member2, |
| 268 verbose: verbose); |
| 269 } |
| 270 } |
| 271 |
| 272 for (LibraryElement library1 in compiler1.libraryLoader.libraries) { |
| 273 LibraryElement library2 = |
| 274 compiler2.libraryLoader.lookupLibrary(library1.canonicalUri); |
| 275 if (library2 != null) { |
| 276 library1.forEachLocalMember((Element member1) { |
| 277 checkMembers(member1, library2.localLookup(member1.name)); |
| 278 }); |
| 279 } |
| 280 } |
| 281 } |
| 282 |
| 283 /// Check equivalence of all resolution impacts. |
| 284 void checkAllImpacts(Compiler compiler1, Compiler compiler2, |
| 285 {bool verbose: false}) { |
| 286 checkLoadedLibraryMembers(compiler1, compiler2, (Element member1) { |
| 287 return compiler1.resolution.hasResolutionImpact(member1); |
| 288 }, checkImpacts, verbose: verbose); |
| 289 } |
| 290 |
| 291 /// Check equivalence of resolution impact for [member1] and [member2]. |
| 292 void checkImpacts( |
| 293 Compiler compiler1, Element member1, Compiler compiler2, Element member2, |
| 294 {bool verbose: false}) { |
| 295 ResolutionImpact impact1 = compiler1.resolution.getResolutionImpact(member1); |
| 296 ResolutionImpact impact2 = compiler2.resolution.getResolutionImpact(member2); |
| 297 |
| 298 if (impact1 == null && impact2 == null) return; |
| 299 |
| 300 if (verbose) { |
| 301 print('Checking impacts for $member1 vs $member2'); |
| 302 } |
| 303 |
| 304 if (impact1 == null) { |
| 305 throw 'Missing impact for $member1. $member2 has $impact2'; |
| 306 } |
| 307 if (impact2 == null) { |
| 308 throw 'Missing impact for $member2. $member1 has $impact1'; |
| 309 } |
| 310 |
| 311 testResolutionImpactEquivalence(impact1, impact2, |
| 312 strategy: const CheckStrategy()); |
| 313 } |
| 314 |
| 315 void checkAllResolvedAsts(Compiler compiler1, Compiler compiler2, |
| 316 {bool verbose: false}) { |
| 317 checkLoadedLibraryMembers(compiler1, compiler2, (Element member1) { |
| 318 return member1 is ExecutableElement && |
| 319 compiler1.resolution.hasResolvedAst(member1); |
| 320 }, checkResolvedAsts, verbose: verbose); |
| 321 } |
| 322 |
| 323 /// Check equivalence of [impact1] and [impact2]. |
| 324 void checkResolvedAsts( |
| 325 Compiler compiler1, Element member1, Compiler compiler2, Element member2, |
| 326 {bool verbose: false}) { |
| 327 if (!compiler2.serialization.isDeserialized(member2)) { |
| 328 return; |
| 329 } |
| 330 ResolvedAst resolvedAst1 = compiler1.resolution.getResolvedAst(member1); |
| 331 ResolvedAst resolvedAst2 = compiler2.serialization.getResolvedAst(member2); |
| 332 |
| 333 if (resolvedAst1 == null || resolvedAst2 == null) return; |
| 334 |
| 335 if (verbose) { |
| 336 print('Checking resolved asts for $member1 vs $member2'); |
| 337 } |
| 338 |
| 339 testResolvedAstEquivalence(resolvedAst1, resolvedAst2, const CheckStrategy()); |
| 340 } |
| 341 |
| 342 void checkNativeClasses( |
| 343 Compiler compiler1, Compiler compiler2, TestStrategy strategy) { |
| 344 Iterable<ClassEntity> nativeClasses1 = compiler1 |
| 345 .backend.nativeResolutionEnqueuerForTesting.nativeClassesForTesting; |
| 346 Iterable<ClassEntity> nativeClasses2 = compiler2 |
| 347 .backend.nativeResolutionEnqueuerForTesting.nativeClassesForTesting; |
| 348 |
| 349 checkSetEquivalence(compiler1, compiler2, 'nativeClasses', nativeClasses1, |
| 350 nativeClasses2, strategy.elementEquivalence); |
| 351 |
| 352 Iterable<ClassEntity> registeredClasses1 = compiler1 |
| 353 .backend.nativeResolutionEnqueuerForTesting.registeredClassesForTesting; |
| 354 Iterable<ClassEntity> registeredClasses2 = compiler2 |
| 355 .backend.nativeResolutionEnqueuerForTesting.registeredClassesForTesting; |
| 356 |
| 357 checkSetEquivalence(compiler1, compiler2, 'registeredClasses', |
| 358 registeredClasses1, registeredClasses2, strategy.elementEquivalence); |
| 359 } |
| 360 |
| 361 void checkNativeBasicData(NativeBasicDataImpl data1, NativeBasicDataImpl data2, |
| 362 TestStrategy strategy) { |
| 363 checkMapEquivalence( |
| 364 data1, |
| 365 data2, |
| 366 'nativeClassTagInfo', |
| 367 data1.nativeClassTagInfo, |
| 368 data2.nativeClassTagInfo, |
| 369 strategy.elementEquivalence, |
| 370 (a, b) => a == b); |
| 371 // TODO(johnniwinther): Check the remaining properties. |
| 372 } |
| 373 |
| 374 void checkBackendUsage( |
| 375 BackendUsageImpl usage1, BackendUsageImpl usage2, TestStrategy strategy) { |
| 376 checkSetEquivalence( |
| 377 usage1, |
| 378 usage2, |
| 379 'globalClassDependencies', |
| 380 usage1.globalClassDependencies, |
| 381 usage2.globalClassDependencies, |
| 382 strategy.elementEquivalence); |
| 383 checkSetEquivalence( |
| 384 usage1, |
| 385 usage2, |
| 386 'globalFunctionDependencies', |
| 387 usage1.globalFunctionDependencies, |
| 388 usage2.globalFunctionDependencies, |
| 389 strategy.elementEquivalence); |
| 390 checkSetEquivalence( |
| 391 usage1, |
| 392 usage2, |
| 393 'helperClassesUsed', |
| 394 usage1.helperClassesUsed, |
| 395 usage2.helperClassesUsed, |
| 396 strategy.elementEquivalence); |
| 397 checkSetEquivalence( |
| 398 usage1, |
| 399 usage2, |
| 400 'helperFunctionsUsed', |
| 401 usage1.helperFunctionsUsed, |
| 402 usage2.helperFunctionsUsed, |
| 403 strategy.elementEquivalence); |
| 404 check( |
| 405 usage1, |
| 406 usage2, |
| 407 'needToInitializeIsolateAffinityTag', |
| 408 usage1.needToInitializeIsolateAffinityTag, |
| 409 usage2.needToInitializeIsolateAffinityTag); |
| 410 check( |
| 411 usage1, |
| 412 usage2, |
| 413 'needToInitializeDispatchProperty', |
| 414 usage1.needToInitializeDispatchProperty, |
| 415 usage2.needToInitializeDispatchProperty); |
| 416 check(usage1, usage2, 'requiresPreamble', usage1.requiresPreamble, |
| 417 usage2.requiresPreamble); |
| 418 check(usage1, usage2, 'isInvokeOnUsed', usage1.isInvokeOnUsed, |
| 419 usage2.isInvokeOnUsed); |
| 420 check(usage1, usage2, 'isRuntimeTypeUsed', usage1.isRuntimeTypeUsed, |
| 421 usage2.isRuntimeTypeUsed); |
| 422 check(usage1, usage2, 'isIsolateInUse', usage1.isIsolateInUse, |
| 423 usage2.isIsolateInUse); |
| 424 check(usage1, usage2, 'isFunctionApplyUsed', usage1.isFunctionApplyUsed, |
| 425 usage2.isFunctionApplyUsed); |
| 426 check(usage1, usage2, 'isNoSuchMethodUsed', usage1.isNoSuchMethodUsed, |
| 427 usage2.isNoSuchMethodUsed); |
| 428 } |
| 429 |
| 430 checkElementEnvironment( |
| 431 ElementEnvironment env1, ElementEnvironment env2, TestStrategy strategy) { |
| 432 checkMembers(MemberEntity member1, MemberEntity member2) { |
| 433 Expect.equals(env1.isDeferredLoadLibraryGetter(member1), |
| 434 env2.isDeferredLoadLibraryGetter(member2)); |
| 435 |
| 436 checkListEquivalence( |
| 437 member1, |
| 438 member2, |
| 439 'metadata', |
| 440 env1.getMemberMetadata(member1), |
| 441 env2.getMemberMetadata(member2), |
| 442 strategy.testConstantValues); |
| 443 } |
| 444 |
| 445 checkSetEquivalence(env1, env2, 'libraries', env1.libraries, env2.libraries, |
| 446 strategy.elementEquivalence, |
| 447 onSameElement: (LibraryEntity lib1, LibraryEntity lib2) { |
| 448 Expect.identical(lib1, env1.lookupLibrary(lib1.canonicalUri)); |
| 449 Expect.identical(lib2, env2.lookupLibrary(lib2.canonicalUri)); |
| 450 |
| 451 List<ClassEntity> classes2 = <ClassEntity>[]; |
| 452 env1.forEachClass(lib1, (ClassEntity cls1) { |
| 453 Expect.identical(cls1, env1.lookupClass(lib1, cls1.name)); |
| 454 |
| 455 String className = cls1.name; |
| 456 ClassEntity cls2 = env2.lookupClass(lib2, className); |
| 457 Expect.isNotNull(cls2, 'Missing class $className in $lib2'); |
| 458 Expect.identical(cls2, env2.lookupClass(lib2, cls2.name)); |
| 459 |
| 460 check(lib1, lib2, 'class:${className}', cls1, cls2, |
| 461 strategy.elementEquivalence); |
| 462 |
| 463 Expect.equals(env1.isGenericClass(cls1), env2.isGenericClass(cls2)); |
| 464 |
| 465 check( |
| 466 cls1, |
| 467 cls2, |
| 468 'superclass', |
| 469 env1.getSuperClass(cls1, skipUnnamedMixinApplications: false), |
| 470 env2.getSuperClass(cls2, skipUnnamedMixinApplications: false), |
| 471 strategy.elementEquivalence); |
| 472 check( |
| 473 cls1, |
| 474 cls2, |
| 475 'superclass', |
| 476 env1.getSuperClass(cls1, skipUnnamedMixinApplications: true), |
| 477 env2.getSuperClass(cls2, skipUnnamedMixinApplications: true), |
| 478 strategy.elementEquivalence); |
| 479 |
| 480 List<InterfaceType> supertypes1 = <InterfaceType>[]; |
| 481 env1.forEachSupertype(cls1, supertypes1.add); |
| 482 List<InterfaceType> supertypes2 = <InterfaceType>[]; |
| 483 env2.forEachSupertype(cls2, supertypes1.add); |
| 484 strategy.testTypeLists( |
| 485 cls1, cls2, 'supertypes', supertypes1, supertypes2); |
| 486 |
| 487 List<ClassEntity> mixins1 = <ClassEntity>[]; |
| 488 env1.forEachMixin(cls1, mixins1.add); |
| 489 List<ClassEntity> mixins2 = <ClassEntity>[]; |
| 490 env2.forEachMixin(cls2, mixins2.add); |
| 491 strategy.testLists( |
| 492 cls1, cls2, 'mixins', mixins1, mixins2, strategy.elementEquivalence); |
| 493 |
| 494 Map<MemberEntity, ClassEntity> members1 = <MemberEntity, ClassEntity>{}; |
| 495 Map<MemberEntity, ClassEntity> members2 = <MemberEntity, ClassEntity>{}; |
| 496 env1.forEachClassMember(cls1, |
| 497 (ClassEntity declarer1, MemberEntity member1) { |
| 498 if (cls1 == declarer1) { |
| 499 Expect.identical( |
| 500 member1, |
| 501 env1.lookupClassMember(cls1, member1.name, |
| 502 setter: member1.isSetter)); |
| 503 } |
| 504 members1[member1] = declarer1; |
| 505 }); |
| 506 env2.forEachClassMember(cls2, |
| 507 (ClassEntity declarer2, MemberEntity member2) { |
| 508 if (cls2 == declarer2) { |
| 509 Expect.identical( |
| 510 member2, |
| 511 env2.lookupClassMember(cls2, member2.name, |
| 512 setter: member2.isSetter)); |
| 513 } |
| 514 members2[member2] = declarer2; |
| 515 }); |
| 516 checkMapEquivalence(cls1, cls2, 'members', members1, members2, (a, b) { |
| 517 bool result = strategy.elementEquivalence(a, b); |
| 518 if (result) checkMembers(a, b); |
| 519 return result; |
| 520 }, strategy.elementEquivalence); |
| 521 |
| 522 classes2.add(cls2); |
| 523 }); |
| 524 env2.forEachClass(lib2, (ClassEntity cls2) { |
| 525 Expect.isTrue(classes2.contains(cls2), "Extra class $cls2 in $lib2"); |
| 526 }); |
| 527 }); |
| 528 // TODO(johnniwinther): Test the remaining properties of [ElementEnvironment]. |
| 529 } |
| 530 |
| 531 bool areInstantiationInfosEquivalent( |
| 532 InstantiationInfo info1, |
| 533 InstantiationInfo info2, |
| 534 bool elementEquivalence(Entity a, Entity b), |
| 535 bool typeEquivalence(DartType a, DartType b)) { |
| 536 checkMaps( |
| 537 info1.instantiationMap, |
| 538 info2.instantiationMap, |
| 539 'instantiationMap of\n ' |
| 540 '${info1.instantiationMap}\nvs ${info2.instantiationMap}', |
| 541 elementEquivalence, |
| 542 (a, b) => areSetsEquivalent( |
| 543 a, b, (a, b) => areInstancesEquivalent(a, b, typeEquivalence))); |
| 544 return true; |
| 545 } |
| 546 |
| 547 bool areInstancesEquivalent(Instance instance1, Instance instance2, |
| 548 bool typeEquivalence(DartType a, DartType b)) { |
| 549 InterfaceType type1 = instance1.type; |
| 550 InterfaceType type2 = instance2.type; |
| 551 return typeEquivalence(type1, type2) && |
| 552 instance1.kind == instance2.kind && |
| 553 instance1.isRedirection == instance2.isRedirection; |
| 554 } |
| 555 |
| 556 void checkResolutionEnqueuers( |
| 557 BackendUsage backendUsage1, |
| 558 BackendUsage backendUsage2, |
| 559 ResolutionEnqueuer enqueuer1, |
| 560 ResolutionEnqueuer enqueuer2, |
| 561 {bool elementEquivalence(Entity a, Entity b): areElementsEquivalent, |
| 562 bool typeEquivalence(DartType a, DartType b): areTypesEquivalent, |
| 563 bool elementFilter(Element element), |
| 564 bool verbose: false}) { |
| 565 ResolutionWorldBuilderBase worldBuilder1 = enqueuer1.worldBuilder; |
| 566 ResolutionWorldBuilderBase worldBuilder2 = enqueuer2.worldBuilder; |
| 567 |
| 568 checkSets( |
| 569 enqueuer1.worldBuilder.instantiatedTypes, |
| 570 enqueuer2.worldBuilder.instantiatedTypes, |
| 571 "Instantiated types mismatch", |
| 572 typeEquivalence, |
| 573 verbose: verbose); |
| 574 |
| 575 checkSets( |
| 576 enqueuer1.worldBuilder.directlyInstantiatedClasses, |
| 577 enqueuer2.worldBuilder.directlyInstantiatedClasses, |
| 578 "Directly instantiated classes mismatch", |
| 579 elementEquivalence, |
| 580 verbose: verbose); |
| 581 |
| 582 checkMaps( |
| 583 worldBuilder1.getInstantiationMap(), |
| 584 worldBuilder2.getInstantiationMap(), |
| 585 "Instantiated classes mismatch", |
| 586 elementEquivalence, |
| 587 (a, b) => areInstantiationInfosEquivalent( |
| 588 a, b, elementEquivalence, typeEquivalence), |
| 589 verbose: verbose); |
| 590 |
| 591 checkSets(enqueuer1.processedEntities, enqueuer2.processedEntities, |
| 592 "Processed element mismatch", elementEquivalence, elementFilter: (e) { |
| 593 return elementFilter != null ? elementFilter(e) : true; |
| 594 }, verbose: verbose); |
| 595 |
| 596 checkSets(enqueuer1.worldBuilder.isChecks, enqueuer2.worldBuilder.isChecks, |
| 597 "Is-check mismatch", typeEquivalence, |
| 598 verbose: verbose); |
| 599 |
| 600 Expect.equals(backendUsage1.isInvokeOnUsed, backendUsage2.isInvokeOnUsed, |
| 601 "JavaScriptBackend.hasInvokeOnSupport mismatch"); |
| 602 Expect.equals( |
| 603 backendUsage1.isFunctionApplyUsed, |
| 604 backendUsage1.isFunctionApplyUsed, |
| 605 "JavaScriptBackend.hasFunctionApplySupport mismatch"); |
| 606 Expect.equals( |
| 607 backendUsage1.isRuntimeTypeUsed, |
| 608 backendUsage2.isRuntimeTypeUsed, |
| 609 "JavaScriptBackend.hasRuntimeTypeSupport mismatch"); |
| 610 Expect.equals(backendUsage1.isIsolateInUse, backendUsage2.isIsolateInUse, |
| 611 "JavaScriptBackend.hasIsolateSupport mismatch"); |
| 612 } |
| OLD | NEW |