| 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 |