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

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: Fix status files Created 7 years, 6 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 7721152828ca122906778004e701eeb526b89d91..08c40e90c7815cd8c92bcd4b9b1662cb662ca0ae 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -114,7 +114,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;
@@ -145,18 +161,20 @@ 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) {
- if (t is InterfaceType) {
- checkedClasses.add(t.element);
- } else if (t is TypedefType) {
- checkedTypedefs.add(t.element);
+ if (!t.isMalformed) {
+ if (t is InterfaceType) {
+ checkedClasses.add(t.element);
+ } else if (t is FunctionType) {
+ checkedFunctionTypes.add(t);
+ }
}
});
}
@@ -1263,6 +1281,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.
karlklose 2013/06/19 14:37:05 This TODO is obsolete now, isn't it?
Johnni Winther 2013/06/21 12:19:15 Yes. Removed.
@@ -1282,7 +1326,9 @@ class CodeEmitterTask extends CompilerTask {
}
}
- generateIsTestsOn(classElement, generateIsTest, generateSubstitution);
+ generateIsTestsOn(classElement, generateIsTest,
+ generateIsFunctionTypeTest, generateFunctionTypeSignature,
+ generateSubstitution);
}
void emitRuntimeTypeSupport(CodeBuffer buffer) {
@@ -1302,6 +1348,19 @@ class CodeEmitterTask extends CompilerTask {
}
};
}
+
+ checkedNonGenericFunctionTypes.forEach((FunctionType type) {
+ String encoding = rti.getTypeEncoding(type);
karlklose 2013/06/19 14:37:05 Create a function addEncoding for l.1353-1354 and
Johnni Winther 2013/06/21 12:19:15 Done.
+ buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N');
+ });
+
+ checkedGenericFunctionTypes.forEach(
+ (ClassElement cls, Set<FunctionType> functionTypes) {
karlklose 2013/06/19 14:37:05 If you replace 'ClassElement cls' with '_' I think
Johnni Winther 2013/06/21 12:19:15 Done.
+ for (FunctionType type in functionTypes) {
+ String encoding = rti.getTypeEncoding(type);
+ buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N');
+ }
+ });
}
/**
@@ -1433,12 +1492,14 @@ 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);
+ type = type.unalias(compiler);
+ CheckedModeHelper helper =
+ backend.getCheckedModeHelper(type, typeCast: false);
+ FunctionElement helperElement = helper.getElement(compiler);
String helperName = namer.isolateAccess(helperElement);
karlklose 2013/06/19 14:37:05 You can inline helperElement here (it would remove
Johnni Winther 2013/06/21 12:19:15 No. [helperElement] is used below.
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);
@@ -1671,14 +1732,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;
}
/**
@@ -1690,6 +1763,8 @@ class CodeEmitterTask extends CompilerTask {
*/
void generateIsTestsOn(ClassElement cls,
void emitIsTest(Element element),
+ void emitIsFunctionTypeTest(FunctionType type),
karlklose 2013/06/19 14:37:05 You could add Typedefs for these three function ty
Johnni Winther 2013/06/21 12:19:15 Done.
+ void emitFunctionTypeSignature(Element method, FunctionType type),
void emitSubstitution(Element element, {emitNull})) {
if (checkedClasses.contains(cls)) {
emitIsTest(cls);
@@ -1741,7 +1816,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.
@@ -1752,8 +1827,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) {
@@ -1795,6 +1872,53 @@ class CodeEmitterTask extends CompilerTask {
}
}
+ void generateFunctionTypeTests(
karlklose 2013/06/19 14:37:05 Could you get rid of this method? I don't think it
Johnni Winther 2013/06/21 12:19:15 Done.
+ 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;
karlklose 2013/06/19 14:37:05 Perhaps alwaysUseSignature would be clearer.
Johnni Winther 2013/06/21 12:19:15 Done.
+ if (hasDynamicFunctionTypeCheck ||
+ neededPredicates > MAX_FUNCTION_TYPE_PREDICATES) {
+ emitFunctionTypeSignature(method, methodType);
+ hasSignature = true;
+ }
+ functionTypeChecks.forEach((FunctionType functionType,
+ bool knownSubtype) {
karlklose 2013/06/19 14:37:05 This should fit on one line.
Johnni Winther 2013/06/21 12:19:15 Done.
+ if (knownSubtype || !hasDynamicFunctionTypeCheck) {
karlklose 2013/06/19 14:37:05 !knownSubtype implies hasDynamicFunctionTypeCheck,
Johnni Winther 2013/06/21 12:19:15 Done.
+ if (hasSignature) {
+ registerDynamicFunctionTypeCheck(functionType);
+ } else {
+ emitIsFunctionTypeTest(functionType);
+ }
+ }
+ });
+ }
+
/**
* Return a function that returns true if its argument is a class
* that needs to be emitted.
@@ -1953,12 +2077,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(
@@ -1966,6 +2084,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);
}
}
@@ -2023,12 +2158,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.
@@ -2084,10 +2221,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;
karlklose 2013/06/19 14:37:05 You can inline rti.
Johnni Winther 2013/06/21 12:19:15 Done.
+ String encoding = rti.getSignatureEncoding(
+ methodType, () => 'this.${fieldNames[0]}');
+ String operatorSignature = namer.operatorSignature();
+ boundClosureBuilder.addProperty(operatorSignature,
+ new jsAst.LiteralExpression(encoding));
+ }
+
+ void emitIsFunctionTypeTest(FunctionType functionType) {
karlklose 2013/06/19 14:37:05 I think we should be more careful with names in th
Johnni Winther 2013/06/21 12:19:15 Yes. Added a TODO to the newly added typedef for t
+ String operator = namer.operatorIsType(functionType);
+ boundClosureBuilder.addProperty(operator,
+ new jsAst.LiteralBool(true));
+ }
+
+ generateFunctionTypeTestsInternal(member, memberType, functionTypeChecks,
+ emitFunctionTypeSignature, emitIsFunctionTypeTest);
boundClosures.add(
js('$classesCollector.$mangledName = #',
@@ -2118,9 +2269,9 @@ class CodeEmitterTask extends CompilerTask {
arguments.add(new jsAst.LiteralNull());
}
+ jsAst.Expression newClosure = js(closureClass).newWith(arguments);
karlklose 2013/06/19 14:37:05 Why this change?
Johnni Winther 2013/06/21 12:19:15 It was needed in a previous iteration. Removed.
jsAst.Expression getterFunction = js.fun(
- parameters,
- js.return_(js(closureClass).newWith(arguments)));
+ parameters, js.return_(newClosure));
defineStub(getterName, getterFunction);
}
@@ -2160,7 +2311,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);
karlklose 2013/06/19 14:37:05 This is not used.
Johnni Winther 2013/06/21 12:19:15 Removed.
for (Selector selector in selectors) {
if (selector.applies(member, compiler)) {
selector = selector.asUntyped;
@@ -2761,6 +2912,15 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
rti.getClassesUsedInSubstitutions(backend, requiredChecks);
addClassesWithSuperclasses(classesUsedInSubstitutions);
+ // 3c. Add classes that contain checked generic function types. These are
+ // needed to store the signature encoding.
+ for (FunctionType type in checkedFunctionTypes) {
+ ClassElement contextClass = Types.getClassContext(type);
+ if (contextClass != null) {
+ neededClasses.add(contextClass);
+ }
+ }
+
// 4. Finally, sort the classes.
List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses);

Powered by Google App Engine
This is Rietveld 408576698