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; |
} |