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 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
630 // hit the method directly. | 630 // hit the method directly. |
631 return; | 631 return; |
632 } | 632 } |
633 ConstantHandler handler = compiler.constantHandler; | 633 ConstantHandler handler = compiler.constantHandler; |
634 List<SourceString> names = selector.getOrderedNamedArguments(); | 634 List<SourceString> names = selector.getOrderedNamedArguments(); |
635 | 635 |
636 String invocationName = namer.invocationName(selector); | 636 String invocationName = namer.invocationName(selector); |
637 if (alreadyGenerated.contains(invocationName)) return; | 637 if (alreadyGenerated.contains(invocationName)) return; |
638 alreadyGenerated.add(invocationName); | 638 alreadyGenerated.add(invocationName); |
639 | 639 |
640 bool isInterceptorClass = | 640 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
641 backend.isInterceptorClass(member.getEnclosingClass()); | |
642 | 641 |
643 // If the method is in an interceptor class, we need to also pass | 642 // If the method is intercepted, we need to also pass |
644 // the actual receiver. | 643 // the actual receiver. |
645 int extraArgumentCount = isInterceptorClass ? 1 : 0; | 644 int extraArgumentCount = isInterceptedMethod ? 1 : 0; |
646 // Use '$receiver' to avoid clashes with other parameter names. Using | 645 // Use '$receiver' to avoid clashes with other parameter names. Using |
647 // '$receiver' works because [:namer.safeName:] used for getting parameter | 646 // '$receiver' works because [:namer.safeName:] used for getting parameter |
648 // names never returns a name beginning with a single '$'. | 647 // names never returns a name beginning with a single '$'. |
649 String receiverArgumentName = r'$receiver'; | 648 String receiverArgumentName = r'$receiver'; |
650 | 649 |
651 // The parameters that this stub takes. | 650 // The parameters that this stub takes. |
652 List<jsAst.Parameter> parametersBuffer = | 651 List<jsAst.Parameter> parametersBuffer = |
653 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | 652 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); |
654 // The arguments that will be passed to the real method. | 653 // The arguments that will be passed to the real method. |
655 List<jsAst.Expression> argumentsBuffer = | 654 List<jsAst.Expression> argumentsBuffer = |
656 new List<jsAst.Expression>( | 655 new List<jsAst.Expression>( |
657 parameters.parameterCount + extraArgumentCount); | 656 parameters.parameterCount + extraArgumentCount); |
658 | 657 |
659 int count = 0; | 658 int count = 0; |
660 if (isInterceptorClass) { | 659 if (isInterceptedMethod) { |
661 count++; | 660 count++; |
662 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | 661 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
663 argumentsBuffer[0] = js[receiverArgumentName]; | 662 argumentsBuffer[0] = js[receiverArgumentName]; |
664 } | 663 } |
665 | 664 |
666 int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1; | 665 int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1; |
667 TreeElements elements = | 666 TreeElements elements = |
668 compiler.enqueuer.resolution.getCachedElements(member); | 667 compiler.enqueuer.resolution.getCachedElements(member); |
669 | 668 |
670 parameters.orderedForEachParameter((Element element) { | 669 parameters.orderedForEachParameter((Element element) { |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
890 } | 889 } |
891 | 890 |
892 /** | 891 /** |
893 * Documentation wanted -- johnniwinther | 892 * Documentation wanted -- johnniwinther |
894 * | 893 * |
895 * Invariant: [classElement] must be a declaration element. | 894 * Invariant: [classElement] must be a declaration element. |
896 */ | 895 */ |
897 void emitInstanceMembers(ClassElement classElement, | 896 void emitInstanceMembers(ClassElement classElement, |
898 ClassBuilder builder) { | 897 ClassBuilder builder) { |
899 assert(invariant(classElement, classElement.isDeclaration)); | 898 assert(invariant(classElement, classElement.isDeclaration)); |
900 if (classElement == backend.objectInterceptorClass) { | |
901 emitInterceptorMethods(builder); | |
902 // The ObjectInterceptor does not have any instance methods. | |
903 return; | |
904 } | |
905 | 899 |
906 void visitMember(ClassElement enclosing, Element member) { | 900 void visitMember(ClassElement enclosing, Element member) { |
907 assert(invariant(classElement, member.isDeclaration)); | 901 assert(invariant(classElement, member.isDeclaration)); |
908 if (member.isInstanceMember()) { | 902 if (member.isInstanceMember()) { |
909 addInstanceMember(member, builder); | 903 addInstanceMember(member, builder); |
910 } | 904 } |
911 } | 905 } |
912 | 906 |
913 // TODO(kasperl): We should make sure to only emit one version of | 907 // TODO(kasperl): We should make sure to only emit one version of |
914 // overridden methods. Right now, we rely on the ordering so the | 908 // overridden methods. Right now, we rely on the ordering so the |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
969 && compiler.enabledNoSuchMethod) { | 963 && compiler.enabledNoSuchMethod) { |
970 // Emit the noSuchMethod handlers on the Object prototype now, | 964 // Emit the noSuchMethod handlers on the Object prototype now, |
971 // so that the code in the dynamicFunction helper can find | 965 // so that the code in the dynamicFunction helper can find |
972 // them. Note that this helper is invoked before analyzing the | 966 // them. Note that this helper is invoked before analyzing the |
973 // full JS script. | 967 // full JS script. |
974 if (!nativeEmitter.handleNoSuchMethod) { | 968 if (!nativeEmitter.handleNoSuchMethod) { |
975 emitNoSuchMethodHandlers(builder.addProperty); | 969 emitNoSuchMethodHandlers(builder.addProperty); |
976 } | 970 } |
977 } | 971 } |
978 | 972 |
979 if (backend.isInterceptorClass(classElement)) { | 973 if (backend.isInterceptorClass(classElement) |
980 // The operator== method in [:Object:] does not take the same | 974 && classElement != compiler.objectClass) { |
981 // number of arguments as an intercepted method, therefore we | 975 // We optimize the operator== on interceptor classes to |
982 // explicitely add one to all interceptor classes. Note that we | 976 // just do a JavaScript double or triple equals. |
983 // would not have do do that if all intercepted methods had | |
984 // a calling convention where the receiver is the first | |
985 // parameter. | |
986 String name = backend.namer.publicInstanceMethodNameByArity( | 977 String name = backend.namer.publicInstanceMethodNameByArity( |
987 const SourceString('=='), 1); | 978 const SourceString('=='), 1); |
988 Function kind = (classElement == backend.jsNullClass) | 979 Function kind = (classElement == backend.jsNullClass) |
989 ? js.equals | 980 ? js.equals |
990 : js.strictEquals; | 981 : js.strictEquals; |
991 builder.addProperty(name, js.fun(['receiver', 'a'], | 982 builder.addProperty(name, js.fun(['receiver', 'a'], |
992 js.block(js.return_(kind(js['receiver'], js['a']))))); | 983 js.block(js.return_(kind(js['receiver'], js['a']))))); |
993 } | 984 } |
994 } | 985 } |
995 | 986 |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1100 if ((isInstantiated && !enclosingClass.isNative()) | 1091 if ((isInstantiated && !enclosingClass.isNative()) |
1101 || needsGetter | 1092 || needsGetter |
1102 || needsSetter) { | 1093 || needsSetter) { |
1103 String accessorName = isShadowed | 1094 String accessorName = isShadowed |
1104 ? namer.shadowedFieldName(member) | 1095 ? namer.shadowedFieldName(member) |
1105 : namer.getName(member); | 1096 : namer.getName(member); |
1106 String fieldName = member.hasFixedBackendName() | 1097 String fieldName = member.hasFixedBackendName() |
1107 ? member.fixedBackendName() | 1098 ? member.fixedBackendName() |
1108 : (isMixinNativeField ? member.name.slowToString() : accessorName); | 1099 : (isMixinNativeField ? member.name.slowToString() : accessorName); |
1109 bool needsCheckedSetter = false; | 1100 bool needsCheckedSetter = false; |
1110 if (needsSetter && compiler.enableTypeAssertions | 1101 if (needsSetter) { |
1111 && canGenerateCheckedSetter(member)) { | 1102 if (compiler.enableTypeAssertions |
1112 needsCheckedSetter = true; | 1103 && canGenerateCheckedSetter(member)) { |
1113 needsSetter = false; | 1104 needsCheckedSetter = true; |
| 1105 needsSetter = false; |
| 1106 } else if (backend.isInterceptedMethod(member)) { |
| 1107 // The [addField] will take care of generating the setter. |
| 1108 needsSetter = false; |
| 1109 } |
1114 } | 1110 } |
1115 // Getters and setters with suffixes will be generated dynamically. | 1111 // Getters and setters with suffixes will be generated dynamically. |
1116 addField(member, | 1112 addField(member, |
1117 fieldName, | 1113 fieldName, |
1118 accessorName, | 1114 accessorName, |
1119 needsGetter, | 1115 needsGetter, |
1120 needsSetter, | 1116 needsSetter, |
1121 needsCheckedSetter); | 1117 needsCheckedSetter); |
1122 } | 1118 } |
1123 } | 1119 } |
(...skipping 17 matching lines...) Expand all Loading... |
1141 // allowed on fields that are in [classElement] we don't need to visit | 1137 // allowed on fields that are in [classElement] we don't need to visit |
1142 // superclasses for non-instantiated classes. | 1138 // superclasses for non-instantiated classes. |
1143 classElement.implementation.forEachInstanceField( | 1139 classElement.implementation.forEachInstanceField( |
1144 visitField, | 1140 visitField, |
1145 includeBackendMembers: true, | 1141 includeBackendMembers: true, |
1146 includeSuperMembers: isInstantiated && !classElement.isNative()); | 1142 includeSuperMembers: isInstantiated && !classElement.isNative()); |
1147 } | 1143 } |
1148 | 1144 |
1149 void generateGetter(Element member, String fieldName, String accessorName, | 1145 void generateGetter(Element member, String fieldName, String accessorName, |
1150 ClassBuilder builder) { | 1146 ClassBuilder builder) { |
| 1147 assert(!backend.isInterceptorClass(member)); |
1151 String getterName = namer.getterNameFromAccessorName(accessorName); | 1148 String getterName = namer.getterNameFromAccessorName(accessorName); |
1152 builder.addProperty(getterName, | 1149 builder.addProperty(getterName, |
1153 js.fun([], js.return_(js['this'][fieldName]))); | 1150 js.fun([], js.return_(js['this'][fieldName]))); |
1154 } | 1151 } |
1155 | 1152 |
1156 void generateSetter(Element member, String fieldName, String accessorName, | 1153 void generateSetter(Element member, String fieldName, String accessorName, |
1157 ClassBuilder builder) { | 1154 ClassBuilder builder) { |
| 1155 assert(!backend.isInterceptorClass(member)); |
1158 String setterName = namer.setterNameFromAccessorName(accessorName); | 1156 String setterName = namer.setterNameFromAccessorName(accessorName); |
| 1157 List<String> args = backend.isInterceptedMethod(member) |
| 1158 ? ['receiver', 'v'] |
| 1159 : ['v']; |
1159 builder.addProperty(setterName, | 1160 builder.addProperty(setterName, |
1160 js.fun(['v'], js['this'][fieldName].assign('v'))); | 1161 js.fun(args, js['this'][fieldName].assign('v'))); |
1161 } | 1162 } |
1162 | 1163 |
1163 bool canGenerateCheckedSetter(Element member) { | 1164 bool canGenerateCheckedSetter(Element member) { |
1164 DartType type = member.computeType(compiler); | 1165 DartType type = member.computeType(compiler); |
1165 if (type.element.isTypeVariable() | 1166 if (type.element.isTypeVariable() |
1166 || type.element == compiler.dynamicClass | 1167 || type.element == compiler.dynamicClass |
1167 || type.element == compiler.objectClass) { | 1168 || type.element == compiler.objectClass) { |
1168 // TODO(ngeoffray): Support type checks on type parameters. | 1169 // TODO(ngeoffray): Support type checks on type parameters. |
1169 return false; | 1170 return false; |
1170 } | 1171 } |
(...skipping 10 matching lines...) Expand all Loading... |
1181 if (type.element.isErroneous()) return; | 1182 if (type.element.isErroneous()) return; |
1182 FunctionElement helperElement | 1183 FunctionElement helperElement |
1183 = backend.getCheckedModeHelper(type, typeCast: false); | 1184 = backend.getCheckedModeHelper(type, typeCast: false); |
1184 String helperName = namer.isolateAccess(helperElement); | 1185 String helperName = namer.isolateAccess(helperElement); |
1185 List<jsAst.Expression> arguments = <jsAst.Expression>[js['v']]; | 1186 List<jsAst.Expression> arguments = <jsAst.Expression>[js['v']]; |
1186 if (helperElement.computeSignature(compiler).parameterCount != 1) { | 1187 if (helperElement.computeSignature(compiler).parameterCount != 1) { |
1187 arguments.add(js.string(namer.operatorIs(type.element))); | 1188 arguments.add(js.string(namer.operatorIs(type.element))); |
1188 } | 1189 } |
1189 | 1190 |
1190 String setterName = namer.setterNameFromAccessorName(accessorName); | 1191 String setterName = namer.setterNameFromAccessorName(accessorName); |
| 1192 List<String> args = backend.isInterceptedMethod(member) |
| 1193 ? ['receiver', 'v'] |
| 1194 : ['v']; |
1191 builder.addProperty(setterName, | 1195 builder.addProperty(setterName, |
1192 js.fun(['v'], js['this'][fieldName].assign(js[helperName](arguments)))); | 1196 js.fun(args, js['this'][fieldName].assign(js[helperName](arguments)))); |
1193 } | 1197 } |
1194 | 1198 |
1195 void emitClassConstructor(ClassElement classElement, ClassBuilder builder) { | 1199 void emitClassConstructor(ClassElement classElement, ClassBuilder builder) { |
1196 /* Do nothing. */ | 1200 /* Do nothing. */ |
1197 } | 1201 } |
1198 | 1202 |
1199 void emitSuper(String superName, ClassBuilder builder) { | 1203 void emitSuper(String superName, ClassBuilder builder) { |
1200 /* Do nothing. */ | 1204 /* Do nothing. */ |
1201 } | 1205 } |
1202 | 1206 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1274 assert(!needsSetter); | 1278 assert(!needsSetter); |
1275 generateCheckedSetter(member, name, accessorName, builder); | 1279 generateCheckedSetter(member, name, accessorName, builder); |
1276 } | 1280 } |
1277 if (!getterAndSetterCanBeImplementedByFieldSpec) { | 1281 if (!getterAndSetterCanBeImplementedByFieldSpec) { |
1278 if (needsGetter) { | 1282 if (needsGetter) { |
1279 generateGetter(member, name, accessorName, builder); | 1283 generateGetter(member, name, accessorName, builder); |
1280 } | 1284 } |
1281 if (needsSetter) { | 1285 if (needsSetter) { |
1282 generateSetter(member, name, accessorName, builder); | 1286 generateSetter(member, name, accessorName, builder); |
1283 } | 1287 } |
| 1288 } else if (backend.isInterceptedMethod(member) |
| 1289 && instanceFieldNeedsSetter(member)) { |
| 1290 generateSetter(member, name, accessorName, builder); |
1284 } | 1291 } |
1285 }); | 1292 }); |
1286 }); | 1293 }); |
1287 } | 1294 } |
1288 | 1295 |
1289 /** | 1296 /** |
1290 * Documentation wanted -- johnniwinther | 1297 * Documentation wanted -- johnniwinther |
1291 * | 1298 * |
1292 * Invariant: [classElement] must be a declaration element. | 1299 * Invariant: [classElement] must be a declaration element. |
1293 */ | 1300 */ |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1336 return arity; | 1343 return arity; |
1337 } | 1344 } |
1338 | 1345 |
1339 int _compareSelectorNames(Selector selector1, Selector selector2) { | 1346 int _compareSelectorNames(Selector selector1, Selector selector2) { |
1340 String name1 = selector1.name.toString(); | 1347 String name1 = selector1.name.toString(); |
1341 String name2 = selector2.name.toString(); | 1348 String name2 = selector2.name.toString(); |
1342 if (name1 != name2) return Comparable.compare(name1, name2); | 1349 if (name1 != name2) return Comparable.compare(name1, name2); |
1343 return _selectorRank(selector1) - _selectorRank(selector2); | 1350 return _selectorRank(selector1) - _selectorRank(selector2); |
1344 } | 1351 } |
1345 | 1352 |
1346 void emitInterceptorMethods(ClassBuilder builder) { | |
1347 // Emit forwarders for the ObjectInterceptor class. We need to | |
1348 // emit all possible sends on intercepted methods. Because of | |
1349 // typed selectors we have to avoid generating the same forwarder | |
1350 // multiple times. | |
1351 Set<String> alreadyGenerated = new Set<String>(); | |
1352 for (Selector selector in | |
1353 backend.usedInterceptors.toList()..sort(_compareSelectorNames)) { | |
1354 String name = backend.namer.invocationName(selector); | |
1355 if (alreadyGenerated.contains(name)) continue; | |
1356 alreadyGenerated.add(name); | |
1357 | |
1358 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | |
1359 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | |
1360 parameters.add(new jsAst.Parameter('receiver')); | |
1361 | |
1362 if (selector.isSetter()) { | |
1363 parameters.add(new jsAst.Parameter('value')); | |
1364 arguments.add(js['value']); | |
1365 } else { | |
1366 for (int i = 0; i < selector.argumentCount; i++) { | |
1367 String argName = 'a$i'; | |
1368 parameters.add(new jsAst.Parameter(argName)); | |
1369 arguments.add(js[argName]); | |
1370 } | |
1371 } | |
1372 jsAst.Fun function = | |
1373 js.fun(parameters, js.return_(js['receiver'][name](arguments))); | |
1374 builder.addProperty(name, function); | |
1375 } | |
1376 } | |
1377 | |
1378 Iterable<Element> getTypedefChecksOn(DartType type) { | 1353 Iterable<Element> getTypedefChecksOn(DartType type) { |
1379 bool isSubtype(TypedefElement typedef) { | 1354 bool isSubtype(TypedefElement typedef) { |
1380 FunctionType typedefType = | 1355 FunctionType typedefType = |
1381 typedef.computeType(compiler).unalias(compiler); | 1356 typedef.computeType(compiler).unalias(compiler); |
1382 return compiler.types.isSubtype(type, typedefType); | 1357 return compiler.types.isSubtype(type, typedefType); |
1383 } | 1358 } |
1384 return checkedTypedefs.where(isSubtype).toList() | 1359 return checkedTypedefs.where(isSubtype).toList() |
1385 ..sort(Elements.compareByPosition); | 1360 ..sort(Elements.compareByPosition); |
1386 } | 1361 } |
1387 | 1362 |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1529 for (Constant constant in constants) { | 1504 for (Constant constant in constants) { |
1530 if (constant is ConstructedConstant) { | 1505 if (constant is ConstructedConstant) { |
1531 Element element = constant.computeType(compiler).element; | 1506 Element element = constant.computeType(compiler).element; |
1532 if (backend.isInterceptorClass(element)) { | 1507 if (backend.isInterceptorClass(element)) { |
1533 needed.add(element); | 1508 needed.add(element); |
1534 } | 1509 } |
1535 } | 1510 } |
1536 } | 1511 } |
1537 | 1512 |
1538 // Add unneeded interceptors to the [unneededClasses] set. | 1513 // Add unneeded interceptors to the [unneededClasses] set. |
1539 for (ClassElement interceptor in backend.interceptedClasses.keys) { | 1514 for (ClassElement interceptor in backend.interceptedClasses) { |
1540 if (!needed.contains(interceptor)) { | 1515 if (!needed.contains(interceptor) |
| 1516 && interceptor != compiler.objectClass) { |
1541 unneededClasses.add(interceptor); | 1517 unneededClasses.add(interceptor); |
1542 } | 1518 } |
1543 } | 1519 } |
1544 | 1520 |
1545 return (ClassElement cls) => !unneededClasses.contains(cls); | 1521 return (ClassElement cls) => !unneededClasses.contains(cls); |
1546 } | 1522 } |
1547 | 1523 |
1548 void emitClasses(CodeBuffer buffer) { | 1524 void emitClasses(CodeBuffer buffer) { |
1549 // Compute the required type checks to know which classes need a | 1525 // Compute the required type checks to know which classes need a |
1550 // 'is$' method. | 1526 // 'is$' method. |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1713 // if they share methods with the same signature. Currently we do this only | 1689 // if they share methods with the same signature. Currently we do this only |
1714 // if there are no optional parameters. Closures with optional parameters | 1690 // if there are no optional parameters. Closures with optional parameters |
1715 // are more difficult to canonicalize because they would need to have the | 1691 // are more difficult to canonicalize because they would need to have the |
1716 // same default values. | 1692 // same default values. |
1717 | 1693 |
1718 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; | 1694 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; |
1719 int parameterCount = member.parameterCount(compiler); | 1695 int parameterCount = member.parameterCount(compiler); |
1720 | 1696 |
1721 Map<int, String> cache; | 1697 Map<int, String> cache; |
1722 String extraArg = null; | 1698 String extraArg = null; |
1723 // Methods on interceptor classes take an extra parameter, which is the | 1699 // Intercepted methods take an extra parameter, which is the |
1724 // actual receiver of the call. | 1700 // receiver of the call. |
1725 bool inInterceptor = backend.isInterceptorClass(member.getEnclosingClass()); | 1701 bool inInterceptor = backend.isInterceptedMethod(member); |
1726 if (inInterceptor) { | 1702 if (inInterceptor) { |
1727 cache = interceptorClosureCache; | 1703 cache = interceptorClosureCache; |
1728 extraArg = 'receiver'; | 1704 extraArg = 'receiver'; |
1729 } else { | 1705 } else { |
1730 cache = boundClosureCache; | 1706 cache = boundClosureCache; |
1731 } | 1707 } |
1732 List<String> fieldNames = compiler.enableMinification | 1708 List<String> fieldNames = compiler.enableMinification |
1733 ? inInterceptor ? const ['a', 'b', 'c'] | 1709 ? inInterceptor ? const ['a', 'b', 'c'] |
1734 : const ['a', 'b'] | 1710 : const ['a', 'b'] |
1735 : inInterceptor ? const ['self', 'target', 'receiver'] | 1711 : inInterceptor ? const ['self', 'target', 'receiver'] |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1838 /** | 1814 /** |
1839 * Documentation wanted -- johnniwinther | 1815 * Documentation wanted -- johnniwinther |
1840 * | 1816 * |
1841 * Invariant: [member] must be a declaration element. | 1817 * Invariant: [member] must be a declaration element. |
1842 */ | 1818 */ |
1843 void emitCallStubForGetter(Element member, | 1819 void emitCallStubForGetter(Element member, |
1844 Set<Selector> selectors, | 1820 Set<Selector> selectors, |
1845 DefineStubFunction defineStub) { | 1821 DefineStubFunction defineStub) { |
1846 assert(invariant(member, member.isDeclaration)); | 1822 assert(invariant(member, member.isDeclaration)); |
1847 LibraryElement memberLibrary = member.getLibrary(); | 1823 LibraryElement memberLibrary = member.getLibrary(); |
1848 // If the class is an interceptor class, the stub gets the | 1824 // If the method is intercepted, the stub gets the |
1849 // receiver explicitely and we need to pass it to the getter call. | 1825 // receiver explicitely and we need to pass it to the getter call. |
1850 bool isInterceptorClass = | 1826 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
1851 backend.isInterceptorClass(member.getEnclosingClass()); | |
1852 | 1827 |
1853 const String receiverArgumentName = r'$receiver'; | 1828 const String receiverArgumentName = r'$receiver'; |
1854 | 1829 |
1855 jsAst.Expression buildGetter() { | 1830 jsAst.Expression buildGetter() { |
1856 if (member.isGetter()) { | 1831 if (member.isGetter()) { |
1857 String getterName = namer.getterName(member); | 1832 String getterName = namer.getterName(member); |
1858 return js['this'][getterName]( | 1833 return js['this'][getterName]( |
1859 isInterceptorClass | 1834 isInterceptedMethod |
1860 ? <jsAst.Expression>[js[receiverArgumentName]] | 1835 ? <jsAst.Expression>[js[receiverArgumentName]] |
1861 : <jsAst.Expression>[]); | 1836 : <jsAst.Expression>[]); |
1862 } else { | 1837 } else { |
1863 String fieldName = member.hasFixedBackendName() | 1838 String fieldName = member.hasFixedBackendName() |
1864 ? member.fixedBackendName() | 1839 ? member.fixedBackendName() |
1865 : namer.instanceFieldName(member); | 1840 : namer.instanceFieldName(member); |
1866 return js['this'][fieldName]; | 1841 return js['this'][fieldName]; |
1867 } | 1842 } |
1868 } | 1843 } |
1869 | 1844 |
1870 // Two selectors may match but differ only in type. To avoid generating | 1845 // Two selectors may match but differ only in type. To avoid generating |
1871 // identical stubs for each we track untyped selectors which already have | 1846 // identical stubs for each we track untyped selectors which already have |
1872 // stubs. | 1847 // stubs. |
1873 Set<Selector> generatedSelectors = new Set<Selector>(); | 1848 Set<Selector> generatedSelectors = new Set<Selector>(); |
1874 | 1849 |
1875 for (Selector selector in selectors) { | 1850 for (Selector selector in selectors) { |
1876 if (selector.applies(member, compiler)) { | 1851 if (selector.applies(member, compiler)) { |
1877 selector = selector.asUntyped; | 1852 selector = selector.asUntyped; |
1878 if (generatedSelectors.contains(selector)) continue; | 1853 if (generatedSelectors.contains(selector)) continue; |
1879 generatedSelectors.add(selector); | 1854 generatedSelectors.add(selector); |
1880 | 1855 |
1881 String invocationName = namer.invocationName(selector); | 1856 String invocationName = namer.invocationName(selector); |
1882 Selector callSelector = new Selector.callClosureFrom(selector); | 1857 Selector callSelector = new Selector.callClosureFrom(selector); |
1883 String closureCallName = namer.invocationName(callSelector); | 1858 String closureCallName = namer.invocationName(callSelector); |
1884 | 1859 |
1885 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | 1860 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; |
1886 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 1861 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
1887 if (isInterceptorClass) { | 1862 if (isInterceptedMethod) { |
1888 parameters.add(new jsAst.Parameter(receiverArgumentName)); | 1863 parameters.add(new jsAst.Parameter(receiverArgumentName)); |
1889 } | 1864 } |
1890 | 1865 |
1891 for (int i = 0; i < selector.argumentCount; i++) { | 1866 for (int i = 0; i < selector.argumentCount; i++) { |
1892 String name = 'arg$i'; | 1867 String name = 'arg$i'; |
1893 parameters.add(new jsAst.Parameter(name)); | 1868 parameters.add(new jsAst.Parameter(name)); |
1894 arguments.add(js[name]); | 1869 arguments.add(js[name]); |
1895 } | 1870 } |
1896 | 1871 |
1897 jsAst.Fun function = js.fun( | 1872 jsAst.Fun function = js.fun( |
1898 parameters, | 1873 parameters, |
1899 js.return_(buildGetter()[closureCallName](arguments))); | 1874 js.return_(buildGetter()[closureCallName](arguments))); |
1900 | 1875 |
1901 defineStub(invocationName, function); | 1876 defineStub(invocationName, function); |
1902 } | 1877 } |
1903 } | 1878 } |
1904 } | 1879 } |
1905 | 1880 |
1906 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { | 1881 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { |
1907 ConstantHandler handler = compiler.constantHandler; | 1882 ConstantHandler handler = compiler.constantHandler; |
1908 Iterable<VariableElement> staticNonFinalFields = | 1883 Iterable<VariableElement> staticNonFinalFields = |
1909 handler.getStaticNonFinalFieldsForEmission(); | 1884 handler.getStaticNonFinalFieldsForEmission(); |
1910 for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { | 1885 for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { |
| 1886 // [:interceptedNames:] is handled in [emitInterceptedNames]. |
| 1887 if (element == backend.interceptedNames) continue; |
1911 compiler.withCurrentElement(element, () { | 1888 compiler.withCurrentElement(element, () { |
1912 Constant initialValue = handler.getInitialValueFor(element); | 1889 Constant initialValue = handler.getInitialValueFor(element); |
1913 jsAst.Expression init = | 1890 jsAst.Expression init = |
1914 js[isolateProperties][namer.getName(element)].assign( | 1891 js[isolateProperties][namer.getName(element)].assign( |
1915 constantEmitter.referenceInInitializationContext(initialValue)); | 1892 constantEmitter.referenceInInitializationContext(initialValue)); |
1916 buffer.add(jsAst.prettyPrint(init, compiler)); | 1893 buffer.add(jsAst.prettyPrint(init, compiler)); |
1917 buffer.add('$N'); | 1894 buffer.add('$N'); |
1918 }); | 1895 }); |
1919 } | 1896 } |
1920 } | 1897 } |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2039 | 2016 |
2040 List<jsAst.Expression> argNames = | 2017 List<jsAst.Expression> argNames = |
2041 selector.getOrderedNamedArguments().map((SourceString name) => | 2018 selector.getOrderedNamedArguments().map((SourceString name) => |
2042 js.string(name.slowToString())).toList(); | 2019 js.string(name.slowToString())).toList(); |
2043 | 2020 |
2044 String internalName = namer.invocationMirrorInternalName(selector); | 2021 String internalName = namer.invocationMirrorInternalName(selector); |
2045 | 2022 |
2046 String createInvocationMirror = namer.getName( | 2023 String createInvocationMirror = namer.getName( |
2047 compiler.createInvocationMirrorElement); | 2024 compiler.createInvocationMirrorElement); |
2048 | 2025 |
| 2026 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); |
2049 jsAst.Expression expression = js['this.$noSuchMethodName']( | 2027 jsAst.Expression expression = js['this.$noSuchMethodName']( |
2050 js[namer.CURRENT_ISOLATE][createInvocationMirror]([ | 2028 [js['this'], |
2051 js.string(methodName), | 2029 js[namer.CURRENT_ISOLATE][createInvocationMirror]([ |
2052 js.string(internalName), | 2030 js.string(methodName), |
2053 type, | 2031 js.string(internalName), |
2054 new jsAst.ArrayInitializer.from( | 2032 type, |
2055 parameters.map((param) => js[param.name]).toList()), | 2033 new jsAst.ArrayInitializer.from( |
2056 new jsAst.ArrayInitializer.from(argNames)])); | 2034 parameters.map((param) => js[param.name]).toList()), |
| 2035 new jsAst.ArrayInitializer.from(argNames)])]); |
| 2036 parameters = backend.isInterceptedName(selector.name) |
| 2037 ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters)) |
| 2038 : parameters; |
2057 return js.fun(parameters, js.return_(expression)); | 2039 return js.fun(parameters, js.return_(expression)); |
2058 } | 2040 } |
2059 | 2041 |
2060 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) { | 2042 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) { |
2061 // Cache the object class and type. | 2043 // Cache the object class and type. |
2062 ClassElement objectClass = compiler.objectClass; | 2044 ClassElement objectClass = compiler.objectClass; |
2063 DartType objectType = objectClass.computeType(compiler); | 2045 DartType objectType = objectClass.computeType(compiler); |
2064 | 2046 |
2065 for (Selector selector in selectors) { | 2047 for (Selector selector in selectors) { |
2066 // Introduce a helper function that determines if the given | 2048 // Introduce a helper function that determines if the given |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2187 dartMainRunner(function() { ${mainCall}; }); | 2169 dartMainRunner(function() { ${mainCall}; }); |
2188 } else { | 2170 } else { |
2189 ${mainCall}; | 2171 ${mainCall}; |
2190 } | 2172 } |
2191 } | 2173 } |
2192 """); | 2174 """); |
2193 addComment('END invoke [main].', buffer); | 2175 addComment('END invoke [main].', buffer); |
2194 } | 2176 } |
2195 | 2177 |
2196 void emitGetInterceptorMethod(CodeBuffer buffer, | 2178 void emitGetInterceptorMethod(CodeBuffer buffer, |
2197 String objectName, | |
2198 String key, | 2179 String key, |
2199 Collection<ClassElement> classes) { | 2180 Collection<ClassElement> classes) { |
2200 jsAst.Statement buildReturnInterceptor(ClassElement cls) { | 2181 jsAst.Statement buildReturnInterceptor(ClassElement cls) { |
2201 return js.return_(js[namer.isolateAccess(cls)]['prototype']); | 2182 return js.return_(js[namer.isolateAccess(cls)]['prototype']); |
2202 } | 2183 } |
2203 | 2184 |
2204 jsAst.VariableUse receiver = js['receiver']; | 2185 jsAst.VariableUse receiver = js['receiver']; |
2205 /** | 2186 /** |
2206 * Build a JavaScrit AST node for doing a type check on | 2187 * Build a JavaScrit AST node for doing a type check on |
2207 * [cls]. [cls] must be an interceptor class. | 2188 * [cls]. [cls] must be an interceptor class. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2239 bool hasString = false; | 2220 bool hasString = false; |
2240 for (ClassElement cls in classes) { | 2221 for (ClassElement cls in classes) { |
2241 if (cls == backend.jsArrayClass) hasArray = true; | 2222 if (cls == backend.jsArrayClass) hasArray = true; |
2242 else if (cls == backend.jsBoolClass) hasBool = true; | 2223 else if (cls == backend.jsBoolClass) hasBool = true; |
2243 else if (cls == backend.jsDoubleClass) hasDouble = true; | 2224 else if (cls == backend.jsDoubleClass) hasDouble = true; |
2244 else if (cls == backend.jsFunctionClass) hasFunction = true; | 2225 else if (cls == backend.jsFunctionClass) hasFunction = true; |
2245 else if (cls == backend.jsIntClass) hasInt = true; | 2226 else if (cls == backend.jsIntClass) hasInt = true; |
2246 else if (cls == backend.jsNullClass) hasNull = true; | 2227 else if (cls == backend.jsNullClass) hasNull = true; |
2247 else if (cls == backend.jsNumberClass) hasNumber = true; | 2228 else if (cls == backend.jsNumberClass) hasNumber = true; |
2248 else if (cls == backend.jsStringClass) hasString = true; | 2229 else if (cls == backend.jsStringClass) hasString = true; |
2249 else throw 'Internal error: $cls'; | 2230 else { |
| 2231 assert(cls == compiler.objectClass); |
| 2232 } |
2250 } | 2233 } |
2251 if (hasDouble) { | 2234 if (hasDouble) { |
2252 assert(!hasNumber); | |
2253 hasNumber = true; | 2235 hasNumber = true; |
2254 } | 2236 } |
2255 if (hasInt) hasNumber = true; | 2237 if (hasInt) hasNumber = true; |
2256 | 2238 |
2257 jsAst.Block block = new jsAst.Block.empty(); | 2239 jsAst.Block block = new jsAst.Block.empty(); |
2258 | 2240 |
2259 if (hasNumber) { | 2241 if (hasNumber) { |
2260 jsAst.Statement whenNumber; | 2242 jsAst.Statement whenNumber; |
2261 | 2243 |
2262 /// Note: there are two number classes in play: Dart's [num], | 2244 /// Note: there are two number classes in play: Dart's [num], |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2295 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass)); | 2277 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass)); |
2296 } | 2278 } |
2297 if (hasBool) { | 2279 if (hasBool) { |
2298 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); | 2280 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); |
2299 } | 2281 } |
2300 // TODO(ahe): It might be faster to check for Array before | 2282 // TODO(ahe): It might be faster to check for Array before |
2301 // function and bool. | 2283 // function and bool. |
2302 if (hasArray) { | 2284 if (hasArray) { |
2303 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); | 2285 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); |
2304 } | 2286 } |
2305 block.statements.add(js.return_(js[objectName]['prototype'])); | 2287 block.statements.add(js.return_(receiver)); |
2306 | 2288 |
2307 buffer.add(jsAst.prettyPrint( | 2289 buffer.add(jsAst.prettyPrint( |
2308 js[isolateProperties][key].assign(js.fun(['receiver'], block)), | 2290 js[isolateProperties][key].assign(js.fun(['receiver'], block)), |
2309 compiler)); | 2291 compiler)); |
2310 buffer.add(N); | 2292 buffer.add(N); |
2311 } | 2293 } |
2312 | 2294 |
2313 /** | 2295 /** |
2314 * Emit all versions of the [:getInterceptor:] method. | 2296 * Emit all versions of the [:getInterceptor:] method. |
2315 */ | 2297 */ |
2316 void emitGetInterceptorMethods(CodeBuffer buffer) { | 2298 void emitGetInterceptorMethods(CodeBuffer buffer) { |
2317 // If no class needs to be intercepted, just return. | |
2318 if (backend.objectInterceptorClass == null) return; | |
2319 String objectName = namer.isolateAccess(backend.objectInterceptorClass); | |
2320 var specializedGetInterceptors = backend.specializedGetInterceptors; | 2299 var specializedGetInterceptors = backend.specializedGetInterceptors; |
2321 for (String name in specializedGetInterceptors.keys.toList()..sort()) { | 2300 for (String name in specializedGetInterceptors.keys.toList()..sort()) { |
2322 Collection<ClassElement> classes = specializedGetInterceptors[name]; | 2301 Collection<ClassElement> classes = specializedGetInterceptors[name]; |
2323 emitGetInterceptorMethod(buffer, objectName, name, classes); | 2302 emitGetInterceptorMethod(buffer, name, classes); |
2324 } | 2303 } |
2325 } | 2304 } |
2326 | 2305 |
2327 void computeNeededClasses() { | 2306 void computeNeededClasses() { |
2328 instantiatedClasses = | 2307 instantiatedClasses = |
2329 compiler.codegenWorld.instantiatedClasses.where(computeClassFilter()) | 2308 compiler.codegenWorld.instantiatedClasses.where(computeClassFilter()) |
2330 .toSet(); | 2309 .toSet(); |
2331 neededClasses = new Set<ClassElement>.from(instantiatedClasses); | 2310 neededClasses = new Set<ClassElement>.from(instantiatedClasses); |
2332 for (ClassElement element in instantiatedClasses) { | 2311 for (ClassElement element in instantiatedClasses) { |
2333 for (ClassElement superclass = element.superclass; | 2312 for (ClassElement superclass = element.superclass; |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2494 } | 2473 } |
2495 return null; | 2474 return null; |
2496 } | 2475 } |
2497 | 2476 |
2498 void emitOneShotInterceptors(CodeBuffer buffer) { | 2477 void emitOneShotInterceptors(CodeBuffer buffer) { |
2499 List<String> names = backend.oneShotInterceptors.keys.toList(); | 2478 List<String> names = backend.oneShotInterceptors.keys.toList(); |
2500 names.sort(); | 2479 names.sort(); |
2501 for (String name in names) { | 2480 for (String name in names) { |
2502 Selector selector = backend.oneShotInterceptors[name]; | 2481 Selector selector = backend.oneShotInterceptors[name]; |
2503 Set<ClassElement> classes = | 2482 Set<ClassElement> classes = |
2504 backend.getInterceptedClassesOn(selector); | 2483 backend.getInterceptedClassesOn(selector.name); |
2505 String getInterceptorName = | 2484 String getInterceptorName = |
2506 namer.getInterceptorName(backend.getInterceptorMethod, classes); | 2485 namer.getInterceptorName(backend.getInterceptorMethod, classes); |
2507 | 2486 |
2508 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | 2487 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; |
2509 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 2488 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
2510 parameters.add(new jsAst.Parameter('receiver')); | 2489 parameters.add(new jsAst.Parameter('receiver')); |
2511 arguments.add(js['receiver']); | 2490 arguments.add(js['receiver']); |
2512 | 2491 |
2513 if (selector.isSetter()) { | 2492 if (selector.isSetter()) { |
2514 parameters.add(new jsAst.Parameter('value')); | 2493 parameters.add(new jsAst.Parameter('value')); |
(...skipping 21 matching lines...) Expand all Loading... |
2536 jsAst.Fun function = js.fun(parameters, body); | 2515 jsAst.Fun function = js.fun(parameters, body); |
2537 | 2516 |
2538 jsAst.PropertyAccess property = | 2517 jsAst.PropertyAccess property = |
2539 js[isolateProperties][name]; | 2518 js[isolateProperties][name]; |
2540 | 2519 |
2541 buffer.add(jsAst.prettyPrint(property.assign(function), compiler)); | 2520 buffer.add(jsAst.prettyPrint(property.assign(function), compiler)); |
2542 buffer.add(N); | 2521 buffer.add(N); |
2543 } | 2522 } |
2544 } | 2523 } |
2545 | 2524 |
| 2525 /** |
| 2526 * If [:invokeOn:] has been compiled, emit all the possible selector names |
| 2527 * that are intercepted into the [:interceptedNames:] top-level |
| 2528 * variable. The implementation of [:invokeOn:] will use it to |
| 2529 * determine whether it should call the method with an extra |
| 2530 * parameter. |
| 2531 */ |
| 2532 void emitInterceptedNames(CodeBuffer buffer) { |
| 2533 if (!compiler.enabledInvokeOn) return; |
| 2534 String name = backend.namer.getName(backend.interceptedNames); |
| 2535 jsAst.PropertyAccess property = js[isolateProperties][name]; |
| 2536 |
| 2537 int index = 0; |
| 2538 List<jsAst.ArrayElement> elements = backend.usedInterceptors.map( |
| 2539 (Selector selector) { |
| 2540 jsAst.Literal str = js.string(namer.invocationName(selector)); |
| 2541 return new jsAst.ArrayElement(index++, str); |
| 2542 }).toList(); |
| 2543 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer( |
| 2544 backend.usedInterceptors.length, |
| 2545 elements); |
| 2546 |
| 2547 buffer.add(jsAst.prettyPrint(property.assign(array), compiler)); |
| 2548 buffer.add(N); |
| 2549 } |
| 2550 |
2546 void emitInitFunction(CodeBuffer buffer) { | 2551 void emitInitFunction(CodeBuffer buffer) { |
2547 jsAst.Fun fun = js.fun([], [ | 2552 jsAst.Fun fun = js.fun([], [ |
2548 js['$isolateProperties = {}'], | 2553 js['$isolateProperties = {}'], |
2549 ] | 2554 ] |
2550 ..addAll(buildDefineClassAndFinishClassFunctionsIfNecessary()) | 2555 ..addAll(buildDefineClassAndFinishClassFunctionsIfNecessary()) |
2551 ..addAll(buildLazyInitializerFunctionIfNecessary()) | 2556 ..addAll(buildLazyInitializerFunctionIfNecessary()) |
2552 ..addAll(buildFinishIsolateConstructor()) | 2557 ..addAll(buildFinishIsolateConstructor()) |
2553 ); | 2558 ); |
2554 jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration( | 2559 jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration( |
2555 new jsAst.VariableDeclaration('init'), fun); | 2560 new jsAst.VariableDeclaration('init'), fun); |
(...skipping 23 matching lines...) Expand all Loading... |
2579 emitStaticFunctionGetters(mainBuffer); | 2584 emitStaticFunctionGetters(mainBuffer); |
2580 // We need to finish the classes before we construct compile time | 2585 // We need to finish the classes before we construct compile time |
2581 // constants. | 2586 // constants. |
2582 emitFinishClassesInvocationIfNecessary(mainBuffer); | 2587 emitFinishClassesInvocationIfNecessary(mainBuffer); |
2583 emitRuntimeClassesAndTests(mainBuffer); | 2588 emitRuntimeClassesAndTests(mainBuffer); |
2584 emitCompileTimeConstants(mainBuffer); | 2589 emitCompileTimeConstants(mainBuffer); |
2585 // Static field initializations require the classes and compile-time | 2590 // Static field initializations require the classes and compile-time |
2586 // constants to be set up. | 2591 // constants to be set up. |
2587 emitStaticNonFinalFieldInitializations(mainBuffer); | 2592 emitStaticNonFinalFieldInitializations(mainBuffer); |
2588 emitOneShotInterceptors(mainBuffer); | 2593 emitOneShotInterceptors(mainBuffer); |
| 2594 emitInterceptedNames(mainBuffer); |
2589 emitGetInterceptorMethods(mainBuffer); | 2595 emitGetInterceptorMethods(mainBuffer); |
2590 emitLazilyInitializedStaticFields(mainBuffer); | 2596 emitLazilyInitializedStaticFields(mainBuffer); |
2591 | 2597 |
2592 isolateProperties = isolatePropertiesName; | 2598 isolateProperties = isolatePropertiesName; |
2593 // The following code should not use the short-hand for the | 2599 // The following code should not use the short-hand for the |
2594 // initialStatics. | 2600 // initialStatics. |
2595 mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=${_}null$N'); | 2601 mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=${_}null$N'); |
2596 if (!boundClosureBuffer.isEmpty) { | 2602 if (!boundClosureBuffer.isEmpty) { |
2597 mainBuffer.add(boundClosureBuffer); | 2603 mainBuffer.add(boundClosureBuffer); |
2598 emitFinishClassesInvocationIfNecessary(mainBuffer); | 2604 emitFinishClassesInvocationIfNecessary(mainBuffer); |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2691 """; | 2697 """; |
2692 const String HOOKS_API_USAGE = """ | 2698 const String HOOKS_API_USAGE = """ |
2693 // The code supports the following hooks: | 2699 // The code supports the following hooks: |
2694 // dartPrint(message) - if this function is defined it is called | 2700 // dartPrint(message) - if this function is defined it is called |
2695 // instead of the Dart [print] method. | 2701 // instead of the Dart [print] method. |
2696 // dartMainRunner(main) - if this function is defined, the Dart [main] | 2702 // dartMainRunner(main) - if this function is defined, the Dart [main] |
2697 // method will not be invoked directly. | 2703 // method will not be invoked directly. |
2698 // Instead, a closure that will invoke [main] is | 2704 // Instead, a closure that will invoke [main] is |
2699 // passed to [dartMainRunner]. | 2705 // passed to [dartMainRunner]. |
2700 """; | 2706 """; |
OLD | NEW |