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 d014da84d46ff6cc79e20af2c967da923399e495..d3e391f8327d5f24eb4e3aeb30d36e5509473537 100644 |
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
@@ -108,7 +108,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; |
@@ -139,17 +155,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); |
} |
}); |
} |
@@ -974,6 +990,19 @@ class CodeEmitterTask extends CompilerTask { |
builder.addProperty(namer.operatorIs(other), code); |
} |
+ void generateIsFunctionTypeTest(FunctionType type) { |
+ builder.addProperty(namer.operatorIsFunctionType(type), |
+ new jsAst.LiteralBool(true)); |
+ } |
+ |
+ void generateFunctionTypeSignature(FunctionType type) { |
+ RuntimeTypeInformation rti = backend.rti; |
+ String encoding = rti.getTypeEncoding(type, alwaysGenerateFunction: true); |
+ String operatorSignature = namer.operatorSignature(); |
+ builder.addProperty(operatorSignature, |
+ new jsAst.LiteralExpression(encoding)); |
+ } |
+ |
void generateSubstitution(Element other, {bool emitNull: false}) { |
RuntimeTypeInformation rti = backend.rti; |
// TODO(karlklose): support typedefs with variables. |
@@ -996,7 +1025,9 @@ class CodeEmitterTask extends CompilerTask { |
} |
} |
- generateIsTestsOn(classElement, generateIsTest, generateSubstitution); |
+ generateIsTestsOn(classElement, generateIsTest, |
+ generateIsFunctionTypeTest, generateFunctionTypeSignature, |
+ generateSubstitution); |
if (identical(classElement, compiler.objectClass) |
&& compiler.enabledNoSuchMethod) { |
@@ -1054,6 +1085,19 @@ 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) { |
+ String encoding = rti.getTypeEncoding(type); |
+ buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); |
+ } |
+ }); |
} |
void visitNativeMixins(ClassElement classElement, |
@@ -1389,14 +1433,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; |
} |
/** |
@@ -1408,6 +1464,8 @@ class CodeEmitterTask extends CompilerTask { |
*/ |
void generateIsTestsOn(ClassElement cls, |
void emitIsTest(Element element), |
+ void emitIsFunctionTypeTest(FunctionType type), |
+ void emitFunctionTypeSignature(FunctionType type), |
void emitSubstitution(Element element, {emitNull})) { |
if (checkedClasses.contains(cls)) { |
emitIsTest(cls); |
@@ -1459,7 +1517,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. |
@@ -1470,7 +1528,21 @@ class CodeEmitterTask extends CompilerTask { |
emitIsTest, |
emitSubstitution, |
generated); |
- getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest); |
+ FunctionType callType = call.computeType(compiler); |
+ Map<FunctionType,bool> functionTypeChecks = |
+ getFunctionTypeChecksOn(callType); |
+ bool hasDynamicFunctionTypeCheck = false; |
+ functionTypeChecks.forEach((FunctionType functionType, |
+ bool knownSubtype) { |
+ emitIsFunctionTypeTest(functionType); |
+ if (!knownSubtype) { |
+ registerDynamicFunctionTypeCheck(functionType); |
+ hasDynamicFunctionTypeCheck = true; |
+ } |
+ }); |
+ if (hasDynamicFunctionTypeCheck) { |
+ emitFunctionTypeSignature(callType); |
+ } |
} |
} |
@@ -1653,12 +1725,28 @@ 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'); |
+ FunctionType elementType = element.computeType(compiler); |
+ Map<FunctionType,bool> functionTypeChecks = |
+ getFunctionTypeChecksOn(elementType); |
+ bool hasDynamicFunctionTypeCheck = false; |
+ functionTypeChecks.forEach((FunctionType functionType, |
+ bool knownSubtype) { |
+ String operator = namer.operatorIsFunctionType(functionType); |
+ buffer.write('$fieldAccess.$operator$_=${_}true$N'); |
+ if (!knownSubtype) { |
+ TypeVariableType typeVariable = functionType.typeVariableOccurrence; |
+ registerDynamicFunctionTypeCheck(functionType); |
+ hasDynamicFunctionTypeCheck = true; |
} |
- ); |
+ }); |
+ if (hasDynamicFunctionTypeCheck) { |
+ RuntimeTypeInformation rti = backend.rti; |
+ String encoding = rti.getTypeEncoding(elementType, |
+ alwaysGenerateFunction: true); |
+ String operatorSignature = namer.operatorSignature(); |
+ buffer.add('$fieldAccess.$operatorSignature$_=${_}$encoding$N'); |
+ } |
+ |
} |
} |
@@ -1719,12 +1807,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. |
@@ -1781,10 +1871,24 @@ class CodeEmitterTask extends CompilerTask { |
boundClosureBuilder.addProperty(invocationName, fun); |
addParameterStubs(callElement, boundClosureBuilder.addProperty); |
- typedefChecks.forEach((Element typedef) { |
- String operator = namer.operatorIs(typedef); |
+ bool hasDynamicFunctionTypeCheck = false; |
+ functionTypeChecks.forEach((FunctionType functionType, |
+ bool knownSubtype) { |
+ String operator = namer.operatorIsFunctionType(functionType); |
boundClosureBuilder.addProperty(operator, new jsAst.LiteralBool(true)); |
+ if (!knownSubtype) { |
+ registerDynamicFunctionTypeCheck(functionType); |
+ hasDynamicFunctionTypeCheck = true; |
+ } |
}); |
+ if (hasDynamicFunctionTypeCheck) { |
+ RuntimeTypeInformation rti = backend.rti; |
+ String encoding = rti.getTypeEncoding(memberType, |
+ alwaysGenerateFunction: true); |
+ String operatorSignature = namer.operatorSignature(); |
+ boundClosureBuilder.addProperty(operatorSignature, |
+ new jsAst.LiteralExpression(encoding)); |
+ } |
boundClosures.add( |
js[classesCollector][mangledName].assign( |
@@ -1811,14 +1915,34 @@ 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))); |
+ forwardRuntimeTypeInfo(newClosure, memberType, classElement)); |
defineStub(getterName, getterFunction); |
} |
/** |
+ * Wraps the [newExpression] of a closure with code for setting the runtime |
+ * type information if needed. |
+ */ |
+ forwardRuntimeTypeInfo(jsAst.Expression newExpression, |
+ DartType memberType, |
+ ClassElement thisClass) { |
+ if (compiler.world.needsRti(thisClass) && |
+ memberType.containsTypeVariables) { |
+ String forwardRuntimeTypeInfo = |
+ namer.isolateAccess(backend.getForwardRuntimeTypeInfo()); |
+ String substitution = namer.substitutionName(thisClass); |
+ return js.return_(new jsAst.Call(js[forwardRuntimeTypeInfo], |
+ [newExpression, js['this.$substitution'], js['this']])); |
+ } else { |
+ return js.return_(newExpression); |
+ } |
+ } |
+ |
+ /** |
* Documentation wanted -- johnniwinther |
* |
* Invariant: [member] must be a declaration element. |
@@ -1853,7 +1977,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; |