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 841c0a88fed00aee9a2760345450609dc7b2014a..876b619b02a70d3cfbf5cb43deab05012a71b5a4 100644 |
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
@@ -112,7 +112,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; |
@@ -143,18 +159,19 @@ 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) { |
+ assert(!t.isMalformed); |
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); |
} |
}); |
} |
@@ -1236,6 +1253,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. |
@@ -1255,7 +1298,9 @@ class CodeEmitterTask extends CompilerTask { |
} |
} |
- generateIsTestsOn(classElement, generateIsTest, generateSubstitution); |
+ generateIsTestsOn(classElement, generateIsTest, |
+ generateIsFunctionTypeTest, generateFunctionTypeSignature, |
+ generateSubstitution); |
} |
void emitRuntimeTypeSupport(CodeBuffer buffer) { |
@@ -1275,6 +1320,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'); |
+ } |
+ }); |
} |
/** |
@@ -1406,12 +1464,13 @@ 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); |
+ CheckedModeHelper helper = |
+ backend.getCheckedModeHelper(type, typeCast: false); |
+ FunctionElement helperElement = helper.getElement(compiler); |
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); |
@@ -1624,14 +1683,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; |
} |
/** |
@@ -1643,6 +1714,8 @@ class CodeEmitterTask extends CompilerTask { |
*/ |
void generateIsTestsOn(ClassElement cls, |
void emitIsTest(Element element), |
+ void emitIsFunctionTypeTest(FunctionType type), |
+ void emitFunctionTypeSignature(Element method, FunctionType type), |
void emitSubstitution(Element element, {emitNull})) { |
if (checkedClasses.contains(cls)) { |
emitIsTest(cls); |
@@ -1694,7 +1767,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. |
@@ -1705,8 +1778,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) { |
@@ -1748,6 +1823,53 @@ 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) { |
+ if (!knownSubtype) { |
+ registerDynamicFunctionTypeCheck(functionType); |
+ hasDynamicFunctionTypeCheck = true; |
+ } else { |
+ neededPredicates++; |
+ } |
+ }); |
+ bool hasSignature = false; |
+ if (hasDynamicFunctionTypeCheck || |
+ neededPredicates > MAX_FUNCTION_TYPE_PREDICATES) { |
+ emitFunctionTypeSignature(method, methodType); |
+ hasSignature = true; |
+ } |
+ functionTypeChecks.forEach((FunctionType functionType, |
+ bool knownSubtype) { |
+ 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. |
@@ -1908,12 +2030,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( |
@@ -1921,6 +2037,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); |
} |
} |
@@ -1978,12 +2111,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. |
@@ -2039,10 +2174,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; |
+ 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 = #', |
@@ -2073,9 +2222,9 @@ class CodeEmitterTask extends CompilerTask { |
arguments.add(new jsAst.LiteralNull()); |
} |
+ 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); |
} |
@@ -2115,7 +2264,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; |