Chromium Code Reviews| 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 c162f557c6dbe96572cbfae62df10b7f1c0d00b9..cad88ac782533022d448aa63d67767e0ba1ec373 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)); |
| @@ -162,11 +164,37 @@ class RuntimeTypes { |
| 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); |
|
karlklose
2013/06/19 14:37:05
I find it quite hard to follow this code because o
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + if (contextClass != null) { |
| + potentiallyAddForRti(contextClass); |
| + } else if (type.kind == TypeKind.FUNCTION) { |
| + void analyzeMethod(Element method) { |
| + DartType memberType = method.computeType(compiler); |
| + contextClass = Types.getClassContext(memberType); |
| + if (contextClass != null && |
| + compiler.types.isPotentialSubtype(memberType, type)) { |
| + potentiallyAddForRti(contextClass); |
| + methodsNeedingRti.add(method); |
| + } |
| + } |
| + compiler.resolverWorld.closurizedGenericMembers.forEach(analyzeMethod); |
|
karlklose
2013/06/19 14:37:05
Long line.
Johnni Winther
2013/06/21 12:19:15
Done.
|
| + 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 +238,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 +282,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 +309,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 +478,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 applySignature = |
| + backend.namer.getName(backend.getApplySignature()); |
| + String contextName = backend.namer.getName(contextClass); |
| + return 'function () {' |
| + ' return ${backend.namer.SETUP_OBJECT}.' |
| + '$applySignature($encoding, $this_, "$contextName"); ' |
| + '}'; |
| + } else { |
| + return encoding; |
| + } |
| + } |
| + |
| String getTypeRepresentation(DartType type, void onVariable(variable)) { |
| // Create a type representation. For type variables call the original |
| // callback for side effects and return a template placeholder. |
| @@ -432,6 +553,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 +573,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 +613,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 +652,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 +717,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 +733,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 +806,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; |
| } |