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

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

Issue 12334070: Support runtime check of function types. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Minor fix 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/runtime_types.dart
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
index b2cedddd2101e909e8de72d72dce1ab328a9fe97..76b9efce76cb64030ce662e8dfbd59f6055bff35 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
@@ -20,6 +20,7 @@ class RuntimeTypes {
final Map<ClassElement, Set<ClassElement>> rtiDependencies;
final Set<ClassElement> classesNeedingRti;
+ final Set<Element> methodsNeedingRti;
// The set of classes that use one of their type variables as expressions
// to get the runtime type.
final Set<ClassElement> classesUsingTypeVariableExpression;
@@ -30,6 +31,7 @@ class RuntimeTypes {
: this.compiler = compiler,
representationGenerator = new TypeRepresentationGenerator(compiler),
classesNeedingRti = new Set<ClassElement>(),
+ methodsNeedingRti = new Set<Element>(),
rtiDependencies = new Map<ClassElement, Set<ClassElement>>(),
classesUsingTypeVariableExpression = new Set<ClassElement>();
@@ -77,7 +79,7 @@ class RuntimeTypes {
InterfaceType interface = type;
do {
for (DartType argument in interface.typeArguments) {
- universe.isChecks.add(argument);
+ universe.registerIsCheck(argument, compiler);
}
interface = interface.element.supertype;
} while (interface != null && !instantiatedTypes.contains(interface));
@@ -100,7 +102,7 @@ class RuntimeTypes {
InterfaceType instance = current.asInstanceOf(cls);
if (instance == null) break;
for (DartType argument in instance.typeArguments) {
- universe.isChecks.add(argument);
+ universe.registerIsCheck(argument, compiler);
}
current = current.element.supertype;
} while (current != null && !instantiatedTypes.contains(current));
@@ -155,18 +157,50 @@ class RuntimeTypes {
if (backend.jsArrayClass != null) {
registerRtiDependency(backend.jsArrayClass, compiler.listClass);
}
- // Compute the set of all classes that need runtime type information.
+ // Compute the set of all classes and methods that need runtime type
+ // information.
compiler.resolverWorld.isChecks.forEach((DartType type) {
if (type.kind == TypeKind.INTERFACE) {
InterfaceType itf = type;
if (!itf.isRaw) {
potentiallyAddForRti(itf.element);
}
- } else if (type.kind == TypeKind.TYPE_VARIABLE) {
- TypeVariableElement variable = type.element;
- potentiallyAddForRti(variable.enclosingElement);
+ } else {
+ ClassElement contextClass = Types.getClassContext(type);
+ if (contextClass != null) {
+ // [type] contains type variables (declared in [contextClass]) if
+ // [contextClass] is non-null. This handles checks against type
+ // variables and function types containing type variables.
+ potentiallyAddForRti(contextClass);
+ }
+ if (type.kind == TypeKind.FUNCTION) {
+ void analyzeMethod(Element method) {
+ DartType memberType = method.computeType(compiler);
+ ClassElement contextClass = Types.getClassContext(memberType);
+ if (contextClass != null &&
+ compiler.types.isPotentialSubtype(memberType, type)) {
+ potentiallyAddForRti(contextClass);
+ methodsNeedingRti.add(method);
+ }
+ }
+ compiler.resolverWorld.closurizedGenericMembers.forEach(
+ analyzeMethod);
+ compiler.resolverWorld.genericCallMethods.forEach(analyzeMethod);
+ }
}
});
+ if (compiler.enableTypeAssertions) {
+ void analyzeMethod(Element method) {
+ DartType memberType = method.computeType(compiler);
+ ClassElement contextClass = Types.getClassContext(memberType);
+ if (contextClass != null) {
+ potentiallyAddForRti(contextClass);
+ methodsNeedingRti.add(method);
+ }
+ }
+ compiler.resolverWorld.closurizedGenericMembers.forEach(analyzeMethod);
+ compiler.resolverWorld.genericCallMethods.forEach(analyzeMethod);
+ }
// Add the classes that need RTI because they use a type variable as
// expression.
classesUsingTypeVariableExpression.forEach(potentiallyAddForRti);
@@ -210,12 +244,43 @@ class RuntimeTypes {
}
void computeRequiredChecks() {
- computeInstantiatedArguments(compiler.codegenWorld);
- computeCheckedArguments(compiler.codegenWorld);
+ Set<DartType> isChecks = compiler.codegenWorld.isChecks;
+ bool hasFunctionTypeCheck =
+ isChecks.any((type) => identical(type.kind, TypeKind.FUNCTION));
+ Set<DartType> instantiatedTypesAndClosures = hasFunctionTypeCheck
+ ? computeInstantiatedTypesAndClosures(compiler.codegenWorld)
+ : compiler.codegenWorld.instantiatedTypes;
+ computeInstantiatedArguments(instantiatedTypesAndClosures, isChecks);
+ computeCheckedArguments(instantiatedTypesAndClosures, isChecks);
cachedRequiredChecks =
computeChecks(allInstantiatedArguments, checkedArguments);
}
+ Set<DartType> computeInstantiatedTypesAndClosures(Universe universe) {
+ Set<DartType> instantiatedTypes =
+ new Set<DartType>.from(universe.instantiatedTypes);
+ for (DartType instantiatedType in universe.instantiatedTypes) {
+ if (instantiatedType.kind == TypeKind.INTERFACE) {
+ Member member =
+ instantiatedType.lookupMember(Compiler.CALL_OPERATOR_NAME);
+ if (member != null) {
+ instantiatedTypes.add(member.computeType(compiler));
+ }
+ }
+ }
+ for (FunctionElement element in universe.staticFunctionsNeedingGetter) {
+ instantiatedTypes.add(element.computeType(compiler));
+ }
+ // TODO(johnniwinther): We should get this information through the
+ // [neededClasses] computed in the emitter instead of storing it and pulling
+ // it from resolution, but currently it would introduce a cyclic dependency
+ // between [computeRequiredChecks] and [computeNeededClasses].
+ for (Element element in compiler.resolverWorld.closurizedMembers) {
+ instantiatedTypes.add(element.computeType(compiler));
+ }
+ return instantiatedTypes;
+ }
+
/**
* Collects all types used in type arguments of instantiated types.
*
@@ -223,14 +288,26 @@ class RuntimeTypes {
* have a type check against this supertype that includes a check against
* the type arguments.
*/
- void computeInstantiatedArguments(Universe universe) {
+ void computeInstantiatedArguments(Set<DartType> instantiatedTypes,
+ Set<DartType> isChecks) {
ArgumentCollector superCollector = new ArgumentCollector(backend);
ArgumentCollector directCollector = new ArgumentCollector(backend);
- for (DartType type in universe.instantiatedTypes) {
+ FunctionArgumentCollector functionArgumentCollector =
+ new FunctionArgumentCollector(backend);
+
+ // We need to add classes occuring in function type arguments, like for
+ // instance 'I' for [: o is C<f> :] where f is [: typedef I f(); :].
+ for (DartType type in isChecks) {
+ functionArgumentCollector.collect(type);
+ }
+
+ for (DartType type in instantiatedTypes) {
directCollector.collect(type);
- ClassElement cls = type.element;
- for (DartType supertype in cls.allSupertypes) {
- superCollector.collect(supertype);
+ if (type.kind == TypeKind.INTERFACE) {
+ ClassElement cls = type.element;
+ for (DartType supertype in cls.allSupertypes) {
+ superCollector.collect(supertype);
+ }
}
}
for (ClassElement cls in superCollector.classes.toList()) {
@@ -238,18 +315,33 @@ class RuntimeTypes {
superCollector.collect(supertype);
}
}
- directlyInstantiatedArguments = directCollector.classes;
+
+ directlyInstantiatedArguments =
+ directCollector.classes..addAll(functionArgumentCollector.classes);
allInstantiatedArguments =
superCollector.classes..addAll(directlyInstantiatedArguments);
}
/// Collects all type arguments used in is-checks.
- void computeCheckedArguments(Universe universe) {
+ void computeCheckedArguments(Set<DartType> instantiatedTypes,
+ Set<DartType> isChecks) {
ArgumentCollector collector = new ArgumentCollector(backend);
- for (DartType type in universe.isChecks) {
+ FunctionArgumentCollector functionArgumentCollector =
+ new FunctionArgumentCollector(backend);
+
+ // We need to add types occuring in function type arguments, like for
+ // instance 'J' for [: (J j) {} is f :] where f is
+ // [: typedef void f(I i); :] and 'J' is a subtype of 'I'.
+ for (DartType type in instantiatedTypes) {
+ functionArgumentCollector.collect(type);
+ }
+
+ for (DartType type in isChecks) {
collector.collect(type);
}
- checkedArguments = collector.classes;
+
+ checkedArguments =
+ collector.classes..addAll(functionArgumentCollector.classes);
}
Set<ClassElement> getClassesUsedInSubstitutions(JavaScriptBackend backend,
@@ -392,6 +484,41 @@ class RuntimeTypes {
return '[$code]';
}
+ String getTypeEncoding(DartType type,
+ {bool alwaysGenerateFunction: false}) {
+ ClassElement contextClass = Types.getClassContext(type);
+ String onVariable(TypeVariableType v) {
+ return v.toString();
+ };
+ String encoding = _getTypeRepresentation(type, onVariable);
+ if (contextClass == null && !alwaysGenerateFunction) {
+ return encoding;
+ } else {
+ String parameters = contextClass != null
+ ? contextClass.typeVariables.toList().join(', ')
+ : '';
+ return 'function ($parameters) { return $encoding; }';
+ }
+ }
+
+ String getSignatureEncoding(DartType type, String generateThis()) {
+ ClassElement contextClass = Types.getClassContext(type);
+ String encoding = getTypeEncoding(type, alwaysGenerateFunction: true);
+ if (contextClass != null) {
+ String this_ = generateThis();
+ JavaScriptBackend backend = compiler.backend;
+ String computeSignature =
+ backend.namer.getName(backend.getComputeSignature());
+ String contextName = backend.namer.getName(contextClass);
+ return 'function () {'
+ ' return ${backend.namer.GLOBAL_OBJECT}.'
+ '$computeSignature($encoding, $this_, "$contextName"); '
+ '}';
+ } else {
+ return encoding;
+ }
+ }
+
String getTypeRepresentation(DartType type, VariableSubstitution onVariable) {
// Create a type representation. For type variables call the original
// callback for side effects and return a template placeholder.
@@ -432,6 +559,9 @@ class TypeRepresentationGenerator extends DartTypeVisitor {
OnVariableCallback onVariable;
StringBuffer builder;
+ JavaScriptBackend get backend => compiler.backend;
+ Namer get namer => backend.namer;
+
TypeRepresentationGenerator(Compiler this.compiler);
/**
@@ -449,8 +579,6 @@ class TypeRepresentationGenerator extends DartTypeVisitor {
}
String getJsName(Element element) {
- JavaScriptBackend backend = compiler.backend;
- Namer namer = backend.namer;
return namer.isolateAccess(backend.getImplementationClass(element));
}
@@ -491,25 +619,26 @@ class TypeRepresentationGenerator extends DartTypeVisitor {
}
visitFunctionType(FunctionType type, _) {
- builder.write('{func: true');
+ builder.write('{${namer.functionTypeTag()}:'
+ ' "${namer.getFunctionTypeName(type)}"');
if (type.returnType.isVoid) {
- builder.write(', retvoid: true');
+ builder.write(', ${namer.functionTypeVoidReturnTag()}: true');
} else if (!type.returnType.isDynamic) {
- builder.write(', ret: ');
+ builder.write(', ${namer.functionTypeReturnTypeTag()}: ');
visit(type.returnType);
}
if (!type.parameterTypes.isEmpty) {
- builder.write(', args: [');
+ builder.write(', ${namer.functionTypeRequiredParametersTag()}: [');
visitList(type.parameterTypes);
builder.write(']');
}
if (!type.optionalParameterTypes.isEmpty) {
- builder.write(', opt: [');
+ builder.write(', ${namer.functionTypeOptionalParametersTag()}: [');
visitList(type.optionalParameterTypes);
builder.write(']');
}
if (!type.namedParameterTypes.isEmpty) {
- builder.write(', named: {');
+ builder.write(', ${namer.functionTypeNamedParametersTag()}: {');
bool first = true;
Link<SourceString> names = type.namedParameters;
Link<DartType> types = type.namedParameterTypes;
@@ -529,8 +658,13 @@ class TypeRepresentationGenerator extends DartTypeVisitor {
builder.write('}');
}
+ visitMalformedType(MalformedType type, _) {
+ // Treat malformed types as dynamic at runtime.
+ builder.write('null');
+ }
+
visitType(DartType type, _) {
- compiler.internalError('Unexpected type: $type');
+ compiler.internalError('Unexpected type: $type (${type.kind})');
}
}
@@ -589,6 +723,10 @@ class ArgumentCollector extends DartTypeVisitor {
// Do not collect [:dynamic:].
}
+ visitTypedefType(TypedefType type, bool isTypeArgument) {
+ type.unalias(backend.compiler).accept(this, isTypeArgument);
+ }
+
visitInterfaceType(InterfaceType type, bool isTypeArgument) {
if (isTypeArgument) {
classes.add(backend.getImplementationClass(type.element));
@@ -601,6 +739,48 @@ class ArgumentCollector extends DartTypeVisitor {
}
}
+class FunctionArgumentCollector extends DartTypeVisitor {
+ final JavaScriptBackend backend;
+ final Set<ClassElement> classes = new Set<ClassElement>();
+
+ FunctionArgumentCollector(this.backend);
+
+ collect(DartType type) {
+ type.accept(this, false);
+ }
+
+ /// Collect all types in the list as if they were arguments of an
+ /// InterfaceType.
+ collectAll(Link<DartType> types) {
+ for (Link<DartType> link = types; !link.isEmpty; link = link.tail) {
+ link.head.accept(this, true);
+ }
+ }
+
+ visitType(DartType type, _) {
+ // Do nothing.
+ }
+
+ visitDynamicType(DynamicType type, _) {
+ // Do not collect [:dynamic:].
+ }
+
+ visitTypedefType(TypedefType type, bool inFunctionType) {
+ type.unalias(backend.compiler).accept(this, inFunctionType);
+ }
+
+ visitInterfaceType(InterfaceType type, bool inFunctionType) {
+ if (inFunctionType) {
+ classes.add(backend.getImplementationClass(type.element));
+ }
+ type.visitChildren(this, inFunctionType);
+ }
+
+ visitFunctionType(FunctionType type, _) {
+ type.visitChildren(this, true);
+ }
+}
+
/**
* Representation of the substitution of type arguments
* when going from the type of a class to one of its supertypes.
@@ -632,7 +812,7 @@ class Substitution {
String formals = parameters.toList().map(variableName).join(', ');
return 'function ($formals) { return $code; }';
} else if (ensureIsFunction) {
- return 'function() { return $code; }';
+ return 'function () { return $code; }';
} else {
return code;
}

Powered by Google App Engine
This is Rietveld 408576698