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 d4df75d16bc34ec30a8e6f3ed970bbb8516cebf2..9febf31fc42b202fbf9325780b1864acc68c0868 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
| @@ -109,7 +109,23 @@ class CodeEmitterTask extends CompilerTask { |
| * 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.
|
| * `F` will occur once in [checkedTypedefs]. |
| */ |
| - Set<TypedefElement> checkedTypedefs; |
| + Set<FunctionType> checkedFunctionTypes; |
| + |
| + Map<ClassElement, Set<FunctionType>> checkedGenericFunctionTypes |
| + = new Map<ClassElement, Set<FunctionType>>(); |
|
karlklose
2013/03/22 13:17:46
Break after '='.
Johnni Winther
2013/06/21 12:19:14
Done.
|
| + |
| + Set<FunctionType> checkedNonGenericFunctionTypes |
|
karlklose
2013/03/22 13:17:46
Ditto.
Johnni Winther
2013/06/21 12:19:14
Done.
|
| + = 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; |
| @@ -140,17 +156,17 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| void computeRequiredTypeChecks() { |
| - assert(checkedClasses == null && checkedTypedefs == null); |
| + assert(checkedClasses == null && checkedFunctionTypes == null); |
| compiler.codegenWorld.addImplicitChecks(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); |
| + } else if (t is FunctionType) { |
| + checkedFunctionTypes.add(t); |
| } |
| }); |
| } |
| @@ -1204,6 +1220,32 @@ class CodeEmitterTask extends CompilerTask { |
| builder.addProperty(namer.operatorIs(other), code); |
| } |
| + 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. |
| @@ -1226,7 +1268,9 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| } |
| - generateIsTestsOn(classElement, generateIsTest, generateSubstitution); |
| + generateIsTestsOn(classElement, generateIsTest, |
| + generateIsFunctionTypeTest, generateFunctionTypeSignature, |
| + generateSubstitution); |
| if (identical(classElement, compiler.objectClass) |
| && compiler.enabledNoSuchMethod) { |
| @@ -1284,6 +1328,23 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| }; |
| } |
| + |
| + checkedNonGenericFunctionTypes.forEach((FunctionType type) { |
| + String encoding = rti.getTypeEncoding(type); |
| + buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); |
| + }); |
| + |
| + checkedGenericFunctionTypes.forEach( |
| + (ClassElement cls, Set<FunctionType> functionTypes) { |
| + 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.
|
| + ClassElement contextClass = Types.getClassContext(type); |
| + if (contextClass != null) { |
| + maybeGenerateHolder(contextClass); |
| + } |
| + String encoding = rti.getTypeEncoding(type); |
| + buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); |
| + } |
| + }); |
| } |
| void visitNativeMixins(ClassElement classElement, |
| @@ -1446,7 +1507,7 @@ class CodeEmitterTask extends CompilerTask { |
| String helperName = namer.isolateAccess(helperElement); |
| 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); |
| @@ -1623,14 +1684,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. |
|
karlklose
2013/03/22 13:17:46
Explain what being false means.
Johnni Winther
2013/06/21 12:19:14
Done.
|
| + */ |
| + // 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; |
| } |
| /** |
| @@ -1642,6 +1715,8 @@ class CodeEmitterTask extends CompilerTask { |
| */ |
| void generateIsTestsOn(ClassElement cls, |
| void emitIsTest(Element element), |
| + void emitIsFunctionTypeTest(FunctionType type), |
| + void emitFunctionTypeSignature(Element method, FunctionType type), |
|
karlklose
2013/03/22 13:17:46
Long line.
Johnni Winther
2013/06/21 12:19:14
Done.
|
| void emitSubstitution(Element element, {emitNull})) { |
| if (checkedClasses.contains(cls)) { |
| emitIsTest(cls); |
| @@ -1693,7 +1768,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) { |
| FunctionElement call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); |
| if (call == null) { |
| // If [cls] is a closure, it has a synthetic call operator method. |
| @@ -1704,8 +1779,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) { |
| @@ -1758,6 +1835,54 @@ class CodeEmitterTask extends CompilerTask { |
| } |
| } |
| + void generateFunctionTypeTests( |
| + 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) { |
|
karlklose
2013/03/22 13:17:46
Align with '(FunctionType...'.
Johnni Winther
2013/06/21 12:19:14
Done.
|
| + if (!knownSubtype) { |
| + 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
|
| + hasDynamicFunctionTypeCheck = true; |
| + } else { |
| + neededPredicates++; |
| + } |
| + }); |
| + bool hasSignature = false; |
|
karlklose
2013/03/22 13:17:46
'signatureEmitted'?
Johnni Winther
2013/06/21 12:19:14
Done.
|
| + if (hasDynamicFunctionTypeCheck || |
| + neededPredicates > MAX_FUNCTION_TYPE_PREDICATES) { |
| + emitFunctionTypeSignature(method, methodType); |
| + hasSignature = true; |
| + } |
| + functionTypeChecks.forEach((FunctionType functionType, |
| + bool knownSubtype) { |
|
karlklose
2013/03/22 13:17:46
Align.
Johnni Winther
2013/06/21 12:19:14
Done.
|
| + if (knownSubtype || !hasDynamicFunctionTypeCheck) { |
| + if (hasSignature) { |
| + registerDynamicFunctionTypeCheck(functionType); |
| + } else { |
| + emitIsFunctionTypeTest(functionType); |
| + } |
| + } |
| + }); |
| + } |
| + |
| /** |
| * Return a function that returns true if its argument is a class |
| * that needs to be emitted. |
| @@ -1887,12 +2012,21 @@ class CodeEmitterTask extends CompilerTask { |
| // in case it is used in spawnFunction. |
| String fieldName = namer.STATIC_CLOSURE_NAME_NAME; |
| buffer.write('$fieldAccess.$fieldName$_=$_"$staticName"$N'); |
| - getTypedefChecksOn(element.computeType(compiler)).forEach( |
| - (Element typedef) { |
| - String operator = namer.operatorIs(typedef); |
| - buffer.write('$fieldAccess.$operator$_=${_}true$N'); |
| - } |
| - ); |
| + |
| + void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| + RuntimeTypes rti = backend.rti; |
| + 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.
|
| + String operatorSignature = namer.operatorSignature(); |
| + buffer.add('$fieldAccess.$operatorSignature$_=${_}$encoding$N'); |
| + } |
| + |
| + void emitIsFunctionTypeTest(FunctionType functionType) { |
| + String operator = namer.operatorIsType(functionType); |
| + buffer.write('$fieldAccess.$operator$_=${_}true$N'); |
| + } |
| + |
| + generateFunctionTypeTests(element, element.computeType(compiler), |
| + emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| } |
| } |
| @@ -1953,12 +2087,14 @@ class CodeEmitterTask extends CompilerTask { |
| : inInterceptor ? const ['self', 'target', 'receiver'] |
| : const ['self', 'target']; |
| - 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. |
| @@ -2015,10 +2151,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, new jsAst.LiteralBool(true)); |
| - }); |
| + |
| + void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| + RuntimeTypes rti = backend.rti; |
| + String encoding = rti.getSignatureEncoding( |
| + methodType, () => 'this.${fieldNames[0]}'); |
| + String operatorSignature = namer.operatorSignature(); |
| + boundClosureBuilder.addProperty(operatorSignature, |
| + new jsAst.LiteralExpression(encoding)); |
| + } |
| + |
| + void emitIsFunctionTypeTest(FunctionType functionType) { |
| + String operator = namer.operatorIsType(functionType); |
| + boundClosureBuilder.addProperty(operator, |
| + new jsAst.LiteralBool(true)); |
| + } |
| + |
| + generateFunctionTypeTestsInternal(member, memberType, functionTypeChecks, |
| + emitFunctionTypeSignature, emitIsFunctionTypeTest); |
| boundClosures.add( |
| js[classesCollector][mangledName].assign( |
| @@ -2045,9 +2195,9 @@ class CodeEmitterTask extends CompilerTask { |
| arguments.add(js[extraArg]); |
| } |
| + jsAst.Expression newClosure = js[closureClass].newWith(arguments); |
| jsAst.Expression getterFunction = js.fun( |
| - parameters, |
| - js.return_(js[closureClass].newWith(arguments))); |
| + parameters, js.return_(newClosure)); |
| defineStub(getterName, getterFunction); |
| } |
| @@ -2087,7 +2237,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); |
| for (Selector selector in selectors) { |
| if (selector.applies(member, compiler)) { |
| selector = selector.asUntyped; |