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 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 * `x is Set<String>` then the ClassElement `Set` will occur once in | 105 * `x is Set<String>` then the ClassElement `Set` will occur once in |
106 * [checkedClasses]. | 106 * [checkedClasses]. |
107 */ | 107 */ |
108 Set<ClassElement> checkedClasses; | 108 Set<ClassElement> checkedClasses; |
109 | 109 |
110 /** | 110 /** |
111 * Raw Typedef symbols occuring in is-checks and type assertions. If the | 111 * Raw Typedef symbols occuring in is-checks and type assertions. If the |
112 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement | 112 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement |
113 * `F` will occur once in [checkedTypedefs]. | 113 * `F` will occur once in [checkedTypedefs]. |
114 */ | 114 */ |
115 Set<TypedefElement> checkedTypedefs; | 115 Set<FunctionType> checkedFunctionTypes; |
| 116 |
| 117 Map<ClassElement, Set<FunctionType>> checkedGenericFunctionTypes |
| 118 = new Map<ClassElement, Set<FunctionType>>(); |
| 119 |
| 120 Set<FunctionType> checkedNonGenericFunctionTypes |
| 121 = new Set<FunctionType>(); |
| 122 |
| 123 void registerDynamicFunctionTypeCheck(FunctionType functionType) { |
| 124 ClassElement classElement = Types.getClassContext(functionType); |
| 125 if (classElement != null) { |
| 126 checkedGenericFunctionTypes.putIfAbsent(classElement, |
| 127 () => new Set<FunctionType>()).add(functionType); |
| 128 } else { |
| 129 checkedNonGenericFunctionTypes.add(functionType); |
| 130 } |
| 131 } |
116 | 132 |
117 final bool generateSourceMap; | 133 final bool generateSourceMap; |
118 | 134 |
119 Iterable<ClassElement> cachedClassesUsingTypeVariableTests; | 135 Iterable<ClassElement> cachedClassesUsingTypeVariableTests; |
120 | 136 |
121 Iterable<ClassElement> get classesUsingTypeVariableTests { | 137 Iterable<ClassElement> get classesUsingTypeVariableTests { |
122 if (cachedClassesUsingTypeVariableTests == null) { | 138 if (cachedClassesUsingTypeVariableTests == null) { |
123 cachedClassesUsingTypeVariableTests = compiler.codegenWorld.isChecks | 139 cachedClassesUsingTypeVariableTests = compiler.codegenWorld.isChecks |
124 .where((DartType t) => t is TypeVariableType) | 140 .where((DartType t) => t is TypeVariableType) |
125 .map((TypeVariableType v) => v.element.getEnclosingClass()) | 141 .map((TypeVariableType v) => v.element.getEnclosingClass()) |
(...skipping 10 matching lines...) Expand all Loading... |
136 constantEmitter = new ConstantEmitter(compiler, namer), | 152 constantEmitter = new ConstantEmitter(compiler, namer), |
137 super(compiler) { | 153 super(compiler) { |
138 nativeEmitter = new NativeEmitter(this); | 154 nativeEmitter = new NativeEmitter(this); |
139 } | 155 } |
140 | 156 |
141 void addComment(String comment, CodeBuffer buffer) { | 157 void addComment(String comment, CodeBuffer buffer) { |
142 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); | 158 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); |
143 } | 159 } |
144 | 160 |
145 void computeRequiredTypeChecks() { | 161 void computeRequiredTypeChecks() { |
146 assert(checkedClasses == null && checkedTypedefs == null); | 162 assert(checkedClasses == null && checkedFunctionTypes == null); |
147 | 163 |
148 backend.rti.addImplicitChecks(compiler.codegenWorld, | 164 backend.rti.addImplicitChecks(compiler.codegenWorld, |
149 classesUsingTypeVariableTests); | 165 classesUsingTypeVariableTests); |
150 | 166 |
151 checkedClasses = new Set<ClassElement>(); | 167 checkedClasses = new Set<ClassElement>(); |
152 checkedTypedefs = new Set<TypedefElement>(); | 168 checkedFunctionTypes = new Set<FunctionType>(); |
153 compiler.codegenWorld.isChecks.forEach((DartType t) { | 169 compiler.codegenWorld.isChecks.forEach((DartType t) { |
| 170 assert(!t.isMalformed); |
154 if (t is InterfaceType) { | 171 if (t is InterfaceType) { |
155 checkedClasses.add(t.element); | 172 checkedClasses.add(t.element); |
156 } else if (t is TypedefType) { | 173 } else if (t is FunctionType) { |
157 checkedTypedefs.add(t.element); | 174 checkedFunctionTypes.add(t); |
158 } | 175 } |
159 }); | 176 }); |
160 } | 177 } |
161 | 178 |
162 ClassElement computeMixinClass(MixinApplicationElement mixinApplication) { | 179 ClassElement computeMixinClass(MixinApplicationElement mixinApplication) { |
163 ClassElement mixin = mixinApplication.mixin; | 180 ClassElement mixin = mixinApplication.mixin; |
164 while (mixin.isMixinApplication) { | 181 while (mixin.isMixinApplication) { |
165 mixinApplication = mixin; | 182 mixinApplication = mixin; |
166 mixin = mixinApplication.mixin; | 183 mixin = mixinApplication.mixin; |
167 } | 184 } |
(...skipping 1061 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1229 | 1246 |
1230 void generateIsTest(Element other) { | 1247 void generateIsTest(Element other) { |
1231 if (other == compiler.objectClass && other != classElement) { | 1248 if (other == compiler.objectClass && other != classElement) { |
1232 // Avoid emitting [:$isObject:] on all classes but [Object]. | 1249 // Avoid emitting [:$isObject:] on all classes but [Object]. |
1233 return; | 1250 return; |
1234 } | 1251 } |
1235 other = backend.getImplementationClass(other); | 1252 other = backend.getImplementationClass(other); |
1236 builder.addProperty(namer.operatorIs(other), js('true')); | 1253 builder.addProperty(namer.operatorIs(other), js('true')); |
1237 } | 1254 } |
1238 | 1255 |
| 1256 void generateIsFunctionTypeTest(FunctionType type) { |
| 1257 String operator = namer.operatorIsType(type); |
| 1258 builder.addProperty(operator, new jsAst.LiteralBool(true)); |
| 1259 } |
| 1260 |
| 1261 void generateFunctionTypeSignature(Element method, FunctionType type) { |
| 1262 assert(method.isImplementation); |
| 1263 String thisAccess = 'this'; |
| 1264 Node node = method.parseNode(compiler); |
| 1265 ClosureClassMap closureData = |
| 1266 compiler.closureToClassMapper.closureMappingCache[node]; |
| 1267 if (closureData != null) { |
| 1268 Element thisElement = |
| 1269 closureData.freeVariableMapping[closureData.thisElement]; |
| 1270 if (thisElement != null) { |
| 1271 String thisName = backend.namer.getName(thisElement); |
| 1272 thisAccess = 'this.$thisName'; |
| 1273 } |
| 1274 } |
| 1275 RuntimeTypes rti = backend.rti; |
| 1276 String encoding = rti.getSignatureEncoding(type, () => '$thisAccess'); |
| 1277 String operatorSignature = namer.operatorSignature(); |
| 1278 builder.addProperty(operatorSignature, |
| 1279 new jsAst.LiteralExpression(encoding)); |
| 1280 } |
| 1281 |
1239 void generateSubstitution(Element other, {bool emitNull: false}) { | 1282 void generateSubstitution(Element other, {bool emitNull: false}) { |
1240 RuntimeTypes rti = backend.rti; | 1283 RuntimeTypes rti = backend.rti; |
1241 // TODO(karlklose): support typedefs with variables. | 1284 // TODO(karlklose): support typedefs with variables. |
1242 jsAst.Expression expression; | 1285 jsAst.Expression expression; |
1243 bool needsNativeCheck = nativeEmitter.requiresNativeIsCheck(other); | 1286 bool needsNativeCheck = nativeEmitter.requiresNativeIsCheck(other); |
1244 if (other.kind == ElementKind.CLASS) { | 1287 if (other.kind == ElementKind.CLASS) { |
1245 String substitution = rti.getSupertypeSubstitution(classElement, other, | 1288 String substitution = rti.getSupertypeSubstitution(classElement, other, |
1246 alwaysGenerateFunction: true); | 1289 alwaysGenerateFunction: true); |
1247 if (substitution != null) { | 1290 if (substitution != null) { |
1248 expression = new jsAst.LiteralExpression(substitution); | 1291 expression = new jsAst.LiteralExpression(substitution); |
1249 } else if (emitNull || needsNativeCheck) { | 1292 } else if (emitNull || needsNativeCheck) { |
1250 expression = new jsAst.LiteralNull(); | 1293 expression = new jsAst.LiteralNull(); |
1251 } | 1294 } |
1252 } | 1295 } |
1253 if (expression != null) { | 1296 if (expression != null) { |
1254 builder.addProperty(namer.substitutionName(other), expression); | 1297 builder.addProperty(namer.substitutionName(other), expression); |
1255 } | 1298 } |
1256 } | 1299 } |
1257 | 1300 |
1258 generateIsTestsOn(classElement, generateIsTest, generateSubstitution); | 1301 generateIsTestsOn(classElement, generateIsTest, |
| 1302 generateIsFunctionTypeTest, generateFunctionTypeSignature, |
| 1303 generateSubstitution); |
1259 } | 1304 } |
1260 | 1305 |
1261 void emitRuntimeTypeSupport(CodeBuffer buffer) { | 1306 void emitRuntimeTypeSupport(CodeBuffer buffer) { |
1262 RuntimeTypes rti = backend.rti; | 1307 RuntimeTypes rti = backend.rti; |
1263 TypeChecks typeChecks = rti.requiredChecks; | 1308 TypeChecks typeChecks = rti.requiredChecks; |
1264 | 1309 |
1265 // Add checks to the constructors of instantiated classes. | 1310 // Add checks to the constructors of instantiated classes. |
1266 for (ClassElement cls in typeChecks) { | 1311 for (ClassElement cls in typeChecks) { |
1267 String holder = namer.isolateAccess(backend.getImplementationClass(cls)); | 1312 String holder = namer.isolateAccess(backend.getImplementationClass(cls)); |
1268 for (TypeCheck check in typeChecks[cls]) { | 1313 for (TypeCheck check in typeChecks[cls]) { |
1269 ClassElement cls = check.cls; | 1314 ClassElement cls = check.cls; |
1270 buffer.write('$holder.${namer.operatorIs(cls)}$_=${_}true$N'); | 1315 buffer.write('$holder.${namer.operatorIs(cls)}$_=${_}true$N'); |
1271 Substitution substitution = check.substitution; | 1316 Substitution substitution = check.substitution; |
1272 if (substitution != null) { | 1317 if (substitution != null) { |
1273 String body = substitution.getCode(rti, false); | 1318 String body = substitution.getCode(rti, false); |
1274 buffer.write('$holder.${namer.substitutionName(cls)}$_=${_}$body$N'); | 1319 buffer.write('$holder.${namer.substitutionName(cls)}$_=${_}$body$N'); |
1275 } | 1320 } |
1276 }; | 1321 }; |
1277 } | 1322 } |
| 1323 |
| 1324 checkedNonGenericFunctionTypes.forEach((FunctionType type) { |
| 1325 String encoding = rti.getTypeEncoding(type); |
| 1326 buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); |
| 1327 }); |
| 1328 |
| 1329 checkedGenericFunctionTypes.forEach( |
| 1330 (ClassElement cls, Set<FunctionType> functionTypes) { |
| 1331 for (FunctionType type in functionTypes) { |
| 1332 String encoding = rti.getTypeEncoding(type); |
| 1333 buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); |
| 1334 } |
| 1335 }); |
1278 } | 1336 } |
1279 | 1337 |
1280 /** | 1338 /** |
1281 * Documentation wanted -- johnniwinther | 1339 * Documentation wanted -- johnniwinther |
1282 * | 1340 * |
1283 * Invariant: [classElement] must be a declaration element. | 1341 * Invariant: [classElement] must be a declaration element. |
1284 */ | 1342 */ |
1285 void visitClassFields(ClassElement classElement, | 1343 void visitClassFields(ClassElement classElement, |
1286 void addField(Element member, | 1344 void addField(Element member, |
1287 String name, | 1345 String name, |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1399 } | 1457 } |
1400 | 1458 |
1401 void generateCheckedSetter(Element member, | 1459 void generateCheckedSetter(Element member, |
1402 String fieldName, | 1460 String fieldName, |
1403 String accessorName, | 1461 String accessorName, |
1404 ClassBuilder builder) { | 1462 ClassBuilder builder) { |
1405 assert(canGenerateCheckedSetter(member)); | 1463 assert(canGenerateCheckedSetter(member)); |
1406 DartType type = member.computeType(compiler); | 1464 DartType type = member.computeType(compiler); |
1407 // TODO(ahe): Generate a dynamic type error here. | 1465 // TODO(ahe): Generate a dynamic type error here. |
1408 if (type.element.isErroneous()) return; | 1466 if (type.element.isErroneous()) return; |
1409 FunctionElement helperElement | 1467 CheckedModeHelper helper = |
1410 = backend.getCheckedModeHelper(type, typeCast: false); | 1468 backend.getCheckedModeHelper(type, typeCast: false); |
| 1469 FunctionElement helperElement = helper.getElement(compiler); |
1411 String helperName = namer.isolateAccess(helperElement); | 1470 String helperName = namer.isolateAccess(helperElement); |
1412 List<jsAst.Expression> arguments = <jsAst.Expression>[js('v')]; | 1471 List<jsAst.Expression> arguments = <jsAst.Expression>[js('v')]; |
1413 if (helperElement.computeSignature(compiler).parameterCount != 1) { | 1472 if (helperElement.computeSignature(compiler).parameterCount != 1) { |
1414 arguments.add(js.string(namer.operatorIs(type.element))); | 1473 arguments.add(js.string(namer.operatorIsType(type))); |
1415 } | 1474 } |
1416 | 1475 |
1417 String setterName = namer.setterNameFromAccessorName(accessorName); | 1476 String setterName = namer.setterNameFromAccessorName(accessorName); |
1418 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) | 1477 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) |
1419 ? 'receiver' : 'this'; | 1478 ? 'receiver' : 'this'; |
1420 List<String> args = backend.isInterceptedMethod(member) | 1479 List<String> args = backend.isInterceptedMethod(member) |
1421 ? ['receiver', 'v'] | 1480 ? ['receiver', 'v'] |
1422 : ['v']; | 1481 : ['v']; |
1423 builder.addProperty(setterName, | 1482 builder.addProperty(setterName, |
1424 js.fun(args, | 1483 js.fun(args, |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1617 return arity; | 1676 return arity; |
1618 } | 1677 } |
1619 | 1678 |
1620 int _compareSelectorNames(Selector selector1, Selector selector2) { | 1679 int _compareSelectorNames(Selector selector1, Selector selector2) { |
1621 String name1 = selector1.name.toString(); | 1680 String name1 = selector1.name.toString(); |
1622 String name2 = selector2.name.toString(); | 1681 String name2 = selector2.name.toString(); |
1623 if (name1 != name2) return Comparable.compare(name1, name2); | 1682 if (name1 != name2) return Comparable.compare(name1, name2); |
1624 return _selectorRank(selector1) - _selectorRank(selector2); | 1683 return _selectorRank(selector1) - _selectorRank(selector2); |
1625 } | 1684 } |
1626 | 1685 |
1627 Iterable<Element> getTypedefChecksOn(DartType type) { | 1686 /** |
1628 bool isSubtype(TypedefElement typedef) { | 1687 * Returns a mapping containing all checked function types for which [type] |
1629 FunctionType typedefType = | 1688 * can be a subtype. A function type is mapped to [:true:] if [type] is |
1630 typedef.computeType(compiler).unalias(compiler); | 1689 * statically known to be a subtype of it. |
1631 return compiler.types.isSubtype(type, typedefType); | 1690 */ |
| 1691 // TODO(johnniwinther): Change to return a mapping from function types to |
| 1692 // a set of variable points and use this to detect statically/dynamically |
| 1693 // known subtype relations. |
| 1694 Map<FunctionType, bool> getFunctionTypeChecksOn(DartType type) { |
| 1695 Map<FunctionType, bool> functionTypeMap = |
| 1696 new LinkedHashMap<FunctionType, bool>(); |
| 1697 for (FunctionType functionType in checkedFunctionTypes) { |
| 1698 if (compiler.types.isSubtype(type, functionType)) { |
| 1699 functionTypeMap[functionType] = true; |
| 1700 } else if (compiler.types.isPotentialSubtype(type, functionType)) { |
| 1701 functionTypeMap[functionType] = false; |
| 1702 } |
1632 } | 1703 } |
1633 return checkedTypedefs.where(isSubtype).toList() | 1704 // TODO(johnniwinther): Ensure stable ordering of the keys. |
1634 ..sort(Elements.compareByPosition); | 1705 return functionTypeMap; |
1635 } | 1706 } |
1636 | 1707 |
1637 /** | 1708 /** |
1638 * Generate "is tests" for [cls]: itself, and the "is tests" for the | 1709 * Generate "is tests" for [cls]: itself, and the "is tests" for the |
1639 * classes it implements and type argument substitution functions for these | 1710 * classes it implements and type argument substitution functions for these |
1640 * tests. We don't need to add the "is tests" of the super class because | 1711 * tests. We don't need to add the "is tests" of the super class because |
1641 * they will be inherited at runtime, but we may need to generate the | 1712 * they will be inherited at runtime, but we may need to generate the |
1642 * substitutions, because they may have changed. | 1713 * substitutions, because they may have changed. |
1643 */ | 1714 */ |
1644 void generateIsTestsOn(ClassElement cls, | 1715 void generateIsTestsOn(ClassElement cls, |
1645 void emitIsTest(Element element), | 1716 void emitIsTest(Element element), |
| 1717 void emitIsFunctionTypeTest(FunctionType type), |
| 1718 void emitFunctionTypeSignature(Element method, Function
Type type), |
1646 void emitSubstitution(Element element, {emitNull})) { | 1719 void emitSubstitution(Element element, {emitNull})) { |
1647 if (checkedClasses.contains(cls)) { | 1720 if (checkedClasses.contains(cls)) { |
1648 emitIsTest(cls); | 1721 emitIsTest(cls); |
1649 emitSubstitution(cls); | 1722 emitSubstitution(cls); |
1650 } | 1723 } |
1651 | 1724 |
1652 RuntimeTypes rti = backend.rti; | 1725 RuntimeTypes rti = backend.rti; |
1653 ClassElement superclass = cls.superclass; | 1726 ClassElement superclass = cls.superclass; |
1654 | 1727 |
1655 bool haveSameTypeVariables(ClassElement a, ClassElement b) { | 1728 bool haveSameTypeVariables(ClassElement a, ClassElement b) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1687 } | 1760 } |
1688 } | 1761 } |
1689 void emitNothing(_, {emitNull}) {}; | 1762 void emitNothing(_, {emitNull}) {}; |
1690 emitSubstitution = emitNothing; | 1763 emitSubstitution = emitNothing; |
1691 } | 1764 } |
1692 | 1765 |
1693 Set<Element> generated = new Set<Element>(); | 1766 Set<Element> generated = new Set<Element>(); |
1694 // A class that defines a [:call:] method implicitly implements | 1767 // A class that defines a [:call:] method implicitly implements |
1695 // [Function] and needs checks for all typedefs that are used in is-checks. | 1768 // [Function] and needs checks for all typedefs that are used in is-checks. |
1696 if (checkedClasses.contains(compiler.functionClass) || | 1769 if (checkedClasses.contains(compiler.functionClass) || |
1697 !checkedTypedefs.isEmpty) { | 1770 !checkedFunctionTypes.isEmpty) { |
1698 Element call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); | 1771 Element call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); |
1699 if (call == null) { | 1772 if (call == null) { |
1700 // If [cls] is a closure, it has a synthetic call operator method. | 1773 // If [cls] is a closure, it has a synthetic call operator method. |
1701 call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME); | 1774 call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME); |
1702 } | 1775 } |
1703 if (call != null && call.isFunction()) { | 1776 if (call != null && call.isFunction()) { |
1704 generateInterfacesIsTests(compiler.functionClass, | 1777 generateInterfacesIsTests(compiler.functionClass, |
1705 emitIsTest, | 1778 emitIsTest, |
1706 emitSubstitution, | 1779 emitSubstitution, |
1707 generated); | 1780 generated); |
1708 getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest); | 1781 FunctionType callType = call.computeType(compiler); |
1709 } | 1782 generateFunctionTypeTests(call, callType, |
| 1783 emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| 1784 } |
1710 } | 1785 } |
1711 | 1786 |
1712 for (DartType interfaceType in cls.interfaces) { | 1787 for (DartType interfaceType in cls.interfaces) { |
1713 generateInterfacesIsTests(interfaceType.element, emitIsTest, | 1788 generateInterfacesIsTests(interfaceType.element, emitIsTest, |
1714 emitSubstitution, generated); | 1789 emitSubstitution, generated); |
1715 } | 1790 } |
1716 } | 1791 } |
1717 | 1792 |
1718 /** | 1793 /** |
1719 * Generate "is tests" where [cls] is being implemented. | 1794 * Generate "is tests" where [cls] is being implemented. |
(...skipping 21 matching lines...) Expand all Loading... |
1741 | 1816 |
1742 // We need to also emit "is checks" for the superclass and its supertypes. | 1817 // We need to also emit "is checks" for the superclass and its supertypes. |
1743 ClassElement superclass = cls.superclass; | 1818 ClassElement superclass = cls.superclass; |
1744 if (superclass != null) { | 1819 if (superclass != null) { |
1745 tryEmitTest(superclass); | 1820 tryEmitTest(superclass); |
1746 generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution, | 1821 generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution, |
1747 alreadyGenerated); | 1822 alreadyGenerated); |
1748 } | 1823 } |
1749 } | 1824 } |
1750 | 1825 |
| 1826 void generateFunctionTypeTests( |
| 1827 Element method, |
| 1828 FunctionType methodType, |
| 1829 void emitFunctionTypeSignature(Element method, FunctionType methodType), |
| 1830 void emitIsFunctionTypeTest(FunctionType functionType)) { |
| 1831 Map<FunctionType, bool> functionTypeChecks = |
| 1832 getFunctionTypeChecksOn(methodType); |
| 1833 generateFunctionTypeTestsInternal(method, methodType, functionTypeChecks, |
| 1834 emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| 1835 } |
| 1836 |
| 1837 const int MAX_FUNCTION_TYPE_PREDICATES = 10; |
| 1838 |
| 1839 void generateFunctionTypeTestsInternal( |
| 1840 Element method, |
| 1841 FunctionType methodType, |
| 1842 Map<FunctionType, bool> functionTypeChecks, |
| 1843 void emitFunctionTypeSignature(Element method, FunctionType methodType), |
| 1844 void emitIsFunctionTypeTest(FunctionType functionType)) { |
| 1845 bool hasDynamicFunctionTypeCheck = false; |
| 1846 int neededPredicates = 0; |
| 1847 functionTypeChecks.forEach((FunctionType functionType, bool knownSubtype) { |
| 1848 if (!knownSubtype) { |
| 1849 registerDynamicFunctionTypeCheck(functionType); |
| 1850 hasDynamicFunctionTypeCheck = true; |
| 1851 } else { |
| 1852 neededPredicates++; |
| 1853 } |
| 1854 }); |
| 1855 bool hasSignature = false; |
| 1856 if (hasDynamicFunctionTypeCheck || |
| 1857 neededPredicates > MAX_FUNCTION_TYPE_PREDICATES) { |
| 1858 emitFunctionTypeSignature(method, methodType); |
| 1859 hasSignature = true; |
| 1860 } |
| 1861 functionTypeChecks.forEach((FunctionType functionType, |
| 1862 bool knownSubtype) { |
| 1863 if (knownSubtype || !hasDynamicFunctionTypeCheck) { |
| 1864 if (hasSignature) { |
| 1865 registerDynamicFunctionTypeCheck(functionType); |
| 1866 } else { |
| 1867 emitIsFunctionTypeTest(functionType); |
| 1868 } |
| 1869 } |
| 1870 }); |
| 1871 } |
| 1872 |
1751 /** | 1873 /** |
1752 * Return a function that returns true if its argument is a class | 1874 * Return a function that returns true if its argument is a class |
1753 * that needs to be emitted. | 1875 * that needs to be emitted. |
1754 */ | 1876 */ |
1755 Function computeClassFilter() { | 1877 Function computeClassFilter() { |
1756 Set<ClassElement> unneededClasses = new Set<ClassElement>(); | 1878 Set<ClassElement> unneededClasses = new Set<ClassElement>(); |
1757 // The [Bool] class is not marked as abstract, but has a factory | 1879 // The [Bool] class is not marked as abstract, but has a factory |
1758 // constructor that always throws. We never need to emit it. | 1880 // constructor that always throws. We never need to emit it. |
1759 unneededClasses.add(compiler.boolClass); | 1881 unneededClasses.add(compiler.boolClass); |
1760 | 1882 |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1901 ClassBuilder closureBuilder = new ClassBuilder(); | 2023 ClassBuilder closureBuilder = new ClassBuilder(); |
1902 // If a static function is used as a closure we need to add its name | 2024 // If a static function is used as a closure we need to add its name |
1903 // in case it is used in spawnFunction. | 2025 // in case it is used in spawnFunction. |
1904 String methodName = namer.STATIC_CLOSURE_NAME_NAME; | 2026 String methodName = namer.STATIC_CLOSURE_NAME_NAME; |
1905 emitClosureClassHeader( | 2027 emitClosureClassHeader( |
1906 mangledName, superName, <String>[invocationName, methodName], | 2028 mangledName, superName, <String>[invocationName, methodName], |
1907 closureBuilder); | 2029 closureBuilder); |
1908 | 2030 |
1909 addParameterStubs(callElement, closureBuilder.addProperty); | 2031 addParameterStubs(callElement, closureBuilder.addProperty); |
1910 | 2032 |
1911 DartType type = element.computeType(compiler); | |
1912 getTypedefChecksOn(type).forEach((Element typedef) { | |
1913 String operator = namer.operatorIs(typedef); | |
1914 closureBuilder.addProperty(operator, js('true')); | |
1915 }); | |
1916 | |
1917 // TODO(ngeoffray): Cache common base classes for closures, bound | 2033 // TODO(ngeoffray): Cache common base classes for closures, bound |
1918 // closures, and static closures that have common type checks. | 2034 // closures, and static closures that have common type checks. |
1919 boundClosures.add( | 2035 boundClosures.add( |
1920 js('$classesCollector.$mangledName = #', | 2036 js('$classesCollector.$mangledName = #', |
1921 closureBuilder.toObjectInitializer())); | 2037 closureBuilder.toObjectInitializer())); |
1922 | 2038 |
1923 staticGetters[element] = closureClassElement; | 2039 staticGetters[element] = closureClassElement; |
| 2040 |
| 2041 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| 2042 RuntimeTypes rti = backend.rti; |
| 2043 String encoding = rti.getSignatureEncoding(methodType, () => 'null'); |
| 2044 String operatorSignature = namer.operatorSignature(); |
| 2045 // TODO(johnniwinther): Make MiniJsParser support function expressions. |
| 2046 closureBuilder.addProperty(operatorSignature, |
| 2047 new jsAst.LiteralExpression(encoding)); |
| 2048 } |
| 2049 |
| 2050 void emitIsFunctionTypeTest(FunctionType functionType) { |
| 2051 String operator = namer.operatorIsType(functionType); |
| 2052 closureBuilder.addProperty(operator, js('true')); |
| 2053 } |
| 2054 |
| 2055 generateFunctionTypeTests(element, element.computeType(compiler), |
| 2056 emitFunctionTypeSignature, emitIsFunctionTypeTest); |
1924 } | 2057 } |
1925 } | 2058 } |
1926 | 2059 |
1927 void emitClosureClassHeader(String mangledName, | 2060 void emitClosureClassHeader(String mangledName, |
1928 String superName, | 2061 String superName, |
1929 List<String> fieldNames, | 2062 List<String> fieldNames, |
1930 ClassBuilder builder) { | 2063 ClassBuilder builder) { |
1931 builder.addProperty('', | 2064 builder.addProperty('', |
1932 js.string("$superName;${fieldNames.join(',')}")); | 2065 js.string("$superName;${fieldNames.join(',')}")); |
1933 } | 2066 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1971 if (inInterceptor) { | 2104 if (inInterceptor) { |
1972 cache = interceptorClosureCache; | 2105 cache = interceptorClosureCache; |
1973 } else { | 2106 } else { |
1974 cache = boundClosureCache; | 2107 cache = boundClosureCache; |
1975 } | 2108 } |
1976 List<String> fieldNames = <String>[]; | 2109 List<String> fieldNames = <String>[]; |
1977 compiler.boundClosureClass.forEachInstanceField((_, Element field) { | 2110 compiler.boundClosureClass.forEachInstanceField((_, Element field) { |
1978 fieldNames.add(namer.getName(field)); | 2111 fieldNames.add(namer.getName(field)); |
1979 }); | 2112 }); |
1980 | 2113 |
1981 Iterable<Element> typedefChecks = | 2114 DartType memberType = member.computeType(compiler); |
1982 getTypedefChecksOn(member.computeType(compiler)); | 2115 Map<FunctionType, bool> functionTypeChecks = |
1983 bool hasTypedefChecks = !typedefChecks.isEmpty; | 2116 getFunctionTypeChecksOn(memberType); |
| 2117 bool hasFunctionTypeChecks = !functionTypeChecks.isEmpty; |
1984 | 2118 |
1985 bool canBeShared = !hasOptionalParameters && !hasTypedefChecks; | 2119 bool canBeShared = !hasOptionalParameters && !hasFunctionTypeChecks; |
1986 | 2120 |
| 2121 ClassElement classElement = member.getEnclosingClass(); |
1987 String closureClass = canBeShared ? cache[parameterCount] : null; | 2122 String closureClass = canBeShared ? cache[parameterCount] : null; |
1988 if (closureClass == null) { | 2123 if (closureClass == null) { |
1989 // Either the class was not cached yet, or there are optional parameters. | 2124 // Either the class was not cached yet, or there are optional parameters. |
1990 // Create a new closure class. | 2125 // Create a new closure class. |
1991 String name; | 2126 String name; |
1992 if (canBeShared) { | 2127 if (canBeShared) { |
1993 if (inInterceptor) { | 2128 if (inInterceptor) { |
1994 name = 'BoundClosure\$i${parameterCount}'; | 2129 name = 'BoundClosure\$i${parameterCount}'; |
1995 } else { | 2130 } else { |
1996 name = 'BoundClosure\$${parameterCount}'; | 2131 name = 'BoundClosure\$${parameterCount}'; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2032 arguments.add(js(name)); | 2167 arguments.add(js(name)); |
2033 } | 2168 } |
2034 | 2169 |
2035 jsAst.Expression fun = js.fun( | 2170 jsAst.Expression fun = js.fun( |
2036 parameters, | 2171 parameters, |
2037 js.return_( | 2172 js.return_( |
2038 js('this')[fieldNames[0]][js('this')[fieldNames[1]]](arguments))); | 2173 js('this')[fieldNames[0]][js('this')[fieldNames[1]]](arguments))); |
2039 boundClosureBuilder.addProperty(invocationName, fun); | 2174 boundClosureBuilder.addProperty(invocationName, fun); |
2040 | 2175 |
2041 addParameterStubs(callElement, boundClosureBuilder.addProperty); | 2176 addParameterStubs(callElement, boundClosureBuilder.addProperty); |
2042 typedefChecks.forEach((Element typedef) { | 2177 |
2043 String operator = namer.operatorIs(typedef); | 2178 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
2044 boundClosureBuilder.addProperty(operator, js('true')); | 2179 RuntimeTypes rti = backend.rti; |
2045 }); | 2180 String encoding = rti.getSignatureEncoding( |
| 2181 methodType, () => 'this.${fieldNames[0]}'); |
| 2182 String operatorSignature = namer.operatorSignature(); |
| 2183 boundClosureBuilder.addProperty(operatorSignature, |
| 2184 new jsAst.LiteralExpression(encoding)); |
| 2185 } |
| 2186 |
| 2187 void emitIsFunctionTypeTest(FunctionType functionType) { |
| 2188 String operator = namer.operatorIsType(functionType); |
| 2189 boundClosureBuilder.addProperty(operator, |
| 2190 new jsAst.LiteralBool(true)); |
| 2191 } |
| 2192 |
| 2193 generateFunctionTypeTestsInternal(member, memberType, functionTypeChecks, |
| 2194 emitFunctionTypeSignature, emitIsFunctionTypeTest); |
2046 | 2195 |
2047 boundClosures.add( | 2196 boundClosures.add( |
2048 js('$classesCollector.$mangledName = #', | 2197 js('$classesCollector.$mangledName = #', |
2049 boundClosureBuilder.toObjectInitializer())); | 2198 boundClosureBuilder.toObjectInitializer())); |
2050 | 2199 |
2051 closureClass = namer.isolateAccess(closureClassElement); | 2200 closureClass = namer.isolateAccess(closureClassElement); |
2052 | 2201 |
2053 // Cache it. | 2202 // Cache it. |
2054 if (canBeShared) { | 2203 if (canBeShared) { |
2055 cache[parameterCount] = closureClass; | 2204 cache[parameterCount] = closureClass; |
(...skipping 10 matching lines...) Expand all Loading... |
2066 arguments.add(js.string(targetName)); | 2215 arguments.add(js.string(targetName)); |
2067 if (inInterceptor) { | 2216 if (inInterceptor) { |
2068 String receiverArg = fieldNames[2]; | 2217 String receiverArg = fieldNames[2]; |
2069 parameters.add(receiverArg); | 2218 parameters.add(receiverArg); |
2070 arguments.add(js(receiverArg)); | 2219 arguments.add(js(receiverArg)); |
2071 } else { | 2220 } else { |
2072 // Put null in the intercepted receiver field. | 2221 // Put null in the intercepted receiver field. |
2073 arguments.add(new jsAst.LiteralNull()); | 2222 arguments.add(new jsAst.LiteralNull()); |
2074 } | 2223 } |
2075 | 2224 |
| 2225 jsAst.Expression newClosure = js(closureClass).newWith(arguments); |
2076 jsAst.Expression getterFunction = js.fun( | 2226 jsAst.Expression getterFunction = js.fun( |
2077 parameters, | 2227 parameters, js.return_(newClosure)); |
2078 js.return_(js(closureClass).newWith(arguments))); | |
2079 | 2228 |
2080 defineStub(getterName, getterFunction); | 2229 defineStub(getterName, getterFunction); |
2081 } | 2230 } |
2082 | 2231 |
2083 /** | 2232 /** |
2084 * Documentation wanted -- johnniwinther | 2233 * Documentation wanted -- johnniwinther |
2085 * | 2234 * |
2086 * Invariant: [member] must be a declaration element. | 2235 * Invariant: [member] must be a declaration element. |
2087 */ | 2236 */ |
2088 void emitCallStubForGetter(Element member, | 2237 void emitCallStubForGetter(Element member, |
(...skipping 19 matching lines...) Expand all Loading... |
2108 ? member.fixedBackendName() | 2257 ? member.fixedBackendName() |
2109 : namer.instanceFieldName(member); | 2258 : namer.instanceFieldName(member); |
2110 return js('this')[fieldName]; | 2259 return js('this')[fieldName]; |
2111 } | 2260 } |
2112 } | 2261 } |
2113 | 2262 |
2114 // Two selectors may match but differ only in type. To avoid generating | 2263 // Two selectors may match but differ only in type. To avoid generating |
2115 // identical stubs for each we track untyped selectors which already have | 2264 // identical stubs for each we track untyped selectors which already have |
2116 // stubs. | 2265 // stubs. |
2117 Set<Selector> generatedSelectors = new Set<Selector>(); | 2266 Set<Selector> generatedSelectors = new Set<Selector>(); |
2118 | 2267 DartType memberType = member.computeType(compiler); |
2119 for (Selector selector in selectors) { | 2268 for (Selector selector in selectors) { |
2120 if (selector.applies(member, compiler)) { | 2269 if (selector.applies(member, compiler)) { |
2121 selector = selector.asUntyped; | 2270 selector = selector.asUntyped; |
2122 if (generatedSelectors.contains(selector)) continue; | 2271 if (generatedSelectors.contains(selector)) continue; |
2123 generatedSelectors.add(selector); | 2272 generatedSelectors.add(selector); |
2124 | 2273 |
2125 String invocationName = namer.invocationName(selector); | 2274 String invocationName = namer.invocationName(selector); |
2126 Selector callSelector = new Selector.callClosureFrom(selector); | 2275 Selector callSelector = new Selector.callClosureFrom(selector); |
2127 String closureCallName = namer.invocationName(callSelector); | 2276 String closureCallName = namer.invocationName(callSelector); |
2128 | 2277 |
(...skipping 1191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3320 | 3469 |
3321 const String HOOKS_API_USAGE = """ | 3470 const String HOOKS_API_USAGE = """ |
3322 // The code supports the following hooks: | 3471 // The code supports the following hooks: |
3323 // dartPrint(message) - if this function is defined it is called | 3472 // dartPrint(message) - if this function is defined it is called |
3324 // instead of the Dart [print] method. | 3473 // instead of the Dart [print] method. |
3325 // dartMainRunner(main) - if this function is defined, the Dart [main] | 3474 // dartMainRunner(main) - if this function is defined, the Dart [main] |
3326 // method will not be invoked directly. | 3475 // method will not be invoked directly. |
3327 // Instead, a closure that will invoke [main] is | 3476 // Instead, a closure that will invoke [main] is |
3328 // passed to [dartMainRunner]. | 3477 // passed to [dartMainRunner]. |
3329 """; | 3478 """; |
OLD | NEW |