Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Unified Diff: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 12334070: Support runtime check of function types. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Register dependency Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;

Powered by Google App Engine
This is Rietveld 408576698