Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
| diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
| index 7721152828ca122906778004e701eeb526b89d91..08c40e90c7815cd8c92bcd4b9b1662cb662ca0ae 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
| @@ -114,7 +114,23 @@ class CodeEmitterTask extends CompilerTask { |
| * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement |
| * `F` will occur once in [checkedTypedefs]. |
| */ |
| - Set<TypedefElement> checkedTypedefs; |
| + Set<FunctionType> checkedFunctionTypes; |
| + |
| + Map<ClassElement, Set<FunctionType>> checkedGenericFunctionTypes |
| + = new Map<ClassElement, Set<FunctionType>>(); |
| + |
| + Set<FunctionType> checkedNonGenericFunctionTypes |
| + = new Set<FunctionType>(); |
| + |
| + void registerDynamicFunctionTypeCheck(FunctionType functionType) { |
| + ClassElement classElement = Types.getClassContext(functionType); |
| + if (classElement != null) { |
| + checkedGenericFunctionTypes.putIfAbsent(classElement, |
| + () => new Set<FunctionType>()).add(functionType); |
| + } else { |
| + checkedNonGenericFunctionTypes.add(functionType); |
| + } |
| + } |
| final bool generateSourceMap; |
| @@ -145,18 +161,20 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| void computeRequiredTypeChecks() { |
| - assert(checkedClasses == null && checkedTypedefs == null); |
| + assert(checkedClasses == null && checkedFunctionTypes == null); |
| backend.rti.addImplicitChecks(compiler.codegenWorld, |
| classesUsingTypeVariableTests); |
| checkedClasses = new Set<ClassElement>(); |
| - checkedTypedefs = new Set<TypedefElement>(); |
| + checkedFunctionTypes = new Set<FunctionType>(); |
| compiler.codegenWorld.isChecks.forEach((DartType t) { |
| - if (t is InterfaceType) { |
| - checkedClasses.add(t.element); |
| - } else if (t is TypedefType) { |
| - checkedTypedefs.add(t.element); |
| + if (!t.isMalformed) { |
| + if (t is InterfaceType) { |
| + checkedClasses.add(t.element); |
| + } else if (t is FunctionType) { |
| + checkedFunctionTypes.add(t); |
| + } |
| } |
| }); |
| } |
| @@ -1263,6 +1281,32 @@ class CodeEmitterTask extends CompilerTask { |
| builder.addProperty(namer.operatorIs(other), js('true')); |
| } |
| + void generateIsFunctionTypeTest(FunctionType type) { |
| + String operator = namer.operatorIsType(type); |
| + builder.addProperty(operator, new jsAst.LiteralBool(true)); |
| + } |
| + |
| + void generateFunctionTypeSignature(Element method, FunctionType type) { |
| + assert(method.isImplementation); |
| + String thisAccess = 'this'; |
| + Node node = method.parseNode(compiler); |
| + ClosureClassMap closureData = |
| + compiler.closureToClassMapper.closureMappingCache[node]; |
| + if (closureData != null) { |
| + Element thisElement = |
| + closureData.freeVariableMapping[closureData.thisElement]; |
| + if (thisElement != null) { |
| + String thisName = backend.namer.getName(thisElement); |
| + thisAccess = 'this.$thisName'; |
| + } |
| + } |
| + RuntimeTypes rti = backend.rti; |
| + String encoding = rti.getSignatureEncoding(type, () => '$thisAccess'); |
| + String operatorSignature = namer.operatorSignature(); |
| + builder.addProperty(operatorSignature, |
| + new jsAst.LiteralExpression(encoding)); |
| + } |
| + |
| void generateSubstitution(Element other, {bool emitNull: false}) { |
| RuntimeTypes rti = backend.rti; |
| // TODO(karlklose): support typedefs with variables. |
|
karlklose
2013/06/19 14:37:05
This TODO is obsolete now, isn't it?
Johnni Winther
2013/06/21 12:19:15
Yes. Removed.
|
| @@ -1282,7 +1326,9 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| } |
| - generateIsTestsOn(classElement, generateIsTest, generateSubstitution); |
| + generateIsTestsOn(classElement, generateIsTest, |
| + generateIsFunctionTypeTest, generateFunctionTypeSignature, |
| + generateSubstitution); |
| } |
| void emitRuntimeTypeSupport(CodeBuffer buffer) { |
| @@ -1302,6 +1348,19 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| }; |
| } |
| + |
| + checkedNonGenericFunctionTypes.forEach((FunctionType type) { |
| + String encoding = rti.getTypeEncoding(type); |
|
karlklose
2013/06/19 14:37:05
Create a function addEncoding for l.1353-1354 and
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); |
| + }); |
| + |
| + checkedGenericFunctionTypes.forEach( |
| + (ClassElement cls, Set<FunctionType> functionTypes) { |
|
karlklose
2013/06/19 14:37:05
If you replace 'ClassElement cls' with '_' I think
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + for (FunctionType type in functionTypes) { |
| + String encoding = rti.getTypeEncoding(type); |
| + buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); |
| + } |
| + }); |
| } |
| /** |
| @@ -1433,12 +1492,14 @@ class CodeEmitterTask extends CompilerTask { |
| DartType type = member.computeType(compiler); |
| // TODO(ahe): Generate a dynamic type error here. |
| if (type.element.isErroneous()) return; |
| - FunctionElement helperElement |
| - = backend.getCheckedModeHelper(type, typeCast: false); |
| + type = type.unalias(compiler); |
| + CheckedModeHelper helper = |
| + backend.getCheckedModeHelper(type, typeCast: false); |
| + FunctionElement helperElement = helper.getElement(compiler); |
| String helperName = namer.isolateAccess(helperElement); |
|
karlklose
2013/06/19 14:37:05
You can inline helperElement here (it would remove
Johnni Winther
2013/06/21 12:19:15
No. [helperElement] is used below.
|
| List<jsAst.Expression> arguments = <jsAst.Expression>[js('v')]; |
| if (helperElement.computeSignature(compiler).parameterCount != 1) { |
| - arguments.add(js.string(namer.operatorIs(type.element))); |
| + arguments.add(js.string(namer.operatorIsType(type))); |
| } |
| String setterName = namer.setterNameFromAccessorName(accessorName); |
| @@ -1671,14 +1732,26 @@ class CodeEmitterTask extends CompilerTask { |
| return _selectorRank(selector1) - _selectorRank(selector2); |
| } |
| - Iterable<Element> getTypedefChecksOn(DartType type) { |
| - bool isSubtype(TypedefElement typedef) { |
| - FunctionType typedefType = |
| - typedef.computeType(compiler).unalias(compiler); |
| - return compiler.types.isSubtype(type, typedefType); |
| + /** |
| + * Returns a mapping containing all checked function types for which [type] |
| + * can be a subtype. A function type is mapped to [:true:] if [type] is |
| + * statically known to be a subtype of it. |
| + */ |
| + // TODO(johnniwinther): Change to return a mapping from function types to |
| + // a set of variable points and use this to detect statically/dynamically |
| + // known subtype relations. |
| + Map<FunctionType, bool> getFunctionTypeChecksOn(DartType type) { |
| + Map<FunctionType, bool> functionTypeMap = |
| + new LinkedHashMap<FunctionType, bool>(); |
| + for (FunctionType functionType in checkedFunctionTypes) { |
| + if (compiler.types.isSubtype(type, functionType)) { |
| + functionTypeMap[functionType] = true; |
| + } else if (compiler.types.isPotentialSubtype(type, functionType)) { |
| + functionTypeMap[functionType] = false; |
| + } |
| } |
| - return checkedTypedefs.where(isSubtype).toList() |
| - ..sort(Elements.compareByPosition); |
| + // TODO(johnniwinther): Ensure stable ordering of the keys. |
| + return functionTypeMap; |
| } |
| /** |
| @@ -1690,6 +1763,8 @@ class CodeEmitterTask extends CompilerTask { |
| */ |
| void generateIsTestsOn(ClassElement cls, |
| void emitIsTest(Element element), |
| + void emitIsFunctionTypeTest(FunctionType type), |
|
karlklose
2013/06/19 14:37:05
You could add Typedefs for these three function ty
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + void emitFunctionTypeSignature(Element method, FunctionType type), |
| void emitSubstitution(Element element, {emitNull})) { |
| if (checkedClasses.contains(cls)) { |
| emitIsTest(cls); |
| @@ -1741,7 +1816,7 @@ class CodeEmitterTask extends CompilerTask { |
| // A class that defines a [:call:] method implicitly implements |
| // [Function] and needs checks for all typedefs that are used in is-checks. |
| if (checkedClasses.contains(compiler.functionClass) || |
| - !checkedTypedefs.isEmpty) { |
| + !checkedFunctionTypes.isEmpty) { |
| Element call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); |
| if (call == null) { |
| // If [cls] is a closure, it has a synthetic call operator method. |
| @@ -1752,8 +1827,10 @@ class CodeEmitterTask extends CompilerTask { |
| emitIsTest, |
| emitSubstitution, |
| generated); |
| - getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest); |
| - } |
| + FunctionType callType = call.computeType(compiler); |
| + generateFunctionTypeTests(call, callType, |
| + emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| + } |
| } |
| for (DartType interfaceType in cls.interfaces) { |
| @@ -1795,6 +1872,53 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| } |
| + void generateFunctionTypeTests( |
|
karlklose
2013/06/19 14:37:05
Could you get rid of this method? I don't think it
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + Element method, |
| + FunctionType methodType, |
| + void emitFunctionTypeSignature(Element method, FunctionType methodType), |
| + void emitIsFunctionTypeTest(FunctionType functionType)) { |
| + Map<FunctionType, bool> functionTypeChecks = |
| + getFunctionTypeChecksOn(methodType); |
| + generateFunctionTypeTestsInternal(method, methodType, functionTypeChecks, |
| + emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| + } |
| + |
| + const int MAX_FUNCTION_TYPE_PREDICATES = 10; |
| + |
| + void generateFunctionTypeTestsInternal( |
| + Element method, |
| + FunctionType methodType, |
| + Map<FunctionType, bool> functionTypeChecks, |
| + void emitFunctionTypeSignature(Element method, FunctionType methodType), |
| + void emitIsFunctionTypeTest(FunctionType functionType)) { |
| + bool hasDynamicFunctionTypeCheck = false; |
| + int neededPredicates = 0; |
| + functionTypeChecks.forEach((FunctionType functionType, bool knownSubtype) { |
| + if (!knownSubtype) { |
| + registerDynamicFunctionTypeCheck(functionType); |
| + hasDynamicFunctionTypeCheck = true; |
| + } else { |
| + neededPredicates++; |
| + } |
| + }); |
| + bool hasSignature = false; |
|
karlklose
2013/06/19 14:37:05
Perhaps alwaysUseSignature would be clearer.
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + if (hasDynamicFunctionTypeCheck || |
| + neededPredicates > MAX_FUNCTION_TYPE_PREDICATES) { |
| + emitFunctionTypeSignature(method, methodType); |
| + hasSignature = true; |
| + } |
| + functionTypeChecks.forEach((FunctionType functionType, |
| + bool knownSubtype) { |
|
karlklose
2013/06/19 14:37:05
This should fit on one line.
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + if (knownSubtype || !hasDynamicFunctionTypeCheck) { |
|
karlklose
2013/06/19 14:37:05
!knownSubtype implies hasDynamicFunctionTypeCheck,
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + if (hasSignature) { |
| + registerDynamicFunctionTypeCheck(functionType); |
| + } else { |
| + emitIsFunctionTypeTest(functionType); |
| + } |
| + } |
| + }); |
| + } |
| + |
| /** |
| * Return a function that returns true if its argument is a class |
| * that needs to be emitted. |
| @@ -1953,12 +2077,6 @@ class CodeEmitterTask extends CompilerTask { |
| addParameterStubs(callElement, closureBuilder.addProperty); |
| - DartType type = element.computeType(compiler); |
| - getTypedefChecksOn(type).forEach((Element typedef) { |
| - String operator = namer.operatorIs(typedef); |
| - closureBuilder.addProperty(operator, js('true')); |
| - }); |
| - |
| // TODO(ngeoffray): Cache common base classes for closures, bound |
| // closures, and static closures that have common type checks. |
| boundClosures.add( |
| @@ -1966,6 +2084,23 @@ class CodeEmitterTask extends CompilerTask { |
| closureBuilder.toObjectInitializer())); |
| staticGetters[element] = closureClassElement; |
| + |
| + void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| + RuntimeTypes rti = backend.rti; |
| + String encoding = rti.getSignatureEncoding(methodType, () => 'null'); |
| + String operatorSignature = namer.operatorSignature(); |
| + // TODO(johnniwinther): Make MiniJsParser support function expressions. |
| + closureBuilder.addProperty(operatorSignature, |
| + new jsAst.LiteralExpression(encoding)); |
| + } |
| + |
| + void emitIsFunctionTypeTest(FunctionType functionType) { |
| + String operator = namer.operatorIsType(functionType); |
| + closureBuilder.addProperty(operator, js('true')); |
| + } |
| + |
| + generateFunctionTypeTests(element, element.computeType(compiler), |
| + emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| } |
| } |
| @@ -2023,12 +2158,14 @@ class CodeEmitterTask extends CompilerTask { |
| fieldNames.add(namer.getName(field)); |
| }); |
| - Iterable<Element> typedefChecks = |
| - getTypedefChecksOn(member.computeType(compiler)); |
| - bool hasTypedefChecks = !typedefChecks.isEmpty; |
| + DartType memberType = member.computeType(compiler); |
| + Map<FunctionType, bool> functionTypeChecks = |
| + getFunctionTypeChecksOn(memberType); |
| + bool hasFunctionTypeChecks = !functionTypeChecks.isEmpty; |
| - bool canBeShared = !hasOptionalParameters && !hasTypedefChecks; |
| + bool canBeShared = !hasOptionalParameters && !hasFunctionTypeChecks; |
| + ClassElement classElement = member.getEnclosingClass(); |
| String closureClass = canBeShared ? cache[parameterCount] : null; |
| if (closureClass == null) { |
| // Either the class was not cached yet, or there are optional parameters. |
| @@ -2084,10 +2221,24 @@ class CodeEmitterTask extends CompilerTask { |
| boundClosureBuilder.addProperty(invocationName, fun); |
| addParameterStubs(callElement, boundClosureBuilder.addProperty); |
| - typedefChecks.forEach((Element typedef) { |
| - String operator = namer.operatorIs(typedef); |
| - boundClosureBuilder.addProperty(operator, js('true')); |
| - }); |
| + |
| + void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| + RuntimeTypes rti = backend.rti; |
|
karlklose
2013/06/19 14:37:05
You can inline rti.
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + String encoding = rti.getSignatureEncoding( |
| + methodType, () => 'this.${fieldNames[0]}'); |
| + String operatorSignature = namer.operatorSignature(); |
| + boundClosureBuilder.addProperty(operatorSignature, |
| + new jsAst.LiteralExpression(encoding)); |
| + } |
| + |
| + void emitIsFunctionTypeTest(FunctionType functionType) { |
|
karlklose
2013/06/19 14:37:05
I think we should be more careful with names in th
Johnni Winther
2013/06/21 12:19:15
Yes. Added a TODO to the newly added typedef for t
|
| + String operator = namer.operatorIsType(functionType); |
| + boundClosureBuilder.addProperty(operator, |
| + new jsAst.LiteralBool(true)); |
| + } |
| + |
| + generateFunctionTypeTestsInternal(member, memberType, functionTypeChecks, |
| + emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| boundClosures.add( |
| js('$classesCollector.$mangledName = #', |
| @@ -2118,9 +2269,9 @@ class CodeEmitterTask extends CompilerTask { |
| arguments.add(new jsAst.LiteralNull()); |
| } |
| + jsAst.Expression newClosure = js(closureClass).newWith(arguments); |
|
karlklose
2013/06/19 14:37:05
Why this change?
Johnni Winther
2013/06/21 12:19:15
It was needed in a previous iteration. Removed.
|
| jsAst.Expression getterFunction = js.fun( |
| - parameters, |
| - js.return_(js(closureClass).newWith(arguments))); |
| + parameters, js.return_(newClosure)); |
| defineStub(getterName, getterFunction); |
| } |
| @@ -2160,7 +2311,7 @@ class CodeEmitterTask extends CompilerTask { |
| // identical stubs for each we track untyped selectors which already have |
| // stubs. |
| Set<Selector> generatedSelectors = new Set<Selector>(); |
| - |
| + DartType memberType = member.computeType(compiler); |
|
karlklose
2013/06/19 14:37:05
This is not used.
Johnni Winther
2013/06/21 12:19:15
Removed.
|
| for (Selector selector in selectors) { |
| if (selector.applies(member, compiler)) { |
| selector = selector.asUntyped; |
| @@ -2761,6 +2912,15 @@ if (typeof document !== "undefined" && document.readyState !== "complete") { |
| rti.getClassesUsedInSubstitutions(backend, requiredChecks); |
| addClassesWithSuperclasses(classesUsedInSubstitutions); |
| + // 3c. Add classes that contain checked generic function types. These are |
| + // needed to store the signature encoding. |
| + for (FunctionType type in checkedFunctionTypes) { |
| + ClassElement contextClass = Types.getClassContext(type); |
| + if (contextClass != null) { |
| + neededClasses.add(contextClass); |
| + } |
| + } |
| + |
| // 4. Finally, sort the classes. |
| List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses); |