Index: tests/compiler/dart2js/kernel/mixin_test.dart |
diff --git a/tests/compiler/dart2js/kernel/mixin_test.dart b/tests/compiler/dart2js/kernel/mixin_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ca56dc15300b83860807a2a0e1b099a4c9d2d941 |
--- /dev/null |
+++ b/tests/compiler/dart2js/kernel/mixin_test.dart |
@@ -0,0 +1,201 @@ |
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+// Test that the optimized algorithm for mixin applications matches the mixins |
+// generated by fasta. |
+library dart2js.kernel.mixins_test; |
+ |
+import 'package:async_helper/async_helper.dart'; |
+import 'package:compiler/src/commandline_options.dart'; |
+import 'package:compiler/src/common_elements.dart'; |
+import 'package:compiler/src/compiler.dart'; |
+import 'package:compiler/src/elements/entities.dart'; |
+import 'package:compiler/src/elements/types.dart'; |
+import 'package:compiler/src/kernel/kernel_strategy.dart'; |
+import 'package:compiler/src/resolution/class_hierarchy.dart'; |
+import 'package:compiler/src/universe/class_set.dart'; |
+import 'package:compiler/src/world.dart'; |
+import 'package:expect/expect.dart'; |
+import '../memory_compiler.dart'; |
+import '../equivalence/check_helpers.dart'; |
+import 'test_helpers.dart'; |
+import 'compiler_helper.dart'; |
+ |
+const SOURCE = const { |
+ 'main.dart': ''' |
+ |
+class Super {} |
+class Mixin1 {} |
+class Mixin2 {} |
+class Sub1 extends Super with Mixin1 {} |
+class Sub2 extends Super with Mixin1, Mixin2 {} |
+class NamedSub1 = Super with Mixin1; |
+class NamedSub2 = Super with Mixin1, Mixin2; |
+ |
+ |
+class GenericSuper<T> {} |
+class GenericMixin1<T> {} |
+class GenericMixin2<T> {} |
+class GenericSub1<T> extends GenericSuper<T> with GenericMixin1<T> {} |
+class GenericSub2<T> extends GenericSuper<T> |
+ with GenericMixin1<T>, GenericMixin2<T> {} |
+class GenericNamedSub1<T> = GenericSuper<T> with GenericMixin1<T>; |
+class GenericNamedSub2<T> = GenericSuper<T> |
+ with GenericMixin1<T>, GenericMixin2<T>; |
+ |
+class FixedSub1a extends GenericSuper<int> with GenericMixin1<int> {} |
+class FixedSub1b extends GenericSuper<int> with GenericMixin1<double> {} |
+class FixedSub2a extends GenericSuper<int> |
+ with GenericMixin1<int>, GenericMixin2<int> {} |
+class FixedSub2b extends GenericSuper<double> |
+ with GenericMixin1<double>, GenericMixin2<double> {} |
+ |
+ |
+main() { |
+ new Super(); |
+ new Mixin1(); |
+ new Mixin2(); |
+ new Sub1(); |
+ new Sub2(); |
+ new NamedSub1(); |
+ new NamedSub2(); |
+ |
+ new GenericSuper<int>(); |
+ new GenericMixin1<int>(); |
+ new GenericMixin2<int>(); |
+ new GenericSub1<int>(); |
+ new GenericSub2<int>(); |
+ new GenericNamedSub1<int>(); |
+ new GenericNamedSub2<int>(); |
+ |
+ new FixedSub1a(); |
+ new FixedSub1b(); |
+ new FixedSub2a(); |
+ new FixedSub2b(); |
+} |
+''' |
+}; |
+ |
+Map<ClassEntity, String> generateClassEnv( |
+ ElementEnvironment env, DartTypes types) { |
+ Map<ClassEntity, String> classEnv = <ClassEntity, String>{}; |
+ |
+ void createEnv(ClassEntity cls) { |
+ classEnv.putIfAbsent(cls, () { |
+ InterfaceType thisType = env.getThisType(cls); |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write('class '); |
+ sb.write(env.getThisType(cls)); |
+ ClassEntity superclass = env.getSuperClass(cls); |
+ if (superclass != null) { |
+ createEnv(superclass); |
+ sb.write(' extends '); |
+ sb.write(types.asInstanceOf(thisType, superclass)); |
+ } |
+ return sb.toString(); |
+ }); |
+ } |
+ |
+ env.forEachClass(env.mainLibrary, createEnv); |
+ |
+ return classEnv; |
+} |
+ |
+main(List<String> args) { |
+ asyncTest(() async { |
+ useOptimizedMixins = true; |
+ |
+ Uri entryPoint = await createTemp(Uri.parse('memory:main.dart'), SOURCE, |
+ printSteps: true); |
+ |
+ print( |
+ '---- compiler from ast -----------------------------------------------'); |
+ var result = |
+ await runCompiler(entryPoint: entryPoint, options: [Flags.analyzeOnly]); |
+ Compiler compiler1 = result.compiler; |
+ |
+ Compiler compiler2 = await compileWithDill( |
+ entryPoint, {}, [Flags.analyzeOnly], |
+ printSteps: true); |
+ |
+ ElementEnvironment env1 = compiler1.frontendStrategy.elementEnvironment; |
+ DartTypes types1 = compiler1.frontendStrategy.dartTypes; |
+ ClosedWorld closedWorld1 = compiler1.resolutionWorldBuilder.closeWorld(); |
+ |
+ KernelFrontEndStrategy frontendStrategy = compiler2.frontendStrategy; |
+ ElementEnvironment env2 = frontendStrategy.elementEnvironment; |
+ DartTypes types2 = frontendStrategy.dartTypes; |
+ ClosedWorld closedWorld2 = compiler2.resolutionWorldBuilder.closeWorld(); |
+ |
+ KernelEquivalence equivalence = |
+ new KernelEquivalence(frontendStrategy.elementMap); |
+ |
+ if (args.contains('-v')) { |
+ Map<ClassEntity, String> classEnv1 = generateClassEnv(env1, types1); |
+ Map<ClassEntity, String> classEnv2 = generateClassEnv(env2, types2); |
+ |
+ print('----'); |
+ classEnv1.forEach((ClassEntity cls, String env) { |
+ print(env); |
+ }); |
+ print('----'); |
+ classEnv2.forEach((ClassEntity cls, String env) { |
+ print(env); |
+ }); |
+ } |
+ |
+ void checkClasses(ClassEntity cls1, ClassEntity cls2) { |
+ if (cls1 == cls2) return; |
+ Expect.isNotNull(cls1, 'Missing class ${cls2.name}'); |
+ Expect.isNotNull(cls2, 'Missing class ${cls1.name}'); |
+ |
+ check(cls1.library, cls2.library, 'class ${cls1.name}', cls1, cls2, |
+ equivalence.entityEquivalence); |
+ InterfaceType thisType1 = types1.getThisType(cls1); |
+ InterfaceType thisType2 = types2.getThisType(cls2); |
+ check(cls1, cls2, 'thisType', thisType1, thisType2, |
+ equivalence.typeEquivalence); |
+ check(cls1, cls2, 'supertype', types1.getSupertype(cls1), |
+ types2.getSupertype(cls2), equivalence.typeEquivalence); |
+ checkClasses(env1.getSuperClass(cls1), env2.getSuperClass(cls2)); |
+ |
+ List<DartType> mixins1 = <DartType>[]; |
+ env1.forEachMixin(cls1, (ClassEntity mixin) { |
+ mixins1.add(types1.asInstanceOf(thisType1, mixin)); |
+ }); |
+ List<DartType> mixins2 = <DartType>[]; |
+ env2.forEachMixin(cls2, (ClassEntity mixin) { |
+ mixins2.add(types2.asInstanceOf(thisType2, mixin)); |
+ }); |
+ checkLists( |
+ mixins1, mixins2, '${cls1.name} mixins', equivalence.typeEquivalence); |
+ |
+ checkLists( |
+ types1.getInterfaces(cls1).toList(), |
+ types2.getInterfaces(cls2).toList(), |
+ '${cls1.name} interfaces', |
+ equivalence.typeEquivalence); |
+ checkLists( |
+ types1.getSupertypes(cls1).toList(), |
+ types2.getSupertypes(cls2).toList(), |
+ '${cls1.name} supertypes', |
+ equivalence.typeEquivalence); |
+ |
+ if (cls1 == compiler1.frontendStrategy.commonElements.objectClass) return; |
+ |
+ ClassHierarchyNode node1 = closedWorld1.getClassHierarchyNode(cls1); |
+ ClassHierarchyNode node2 = closedWorld2.getClassHierarchyNode(cls2); |
+ checkSets( |
+ new Set.from(node1.directSubclasses), |
+ new Set.from(node2.directSubclasses), |
+ '${cls1.name} direct subclasses', |
+ (a, b) => equivalence.entityEquivalence(a.cls, b.cls)); |
+ } |
+ |
+ env1.forEachClass(env1.mainLibrary, (ClassEntity cls1) { |
+ ClassEntity cls2 = env2.lookupClass(env2.mainLibrary, cls1.name); |
+ checkClasses(cls1, cls2); |
+ }); |
+ }); |
+} |