OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 part of js_backend; | 5 part of js_backend; |
6 | 6 |
7 /** | 7 /** |
8 * A function element that represents a closure call. The signature is copied | 8 * A function element that represents a closure call. The signature is copied |
9 * from the given element. | 9 * from the given element. |
10 */ | 10 */ |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 | 92 |
93 /** | 93 /** |
94 * Raw ClassElement symbols occuring in is-checks and type assertions. If the | 94 * Raw ClassElement symbols occuring in is-checks and type assertions. If the |
95 * program contains parameterized checks `x is Set<int>` and | 95 * program contains parameterized checks `x is Set<int>` and |
96 * `x is Set<String>` then the ClassElement `Set` will occur once in | 96 * `x is Set<String>` then the ClassElement `Set` will occur once in |
97 * [checkedClasses]. | 97 * [checkedClasses]. |
98 */ | 98 */ |
99 Set<ClassElement> checkedClasses; | 99 Set<ClassElement> checkedClasses; |
100 | 100 |
101 /** | 101 /** |
| 102 * Set of JS native classes (or 'Object') that need a [:$nativeCheck:] method, |
| 103 * because they could be used as a type argument in an is-check. |
| 104 * |
| 105 * For example, in the following program, the class int needs a native check |
| 106 * to correctly match integers in the is-check: |
| 107 * class Check<T> { foo(o) => o is T; } |
| 108 * main() => new Check<int>().foo(3); |
| 109 */ |
| 110 Set<ClassElement> requiredNativeChecks; |
| 111 |
| 112 /** |
102 * Raw Typedef symbols occuring in is-checks and type assertions. If the | 113 * Raw Typedef symbols occuring in is-checks and type assertions. If the |
103 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement | 114 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement |
104 * `F` will occur once in [checkedTypedefs]. | 115 * `F` will occur once in [checkedTypedefs]. |
105 */ | 116 */ |
106 Set<TypedefElement> checkedTypedefs; | 117 Set<TypedefElement> checkedTypedefs; |
107 | 118 |
108 final bool generateSourceMap; | 119 final bool generateSourceMap; |
109 | 120 |
| 121 Iterable<ClassElement> cachedClassesUsingTypeVariableTests; |
| 122 |
| 123 Iterable<ClassElement> get classesUsingTypeVariableTests { |
| 124 if (cachedClassesUsingTypeVariableTests == null) { |
| 125 cachedClassesUsingTypeVariableTests = backend.rti.isChecks |
| 126 .where((DartType t) => t is TypeVariableType) |
| 127 .map((TypeVariableType v) => v.element.getEnclosingClass()); |
| 128 } |
| 129 return cachedClassesUsingTypeVariableTests; |
| 130 } |
| 131 |
110 CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) | 132 CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap) |
111 : boundClosureBuffer = new CodeBuffer(), | 133 : boundClosureBuffer = new CodeBuffer(), |
112 mainBuffer = new CodeBuffer(), | 134 mainBuffer = new CodeBuffer(), |
113 this.namer = namer, | 135 this.namer = namer, |
114 boundClosureCache = new Map<int, String>(), | 136 boundClosureCache = new Map<int, String>(), |
115 interceptorClosureCache = new Map<int, String>(), | 137 interceptorClosureCache = new Map<int, String>(), |
116 constantEmitter = new ConstantEmitter(compiler, namer), | 138 constantEmitter = new ConstantEmitter(compiler, namer), |
117 super(compiler) { | 139 super(compiler) { |
118 nativeEmitter = new NativeEmitter(this); | 140 nativeEmitter = new NativeEmitter(this); |
119 } | 141 } |
120 | 142 |
121 void computeRequiredTypeChecks() { | 143 void computeRequiredTypeChecks() { |
122 assert(checkedClasses == null); | 144 assert(checkedClasses == null && |
| 145 checkedTypedefs == null && |
| 146 requiredNativeChecks == null); |
| 147 |
| 148 // Compute type arguments of classes that potentially use their type |
| 149 // variables in is-checks and compute the (small) set of classes that |
| 150 // need native check methods (consult the documentation of |
| 151 // [requiredNativeChecks] for more information). |
| 152 requiredNativeChecks = new Set<ClassElement>(); |
| 153 Iterable<ClassElement> classes = classesUsingTypeVariableTests; |
| 154 if (!classes.isEmpty) { |
| 155 // Find all instantiated types that are a subtype of a class that uses |
| 156 // one of its type arguments in an is-check and add the arguments to the |
| 157 // set of is-checks. |
| 158 // TODO(karlklose): replace this with code that uses a subtype lookup |
| 159 // datastructure in the world. |
| 160 for (DartType type in compiler.codegenWorld.instantiatedTypes) { |
| 161 if (type.kind != TypeKind.INTERFACE) continue; |
| 162 InterfaceType classType = type; |
| 163 for (ClassElement cls in classes) { |
| 164 // We need the type as instance of its superclass anyway, so we just |
| 165 // try to compute the substitution; if the result is [:null:], the |
| 166 // classes are not related. |
| 167 InterfaceType instance = classType.asInstanceOf(cls); |
| 168 if (instance == null) continue; |
| 169 Link<DartType> typeArguments = instance.typeArguments; |
| 170 for (DartType argument in typeArguments) { |
| 171 Element element = argument.element; |
| 172 JavaScriptBackend backend = compiler.backend; |
| 173 if (backend.rti.needsNativeCheck(element)) { |
| 174 requiredNativeChecks.add(element); |
| 175 } |
| 176 compiler.codegenWorld.isChecks.add(argument); |
| 177 } |
| 178 } |
| 179 } |
| 180 } |
| 181 |
123 checkedClasses = new Set<ClassElement>(); | 182 checkedClasses = new Set<ClassElement>(); |
124 checkedTypedefs = new Set<TypedefElement>(); | 183 checkedTypedefs = new Set<TypedefElement>(); |
125 compiler.codegenWorld.isChecks.forEach((DartType t) { | 184 compiler.codegenWorld.isChecks.forEach((DartType t) { |
126 if (t is InterfaceType) { | 185 if (t is InterfaceType) { |
127 checkedClasses.add(t.element); | 186 checkedClasses.add(t.element); |
128 } else if (t is TypedefType) { | 187 } else if (t is TypedefType) { |
129 checkedTypedefs.add(t.element); | 188 checkedTypedefs.add(t.element); |
130 } | 189 } |
131 }); | 190 }); |
132 } | 191 } |
(...skipping 935 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1068 includeSuperMembers: false); | 1127 includeSuperMembers: false); |
1069 }); | 1128 }); |
1070 | 1129 |
1071 classElement.implementation.forEachMember( | 1130 classElement.implementation.forEachMember( |
1072 visitMember, | 1131 visitMember, |
1073 includeBackendMembers: true, | 1132 includeBackendMembers: true, |
1074 includeSuperMembers: false); | 1133 includeSuperMembers: false); |
1075 | 1134 |
1076 void generateIsTest(Element other) { | 1135 void generateIsTest(Element other) { |
1077 jsAst.Expression code; | 1136 jsAst.Expression code; |
1078 if (compiler.objectClass == other) return; | 1137 if (other == compiler.objectClass) return; |
1079 if (nativeEmitter.requiresNativeIsCheck(other)) { | 1138 if (nativeEmitter.requiresNativeIsCheck(other)) { |
1080 code = js.fun([], [js.return_(true)]); | 1139 code = js.fun([], [js.return_(true)]); |
1081 } else { | 1140 } else { |
1082 code = new jsAst.LiteralBool(true); | 1141 code = new jsAst.LiteralBool(true); |
1083 } | 1142 } |
1084 builder.addProperty(namer.operatorIs(other), code); | 1143 builder.addProperty(namer.operatorIs(other), code); |
1085 } | 1144 } |
1086 | 1145 |
1087 void generateSubstitution(Element other, {bool emitNull: false}) { | 1146 void generateSubstitution(Element other, {bool emitNull: false}) { |
1088 RuntimeTypeInformation rti = backend.rti; | 1147 RuntimeTypeInformation rti = backend.rti; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1129 String name = backend.namer.publicInstanceMethodNameByArity( | 1188 String name = backend.namer.publicInstanceMethodNameByArity( |
1130 const SourceString('=='), 1); | 1189 const SourceString('=='), 1); |
1131 Function kind = (classElement == backend.jsNullClass) | 1190 Function kind = (classElement == backend.jsNullClass) |
1132 ? js.equals | 1191 ? js.equals |
1133 : js.strictEquals; | 1192 : js.strictEquals; |
1134 builder.addProperty(name, js.fun(['receiver', 'a'], | 1193 builder.addProperty(name, js.fun(['receiver', 'a'], |
1135 js.block(js.return_(kind(js['receiver'], js['a']))))); | 1194 js.block(js.return_(kind(js['receiver'], js['a']))))); |
1136 } | 1195 } |
1137 } | 1196 } |
1138 | 1197 |
1139 void emitRuntimeClassesAndTests(CodeBuffer buffer) { | 1198 void emitRuntimeTypeSupport(CodeBuffer buffer) { |
1140 RuntimeTypeInformation rti = backend.rti; | 1199 RuntimeTypeInformation rti = backend.rti; |
1141 TypeChecks typeChecks = rti.getRequiredChecks(); | 1200 TypeChecks typeChecks = rti.getRequiredChecks(); |
1142 | 1201 |
1143 bool needsHolder(ClassElement cls) { | 1202 bool needsHolder(ClassElement cls) { |
1144 return !neededClasses.contains(cls) || cls.isNative() || | 1203 return !neededClasses.contains(cls) || cls.isNative() || |
1145 rti.isJsNative(cls); | 1204 rti.isJsNative(cls); |
1146 } | 1205 } |
1147 | 1206 |
1148 /** | 1207 /** |
1149 * Generates a holder object if it is needed. A holder is a JavaScript | 1208 * Generates a holder object if it is needed. A holder is a JavaScript |
1150 * object literal with a field [builtin$cls] that contains the name of the | 1209 * object literal with a field [builtin$cls] that contains the name of the |
1151 * class as a string (just like object constructors do). The is-checks for | 1210 * class as a string (just like object constructors do). The is-checks for |
1152 * the class are are added to the holder object later. | 1211 * the class are are added to the holder object later. |
1153 */ | 1212 */ |
1154 void maybeGenerateHolder(ClassElement cls) { | 1213 void maybeGenerateHolder(ClassElement cls) { |
1155 if (!needsHolder(cls)) return; | 1214 if (!needsHolder(cls)) return; |
1156 String holder = namer.isolateAccess(cls); | 1215 String holder = namer.isolateAccess(cls); |
1157 String name = namer.getName(cls); | 1216 String name = namer.getName(cls); |
1158 buffer.add("$holder$_=$_{builtin\$cls:$_'$name'"); | 1217 buffer.add("$holder$_=$_{builtin\$cls:$_'$name'}$N"); |
1159 buffer.add('}$N'); | |
1160 } | 1218 } |
1161 | 1219 |
1162 // Create representation objects for classes that we do not have a class | 1220 // Create representation objects for classes that we do not have a class |
1163 // definition for (because they are uninstantiated or native). | 1221 // definition for (because they are uninstantiated or native). |
1164 for (ClassElement cls in rti.allArguments) { | 1222 for (ClassElement cls in rti.allArguments) { |
1165 maybeGenerateHolder(cls); | 1223 maybeGenerateHolder(cls); |
1166 } | 1224 } |
1167 | 1225 |
1168 // Add checks to the constructors of instantiated classes or to the created | 1226 // Add checks to the constructors of instantiated classes or to the created |
1169 // holder object. | 1227 // holder object. |
1170 for (ClassElement cls in typeChecks) { | 1228 for (ClassElement cls in typeChecks) { |
1171 String holder = namer.isolateAccess(cls); | 1229 String holder = namer.isolateAccess(cls); |
1172 for (ClassElement check in typeChecks[cls]) { | 1230 for (ClassElement check in typeChecks[cls]) { |
1173 buffer.add('$holder.${namer.operatorIs(check)}$_=${_}true$N'); | 1231 buffer.add('$holder.${namer.operatorIs(check)}$_=${_}true$N'); |
1174 String body = rti.getSupertypeSubstitution(cls, check); | 1232 String body = rti.getSupertypeSubstitution(cls, check); |
1175 if (body != null) { | 1233 if (body != null) { |
1176 buffer.add('$holder.${namer.substitutionName(check)}$_=${_}$body$N'); | 1234 buffer.add('$holder.${namer.substitutionName(check)}$_=${_}$body$N'); |
1177 } | 1235 } |
1178 }; | 1236 }; |
1179 } | 1237 } |
| 1238 |
| 1239 // Emit native check methods for the class representations of native types |
| 1240 // that could be used in an is-check against a type variable. |
| 1241 requiredNativeChecks.forEach((ClassElement cls) { |
| 1242 jsAst.Expression nativeCheck = rti.getNativeCheck(cls); |
| 1243 String holder = namer.isolateAccess(cls); |
| 1244 buffer.add('$holder.${namer.getNativeCheckName()}$_=$_'); |
| 1245 buffer.addBuffer(jsAst.prettyPrint(nativeCheck, compiler)); |
| 1246 buffer.add('$N'); |
| 1247 }); |
1180 } | 1248 } |
1181 | 1249 |
1182 void visitNativeMixins(ClassElement classElement, | 1250 void visitNativeMixins(ClassElement classElement, |
1183 void visit(MixinApplicationElement mixinApplication)) { | 1251 void visit(MixinApplicationElement mixinApplication)) { |
1184 if (!classElement.isNative()) return; | 1252 if (!classElement.isNative()) return; |
1185 // Use recursion to make sure to visit the superclasses before the | 1253 // Use recursion to make sure to visit the superclasses before the |
1186 // subclasses. Once we start keeping track of the emitted fields | 1254 // subclasses. Once we start keeping track of the emitted fields |
1187 // and members, we're going to want to visit these in the other | 1255 // and members, we're going to want to visit these in the other |
1188 // order so we get the most specialized definition first. | 1256 // order so we get the most specialized definition first. |
1189 void recurse(ClassElement cls) { | 1257 void recurse(ClassElement cls) { |
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1558 // substitutions for all checks and make emitSubstitution a NOP for the | 1626 // substitutions for all checks and make emitSubstitution a NOP for the |
1559 // rest of this function. | 1627 // rest of this function. |
1560 Set<ClassElement> emitted = new Set<ClassElement>(); | 1628 Set<ClassElement> emitted = new Set<ClassElement>(); |
1561 // TODO(karlklose): move the computation of these checks to | 1629 // TODO(karlklose): move the computation of these checks to |
1562 // RuntimeTypeInformation. | 1630 // RuntimeTypeInformation. |
1563 if (compiler.world.needsRti(cls)) { | 1631 if (compiler.world.needsRti(cls)) { |
1564 emitSubstitution(superclass, emitNull: true); | 1632 emitSubstitution(superclass, emitNull: true); |
1565 emitted.add(superclass); | 1633 emitted.add(superclass); |
1566 } | 1634 } |
1567 for (DartType supertype in cls.allSupertypes) { | 1635 for (DartType supertype in cls.allSupertypes) { |
| 1636 ClassElement superclass = supertype.element; |
| 1637 if (classesUsingTypeVariableTests.contains(superclass)) { |
| 1638 emitSubstitution(superclass, emitNull: true); |
| 1639 emitted.add(superclass); |
| 1640 } |
1568 for (ClassElement check in checkedClasses) { | 1641 for (ClassElement check in checkedClasses) { |
1569 if (supertype.element == check && !emitted.contains(check)) { | 1642 if (supertype.element == check && !emitted.contains(check)) { |
1570 // Generate substitution. If no substitution is necessary, emit | 1643 // Generate substitution. If no substitution is necessary, emit |
1571 // [:null:] to overwrite a (possibly) existing substitution from the | 1644 // [:null:] to overwrite a (possibly) existing substitution from the |
1572 // super classes. | 1645 // super classes. |
1573 emitSubstitution(check, emitNull: true); | 1646 emitSubstitution(check, emitNull: true); |
1574 emitted.add(check); | 1647 emitted.add(check); |
1575 } | 1648 } |
1576 } | 1649 } |
1577 } | 1650 } |
(...skipping 997 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2575 ..addAll(buildFinishIsolateConstructor()) | 2648 ..addAll(buildFinishIsolateConstructor()) |
2576 ); | 2649 ); |
2577 jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration( | 2650 jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration( |
2578 new jsAst.VariableDeclaration('init'), fun); | 2651 new jsAst.VariableDeclaration('init'), fun); |
2579 buffer.add(jsAst.prettyPrint(decl, compiler).getText()); | 2652 buffer.add(jsAst.prettyPrint(decl, compiler).getText()); |
2580 } | 2653 } |
2581 | 2654 |
2582 String assembleProgram() { | 2655 String assembleProgram() { |
2583 measure(() { | 2656 measure(() { |
2584 computeNeededClasses(); | 2657 computeNeededClasses(); |
2585 | |
2586 mainBuffer.add(GENERATED_BY); | 2658 mainBuffer.add(GENERATED_BY); |
2587 if (!compiler.enableMinification) mainBuffer.add(HOOKS_API_USAGE); | 2659 if (!compiler.enableMinification) mainBuffer.add(HOOKS_API_USAGE); |
2588 mainBuffer.add('function ${namer.isolateName}()$_{}\n'); | 2660 mainBuffer.add('function ${namer.isolateName}()$_{}\n'); |
2589 mainBuffer.add('init()$N$n'); | 2661 mainBuffer.add('init()$N$n'); |
2590 // Shorten the code by using "$$" as temporary. | 2662 // Shorten the code by using "$$" as temporary. |
2591 classesCollector = r"$$"; | 2663 classesCollector = r"$$"; |
2592 mainBuffer.add('var $classesCollector$_=$_{}$N'); | 2664 mainBuffer.add('var $classesCollector$_=$_{}$N'); |
2593 // Shorten the code by using [namer.CURRENT_ISOLATE] as temporary. | 2665 // Shorten the code by using [namer.CURRENT_ISOLATE] as temporary. |
2594 isolateProperties = namer.CURRENT_ISOLATE; | 2666 isolateProperties = namer.CURRENT_ISOLATE; |
2595 mainBuffer.add( | 2667 mainBuffer.add( |
2596 'var $isolateProperties$_=$_$isolatePropertiesName$N'); | 2668 'var $isolateProperties$_=$_$isolatePropertiesName$N'); |
2597 emitClasses(mainBuffer); | 2669 emitClasses(mainBuffer); |
2598 mainBuffer.add(boundClosureBuffer); | 2670 mainBuffer.add(boundClosureBuffer); |
2599 // Clear the buffer, so that we can reuse it for the native classes. | 2671 // Clear the buffer, so that we can reuse it for the native classes. |
2600 boundClosureBuffer.clear(); | 2672 boundClosureBuffer.clear(); |
2601 emitStaticFunctions(mainBuffer); | 2673 emitStaticFunctions(mainBuffer); |
2602 emitStaticFunctionGetters(mainBuffer); | 2674 emitStaticFunctionGetters(mainBuffer); |
2603 // We need to finish the classes before we construct compile time | 2675 // We need to finish the classes before we construct compile time |
2604 // constants. | 2676 // constants. |
2605 emitFinishClassesInvocationIfNecessary(mainBuffer); | 2677 emitFinishClassesInvocationIfNecessary(mainBuffer); |
2606 emitRuntimeClassesAndTests(mainBuffer); | 2678 emitRuntimeTypeSupport(mainBuffer); |
2607 emitCompileTimeConstants(mainBuffer); | 2679 emitCompileTimeConstants(mainBuffer); |
2608 // Static field initializations require the classes and compile-time | 2680 // Static field initializations require the classes and compile-time |
2609 // constants to be set up. | 2681 // constants to be set up. |
2610 emitStaticNonFinalFieldInitializations(mainBuffer); | 2682 emitStaticNonFinalFieldInitializations(mainBuffer); |
2611 emitOneShotInterceptors(mainBuffer); | 2683 emitOneShotInterceptors(mainBuffer); |
2612 emitGetInterceptorMethods(mainBuffer); | 2684 emitGetInterceptorMethods(mainBuffer); |
2613 emitLazilyInitializedStaticFields(mainBuffer); | 2685 emitLazilyInitializedStaticFields(mainBuffer); |
2614 | 2686 |
2615 isolateProperties = isolatePropertiesName; | 2687 isolateProperties = isolatePropertiesName; |
2616 // The following code should not use the short-hand for the | 2688 // The following code should not use the short-hand for the |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2676 """; | 2748 """; |
2677 const String HOOKS_API_USAGE = """ | 2749 const String HOOKS_API_USAGE = """ |
2678 // The code supports the following hooks: | 2750 // The code supports the following hooks: |
2679 // dartPrint(message) - if this function is defined it is called | 2751 // dartPrint(message) - if this function is defined it is called |
2680 // instead of the Dart [print] method. | 2752 // instead of the Dart [print] method. |
2681 // dartMainRunner(main) - if this function is defined, the Dart [main] | 2753 // dartMainRunner(main) - if this function is defined, the Dart [main] |
2682 // method will not be invoked directly. | 2754 // method will not be invoked directly. |
2683 // Instead, a closure that will invoke [main] is | 2755 // Instead, a closure that will invoke [main] is |
2684 // passed to [dartMainRunner]. | 2756 // passed to [dartMainRunner]. |
2685 """; | 2757 """; |
OLD | NEW |