| 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 745 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 756 visitMember, | 756 visitMember, |
| 757 includeBackendMembers: true, | 757 includeBackendMembers: true, |
| 758 includeSuperMembers: false); | 758 includeSuperMembers: false); |
| 759 }); | 759 }); |
| 760 | 760 |
| 761 classElement.implementation.forEachMember( | 761 classElement.implementation.forEachMember( |
| 762 visitMember, | 762 visitMember, |
| 763 includeBackendMembers: true, | 763 includeBackendMembers: true, |
| 764 includeSuperMembers: false); | 764 includeSuperMembers: false); |
| 765 | 765 |
| 766 generateIsTestsOn(classElement, (Element other) { | 766 void generateIsTest(Element other) { |
| 767 js.Expression code; | 767 js.Expression code; |
| 768 if (compiler.objectClass == other) return; | 768 if (compiler.objectClass == other) return; |
| 769 if (nativeEmitter.requiresNativeIsCheck(other)) { | 769 if (nativeEmitter.requiresNativeIsCheck(other)) { |
| 770 code = js.fun([], js.block1(js.return_(new js.LiteralBool(true)))); | 770 code = js.fun([], js.block1(js.return_(new js.LiteralBool(true)))); |
| 771 } else { | 771 } else { |
| 772 code = new js.LiteralBool(true); | 772 code = new js.LiteralBool(true); |
| 773 } | 773 } |
| 774 builder.addProperty(namer.operatorIs(other), code); | 774 builder.addProperty(namer.operatorIs(other), code); |
| 775 }); | 775 } |
| 776 |
| 777 void generateSubstitution(Element other, {bool emitNull: false}) { |
| 778 RuntimeTypeInformation rti = backend.rti; |
| 779 // TODO(karlklose): support typedefs with variables. |
| 780 if (other.kind == ElementKind.CLASS) { |
| 781 String substitution = rti.getSupertypeSubstitution(classElement, other, |
| 782 alwaysGenerateFunction: true); |
| 783 if (substitution != null) { |
| 784 builder.addProperty(namer.substitutionName(other), |
| 785 new js.LiteralExpression(substitution)); |
| 786 } else if (emitNull) { |
| 787 builder.addProperty(namer.substitutionName(other), |
| 788 new js.LiteralNull()); |
| 789 } |
| 790 } |
| 791 } |
| 792 |
| 793 generateIsTestsOn(classElement, generateIsTest, generateSubstitution); |
| 776 | 794 |
| 777 if (identical(classElement, compiler.objectClass) | 795 if (identical(classElement, compiler.objectClass) |
| 778 && compiler.enabledNoSuchMethod) { | 796 && compiler.enabledNoSuchMethod) { |
| 779 // Emit the noSuchMethod handlers on the Object prototype now, | 797 // Emit the noSuchMethod handlers on the Object prototype now, |
| 780 // so that the code in the dynamicFunction helper can find | 798 // so that the code in the dynamicFunction helper can find |
| 781 // them. Note that this helper is invoked before analyzing the | 799 // them. Note that this helper is invoked before analyzing the |
| 782 // full JS script. | 800 // full JS script. |
| 783 if (!nativeEmitter.handleNoSuchMethod) { | 801 if (!nativeEmitter.handleNoSuchMethod) { |
| 784 emitNoSuchMethodHandlers(builder.addProperty); | 802 emitNoSuchMethodHandlers(builder.addProperty); |
| 785 } | 803 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 798 ? js.equals | 816 ? js.equals |
| 799 : js.strictEquals; | 817 : js.strictEquals; |
| 800 builder.addProperty(name, js.fun(['receiver', 'a'], | 818 builder.addProperty(name, js.fun(['receiver', 'a'], |
| 801 js.block1(js.return_(kind(js.use('receiver'), js.use('a')))))); | 819 js.block1(js.return_(kind(js.use('receiver'), js.use('a')))))); |
| 802 } | 820 } |
| 803 } | 821 } |
| 804 | 822 |
| 805 void emitRuntimeClassesAndTests(CodeBuffer buffer) { | 823 void emitRuntimeClassesAndTests(CodeBuffer buffer) { |
| 806 JavaScriptBackend backend = compiler.backend; | 824 JavaScriptBackend backend = compiler.backend; |
| 807 RuntimeTypeInformation rti = backend.rti; | 825 RuntimeTypeInformation rti = backend.rti; |
| 808 | 826 TypeChecks typeChecks = rti.getRequiredChecks(); |
| 809 TypeChecks typeChecks = rti.computeRequiredChecks(); | |
| 810 | 827 |
| 811 bool needsHolder(ClassElement cls) { | 828 bool needsHolder(ClassElement cls) { |
| 812 return !neededClasses.contains(cls) || cls.isNative() || | 829 return !neededClasses.contains(cls) || cls.isNative() || |
| 813 rti.isJsNative(cls); | 830 rti.isJsNative(cls); |
| 814 } | 831 } |
| 815 | 832 |
| 833 /** |
| 834 * Generates a holder object if it is needed. A holder is a JavaScript |
| 835 * object literal with a field [builtin$cls] that contains the name of the |
| 836 * class as a string (just like object constructors do). The is-checks for |
| 837 * the class are are added to the holder object later. |
| 838 */ |
| 816 void maybeGenerateHolder(ClassElement cls) { | 839 void maybeGenerateHolder(ClassElement cls) { |
| 817 if (!needsHolder(cls)) return; | 840 if (!needsHolder(cls)) return; |
| 818 | |
| 819 String holder = namer.isolateAccess(cls); | 841 String holder = namer.isolateAccess(cls); |
| 820 String name = namer.getName(cls); | 842 String name = namer.getName(cls); |
| 821 buffer.add("$holder$_=$_{builtin\$cls:$_'$name'"); | 843 buffer.add("$holder$_=$_{builtin\$cls:$_'$name'"); |
| 822 for (ClassElement check in typeChecks[cls]) { | |
| 823 buffer.add(',$_${namer.operatorIs(check)}:${_}true'); | |
| 824 }; | |
| 825 buffer.add('}$N'); | 844 buffer.add('}$N'); |
| 826 } | 845 } |
| 827 | 846 |
| 828 // Create representation objects for classes that we do not have a class | 847 // Create representation objects for classes that we do not have a class |
| 829 // definition for (because they are uninstantiated or native). | 848 // definition for (because they are uninstantiated or native). |
| 830 for (ClassElement cls in rti.allArguments) { | 849 for (ClassElement cls in rti.allArguments) { |
| 831 maybeGenerateHolder(cls); | 850 maybeGenerateHolder(cls); |
| 832 } | 851 } |
| 833 | 852 |
| 834 // Add checks to the constructors of instantiated classes. | 853 // Add checks to the constructors of instantiated classes or to the created |
| 854 // holder object. |
| 835 for (ClassElement cls in typeChecks) { | 855 for (ClassElement cls in typeChecks) { |
| 836 if (needsHolder(cls)) { | |
| 837 // We already emitted the is-checks in the object definition for this | |
| 838 // class. | |
| 839 continue; | |
| 840 } | |
| 841 String holder = namer.isolateAccess(cls); | 856 String holder = namer.isolateAccess(cls); |
| 842 for (ClassElement check in typeChecks[cls]) { | 857 for (ClassElement check in typeChecks[cls]) { |
| 843 buffer.add('$holder.${namer.operatorIs(check)}$_=${_}true$N'); | 858 buffer.add('$holder.${namer.operatorIs(check)}$_=${_}true$N'); |
| 859 String body = rti.getSupertypeSubstitution(cls, check); |
| 860 if (body != null) { |
| 861 buffer.add('$holder.${namer.substitutionName(check)}$_=${_}$body$N'); |
| 862 } |
| 844 }; | 863 }; |
| 845 } | 864 } |
| 846 } | 865 } |
| 847 | 866 |
| 848 void visitNativeMixins(ClassElement classElement, | 867 void visitNativeMixins(ClassElement classElement, |
| 849 void visit(MixinApplicationElement mixinApplication)) { | 868 void visit(MixinApplicationElement mixinApplication)) { |
| 850 if (!classElement.isNative()) return; | 869 if (!classElement.isNative()) return; |
| 851 // Use recursion to make sure to visit the superclasses before the | 870 // Use recursion to make sure to visit the superclasses before the |
| 852 // subclasses. Once we start keeping track of the emitted fields | 871 // subclasses. Once we start keeping track of the emitted fields |
| 853 // and members, we're going to want to visit these in the other | 872 // and members, we're going to want to visit these in the other |
| (...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1184 Iterable<Element> getTypedefChecksOn(DartType type) { | 1203 Iterable<Element> getTypedefChecksOn(DartType type) { |
| 1185 return checkedTypedefs.where((TypedefElement typedef) { | 1204 return checkedTypedefs.where((TypedefElement typedef) { |
| 1186 FunctionType typedefType = | 1205 FunctionType typedefType = |
| 1187 typedef.computeType(compiler).unalias(compiler); | 1206 typedef.computeType(compiler).unalias(compiler); |
| 1188 return compiler.types.isSubtype(type, typedefType); | 1207 return compiler.types.isSubtype(type, typedefType); |
| 1189 }); | 1208 }); |
| 1190 } | 1209 } |
| 1191 | 1210 |
| 1192 /** | 1211 /** |
| 1193 * Generate "is tests" for [cls]: itself, and the "is tests" for the | 1212 * Generate "is tests" for [cls]: itself, and the "is tests" for the |
| 1194 * classes it implements. We don't need to add the "is tests" of the | 1213 * classes it implements and type argument substitution functions for these |
| 1195 * super class because they will be inherited at runtime. | 1214 * tests. We don't need to add the "is tests" of the super class because |
| 1215 * they will be inherited at runtime, but we may need to generate the |
| 1216 * substitutions, because they may have changed. |
| 1196 */ | 1217 */ |
| 1197 void generateIsTestsOn(ClassElement cls, | 1218 void generateIsTestsOn(ClassElement cls, |
| 1198 void emitIsTest(Element element)) { | 1219 void emitIsTest(Element element), |
| 1220 void emitSubstitution(Element element, {emitNull})) { |
| 1199 if (checkedClasses.contains(cls)) { | 1221 if (checkedClasses.contains(cls)) { |
| 1200 emitIsTest(cls); | 1222 emitIsTest(cls); |
| 1223 emitSubstitution(cls); |
| 1224 } |
| 1225 |
| 1226 JavaScriptBackend jsBackend = compiler.backend; |
| 1227 RuntimeTypeInformation rti = jsBackend.rti; |
| 1228 ClassElement superclass = cls.superclass; |
| 1229 |
| 1230 bool haveSameTypeVariables(ClassElement a, ClassElement b) { |
| 1231 if (a.isClosure()) return true; |
| 1232 return a.typeVariables == b.typeVariables; |
| 1233 } |
| 1234 |
| 1235 if (superclass != null && superclass != compiler.objectClass && |
| 1236 !haveSameTypeVariables(cls, superclass)) { |
| 1237 // We cannot inherit the generated substitutions, because the type |
| 1238 // variable layout for this class is different. Instead we generate |
| 1239 // substitutions for all checks and make emitSubstitution a NOP for the |
| 1240 // rest of this function. |
| 1241 for (ClassElement check in checkedClasses) { |
| 1242 for (DartType supertype in cls.allSupertypes) { |
| 1243 if (supertype.element == check) { |
| 1244 // Generate substitution. If no substitution is necessary, emit |
| 1245 // [:null:] to overwrite a (possibly) existing substitution from the |
| 1246 // super classes. |
| 1247 emitSubstitution(check, emitNull: true); |
| 1248 } |
| 1249 } |
| 1250 } |
| 1251 emitSubstitution = (_) => {}; |
| 1201 } | 1252 } |
| 1202 | 1253 |
| 1203 Set<Element> generated = new Set<Element>(); | 1254 Set<Element> generated = new Set<Element>(); |
| 1204 // A class that defines a [:call:] method implicitly implements | 1255 // A class that defines a [:call:] method implicitly implements |
| 1205 // [Function] and needs checks for all typedefs that are used in is-checks. | 1256 // [Function] and needs checks for all typedefs that are used in is-checks. |
| 1206 if (checkedClasses.contains(compiler.functionClass) || | 1257 if (checkedClasses.contains(compiler.functionClass) || |
| 1207 !checkedTypedefs.isEmpty) { | 1258 !checkedTypedefs.isEmpty) { |
| 1208 FunctionElement call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); | 1259 FunctionElement call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); |
| 1209 if (call == null) { | 1260 if (call == null) { |
| 1210 // If [cls] is a closure, it has a synthetic call operator method. | 1261 // If [cls] is a closure, it has a synthetic call operator method. |
| 1211 call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME); | 1262 call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME); |
| 1212 } | 1263 } |
| 1213 if (call != null) { | 1264 if (call != null) { |
| 1214 generateInterfacesIsTests(compiler.functionClass, | 1265 generateInterfacesIsTests(compiler.functionClass, |
| 1215 emitIsTest, | 1266 emitIsTest, |
| 1267 emitSubstitution, |
| 1216 generated); | 1268 generated); |
| 1217 getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest); | 1269 getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest); |
| 1218 } | 1270 } |
| 1219 } | 1271 } |
| 1220 | 1272 |
| 1221 for (DartType interfaceType in cls.interfaces) { | 1273 for (DartType interfaceType in cls.interfaces) { |
| 1222 generateInterfacesIsTests(interfaceType.element, emitIsTest, generated); | 1274 generateInterfacesIsTests(interfaceType.element, emitIsTest, |
| 1275 emitSubstitution, generated); |
| 1223 } | 1276 } |
| 1224 | 1277 |
| 1225 // For native classes, we also have to run through their mixin | 1278 // For native classes, we also have to run through their mixin |
| 1226 // applications and make sure we deal with 'is' tests correctly | 1279 // applications and make sure we deal with 'is' tests correctly |
| 1227 // for those. | 1280 // for those. |
| 1228 visitNativeMixins(cls, (MixinApplicationElement mixin) { | 1281 visitNativeMixins(cls, (MixinApplicationElement mixin) { |
| 1229 for (DartType interfaceType in mixin.interfaces) { | 1282 for (DartType interfaceType in mixin.interfaces) { |
| 1230 ClassElement interfaceElement = interfaceType.element; | 1283 ClassElement interfaceElement = interfaceType.element; |
| 1231 generateInterfacesIsTests(interfaceType.element, emitIsTest, generated); | 1284 generateInterfacesIsTests(interfaceType.element, emitIsTest, |
| 1285 emitSubstitution, generated); |
| 1232 } | 1286 } |
| 1233 }); | 1287 }); |
| 1234 } | 1288 } |
| 1235 | 1289 |
| 1236 /** | 1290 /** |
| 1237 * Generate "is tests" where [cls] is being implemented. | 1291 * Generate "is tests" where [cls] is being implemented. |
| 1238 */ | 1292 */ |
| 1239 void generateInterfacesIsTests(ClassElement cls, | 1293 void generateInterfacesIsTests(ClassElement cls, |
| 1240 void emitIsTest(ClassElement element), | 1294 void emitIsTest(ClassElement element), |
| 1295 void emitSubstitution(ClassElement element), |
| 1241 Set<Element> alreadyGenerated) { | 1296 Set<Element> alreadyGenerated) { |
| 1242 void tryEmitTest(ClassElement cls) { | 1297 void tryEmitTest(ClassElement check) { |
| 1243 if (!alreadyGenerated.contains(cls) && checkedClasses.contains(cls)) { | 1298 if (!alreadyGenerated.contains(check) && checkedClasses.contains(check)) { |
| 1244 alreadyGenerated.add(cls); | 1299 alreadyGenerated.add(check); |
| 1245 emitIsTest(cls); | 1300 emitIsTest(check); |
| 1301 emitSubstitution(check); |
| 1246 } | 1302 } |
| 1247 }; | 1303 }; |
| 1248 | 1304 |
| 1249 tryEmitTest(cls); | 1305 tryEmitTest(cls); |
| 1250 | 1306 |
| 1251 for (DartType interfaceType in cls.interfaces) { | 1307 for (DartType interfaceType in cls.interfaces) { |
| 1252 Element element = interfaceType.element; | 1308 Element element = interfaceType.element; |
| 1253 tryEmitTest(element); | 1309 tryEmitTest(element); |
| 1254 generateInterfacesIsTests(element, emitIsTest, alreadyGenerated); | 1310 generateInterfacesIsTests(element, emitIsTest, emitSubstitution, |
| 1311 alreadyGenerated); |
| 1255 } | 1312 } |
| 1256 | 1313 |
| 1257 // We need to also emit "is checks" for the superclass and its supertypes. | 1314 // We need to also emit "is checks" for the superclass and its supertypes. |
| 1258 ClassElement superclass = cls.superclass; | 1315 ClassElement superclass = cls.superclass; |
| 1259 if (superclass != null) { | 1316 if (superclass != null) { |
| 1260 tryEmitTest(superclass); | 1317 tryEmitTest(superclass); |
| 1261 generateInterfacesIsTests(superclass, emitIsTest, alreadyGenerated); | 1318 generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution, |
| 1319 alreadyGenerated); |
| 1262 } | 1320 } |
| 1263 } | 1321 } |
| 1264 | 1322 |
| 1265 /** | 1323 /** |
| 1266 * Return a function that returns true if its argument is a class | 1324 * Return a function that returns true if its argument is a class |
| 1267 * that needs to be emitted. | 1325 * that needs to be emitted. |
| 1268 */ | 1326 */ |
| 1269 Function computeClassFilter() { | 1327 Function computeClassFilter() { |
| 1270 Set<ClassElement> unneededClasses = new Set<ClassElement>(); | 1328 Set<ClassElement> unneededClasses = new Set<ClassElement>(); |
| 1271 // The [Bool] class is not marked as abstract, but has a factory | 1329 // The [Bool] class is not marked as abstract, but has a factory |
| (...skipping 1016 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2288 """; | 2346 """; |
| 2289 const String HOOKS_API_USAGE = """ | 2347 const String HOOKS_API_USAGE = """ |
| 2290 // The code supports the following hooks: | 2348 // The code supports the following hooks: |
| 2291 // dartPrint(message) - if this function is defined it is called | 2349 // dartPrint(message) - if this function is defined it is called |
| 2292 // instead of the Dart [print] method. | 2350 // instead of the Dart [print] method. |
| 2293 // dartMainRunner(main) - if this function is defined, the Dart [main] | 2351 // dartMainRunner(main) - if this function is defined, the Dart [main] |
| 2294 // method will not be invoked directly. | 2352 // method will not be invoked directly. |
| 2295 // Instead, a closure that will invoke [main] is | 2353 // Instead, a closure that will invoke [main] is |
| 2296 // passed to [dartMainRunner]. | 2354 // passed to [dartMainRunner]. |
| 2297 """; | 2355 """; |
| OLD | NEW |