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