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