Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 12334070: Support runtime check of function types. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: New check encoding Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 /** 99 /**
100 * Raw ClassElement symbols occuring in is-checks and type assertions. If the 100 * Raw ClassElement symbols occuring in is-checks and type assertions. If the
101 * program contains parameterized checks `x is Set<int>` and 101 * program contains parameterized checks `x is Set<int>` and
102 * `x is Set<String>` then the ClassElement `Set` will occur once in 102 * `x is Set<String>` then the ClassElement `Set` will occur once in
103 * [checkedClasses]. 103 * [checkedClasses].
104 */ 104 */
105 Set<ClassElement> checkedClasses; 105 Set<ClassElement> checkedClasses;
106 106
107 /** 107 /**
108 * Raw Typedef symbols occuring in is-checks and type assertions. If the 108 * Raw Typedef symbols occuring in is-checks and type assertions. If the
109 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement 109 * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement
karlklose 2013/03/22 13:17:46 Please update comment and change to use [::] inste
Johnni Winther 2013/06/21 12:19:14 Done.
110 * `F` will occur once in [checkedTypedefs]. 110 * `F` will occur once in [checkedTypedefs].
111 */ 111 */
112 Set<TypedefElement> checkedTypedefs; 112 Set<FunctionType> checkedFunctionTypes;
113
114 Map<ClassElement, Set<FunctionType>> checkedGenericFunctionTypes
115 = new Map<ClassElement, Set<FunctionType>>();
karlklose 2013/03/22 13:17:46 Break after '='.
Johnni Winther 2013/06/21 12:19:14 Done.
116
117 Set<FunctionType> checkedNonGenericFunctionTypes
karlklose 2013/03/22 13:17:46 Ditto.
Johnni Winther 2013/06/21 12:19:14 Done.
118 = new Set<FunctionType>();
119
120 void registerDynamicFunctionTypeCheck(FunctionType functionType) {
121 ClassElement classElement = Types.getClassContext(functionType);
122 if (classElement != null) {
123 checkedGenericFunctionTypes.putIfAbsent(classElement,
124 () => new Set<FunctionType>()).add(functionType);
125 } else {
126 checkedNonGenericFunctionTypes.add(functionType);
127 }
128 }
113 129
114 final bool generateSourceMap; 130 final bool generateSourceMap;
115 131
116 Iterable<ClassElement> cachedClassesUsingTypeVariableTests; 132 Iterable<ClassElement> cachedClassesUsingTypeVariableTests;
117 133
118 Iterable<ClassElement> get classesUsingTypeVariableTests { 134 Iterable<ClassElement> get classesUsingTypeVariableTests {
119 if (cachedClassesUsingTypeVariableTests == null) { 135 if (cachedClassesUsingTypeVariableTests == null) {
120 cachedClassesUsingTypeVariableTests = compiler.codegenWorld.isChecks 136 cachedClassesUsingTypeVariableTests = compiler.codegenWorld.isChecks
121 .where((DartType t) => t is TypeVariableType) 137 .where((DartType t) => t is TypeVariableType)
122 .map((TypeVariableType v) => v.element.getEnclosingClass()) 138 .map((TypeVariableType v) => v.element.getEnclosingClass())
(...skipping 10 matching lines...) Expand all
133 constantEmitter = new ConstantEmitter(compiler, namer), 149 constantEmitter = new ConstantEmitter(compiler, namer),
134 super(compiler) { 150 super(compiler) {
135 nativeEmitter = new NativeEmitter(this); 151 nativeEmitter = new NativeEmitter(this);
136 } 152 }
137 153
138 void addComment(String comment, CodeBuffer buffer) { 154 void addComment(String comment, CodeBuffer buffer) {
139 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); 155 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler));
140 } 156 }
141 157
142 void computeRequiredTypeChecks() { 158 void computeRequiredTypeChecks() {
143 assert(checkedClasses == null && checkedTypedefs == null); 159 assert(checkedClasses == null && checkedFunctionTypes == null);
144 160
145 compiler.codegenWorld.addImplicitChecks(classesUsingTypeVariableTests); 161 compiler.codegenWorld.addImplicitChecks(classesUsingTypeVariableTests);
146 162
147 checkedClasses = new Set<ClassElement>(); 163 checkedClasses = new Set<ClassElement>();
148 checkedTypedefs = new Set<TypedefElement>(); 164 checkedFunctionTypes = new Set<FunctionType>();
149 compiler.codegenWorld.isChecks.forEach((DartType t) { 165 compiler.codegenWorld.isChecks.forEach((DartType t) {
150 if (t is InterfaceType) { 166 if (t is InterfaceType) {
151 checkedClasses.add(t.element); 167 checkedClasses.add(t.element);
152 } else if (t is TypedefType) { 168 } else if (t is FunctionType) {
153 checkedTypedefs.add(t.element); 169 checkedFunctionTypes.add(t);
154 } 170 }
155 }); 171 });
156 } 172 }
157 173
158 jsAst.Expression constantReference(Constant value) { 174 jsAst.Expression constantReference(Constant value) {
159 return constantEmitter.reference(value); 175 return constantEmitter.reference(value);
160 } 176 }
161 177
162 jsAst.Expression constantInitializerExpression(Constant value) { 178 jsAst.Expression constantInitializerExpression(Constant value) {
163 return constantEmitter.initializationExpression(value); 179 return constantEmitter.initializationExpression(value);
(...skipping 1033 matching lines...) Expand 10 before | Expand all | Expand 10 after
1197 return; 1213 return;
1198 } 1214 }
1199 if (nativeEmitter.requiresNativeIsCheck(other)) { 1215 if (nativeEmitter.requiresNativeIsCheck(other)) {
1200 code = js.fun([], [js.return_(true)]); 1216 code = js.fun([], [js.return_(true)]);
1201 } else { 1217 } else {
1202 code = new jsAst.LiteralBool(true); 1218 code = new jsAst.LiteralBool(true);
1203 } 1219 }
1204 builder.addProperty(namer.operatorIs(other), code); 1220 builder.addProperty(namer.operatorIs(other), code);
1205 } 1221 }
1206 1222
1223 void generateIsFunctionTypeTest(FunctionType type) {
1224 String operator = namer.operatorIsType(type);
1225 builder.addProperty(operator, new jsAst.LiteralBool(true));
1226 }
1227
1228 void generateFunctionTypeSignature(Element method, FunctionType type) {
1229 assert(method.isImplementation);
1230 String thisAccess = 'this';
1231 Node node = method.parseNode(compiler);
1232 ClosureClassMap closureData =
1233 compiler.closureToClassMapper.closureMappingCache[node];
1234 if (closureData != null) {
1235 Element thisElement =
1236 closureData.freeVariableMapping[closureData.thisElement];
1237 if (thisElement != null) {
1238 String thisName = backend.namer.getName(thisElement);
1239 thisAccess = 'this.$thisName';
1240 }
1241 }
1242 RuntimeTypes rti = backend.rti;
1243 String encoding = rti.getSignatureEncoding(type, () => '$thisAccess');
1244 String operatorSignature = namer.operatorSignature();
1245 builder.addProperty(operatorSignature,
1246 new jsAst.LiteralExpression(encoding));
1247 }
1248
1207 void generateSubstitution(Element other, {bool emitNull: false}) { 1249 void generateSubstitution(Element other, {bool emitNull: false}) {
1208 RuntimeTypes rti = backend.rti; 1250 RuntimeTypes rti = backend.rti;
1209 // TODO(karlklose): support typedefs with variables. 1251 // TODO(karlklose): support typedefs with variables.
1210 jsAst.Expression expression; 1252 jsAst.Expression expression;
1211 bool needsNativeCheck = nativeEmitter.requiresNativeIsCheck(other); 1253 bool needsNativeCheck = nativeEmitter.requiresNativeIsCheck(other);
1212 if (other.kind == ElementKind.CLASS) { 1254 if (other.kind == ElementKind.CLASS) {
1213 String substitution = rti.getSupertypeSubstitution(classElement, other, 1255 String substitution = rti.getSupertypeSubstitution(classElement, other,
1214 alwaysGenerateFunction: true); 1256 alwaysGenerateFunction: true);
1215 if (substitution != null) { 1257 if (substitution != null) {
1216 expression = new jsAst.LiteralExpression(substitution); 1258 expression = new jsAst.LiteralExpression(substitution);
1217 } else if (emitNull || needsNativeCheck) { 1259 } else if (emitNull || needsNativeCheck) {
1218 expression = new jsAst.LiteralNull(); 1260 expression = new jsAst.LiteralNull();
1219 } 1261 }
1220 } 1262 }
1221 if (expression != null) { 1263 if (expression != null) {
1222 if (needsNativeCheck) { 1264 if (needsNativeCheck) {
1223 expression = js.fun([], js.return_(expression)); 1265 expression = js.fun([], js.return_(expression));
1224 } 1266 }
1225 builder.addProperty(namer.substitutionName(other), expression); 1267 builder.addProperty(namer.substitutionName(other), expression);
1226 } 1268 }
1227 } 1269 }
1228 1270
1229 generateIsTestsOn(classElement, generateIsTest, generateSubstitution); 1271 generateIsTestsOn(classElement, generateIsTest,
1272 generateIsFunctionTypeTest, generateFunctionTypeSignature,
1273 generateSubstitution);
1230 1274
1231 if (identical(classElement, compiler.objectClass) 1275 if (identical(classElement, compiler.objectClass)
1232 && compiler.enabledNoSuchMethod) { 1276 && compiler.enabledNoSuchMethod) {
1233 // Emit the noSuchMethod handlers on the Object prototype now, 1277 // Emit the noSuchMethod handlers on the Object prototype now,
1234 // so that the code in the dynamicFunction helper can find 1278 // so that the code in the dynamicFunction helper can find
1235 // them. Note that this helper is invoked before analyzing the 1279 // them. Note that this helper is invoked before analyzing the
1236 // full JS script. 1280 // full JS script.
1237 if (!nativeEmitter.handleNoSuchMethod) { 1281 if (!nativeEmitter.handleNoSuchMethod) {
1238 emitNoSuchMethodHandlers(builder.addProperty); 1282 emitNoSuchMethodHandlers(builder.addProperty);
1239 } 1283 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1273 } 1317 }
1274 1318
1275 // Add checks to the constructors of instantiated classes or to the created 1319 // Add checks to the constructors of instantiated classes or to the created
1276 // holder object. 1320 // holder object.
1277 for (ClassElement cls in typeChecks) { 1321 for (ClassElement cls in typeChecks) {
1278 String holder = namer.isolateAccess(cls); 1322 String holder = namer.isolateAccess(cls);
1279 for (ClassElement check in typeChecks[cls]) { 1323 for (ClassElement check in typeChecks[cls]) {
1280 buffer.write('$holder.${namer.operatorIs(check)}$_=${_}true$N'); 1324 buffer.write('$holder.${namer.operatorIs(check)}$_=${_}true$N');
1281 String body = rti.getSupertypeSubstitution(cls, check); 1325 String body = rti.getSupertypeSubstitution(cls, check);
1282 if (body != null) { 1326 if (body != null) {
1283 buffer.write('$holder.${namer.substitutionName(check)}$_=${_}$body$N') ; 1327 buffer.write('$holder.${namer.substitutionName(check)}$_=${_}$body$N') ;
karlklose 2013/03/22 13:17:46 Long line.
Johnni Winther 2013/06/21 12:19:14 Stale.
1284 } 1328 }
1285 }; 1329 };
1286 } 1330 }
1331
1332 checkedNonGenericFunctionTypes.forEach((FunctionType type) {
1333 String encoding = rti.getTypeEncoding(type);
1334 buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N');
1335 });
1336
1337 checkedGenericFunctionTypes.forEach(
1338 (ClassElement cls, Set<FunctionType> functionTypes) {
1339 for (FunctionType type in functionTypes) {
karlklose 2013/03/22 13:17:46 I would indent the body of the function by two spa
Johnni Winther 2013/06/21 12:19:14 Stale.
1340 ClassElement contextClass = Types.getClassContext(type);
1341 if (contextClass != null) {
1342 maybeGenerateHolder(contextClass);
1343 }
1344 String encoding = rti.getTypeEncoding(type);
1345 buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N');
1346 }
1347 });
1287 } 1348 }
1288 1349
1289 void visitNativeMixins(ClassElement classElement, 1350 void visitNativeMixins(ClassElement classElement,
1290 void visit(MixinApplicationElement mixinApplication)) { 1351 void visit(MixinApplicationElement mixinApplication)) {
1291 if (!classElement.isNative()) return; 1352 if (!classElement.isNative()) return;
1292 // Use recursion to make sure to visit the superclasses before the 1353 // Use recursion to make sure to visit the superclasses before the
1293 // subclasses. Once we start keeping track of the emitted fields 1354 // subclasses. Once we start keeping track of the emitted fields
1294 // and members, we're going to want to visit these in the other 1355 // and members, we're going to want to visit these in the other
1295 // order so we get the most specialized definition first. 1356 // order so we get the most specialized definition first.
1296 void recurse(ClassElement cls) { 1357 void recurse(ClassElement cls) {
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
1439 ClassBuilder builder) { 1500 ClassBuilder builder) {
1440 assert(canGenerateCheckedSetter(member)); 1501 assert(canGenerateCheckedSetter(member));
1441 DartType type = member.computeType(compiler); 1502 DartType type = member.computeType(compiler);
1442 // TODO(ahe): Generate a dynamic type error here. 1503 // TODO(ahe): Generate a dynamic type error here.
1443 if (type.element.isErroneous()) return; 1504 if (type.element.isErroneous()) return;
1444 FunctionElement helperElement 1505 FunctionElement helperElement
1445 = backend.getCheckedModeHelper(type, typeCast: false); 1506 = backend.getCheckedModeHelper(type, typeCast: false);
1446 String helperName = namer.isolateAccess(helperElement); 1507 String helperName = namer.isolateAccess(helperElement);
1447 List<jsAst.Expression> arguments = <jsAst.Expression>[js['v']]; 1508 List<jsAst.Expression> arguments = <jsAst.Expression>[js['v']];
1448 if (helperElement.computeSignature(compiler).parameterCount != 1) { 1509 if (helperElement.computeSignature(compiler).parameterCount != 1) {
1449 arguments.add(js.string(namer.operatorIs(type.element))); 1510 arguments.add(js.string(namer.operatorIsType(type)));
1450 } 1511 }
1451 1512
1452 String setterName = namer.setterNameFromAccessorName(accessorName); 1513 String setterName = namer.setterNameFromAccessorName(accessorName);
1453 String receiver = backend.isInterceptorClass(member.getEnclosingClass()) 1514 String receiver = backend.isInterceptorClass(member.getEnclosingClass())
1454 ? 'receiver' : 'this'; 1515 ? 'receiver' : 'this';
1455 List<String> args = backend.isInterceptedMethod(member) 1516 List<String> args = backend.isInterceptedMethod(member)
1456 ? ['receiver', 'v'] 1517 ? ['receiver', 'v']
1457 : ['v']; 1518 : ['v'];
1458 builder.addProperty(setterName, 1519 builder.addProperty(setterName,
1459 js.fun(args, 1520 js.fun(args,
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
1616 return arity; 1677 return arity;
1617 } 1678 }
1618 1679
1619 int _compareSelectorNames(Selector selector1, Selector selector2) { 1680 int _compareSelectorNames(Selector selector1, Selector selector2) {
1620 String name1 = selector1.name.toString(); 1681 String name1 = selector1.name.toString();
1621 String name2 = selector2.name.toString(); 1682 String name2 = selector2.name.toString();
1622 if (name1 != name2) return Comparable.compare(name1, name2); 1683 if (name1 != name2) return Comparable.compare(name1, name2);
1623 return _selectorRank(selector1) - _selectorRank(selector2); 1684 return _selectorRank(selector1) - _selectorRank(selector2);
1624 } 1685 }
1625 1686
1626 Iterable<Element> getTypedefChecksOn(DartType type) { 1687 /**
1627 bool isSubtype(TypedefElement typedef) { 1688 * Returns a mapping containing all checked function types for which [type]
1628 FunctionType typedefType = 1689 * can be a subtype. A function type is mapped to [:true:] if [type] is
1629 typedef.computeType(compiler).unalias(compiler); 1690 * statically known to be a subtype of it.
karlklose 2013/03/22 13:17:46 Explain what being false means.
Johnni Winther 2013/06/21 12:19:14 Done.
1630 return compiler.types.isSubtype(type, typedefType); 1691 */
1692 // TODO(johnniwinther): Change to return a mapping from function types to
1693 // a set of variable points and use this to detect statically/dynamically
1694 // known subtype relations.
1695 Map<FunctionType, bool> getFunctionTypeChecksOn(DartType type) {
1696 Map<FunctionType, bool> functionTypeMap =
1697 new LinkedHashMap<FunctionType, bool>();
1698 for (FunctionType functionType in checkedFunctionTypes) {
1699 if (compiler.types.isSubtype(type, functionType)) {
1700 functionTypeMap[functionType] = true;
1701 } else if (compiler.types.isPotentialSubtype(type, functionType)) {
1702 functionTypeMap[functionType] = false;
1703 }
1631 } 1704 }
1632 return checkedTypedefs.where(isSubtype).toList() 1705 // TODO(johnniwinther): Ensure stable ordering of the keys.
1633 ..sort(Elements.compareByPosition); 1706 return functionTypeMap;
1634 } 1707 }
1635 1708
1636 /** 1709 /**
1637 * Generate "is tests" for [cls]: itself, and the "is tests" for the 1710 * Generate "is tests" for [cls]: itself, and the "is tests" for the
1638 * classes it implements and type argument substitution functions for these 1711 * classes it implements and type argument substitution functions for these
1639 * tests. We don't need to add the "is tests" of the super class because 1712 * tests. We don't need to add the "is tests" of the super class because
1640 * they will be inherited at runtime, but we may need to generate the 1713 * they will be inherited at runtime, but we may need to generate the
1641 * substitutions, because they may have changed. 1714 * substitutions, because they may have changed.
1642 */ 1715 */
1643 void generateIsTestsOn(ClassElement cls, 1716 void generateIsTestsOn(ClassElement cls,
1644 void emitIsTest(Element element), 1717 void emitIsTest(Element element),
1718 void emitIsFunctionTypeTest(FunctionType type),
1719 void emitFunctionTypeSignature(Element method, Function Type type),
karlklose 2013/03/22 13:17:46 Long line.
Johnni Winther 2013/06/21 12:19:14 Done.
1645 void emitSubstitution(Element element, {emitNull})) { 1720 void emitSubstitution(Element element, {emitNull})) {
1646 if (checkedClasses.contains(cls)) { 1721 if (checkedClasses.contains(cls)) {
1647 emitIsTest(cls); 1722 emitIsTest(cls);
1648 emitSubstitution(cls); 1723 emitSubstitution(cls);
1649 } 1724 }
1650 1725
1651 RuntimeTypes rti = backend.rti; 1726 RuntimeTypes rti = backend.rti;
1652 ClassElement superclass = cls.superclass; 1727 ClassElement superclass = cls.superclass;
1653 1728
1654 bool haveSameTypeVariables(ClassElement a, ClassElement b) { 1729 bool haveSameTypeVariables(ClassElement a, ClassElement b) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1686 } 1761 }
1687 } 1762 }
1688 void emitNothing(_, {emitNull}) {}; 1763 void emitNothing(_, {emitNull}) {};
1689 emitSubstitution = emitNothing; 1764 emitSubstitution = emitNothing;
1690 } 1765 }
1691 1766
1692 Set<Element> generated = new Set<Element>(); 1767 Set<Element> generated = new Set<Element>();
1693 // A class that defines a [:call:] method implicitly implements 1768 // A class that defines a [:call:] method implicitly implements
1694 // [Function] and needs checks for all typedefs that are used in is-checks. 1769 // [Function] and needs checks for all typedefs that are used in is-checks.
1695 if (checkedClasses.contains(compiler.functionClass) || 1770 if (checkedClasses.contains(compiler.functionClass) ||
1696 !checkedTypedefs.isEmpty) { 1771 !checkedFunctionTypes.isEmpty) {
1697 FunctionElement call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); 1772 FunctionElement call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME);
1698 if (call == null) { 1773 if (call == null) {
1699 // If [cls] is a closure, it has a synthetic call operator method. 1774 // If [cls] is a closure, it has a synthetic call operator method.
1700 call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME); 1775 call = cls.lookupBackendMember(Compiler.CALL_OPERATOR_NAME);
1701 } 1776 }
1702 if (call != null) { 1777 if (call != null) {
1703 generateInterfacesIsTests(compiler.functionClass, 1778 generateInterfacesIsTests(compiler.functionClass,
1704 emitIsTest, 1779 emitIsTest,
1705 emitSubstitution, 1780 emitSubstitution,
1706 generated); 1781 generated);
1707 getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest); 1782 FunctionType callType = call.computeType(compiler);
1708 } 1783 generateFunctionTypeTests(call, callType,
1784 emitFunctionTypeSignature, emitIsFunctionTypeTest);
1785 }
1709 } 1786 }
1710 1787
1711 for (DartType interfaceType in cls.interfaces) { 1788 for (DartType interfaceType in cls.interfaces) {
1712 generateInterfacesIsTests(interfaceType.element, emitIsTest, 1789 generateInterfacesIsTests(interfaceType.element, emitIsTest,
1713 emitSubstitution, generated); 1790 emitSubstitution, generated);
1714 } 1791 }
1715 1792
1716 // For native classes, we also have to run through their mixin 1793 // For native classes, we also have to run through their mixin
1717 // applications and make sure we deal with 'is' tests correctly 1794 // applications and make sure we deal with 'is' tests correctly
1718 // for those. 1795 // for those.
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1751 1828
1752 // We need to also emit "is checks" for the superclass and its supertypes. 1829 // We need to also emit "is checks" for the superclass and its supertypes.
1753 ClassElement superclass = cls.superclass; 1830 ClassElement superclass = cls.superclass;
1754 if (superclass != null) { 1831 if (superclass != null) {
1755 tryEmitTest(superclass); 1832 tryEmitTest(superclass);
1756 generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution, 1833 generateInterfacesIsTests(superclass, emitIsTest, emitSubstitution,
1757 alreadyGenerated); 1834 alreadyGenerated);
1758 } 1835 }
1759 } 1836 }
1760 1837
1838 void generateFunctionTypeTests(
1839 Element method,
1840 FunctionType methodType,
1841 void emitFunctionTypeSignature(Element method, FunctionType methodType),
1842 void emitIsFunctionTypeTest(FunctionType functionType)) {
1843 Map<FunctionType, bool> functionTypeChecks =
1844 getFunctionTypeChecksOn(methodType);
1845 generateFunctionTypeTestsInternal(method, methodType, functionTypeChecks,
1846 emitFunctionTypeSignature, emitIsFunctionTypeTest);
1847 }
1848
1849 const int MAX_FUNCTION_TYPE_PREDICATES = 10;
1850
1851 void generateFunctionTypeTestsInternal(
1852 Element method,
1853 FunctionType methodType,
1854 Map<FunctionType, bool> functionTypeChecks,
1855 void emitFunctionTypeSignature(Element method, FunctionType methodType),
1856 void emitIsFunctionTypeTest(FunctionType functionType)) {
1857 bool hasDynamicFunctionTypeCheck = false;
1858 int neededPredicates = 0;
1859 functionTypeChecks.forEach((FunctionType functionType,
1860 bool knownSubtype) {
karlklose 2013/03/22 13:17:46 Align with '(FunctionType...'.
Johnni Winther 2013/06/21 12:19:14 Done.
1861 if (!knownSubtype) {
1862 registerDynamicFunctionTypeCheck(functionType);
karlklose 2013/03/22 13:17:46 Why are we registering it in the emitter. Shouldn'
Johnni Winther 2013/06/21 12:19:14 Which function type check that are dynamic depends
1863 hasDynamicFunctionTypeCheck = true;
1864 } else {
1865 neededPredicates++;
1866 }
1867 });
1868 bool hasSignature = false;
karlklose 2013/03/22 13:17:46 'signatureEmitted'?
Johnni Winther 2013/06/21 12:19:14 Done.
1869 if (hasDynamicFunctionTypeCheck ||
1870 neededPredicates > MAX_FUNCTION_TYPE_PREDICATES) {
1871 emitFunctionTypeSignature(method, methodType);
1872 hasSignature = true;
1873 }
1874 functionTypeChecks.forEach((FunctionType functionType,
1875 bool knownSubtype) {
karlklose 2013/03/22 13:17:46 Align.
Johnni Winther 2013/06/21 12:19:14 Done.
1876 if (knownSubtype || !hasDynamicFunctionTypeCheck) {
1877 if (hasSignature) {
1878 registerDynamicFunctionTypeCheck(functionType);
1879 } else {
1880 emitIsFunctionTypeTest(functionType);
1881 }
1882 }
1883 });
1884 }
1885
1761 /** 1886 /**
1762 * Return a function that returns true if its argument is a class 1887 * Return a function that returns true if its argument is a class
1763 * that needs to be emitted. 1888 * that needs to be emitted.
1764 */ 1889 */
1765 Function computeClassFilter() { 1890 Function computeClassFilter() {
1766 Set<ClassElement> unneededClasses = new Set<ClassElement>(); 1891 Set<ClassElement> unneededClasses = new Set<ClassElement>();
1767 // The [Bool] class is not marked as abstract, but has a factory 1892 // The [Bool] class is not marked as abstract, but has a factory
1768 // constructor that always throws. We never need to emit it. 1893 // constructor that always throws. We never need to emit it.
1769 unneededClasses.add(compiler.boolClass); 1894 unneededClasses.add(compiler.boolClass);
1770 1895
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
1880 jsAst.Expression assignment = 2005 jsAst.Expression assignment =
1881 js[isolateProperties][staticName][name].assign(value); 2006 js[isolateProperties][staticName][name].assign(value);
1882 buffer.write(jsAst.prettyPrint(assignment.toStatement(), compiler)); 2007 buffer.write(jsAst.prettyPrint(assignment.toStatement(), compiler));
1883 buffer.write('$N'); 2008 buffer.write('$N');
1884 }); 2009 });
1885 2010
1886 // If a static function is used as a closure we need to add its name 2011 // If a static function is used as a closure we need to add its name
1887 // in case it is used in spawnFunction. 2012 // in case it is used in spawnFunction.
1888 String fieldName = namer.STATIC_CLOSURE_NAME_NAME; 2013 String fieldName = namer.STATIC_CLOSURE_NAME_NAME;
1889 buffer.write('$fieldAccess.$fieldName$_=$_"$staticName"$N'); 2014 buffer.write('$fieldAccess.$fieldName$_=$_"$staticName"$N');
1890 getTypedefChecksOn(element.computeType(compiler)).forEach( 2015
1891 (Element typedef) { 2016 void emitFunctionTypeSignature(Element method, FunctionType methodType) {
1892 String operator = namer.operatorIs(typedef); 2017 RuntimeTypes rti = backend.rti;
1893 buffer.write('$fieldAccess.$operator$_=${_}true$N'); 2018 String encoding = rti.getSignatureEncoding(methodType, () => 'null');
karlklose 2013/03/22 13:17:46 Please explain what null means here.
Johnni Winther 2013/06/21 12:19:14 Done.
1894 } 2019 String operatorSignature = namer.operatorSignature();
1895 ); 2020 buffer.add('$fieldAccess.$operatorSignature$_=${_}$encoding$N');
2021 }
2022
2023 void emitIsFunctionTypeTest(FunctionType functionType) {
2024 String operator = namer.operatorIsType(functionType);
2025 buffer.write('$fieldAccess.$operator$_=${_}true$N');
2026 }
2027
2028 generateFunctionTypeTests(element, element.computeType(compiler),
2029 emitFunctionTypeSignature, emitIsFunctionTypeTest);
1896 } 2030 }
1897 } 2031 }
1898 2032
1899 void emitBoundClosureClassHeader(String mangledName, 2033 void emitBoundClosureClassHeader(String mangledName,
1900 String superName, 2034 String superName,
1901 List<String> fieldNames, 2035 List<String> fieldNames,
1902 ClassBuilder builder) { 2036 ClassBuilder builder) {
1903 builder.addProperty('', 2037 builder.addProperty('',
1904 js.string("$superName;${fieldNames.join(',')}")); 2038 js.string("$superName;${fieldNames.join(',')}"));
1905 } 2039 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1946 extraArg = 'receiver'; 2080 extraArg = 'receiver';
1947 } else { 2081 } else {
1948 cache = boundClosureCache; 2082 cache = boundClosureCache;
1949 } 2083 }
1950 List<String> fieldNames = compiler.enableMinification 2084 List<String> fieldNames = compiler.enableMinification
1951 ? inInterceptor ? const ['a', 'b', 'c'] 2085 ? inInterceptor ? const ['a', 'b', 'c']
1952 : const ['a', 'b'] 2086 : const ['a', 'b']
1953 : inInterceptor ? const ['self', 'target', 'receiver'] 2087 : inInterceptor ? const ['self', 'target', 'receiver']
1954 : const ['self', 'target']; 2088 : const ['self', 'target'];
1955 2089
1956 Iterable<Element> typedefChecks = 2090 DartType memberType = member.computeType(compiler);
1957 getTypedefChecksOn(member.computeType(compiler)); 2091 Map<FunctionType, bool> functionTypeChecks =
1958 bool hasTypedefChecks = !typedefChecks.isEmpty; 2092 getFunctionTypeChecksOn(memberType);
2093 bool hasFunctionTypeChecks = !functionTypeChecks.isEmpty;
1959 2094
1960 bool canBeShared = !hasOptionalParameters && !hasTypedefChecks; 2095 bool canBeShared = !hasOptionalParameters && !hasFunctionTypeChecks;
1961 2096
2097 ClassElement classElement = member.getEnclosingClass();
1962 String closureClass = canBeShared ? cache[parameterCount] : null; 2098 String closureClass = canBeShared ? cache[parameterCount] : null;
1963 if (closureClass == null) { 2099 if (closureClass == null) {
1964 // Either the class was not cached yet, or there are optional parameters. 2100 // Either the class was not cached yet, or there are optional parameters.
1965 // Create a new closure class. 2101 // Create a new closure class.
1966 String name; 2102 String name;
1967 if (canBeShared) { 2103 if (canBeShared) {
1968 if (inInterceptor) { 2104 if (inInterceptor) {
1969 name = 'BoundClosure\$i${parameterCount}'; 2105 name = 'BoundClosure\$i${parameterCount}';
1970 } else { 2106 } else {
1971 name = 'BoundClosure\$${parameterCount}'; 2107 name = 'BoundClosure\$${parameterCount}';
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
2008 arguments.add(js[name]); 2144 arguments.add(js[name]);
2009 } 2145 }
2010 2146
2011 jsAst.Expression fun = js.fun( 2147 jsAst.Expression fun = js.fun(
2012 parameters, 2148 parameters,
2013 js.return_( 2149 js.return_(
2014 js['this'][fieldNames[0]][js['this'][fieldNames[1]]](arguments))); 2150 js['this'][fieldNames[0]][js['this'][fieldNames[1]]](arguments)));
2015 boundClosureBuilder.addProperty(invocationName, fun); 2151 boundClosureBuilder.addProperty(invocationName, fun);
2016 2152
2017 addParameterStubs(callElement, boundClosureBuilder.addProperty); 2153 addParameterStubs(callElement, boundClosureBuilder.addProperty);
2018 typedefChecks.forEach((Element typedef) { 2154
2019 String operator = namer.operatorIs(typedef); 2155 void emitFunctionTypeSignature(Element method, FunctionType methodType) {
2020 boundClosureBuilder.addProperty(operator, new jsAst.LiteralBool(true)); 2156 RuntimeTypes rti = backend.rti;
2021 }); 2157 String encoding = rti.getSignatureEncoding(
2158 methodType, () => 'this.${fieldNames[0]}');
2159 String operatorSignature = namer.operatorSignature();
2160 boundClosureBuilder.addProperty(operatorSignature,
2161 new jsAst.LiteralExpression(encoding));
2162 }
2163
2164 void emitIsFunctionTypeTest(FunctionType functionType) {
2165 String operator = namer.operatorIsType(functionType);
2166 boundClosureBuilder.addProperty(operator,
2167 new jsAst.LiteralBool(true));
2168 }
2169
2170 generateFunctionTypeTestsInternal(member, memberType, functionTypeChecks,
2171 emitFunctionTypeSignature, emitIsFunctionTypeTest);
2022 2172
2023 boundClosures.add( 2173 boundClosures.add(
2024 js[classesCollector][mangledName].assign( 2174 js[classesCollector][mangledName].assign(
2025 boundClosureBuilder.toObjectInitializer())); 2175 boundClosureBuilder.toObjectInitializer()));
2026 2176
2027 closureClass = namer.isolateAccess(closureClassElement); 2177 closureClass = namer.isolateAccess(closureClassElement);
2028 2178
2029 // Cache it. 2179 // Cache it.
2030 if (canBeShared) { 2180 if (canBeShared) {
2031 cache[parameterCount] = closureClass; 2181 cache[parameterCount] = closureClass;
2032 } 2182 }
2033 } 2183 }
2034 2184
2035 // And finally the getter. 2185 // And finally the getter.
2036 String getterName = namer.getterName(member); 2186 String getterName = namer.getterName(member);
2037 String targetName = namer.instanceMethodName(member); 2187 String targetName = namer.instanceMethodName(member);
2038 2188
2039 List<String> parameters = <String>[]; 2189 List<String> parameters = <String>[];
2040 List<jsAst.Expression> arguments = <jsAst.Expression>[]; 2190 List<jsAst.Expression> arguments = <jsAst.Expression>[];
2041 arguments.add(js['this']); 2191 arguments.add(js['this']);
2042 arguments.add(js.string(targetName)); 2192 arguments.add(js.string(targetName));
2043 if (inInterceptor) { 2193 if (inInterceptor) {
2044 parameters.add(extraArg); 2194 parameters.add(extraArg);
2045 arguments.add(js[extraArg]); 2195 arguments.add(js[extraArg]);
2046 } 2196 }
2047 2197
2198 jsAst.Expression newClosure = js[closureClass].newWith(arguments);
2048 jsAst.Expression getterFunction = js.fun( 2199 jsAst.Expression getterFunction = js.fun(
2049 parameters, 2200 parameters, js.return_(newClosure));
2050 js.return_(js[closureClass].newWith(arguments)));
2051 2201
2052 defineStub(getterName, getterFunction); 2202 defineStub(getterName, getterFunction);
2053 } 2203 }
2054 2204
2055 /** 2205 /**
2056 * Documentation wanted -- johnniwinther 2206 * Documentation wanted -- johnniwinther
2057 * 2207 *
2058 * Invariant: [member] must be a declaration element. 2208 * Invariant: [member] must be a declaration element.
2059 */ 2209 */
2060 void emitCallStubForGetter(Element member, 2210 void emitCallStubForGetter(Element member,
(...skipping 19 matching lines...) Expand all
2080 ? member.fixedBackendName() 2230 ? member.fixedBackendName()
2081 : namer.instanceFieldName(member); 2231 : namer.instanceFieldName(member);
2082 return js['this'][fieldName]; 2232 return js['this'][fieldName];
2083 } 2233 }
2084 } 2234 }
2085 2235
2086 // Two selectors may match but differ only in type. To avoid generating 2236 // Two selectors may match but differ only in type. To avoid generating
2087 // identical stubs for each we track untyped selectors which already have 2237 // identical stubs for each we track untyped selectors which already have
2088 // stubs. 2238 // stubs.
2089 Set<Selector> generatedSelectors = new Set<Selector>(); 2239 Set<Selector> generatedSelectors = new Set<Selector>();
2090 2240 DartType memberType = member.computeType(compiler);
2091 for (Selector selector in selectors) { 2241 for (Selector selector in selectors) {
2092 if (selector.applies(member, compiler)) { 2242 if (selector.applies(member, compiler)) {
2093 selector = selector.asUntyped; 2243 selector = selector.asUntyped;
2094 if (generatedSelectors.contains(selector)) continue; 2244 if (generatedSelectors.contains(selector)) continue;
2095 generatedSelectors.add(selector); 2245 generatedSelectors.add(selector);
2096 2246
2097 String invocationName = namer.invocationName(selector); 2247 String invocationName = namer.invocationName(selector);
2098 Selector callSelector = new Selector.callClosureFrom(selector); 2248 Selector callSelector = new Selector.callClosureFrom(selector);
2099 String closureCallName = namer.invocationName(callSelector); 2249 String closureCallName = namer.invocationName(callSelector);
2100 2250
(...skipping 957 matching lines...) Expand 10 before | Expand all | Expand 10 after
3058 """; 3208 """;
3059 const String HOOKS_API_USAGE = """ 3209 const String HOOKS_API_USAGE = """
3060 // The code supports the following hooks: 3210 // The code supports the following hooks:
3061 // dartPrint(message) - if this function is defined it is called 3211 // dartPrint(message) - if this function is defined it is called
3062 // instead of the Dart [print] method. 3212 // instead of the Dart [print] method.
3063 // dartMainRunner(main) - if this function is defined, the Dart [main] 3213 // dartMainRunner(main) - if this function is defined, the Dart [main]
3064 // method will not be invoked directly. 3214 // method will not be invoked directly.
3065 // Instead, a closure that will invoke [main] is 3215 // Instead, a closure that will invoke [main] is
3066 // passed to [dartMainRunner]. 3216 // passed to [dartMainRunner].
3067 """; 3217 """;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698