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 635 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
646 // hit the method directly. | 646 // hit the method directly. |
647 return; | 647 return; |
648 } | 648 } |
649 ConstantHandler handler = compiler.constantHandler; | 649 ConstantHandler handler = compiler.constantHandler; |
650 List<SourceString> names = selector.getOrderedNamedArguments(); | 650 List<SourceString> names = selector.getOrderedNamedArguments(); |
651 | 651 |
652 String invocationName = namer.invocationName(selector); | 652 String invocationName = namer.invocationName(selector); |
653 if (alreadyGenerated.contains(invocationName)) return; | 653 if (alreadyGenerated.contains(invocationName)) return; |
654 alreadyGenerated.add(invocationName); | 654 alreadyGenerated.add(invocationName); |
655 | 655 |
656 bool isInterceptorClass = | 656 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
657 backend.isInterceptorClass(member.getEnclosingClass()); | |
658 | 657 |
659 // If the method is in an interceptor class, we need to also pass | 658 // If the method is intercepted, we need to also pass |
660 // the actual receiver. | 659 // the actual receiver. |
661 int extraArgumentCount = isInterceptorClass ? 1 : 0; | 660 int extraArgumentCount = isInterceptedMethod ? 1 : 0; |
662 // Use '$receiver' to avoid clashes with other parameter names. Using | 661 // Use '$receiver' to avoid clashes with other parameter names. Using |
663 // '$receiver' works because [:namer.safeName:] used for getting parameter | 662 // '$receiver' works because [:namer.safeName:] used for getting parameter |
664 // names never returns a name beginning with a single '$'. | 663 // names never returns a name beginning with a single '$'. |
665 String receiverArgumentName = r'$receiver'; | 664 String receiverArgumentName = r'$receiver'; |
666 | 665 |
667 // The parameters that this stub takes. | 666 // The parameters that this stub takes. |
668 List<jsAst.Parameter> parametersBuffer = | 667 List<jsAst.Parameter> parametersBuffer = |
669 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | 668 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); |
670 // The arguments that will be passed to the real method. | 669 // The arguments that will be passed to the real method. |
671 List<jsAst.Expression> argumentsBuffer = | 670 List<jsAst.Expression> argumentsBuffer = |
672 new List<jsAst.Expression>( | 671 new List<jsAst.Expression>( |
673 parameters.parameterCount + extraArgumentCount); | 672 parameters.parameterCount + extraArgumentCount); |
674 | 673 |
675 int count = 0; | 674 int count = 0; |
676 if (isInterceptorClass) { | 675 if (isInterceptedMethod) { |
677 count++; | 676 count++; |
678 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | 677 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
679 argumentsBuffer[0] = js[receiverArgumentName]; | 678 argumentsBuffer[0] = js[receiverArgumentName]; |
680 } | 679 } |
681 | 680 |
682 int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1; | 681 int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1; |
683 TreeElements elements = | 682 TreeElements elements = |
684 compiler.enqueuer.resolution.getCachedElements(member); | 683 compiler.enqueuer.resolution.getCachedElements(member); |
685 | 684 |
686 parameters.orderedForEachParameter((Element element) { | 685 parameters.orderedForEachParameter((Element element) { |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
906 } | 905 } |
907 | 906 |
908 /** | 907 /** |
909 * Documentation wanted -- johnniwinther | 908 * Documentation wanted -- johnniwinther |
910 * | 909 * |
911 * Invariant: [classElement] must be a declaration element. | 910 * Invariant: [classElement] must be a declaration element. |
912 */ | 911 */ |
913 void emitInstanceMembers(ClassElement classElement, | 912 void emitInstanceMembers(ClassElement classElement, |
914 ClassBuilder builder) { | 913 ClassBuilder builder) { |
915 assert(invariant(classElement, classElement.isDeclaration)); | 914 assert(invariant(classElement, classElement.isDeclaration)); |
916 if (classElement == backend.objectInterceptorClass) { | |
917 emitInterceptorMethods(builder); | |
918 // The ObjectInterceptor does not have any instance methods. | |
919 return; | |
920 } | |
921 | 915 |
922 void visitMember(ClassElement enclosing, Element member) { | 916 void visitMember(ClassElement enclosing, Element member) { |
923 assert(invariant(classElement, member.isDeclaration)); | 917 assert(invariant(classElement, member.isDeclaration)); |
924 if (member.isInstanceMember()) { | 918 if (member.isInstanceMember()) { |
925 addInstanceMember(member, builder); | 919 addInstanceMember(member, builder); |
926 } | 920 } |
927 } | 921 } |
928 | 922 |
929 // TODO(kasperl): We should make sure to only emit one version of | 923 // TODO(kasperl): We should make sure to only emit one version of |
930 // overridden methods. Right now, we rely on the ordering so the | 924 // overridden methods. Right now, we rely on the ordering so the |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
988 && compiler.enabledNoSuchMethod) { | 982 && compiler.enabledNoSuchMethod) { |
989 // Emit the noSuchMethod handlers on the Object prototype now, | 983 // Emit the noSuchMethod handlers on the Object prototype now, |
990 // so that the code in the dynamicFunction helper can find | 984 // so that the code in the dynamicFunction helper can find |
991 // them. Note that this helper is invoked before analyzing the | 985 // them. Note that this helper is invoked before analyzing the |
992 // full JS script. | 986 // full JS script. |
993 if (!nativeEmitter.handleNoSuchMethod) { | 987 if (!nativeEmitter.handleNoSuchMethod) { |
994 emitNoSuchMethodHandlers(builder.addProperty); | 988 emitNoSuchMethodHandlers(builder.addProperty); |
995 } | 989 } |
996 } | 990 } |
997 | 991 |
998 if (backend.isInterceptorClass(classElement)) { | 992 if (backend.isInterceptorClass(classElement) |
999 // The operator== method in [:Object:] does not take the same | 993 && classElement != compiler.objectClass) { |
1000 // number of arguments as an intercepted method, therefore we | 994 // We optimize the operator== on interceptor classes to |
1001 // explicitely add one to all interceptor classes. Note that we | 995 // just do a JavaScript double or triple equals. |
1002 // would not have do do that if all intercepted methods had | |
1003 // a calling convention where the receiver is the first | |
1004 // parameter. | |
1005 String name = backend.namer.publicInstanceMethodNameByArity( | 996 String name = backend.namer.publicInstanceMethodNameByArity( |
1006 const SourceString('=='), 1); | 997 const SourceString('=='), 1); |
1007 Function kind = (classElement == backend.jsNullClass) | 998 Function kind = (classElement == backend.jsNullClass) |
1008 ? js.equals | 999 ? js.equals |
1009 : js.strictEquals; | 1000 : js.strictEquals; |
1010 builder.addProperty(name, js.fun(['receiver', 'a'], | 1001 builder.addProperty(name, js.fun(['receiver', 'a'], |
1011 js.block(js.return_(kind(js['receiver'], js['a']))))); | 1002 js.block(js.return_(kind(js['receiver'], js['a']))))); |
1012 } | 1003 } |
1013 } | 1004 } |
1014 | 1005 |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1122 if ((isInstantiated && !enclosingClass.isNative()) | 1113 if ((isInstantiated && !enclosingClass.isNative()) |
1123 || needsGetter | 1114 || needsGetter |
1124 || needsSetter) { | 1115 || needsSetter) { |
1125 String accessorName = isShadowed | 1116 String accessorName = isShadowed |
1126 ? namer.shadowedFieldName(member) | 1117 ? namer.shadowedFieldName(member) |
1127 : namer.getName(member); | 1118 : namer.getName(member); |
1128 String fieldName = member.hasFixedBackendName() | 1119 String fieldName = member.hasFixedBackendName() |
1129 ? member.fixedBackendName() | 1120 ? member.fixedBackendName() |
1130 : (isMixinNativeField ? member.name.slowToString() : accessorName); | 1121 : (isMixinNativeField ? member.name.slowToString() : accessorName); |
1131 bool needsCheckedSetter = false; | 1122 bool needsCheckedSetter = false; |
1132 if (needsSetter && compiler.enableTypeAssertions | 1123 if (needsSetter) { |
1133 && canGenerateCheckedSetter(member)) { | 1124 if (compiler.enableTypeAssertions |
1134 needsCheckedSetter = true; | 1125 && canGenerateCheckedSetter(member)) { |
1135 needsSetter = false; | 1126 needsCheckedSetter = true; |
| 1127 needsSetter = false; |
| 1128 } else if (backend.isInterceptedMethod(member)) { |
| 1129 // The [addField] will take care of generating the setter. |
| 1130 needsSetter = false; |
| 1131 } |
1136 } | 1132 } |
1137 // Getters and setters with suffixes will be generated dynamically. | 1133 // Getters and setters with suffixes will be generated dynamically. |
1138 addField(member, | 1134 addField(member, |
1139 fieldName, | 1135 fieldName, |
1140 accessorName, | 1136 accessorName, |
1141 needsGetter, | 1137 needsGetter, |
1142 needsSetter, | 1138 needsSetter, |
1143 needsCheckedSetter); | 1139 needsCheckedSetter); |
1144 } | 1140 } |
1145 } | 1141 } |
(...skipping 17 matching lines...) Expand all Loading... |
1163 // allowed on fields that are in [classElement] we don't need to visit | 1159 // allowed on fields that are in [classElement] we don't need to visit |
1164 // superclasses for non-instantiated classes. | 1160 // superclasses for non-instantiated classes. |
1165 classElement.implementation.forEachInstanceField( | 1161 classElement.implementation.forEachInstanceField( |
1166 visitField, | 1162 visitField, |
1167 includeBackendMembers: true, | 1163 includeBackendMembers: true, |
1168 includeSuperMembers: isInstantiated && !classElement.isNative()); | 1164 includeSuperMembers: isInstantiated && !classElement.isNative()); |
1169 } | 1165 } |
1170 | 1166 |
1171 void generateGetter(Element member, String fieldName, String accessorName, | 1167 void generateGetter(Element member, String fieldName, String accessorName, |
1172 ClassBuilder builder) { | 1168 ClassBuilder builder) { |
| 1169 assert(!backend.isInterceptorClass(member)); |
1173 String getterName = namer.getterNameFromAccessorName(accessorName); | 1170 String getterName = namer.getterNameFromAccessorName(accessorName); |
1174 builder.addProperty(getterName, | 1171 builder.addProperty(getterName, |
1175 js.fun([], js.return_(js['this'][fieldName]))); | 1172 js.fun([], js.return_(js['this'][fieldName]))); |
1176 } | 1173 } |
1177 | 1174 |
1178 void generateSetter(Element member, String fieldName, String accessorName, | 1175 void generateSetter(Element member, String fieldName, String accessorName, |
1179 ClassBuilder builder) { | 1176 ClassBuilder builder) { |
| 1177 assert(!backend.isInterceptorClass(member)); |
1180 String setterName = namer.setterNameFromAccessorName(accessorName); | 1178 String setterName = namer.setterNameFromAccessorName(accessorName); |
| 1179 List<String> args = backend.isInterceptedMethod(member) |
| 1180 ? ['receiver', 'v'] |
| 1181 : ['v']; |
1181 builder.addProperty(setterName, | 1182 builder.addProperty(setterName, |
1182 js.fun(['v'], js['this'][fieldName].assign('v'))); | 1183 js.fun(args, js['this'][fieldName].assign('v'))); |
1183 } | 1184 } |
1184 | 1185 |
1185 bool canGenerateCheckedSetter(Element member) { | 1186 bool canGenerateCheckedSetter(Element member) { |
1186 DartType type = member.computeType(compiler); | 1187 DartType type = member.computeType(compiler); |
1187 if (type.element.isTypeVariable() | 1188 if (type.element.isTypeVariable() |
1188 || type.element == compiler.dynamicClass | 1189 || type.element == compiler.dynamicClass |
1189 || type.element == compiler.objectClass) { | 1190 || type.element == compiler.objectClass) { |
1190 // TODO(ngeoffray): Support type checks on type parameters. | 1191 // TODO(ngeoffray): Support type checks on type parameters. |
1191 return false; | 1192 return false; |
1192 } | 1193 } |
(...skipping 10 matching lines...) Expand all Loading... |
1203 if (type.element.isErroneous()) return; | 1204 if (type.element.isErroneous()) return; |
1204 FunctionElement helperElement | 1205 FunctionElement helperElement |
1205 = backend.getCheckedModeHelper(type, typeCast: false); | 1206 = backend.getCheckedModeHelper(type, typeCast: false); |
1206 String helperName = namer.isolateAccess(helperElement); | 1207 String helperName = namer.isolateAccess(helperElement); |
1207 List<jsAst.Expression> arguments = <jsAst.Expression>[js['v']]; | 1208 List<jsAst.Expression> arguments = <jsAst.Expression>[js['v']]; |
1208 if (helperElement.computeSignature(compiler).parameterCount != 1) { | 1209 if (helperElement.computeSignature(compiler).parameterCount != 1) { |
1209 arguments.add(js.string(namer.operatorIs(type.element))); | 1210 arguments.add(js.string(namer.operatorIs(type.element))); |
1210 } | 1211 } |
1211 | 1212 |
1212 String setterName = namer.setterNameFromAccessorName(accessorName); | 1213 String setterName = namer.setterNameFromAccessorName(accessorName); |
| 1214 List<String> args = backend.isInterceptedMethod(member) |
| 1215 ? ['receiver', 'v'] |
| 1216 : ['v']; |
1213 builder.addProperty(setterName, | 1217 builder.addProperty(setterName, |
1214 js.fun(['v'], js['this'][fieldName].assign(js[helperName](arguments)))); | 1218 js.fun(args, js['this'][fieldName].assign(js[helperName](arguments)))); |
1215 } | 1219 } |
1216 | 1220 |
1217 void emitClassConstructor(ClassElement classElement, ClassBuilder builder) { | 1221 void emitClassConstructor(ClassElement classElement, ClassBuilder builder) { |
1218 /* Do nothing. */ | 1222 /* Do nothing. */ |
1219 } | 1223 } |
1220 | 1224 |
1221 void emitSuper(String superName, ClassBuilder builder) { | 1225 void emitSuper(String superName, ClassBuilder builder) { |
1222 /* Do nothing. */ | 1226 /* Do nothing. */ |
1223 } | 1227 } |
1224 | 1228 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1296 assert(!needsSetter); | 1300 assert(!needsSetter); |
1297 generateCheckedSetter(member, name, accessorName, builder); | 1301 generateCheckedSetter(member, name, accessorName, builder); |
1298 } | 1302 } |
1299 if (!getterAndSetterCanBeImplementedByFieldSpec) { | 1303 if (!getterAndSetterCanBeImplementedByFieldSpec) { |
1300 if (needsGetter) { | 1304 if (needsGetter) { |
1301 generateGetter(member, name, accessorName, builder); | 1305 generateGetter(member, name, accessorName, builder); |
1302 } | 1306 } |
1303 if (needsSetter) { | 1307 if (needsSetter) { |
1304 generateSetter(member, name, accessorName, builder); | 1308 generateSetter(member, name, accessorName, builder); |
1305 } | 1309 } |
| 1310 } else if (backend.isInterceptedMethod(member) |
| 1311 && instanceFieldNeedsSetter(member)) { |
| 1312 generateSetter(member, name, accessorName, builder); |
1306 } | 1313 } |
1307 }); | 1314 }); |
1308 }); | 1315 }); |
1309 } | 1316 } |
1310 | 1317 |
1311 /** | 1318 /** |
1312 * Documentation wanted -- johnniwinther | 1319 * Documentation wanted -- johnniwinther |
1313 * | 1320 * |
1314 * Invariant: [classElement] must be a declaration element. | 1321 * Invariant: [classElement] must be a declaration element. |
1315 */ | 1322 */ |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1355 return arity; | 1362 return arity; |
1356 } | 1363 } |
1357 | 1364 |
1358 int _compareSelectorNames(Selector selector1, Selector selector2) { | 1365 int _compareSelectorNames(Selector selector1, Selector selector2) { |
1359 String name1 = selector1.name.toString(); | 1366 String name1 = selector1.name.toString(); |
1360 String name2 = selector2.name.toString(); | 1367 String name2 = selector2.name.toString(); |
1361 if (name1 != name2) return Comparable.compare(name1, name2); | 1368 if (name1 != name2) return Comparable.compare(name1, name2); |
1362 return _selectorRank(selector1) - _selectorRank(selector2); | 1369 return _selectorRank(selector1) - _selectorRank(selector2); |
1363 } | 1370 } |
1364 | 1371 |
1365 void emitInterceptorMethods(ClassBuilder builder) { | |
1366 // Emit forwarders for the ObjectInterceptor class. We need to | |
1367 // emit all possible sends on intercepted methods. Because of | |
1368 // typed selectors we have to avoid generating the same forwarder | |
1369 // multiple times. | |
1370 Set<String> alreadyGenerated = new Set<String>(); | |
1371 for (Selector selector in | |
1372 backend.usedInterceptors.toList()..sort(_compareSelectorNames)) { | |
1373 String name = backend.namer.invocationName(selector); | |
1374 if (alreadyGenerated.contains(name)) continue; | |
1375 alreadyGenerated.add(name); | |
1376 | |
1377 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | |
1378 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | |
1379 parameters.add(new jsAst.Parameter('receiver')); | |
1380 | |
1381 if (selector.isSetter()) { | |
1382 parameters.add(new jsAst.Parameter('value')); | |
1383 arguments.add(js['value']); | |
1384 } else { | |
1385 for (int i = 0; i < selector.argumentCount; i++) { | |
1386 String argName = 'a$i'; | |
1387 parameters.add(new jsAst.Parameter(argName)); | |
1388 arguments.add(js[argName]); | |
1389 } | |
1390 } | |
1391 jsAst.Fun function = | |
1392 js.fun(parameters, js.return_(js['receiver'][name](arguments))); | |
1393 builder.addProperty(name, function); | |
1394 } | |
1395 } | |
1396 | |
1397 Iterable<Element> getTypedefChecksOn(DartType type) { | 1372 Iterable<Element> getTypedefChecksOn(DartType type) { |
1398 bool isSubtype(TypedefElement typedef) { | 1373 bool isSubtype(TypedefElement typedef) { |
1399 FunctionType typedefType = | 1374 FunctionType typedefType = |
1400 typedef.computeType(compiler).unalias(compiler); | 1375 typedef.computeType(compiler).unalias(compiler); |
1401 return compiler.types.isSubtype(type, typedefType); | 1376 return compiler.types.isSubtype(type, typedefType); |
1402 } | 1377 } |
1403 return checkedTypedefs.where(isSubtype).toList() | 1378 return checkedTypedefs.where(isSubtype).toList() |
1404 ..sort(Elements.compareByPosition); | 1379 ..sort(Elements.compareByPosition); |
1405 } | 1380 } |
1406 | 1381 |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1553 for (Constant constant in constants) { | 1528 for (Constant constant in constants) { |
1554 if (constant is ConstructedConstant) { | 1529 if (constant is ConstructedConstant) { |
1555 Element element = constant.computeType(compiler).element; | 1530 Element element = constant.computeType(compiler).element; |
1556 if (backend.isInterceptorClass(element)) { | 1531 if (backend.isInterceptorClass(element)) { |
1557 needed.add(element); | 1532 needed.add(element); |
1558 } | 1533 } |
1559 } | 1534 } |
1560 } | 1535 } |
1561 | 1536 |
1562 // Add unneeded interceptors to the [unneededClasses] set. | 1537 // Add unneeded interceptors to the [unneededClasses] set. |
1563 for (ClassElement interceptor in backend.interceptedClasses.keys) { | 1538 for (ClassElement interceptor in backend.interceptedClasses) { |
1564 if (!needed.contains(interceptor)) { | 1539 if (!needed.contains(interceptor) |
| 1540 && interceptor != compiler.objectClass) { |
1565 unneededClasses.add(interceptor); | 1541 unneededClasses.add(interceptor); |
1566 } | 1542 } |
1567 } | 1543 } |
1568 | 1544 |
1569 return (ClassElement cls) => !unneededClasses.contains(cls); | 1545 return (ClassElement cls) => !unneededClasses.contains(cls); |
1570 } | 1546 } |
1571 | 1547 |
1572 void emitClosureClassIfNeeded(CodeBuffer buffer) { | 1548 void emitClosureClassIfNeeded(CodeBuffer buffer) { |
1573 // The closure class could have become necessary because of the generation | 1549 // The closure class could have become necessary because of the generation |
1574 // of stubs. | 1550 // of stubs. |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1704 // if they share methods with the same signature. Currently we do this only | 1680 // if they share methods with the same signature. Currently we do this only |
1705 // if there are no optional parameters. Closures with optional parameters | 1681 // if there are no optional parameters. Closures with optional parameters |
1706 // are more difficult to canonicalize because they would need to have the | 1682 // are more difficult to canonicalize because they would need to have the |
1707 // same default values. | 1683 // same default values. |
1708 | 1684 |
1709 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; | 1685 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; |
1710 int parameterCount = member.parameterCount(compiler); | 1686 int parameterCount = member.parameterCount(compiler); |
1711 | 1687 |
1712 Map<int, String> cache; | 1688 Map<int, String> cache; |
1713 String extraArg = null; | 1689 String extraArg = null; |
1714 // Methods on interceptor classes take an extra parameter, which is the | 1690 // Intercepted methods take an extra parameter, which is the |
1715 // actual receiver of the call. | 1691 // receiver of the call. |
1716 bool inInterceptor = backend.isInterceptorClass(member.getEnclosingClass()); | 1692 bool inInterceptor = backend.isInterceptedMethod(member); |
1717 if (inInterceptor) { | 1693 if (inInterceptor) { |
1718 cache = interceptorClosureCache; | 1694 cache = interceptorClosureCache; |
1719 extraArg = 'receiver'; | 1695 extraArg = 'receiver'; |
1720 } else { | 1696 } else { |
1721 cache = boundClosureCache; | 1697 cache = boundClosureCache; |
1722 } | 1698 } |
1723 List<String> fieldNames = compiler.enableMinification | 1699 List<String> fieldNames = compiler.enableMinification |
1724 ? inInterceptor ? const ['a', 'b', 'c'] | 1700 ? inInterceptor ? const ['a', 'b', 'c'] |
1725 : const ['a', 'b'] | 1701 : const ['a', 'b'] |
1726 : inInterceptor ? const ['self', 'target', 'receiver'] | 1702 : inInterceptor ? const ['self', 'target', 'receiver'] |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1827 /** | 1803 /** |
1828 * Documentation wanted -- johnniwinther | 1804 * Documentation wanted -- johnniwinther |
1829 * | 1805 * |
1830 * Invariant: [member] must be a declaration element. | 1806 * Invariant: [member] must be a declaration element. |
1831 */ | 1807 */ |
1832 void emitCallStubForGetter(Element member, | 1808 void emitCallStubForGetter(Element member, |
1833 Set<Selector> selectors, | 1809 Set<Selector> selectors, |
1834 DefineStubFunction defineStub) { | 1810 DefineStubFunction defineStub) { |
1835 assert(invariant(member, member.isDeclaration)); | 1811 assert(invariant(member, member.isDeclaration)); |
1836 LibraryElement memberLibrary = member.getLibrary(); | 1812 LibraryElement memberLibrary = member.getLibrary(); |
1837 // If the class is an interceptor class, the stub gets the | 1813 // If the method is intercepted, the stub gets the |
1838 // receiver explicitely and we need to pass it to the getter call. | 1814 // receiver explicitely and we need to pass it to the getter call. |
1839 bool isInterceptorClass = | 1815 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
1840 backend.isInterceptorClass(member.getEnclosingClass()); | |
1841 | 1816 |
1842 const String receiverArgumentName = r'$receiver'; | 1817 const String receiverArgumentName = r'$receiver'; |
1843 | 1818 |
1844 jsAst.Expression buildGetter() { | 1819 jsAst.Expression buildGetter() { |
1845 if (member.isGetter()) { | 1820 if (member.isGetter()) { |
1846 String getterName = namer.getterName(member); | 1821 String getterName = namer.getterName(member); |
1847 return js['this'][getterName]( | 1822 return js['this'][getterName]( |
1848 isInterceptorClass | 1823 isInterceptedMethod |
1849 ? <jsAst.Expression>[js[receiverArgumentName]] | 1824 ? <jsAst.Expression>[js[receiverArgumentName]] |
1850 : <jsAst.Expression>[]); | 1825 : <jsAst.Expression>[]); |
1851 } else { | 1826 } else { |
1852 String fieldName = member.hasFixedBackendName() | 1827 String fieldName = member.hasFixedBackendName() |
1853 ? member.fixedBackendName() | 1828 ? member.fixedBackendName() |
1854 : namer.instanceFieldName(member); | 1829 : namer.instanceFieldName(member); |
1855 return js['this'][fieldName]; | 1830 return js['this'][fieldName]; |
1856 } | 1831 } |
1857 } | 1832 } |
1858 | 1833 |
1859 // Two selectors may match but differ only in type. To avoid generating | 1834 // Two selectors may match but differ only in type. To avoid generating |
1860 // identical stubs for each we track untyped selectors which already have | 1835 // identical stubs for each we track untyped selectors which already have |
1861 // stubs. | 1836 // stubs. |
1862 Set<Selector> generatedSelectors = new Set<Selector>(); | 1837 Set<Selector> generatedSelectors = new Set<Selector>(); |
1863 | 1838 |
1864 for (Selector selector in selectors) { | 1839 for (Selector selector in selectors) { |
1865 if (selector.applies(member, compiler)) { | 1840 if (selector.applies(member, compiler)) { |
1866 selector = selector.asUntyped; | 1841 selector = selector.asUntyped; |
1867 if (generatedSelectors.contains(selector)) continue; | 1842 if (generatedSelectors.contains(selector)) continue; |
1868 generatedSelectors.add(selector); | 1843 generatedSelectors.add(selector); |
1869 | 1844 |
1870 String invocationName = namer.invocationName(selector); | 1845 String invocationName = namer.invocationName(selector); |
1871 Selector callSelector = new Selector.callClosureFrom(selector); | 1846 Selector callSelector = new Selector.callClosureFrom(selector); |
1872 String closureCallName = namer.invocationName(callSelector); | 1847 String closureCallName = namer.invocationName(callSelector); |
1873 | 1848 |
1874 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | 1849 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; |
1875 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 1850 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
1876 if (isInterceptorClass) { | 1851 if (isInterceptedMethod) { |
1877 parameters.add(new jsAst.Parameter(receiverArgumentName)); | 1852 parameters.add(new jsAst.Parameter(receiverArgumentName)); |
1878 } | 1853 } |
1879 | 1854 |
1880 for (int i = 0; i < selector.argumentCount; i++) { | 1855 for (int i = 0; i < selector.argumentCount; i++) { |
1881 String name = 'arg$i'; | 1856 String name = 'arg$i'; |
1882 parameters.add(new jsAst.Parameter(name)); | 1857 parameters.add(new jsAst.Parameter(name)); |
1883 arguments.add(js[name]); | 1858 arguments.add(js[name]); |
1884 } | 1859 } |
1885 | 1860 |
1886 jsAst.Fun function = js.fun( | 1861 jsAst.Fun function = js.fun( |
1887 parameters, | 1862 parameters, |
1888 js.return_(buildGetter()[closureCallName](arguments))); | 1863 js.return_(buildGetter()[closureCallName](arguments))); |
1889 | 1864 |
1890 defineStub(invocationName, function); | 1865 defineStub(invocationName, function); |
1891 } | 1866 } |
1892 } | 1867 } |
1893 } | 1868 } |
1894 | 1869 |
1895 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { | 1870 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { |
1896 ConstantHandler handler = compiler.constantHandler; | 1871 ConstantHandler handler = compiler.constantHandler; |
1897 Iterable<VariableElement> staticNonFinalFields = | 1872 Iterable<VariableElement> staticNonFinalFields = |
1898 handler.getStaticNonFinalFieldsForEmission(); | 1873 handler.getStaticNonFinalFieldsForEmission(); |
1899 for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { | 1874 for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { |
| 1875 // [:interceptedNames:] is handled in [emitInterceptedNames]. |
| 1876 if (element == backend.interceptedNames) continue; |
1900 compiler.withCurrentElement(element, () { | 1877 compiler.withCurrentElement(element, () { |
1901 Constant initialValue = handler.getInitialValueFor(element); | 1878 Constant initialValue = handler.getInitialValueFor(element); |
1902 jsAst.Expression init = | 1879 jsAst.Expression init = |
1903 js[isolateProperties][namer.getName(element)].assign( | 1880 js[isolateProperties][namer.getName(element)].assign( |
1904 constantEmitter.referenceInInitializationContext(initialValue)); | 1881 constantEmitter.referenceInInitializationContext(initialValue)); |
1905 buffer.add(jsAst.prettyPrint(init, compiler)); | 1882 buffer.add(jsAst.prettyPrint(init, compiler)); |
1906 buffer.add('$N'); | 1883 buffer.add('$N'); |
1907 }); | 1884 }); |
1908 } | 1885 } |
1909 } | 1886 } |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2028 | 2005 |
2029 List<jsAst.Expression> argNames = | 2006 List<jsAst.Expression> argNames = |
2030 selector.getOrderedNamedArguments().map((SourceString name) => | 2007 selector.getOrderedNamedArguments().map((SourceString name) => |
2031 js.string(name.slowToString())).toList(); | 2008 js.string(name.slowToString())).toList(); |
2032 | 2009 |
2033 String internalName = namer.invocationMirrorInternalName(selector); | 2010 String internalName = namer.invocationMirrorInternalName(selector); |
2034 | 2011 |
2035 String createInvocationMirror = namer.getName( | 2012 String createInvocationMirror = namer.getName( |
2036 compiler.createInvocationMirrorElement); | 2013 compiler.createInvocationMirrorElement); |
2037 | 2014 |
| 2015 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); |
2038 jsAst.Expression expression = js['this.$noSuchMethodName']( | 2016 jsAst.Expression expression = js['this.$noSuchMethodName']( |
2039 js[namer.CURRENT_ISOLATE][createInvocationMirror]([ | 2017 [js['this'], |
2040 js.string(methodName), | 2018 js[namer.CURRENT_ISOLATE][createInvocationMirror]([ |
2041 js.string(internalName), | 2019 js.string(methodName), |
2042 type, | 2020 js.string(internalName), |
2043 new jsAst.ArrayInitializer.from( | 2021 type, |
2044 parameters.map((param) => js[param.name]).toList()), | 2022 new jsAst.ArrayInitializer.from( |
2045 new jsAst.ArrayInitializer.from(argNames)])); | 2023 parameters.map((param) => js[param.name]).toList()), |
| 2024 new jsAst.ArrayInitializer.from(argNames)])]); |
| 2025 parameters = backend.isInterceptedName(selector.name) |
| 2026 ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters)) |
| 2027 : parameters; |
2046 return js.fun(parameters, js.return_(expression)); | 2028 return js.fun(parameters, js.return_(expression)); |
2047 } | 2029 } |
2048 | 2030 |
2049 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) { | 2031 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) { |
2050 // Cache the object class and type. | 2032 // Cache the object class and type. |
2051 ClassElement objectClass = compiler.objectClass; | 2033 ClassElement objectClass = compiler.objectClass; |
2052 DartType objectType = objectClass.computeType(compiler); | 2034 DartType objectType = objectClass.computeType(compiler); |
2053 | 2035 |
2054 for (Selector selector in selectors) { | 2036 for (Selector selector in selectors) { |
2055 // Introduce a helper function that determines if the given | 2037 // Introduce a helper function that determines if the given |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2176 dartMainRunner(function() { ${mainCall}; }); | 2158 dartMainRunner(function() { ${mainCall}; }); |
2177 } else { | 2159 } else { |
2178 ${mainCall}; | 2160 ${mainCall}; |
2179 } | 2161 } |
2180 } | 2162 } |
2181 """); | 2163 """); |
2182 addComment('END invoke [main].', buffer); | 2164 addComment('END invoke [main].', buffer); |
2183 } | 2165 } |
2184 | 2166 |
2185 void emitGetInterceptorMethod(CodeBuffer buffer, | 2167 void emitGetInterceptorMethod(CodeBuffer buffer, |
2186 String objectName, | |
2187 String key, | 2168 String key, |
2188 Collection<ClassElement> classes) { | 2169 Collection<ClassElement> classes) { |
2189 jsAst.Statement buildReturnInterceptor(ClassElement cls) { | 2170 jsAst.Statement buildReturnInterceptor(ClassElement cls) { |
2190 return js.return_(js[namer.isolateAccess(cls)]['prototype']); | 2171 return js.return_(js[namer.isolateAccess(cls)]['prototype']); |
2191 } | 2172 } |
2192 | 2173 |
2193 jsAst.VariableUse receiver = js['receiver']; | 2174 jsAst.VariableUse receiver = js['receiver']; |
2194 /** | 2175 /** |
2195 * Build a JavaScrit AST node for doing a type check on | 2176 * Build a JavaScrit AST node for doing a type check on |
2196 * [cls]. [cls] must be an interceptor class. | 2177 * [cls]. [cls] must be an interceptor class. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2228 bool hasString = false; | 2209 bool hasString = false; |
2229 for (ClassElement cls in classes) { | 2210 for (ClassElement cls in classes) { |
2230 if (cls == backend.jsArrayClass) hasArray = true; | 2211 if (cls == backend.jsArrayClass) hasArray = true; |
2231 else if (cls == backend.jsBoolClass) hasBool = true; | 2212 else if (cls == backend.jsBoolClass) hasBool = true; |
2232 else if (cls == backend.jsDoubleClass) hasDouble = true; | 2213 else if (cls == backend.jsDoubleClass) hasDouble = true; |
2233 else if (cls == backend.jsFunctionClass) hasFunction = true; | 2214 else if (cls == backend.jsFunctionClass) hasFunction = true; |
2234 else if (cls == backend.jsIntClass) hasInt = true; | 2215 else if (cls == backend.jsIntClass) hasInt = true; |
2235 else if (cls == backend.jsNullClass) hasNull = true; | 2216 else if (cls == backend.jsNullClass) hasNull = true; |
2236 else if (cls == backend.jsNumberClass) hasNumber = true; | 2217 else if (cls == backend.jsNumberClass) hasNumber = true; |
2237 else if (cls == backend.jsStringClass) hasString = true; | 2218 else if (cls == backend.jsStringClass) hasString = true; |
2238 else throw 'Internal error: $cls'; | 2219 else { |
| 2220 assert(cls == compiler.objectClass); |
| 2221 } |
2239 } | 2222 } |
2240 if (hasDouble) { | 2223 if (hasDouble) { |
2241 assert(!hasNumber); | |
2242 hasNumber = true; | 2224 hasNumber = true; |
2243 } | 2225 } |
2244 if (hasInt) hasNumber = true; | 2226 if (hasInt) hasNumber = true; |
2245 | 2227 |
2246 jsAst.Block block = new jsAst.Block.empty(); | 2228 jsAst.Block block = new jsAst.Block.empty(); |
2247 | 2229 |
2248 if (hasNumber) { | 2230 if (hasNumber) { |
2249 jsAst.Statement whenNumber; | 2231 jsAst.Statement whenNumber; |
2250 | 2232 |
2251 /// Note: there are two number classes in play: Dart's [num], | 2233 /// Note: there are two number classes in play: Dart's [num], |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2284 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass)); | 2266 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass)); |
2285 } | 2267 } |
2286 if (hasBool) { | 2268 if (hasBool) { |
2287 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); | 2269 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); |
2288 } | 2270 } |
2289 // TODO(ahe): It might be faster to check for Array before | 2271 // TODO(ahe): It might be faster to check for Array before |
2290 // function and bool. | 2272 // function and bool. |
2291 if (hasArray) { | 2273 if (hasArray) { |
2292 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); | 2274 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); |
2293 } | 2275 } |
2294 block.statements.add(js.return_(js[objectName]['prototype'])); | 2276 block.statements.add(js.return_(receiver)); |
2295 | 2277 |
2296 buffer.add(jsAst.prettyPrint( | 2278 buffer.add(jsAst.prettyPrint( |
2297 js[isolateProperties][key].assign(js.fun(['receiver'], block)), | 2279 js[isolateProperties][key].assign(js.fun(['receiver'], block)), |
2298 compiler)); | 2280 compiler)); |
2299 buffer.add(N); | 2281 buffer.add(N); |
2300 } | 2282 } |
2301 | 2283 |
2302 /** | 2284 /** |
2303 * Emit all versions of the [:getInterceptor:] method. | 2285 * Emit all versions of the [:getInterceptor:] method. |
2304 */ | 2286 */ |
2305 void emitGetInterceptorMethods(CodeBuffer buffer) { | 2287 void emitGetInterceptorMethods(CodeBuffer buffer) { |
2306 // If no class needs to be intercepted, just return. | |
2307 if (backend.objectInterceptorClass == null) return; | |
2308 String objectName = namer.isolateAccess(backend.objectInterceptorClass); | |
2309 var specializedGetInterceptors = backend.specializedGetInterceptors; | 2288 var specializedGetInterceptors = backend.specializedGetInterceptors; |
2310 for (String name in specializedGetInterceptors.keys.toList()..sort()) { | 2289 for (String name in specializedGetInterceptors.keys.toList()..sort()) { |
2311 Collection<ClassElement> classes = specializedGetInterceptors[name]; | 2290 Collection<ClassElement> classes = specializedGetInterceptors[name]; |
2312 emitGetInterceptorMethod(buffer, objectName, name, classes); | 2291 emitGetInterceptorMethod(buffer, name, classes); |
2313 } | 2292 } |
2314 } | 2293 } |
2315 | 2294 |
2316 /** | 2295 /** |
2317 * Compute all the classes that must be emitted. | 2296 * Compute all the classes that must be emitted. |
2318 */ | 2297 */ |
2319 void computeNeededClasses() { | 2298 void computeNeededClasses() { |
2320 instantiatedClasses = | 2299 instantiatedClasses = |
2321 compiler.codegenWorld.instantiatedClasses.where(computeClassFilter()) | 2300 compiler.codegenWorld.instantiatedClasses.where(computeClassFilter()) |
2322 .toSet(); | 2301 .toSet(); |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2528 } | 2507 } |
2529 return null; | 2508 return null; |
2530 } | 2509 } |
2531 | 2510 |
2532 void emitOneShotInterceptors(CodeBuffer buffer) { | 2511 void emitOneShotInterceptors(CodeBuffer buffer) { |
2533 List<String> names = backend.oneShotInterceptors.keys.toList(); | 2512 List<String> names = backend.oneShotInterceptors.keys.toList(); |
2534 names.sort(); | 2513 names.sort(); |
2535 for (String name in names) { | 2514 for (String name in names) { |
2536 Selector selector = backend.oneShotInterceptors[name]; | 2515 Selector selector = backend.oneShotInterceptors[name]; |
2537 Set<ClassElement> classes = | 2516 Set<ClassElement> classes = |
2538 backend.getInterceptedClassesOn(selector); | 2517 backend.getInterceptedClassesOn(selector.name); |
2539 String getInterceptorName = | 2518 String getInterceptorName = |
2540 namer.getInterceptorName(backend.getInterceptorMethod, classes); | 2519 namer.getInterceptorName(backend.getInterceptorMethod, classes); |
2541 | 2520 |
2542 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | 2521 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; |
2543 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 2522 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
2544 parameters.add(new jsAst.Parameter('receiver')); | 2523 parameters.add(new jsAst.Parameter('receiver')); |
2545 arguments.add(js['receiver']); | 2524 arguments.add(js['receiver']); |
2546 | 2525 |
2547 if (selector.isSetter()) { | 2526 if (selector.isSetter()) { |
2548 parameters.add(new jsAst.Parameter('value')); | 2527 parameters.add(new jsAst.Parameter('value')); |
(...skipping 21 matching lines...) Expand all Loading... |
2570 jsAst.Fun function = js.fun(parameters, body); | 2549 jsAst.Fun function = js.fun(parameters, body); |
2571 | 2550 |
2572 jsAst.PropertyAccess property = | 2551 jsAst.PropertyAccess property = |
2573 js[isolateProperties][name]; | 2552 js[isolateProperties][name]; |
2574 | 2553 |
2575 buffer.add(jsAst.prettyPrint(property.assign(function), compiler)); | 2554 buffer.add(jsAst.prettyPrint(property.assign(function), compiler)); |
2576 buffer.add(N); | 2555 buffer.add(N); |
2577 } | 2556 } |
2578 } | 2557 } |
2579 | 2558 |
| 2559 /** |
| 2560 * If [:invokeOn:] has been compiled, emit all the possible selector names |
| 2561 * that are intercepted into the [:interceptedNames:] top-level |
| 2562 * variable. The implementation of [:invokeOn:] will use it to |
| 2563 * determine whether it should call the method with an extra |
| 2564 * parameter. |
| 2565 */ |
| 2566 void emitInterceptedNames(CodeBuffer buffer) { |
| 2567 if (!compiler.enabledInvokeOn) return; |
| 2568 String name = backend.namer.getName(backend.interceptedNames); |
| 2569 jsAst.PropertyAccess property = js[isolateProperties][name]; |
| 2570 |
| 2571 int index = 0; |
| 2572 List<jsAst.ArrayElement> elements = backend.usedInterceptors.map( |
| 2573 (Selector selector) { |
| 2574 jsAst.Literal str = js.string(namer.invocationName(selector)); |
| 2575 return new jsAst.ArrayElement(index++, str); |
| 2576 }).toList(); |
| 2577 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer( |
| 2578 backend.usedInterceptors.length, |
| 2579 elements); |
| 2580 |
| 2581 buffer.add(jsAst.prettyPrint(property.assign(array), compiler)); |
| 2582 buffer.add(N); |
| 2583 } |
| 2584 |
2580 void emitInitFunction(CodeBuffer buffer) { | 2585 void emitInitFunction(CodeBuffer buffer) { |
2581 jsAst.Fun fun = js.fun([], [ | 2586 jsAst.Fun fun = js.fun([], [ |
2582 js['$isolateProperties = {}'], | 2587 js['$isolateProperties = {}'], |
2583 ] | 2588 ] |
2584 ..addAll(buildDefineClassAndFinishClassFunctionsIfNecessary()) | 2589 ..addAll(buildDefineClassAndFinishClassFunctionsIfNecessary()) |
2585 ..addAll(buildLazyInitializerFunctionIfNecessary()) | 2590 ..addAll(buildLazyInitializerFunctionIfNecessary()) |
2586 ..addAll(buildFinishIsolateConstructor()) | 2591 ..addAll(buildFinishIsolateConstructor()) |
2587 ); | 2592 ); |
2588 jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration( | 2593 jsAst.FunctionDeclaration decl = new jsAst.FunctionDeclaration( |
2589 new jsAst.VariableDeclaration('init'), fun); | 2594 new jsAst.VariableDeclaration('init'), fun); |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2671 | 2676 |
2672 emitStaticFunctions(mainBuffer); | 2677 emitStaticFunctions(mainBuffer); |
2673 emitStaticFunctionGetters(mainBuffer); | 2678 emitStaticFunctionGetters(mainBuffer); |
2674 | 2679 |
2675 emitRuntimeTypeSupport(mainBuffer); | 2680 emitRuntimeTypeSupport(mainBuffer); |
2676 emitCompileTimeConstants(mainBuffer); | 2681 emitCompileTimeConstants(mainBuffer); |
2677 // Static field initializations require the classes and compile-time | 2682 // Static field initializations require the classes and compile-time |
2678 // constants to be set up. | 2683 // constants to be set up. |
2679 emitStaticNonFinalFieldInitializations(mainBuffer); | 2684 emitStaticNonFinalFieldInitializations(mainBuffer); |
2680 emitOneShotInterceptors(mainBuffer); | 2685 emitOneShotInterceptors(mainBuffer); |
| 2686 emitInterceptedNames(mainBuffer); |
2681 emitGetInterceptorMethods(mainBuffer); | 2687 emitGetInterceptorMethods(mainBuffer); |
2682 emitLazilyInitializedStaticFields(mainBuffer); | 2688 emitLazilyInitializedStaticFields(mainBuffer); |
2683 | 2689 |
2684 isolateProperties = isolatePropertiesName; | 2690 isolateProperties = isolatePropertiesName; |
2685 // The following code should not use the short-hand for the | 2691 // The following code should not use the short-hand for the |
2686 // initialStatics. | 2692 // initialStatics. |
2687 mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=${_}null$N'); | 2693 mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=${_}null$N'); |
2688 | 2694 |
2689 emitFinishIsolateConstructorInvocation(mainBuffer); | 2695 emitFinishIsolateConstructorInvocation(mainBuffer); |
2690 mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=' | 2696 mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=' |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2771 """; | 2777 """; |
2772 const String HOOKS_API_USAGE = """ | 2778 const String HOOKS_API_USAGE = """ |
2773 // The code supports the following hooks: | 2779 // The code supports the following hooks: |
2774 // dartPrint(message) - if this function is defined it is called | 2780 // dartPrint(message) - if this function is defined it is called |
2775 // instead of the Dart [print] method. | 2781 // instead of the Dart [print] method. |
2776 // dartMainRunner(main) - if this function is defined, the Dart [main] | 2782 // dartMainRunner(main) - if this function is defined, the Dart [main] |
2777 // method will not be invoked directly. | 2783 // method will not be invoked directly. |
2778 // Instead, a closure that will invoke [main] is | 2784 // Instead, a closure that will invoke [main] is |
2779 // passed to [dartMainRunner]. | 2785 // passed to [dartMainRunner]. |
2780 """; | 2786 """; |
OLD | NEW |