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