Linter Demo Errors: 6Warnings: 123File: /home/fstrocco/Dart/dart/benchmark/compiler/lib/src/js_backend/runtime_types.dart // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. part of js_backend; /// For each class, stores the possible class subtype tests that could succeed. abstract class TypeChecks { /// Get the set of checks required for class [element]. Iterable operator[](ClassElement element); /// Get the iterator for all classes that need type checks. Iterator get iterator; } typedef jsAst.Expression OnVariableCallback(TypeVariableType variable); typedef bool ShouldEncodeTypedefCallback(TypedefType variable); class RuntimeTypes { final Compiler compiler; final TypeRepresentationGenerator representationGenerator; final Map> rtiDependencies; final Set classesNeedingRti; final Set methodsNeedingRti; // The set of classes that use one of their type variables as expressions // to get the runtime type. final Set classesUsingTypeVariableExpression; // The set of type arguments tested against type variable bounds. final Set checkedTypeArguments; // The set of tested type variable bounds. final Set checkedBounds; JavaScriptBackend get backend => compiler.backend; String get getFunctionThatReturnsNullName => backend.namer.internalGlobal('functionThatReturnsNull'); RuntimeTypes(Compiler compiler) : this.compiler = compiler, representationGenerator = new TypeRepresentationGenerator(compiler), classesNeedingRti = new Set(), methodsNeedingRti = new Set(), rtiDependencies = new Map>(), classesUsingTypeVariableExpression = new Set(), checkedTypeArguments = new Set(), checkedBounds = new Set(); Set directlyInstantiatedArguments; Set allInstantiatedArguments; Set checkedArguments; void registerRtiDependency(Element element, Element dependency) { // We're not dealing with typedef for now. if (!element.isClass || !dependency.isClass) return; Set classes = rtiDependencies.putIfAbsent(element, () => new Set()); classes.add(dependency); } void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, DartType bound) { checkedTypeArguments.add(typeArgument); checkedBounds.add(bound); } // TODO(21969): remove this and analyze instantiated types and factory calls // instead to find out which types are instantiated, if finitely many, or if // we have to use the more imprecise generic algorithm. bool get cannotDetermineInstantiatedTypesPrecisely => true; /** * Compute type arguments of classes that use one of their type variables in * is-checks and add the is-checks that they imply. * * This function must be called after all is-checks have been registered. * * TODO(karlklose): move these computations into a function producing an * immutable datastructure. */ void addImplicitChecks(Universe universe, Iterable classesUsingChecks) { // If there are no classes that use their variables in checks, there is // nothing to do. if (classesUsingChecks.isEmpty) return; Set instantiatedTypes = universe.instantiatedTypes; if (cannotDetermineInstantiatedTypesPrecisely) { for (DartType type in instantiatedTypes) { if (type.kind != TypeKind.INTERFACE) continue; InterfaceType interface = type; do { for (DartType argument in interface.typeArguments) { universe.registerIsCheck(argument, compiler); } interface = interface.element.supertype; } while (interface != null && !instantiatedTypes.contains(interface)); } } else { // Find all instantiated types that are a subtype of a class that uses // one of its type arguments in an is-check and add the arguments to the // set of is-checks. // TODO(karlklose): replace this with code that uses a subtype lookup // datastructure in the world. for (DartType type in instantiatedTypes) { if (type.kind != TypeKind.INTERFACE) continue; InterfaceType classType = type; for (ClassElement cls in classesUsingChecks) { InterfaceType current = classType; do { // We need the type as instance of its superclass anyway, so we just // try to compute the substitution; if the result is [:null:], the // classes are not related. InterfaceType instance = current.asInstanceOf(cls); if (instance == null) break; for (DartType argument in instance.typeArguments) { universe.registerIsCheck(argument, compiler); } current = current.element.supertype; } while (current != null && !instantiatedTypes.contains(current)); } } } } void computeClassesNeedingRti() { // Find the classes that need runtime type information. Such // classes are: // (1) used in a is check with type variables, // (2) dependencies of classes in (1), // (3) subclasses of (2) and (3). void potentiallyAddForRti(ClassElement cls) { assert(invariant(cls, cls.isDeclaration)); if (cls.typeVariables.isEmpty) return; if (classesNeedingRti.contains(cls)) return; classesNeedingRti.add(cls); // TODO(ngeoffray): This should use subclasses, not subtypes. Iterable classes = compiler.world.subtypesOf(cls); classes.forEach((ClassElement sub) { potentiallyAddForRti(sub); }); Set dependencies = rtiDependencies[cls]; if (dependencies != null) { dependencies.forEach((ClassElement other) { potentiallyAddForRti(other); }); } } Set classesUsingTypeVariableTests = new Set(); compiler.resolverWorld.isChecks.forEach((DartType type) { if (type.isTypeVariable) { TypeVariableElement variable = type.element; classesUsingTypeVariableTests.add(variable.typeDeclaration); } }); // Add is-checks that result from classes using type variables in checks. addImplicitChecks(compiler.resolverWorld, classesUsingTypeVariableTests); // Add the rti dependencies that are implicit in the way the backend // generates code: when we create a new [List], we actually create // a JSArray in the backend and we need to add type arguments to // the calls of the list constructor whenever we determine that // JSArray needs type arguments. // TODO(karlklose): make this dependency visible from code. if (backend.jsArrayClass != null) { registerRtiDependency(backend.jsArrayClass, compiler.listClass); } // Compute the set of all classes and methods that need runtime type // information. compiler.resolverWorld.isChecks.forEach((DartType type) { if (type.isInterfaceType) { InterfaceType itf = type; if (!itf.treatAsRaw) { potentiallyAddForRti(itf.element); } } 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.isFunctionType) { void analyzeMethod(TypedElement method) { DartType memberType = method.type; ClassElement contextClass = Types.getClassContext(memberType); if (contextClass != null && compiler.types.isPotentialSubtype(memberType, type)) { potentiallyAddForRti(contextClass); methodsNeedingRti.add(method); } } compiler.resolverWorld.closuresWithFreeTypeVariables.forEach( analyzeMethod); compiler.resolverWorld.callMethodsWithFreeTypeVariables.forEach( analyzeMethod); } } }); if (compiler.enableTypeAssertions) { void analyzeMethod(TypedElement method) { DartType memberType = method.type; ClassElement contextClass = Types.getClassContext(memberType); if (contextClass != null) { potentiallyAddForRti(contextClass); methodsNeedingRti.add(method); } } compiler.resolverWorld.closuresWithFreeTypeVariables.forEach( analyzeMethod); compiler.resolverWorld.callMethodsWithFreeTypeVariables.forEach( analyzeMethod); } // Add the classes that need RTI because they use a type variable as // expression. classesUsingTypeVariableExpression.forEach(potentiallyAddForRti); } TypeChecks cachedRequiredChecks; TypeChecks get requiredChecks { if (cachedRequiredChecks == null) { computeRequiredChecks(); } assert(cachedRequiredChecks != null); return cachedRequiredChecks; } /// Compute the required type checkes and substitutions for the given /// instantitated and checked classes. TypeChecks computeChecks(Set instantiated, Set checked) { // Run through the combination of instantiated and checked // arguments and record all combination where the element of a checked // argument is a superclass of the element of an instantiated type. TypeCheckMapping result = new TypeCheckMapping(); for (ClassElement element in instantiated) { if (checked.contains(element)) { result.add(element, element, null); } // Find all supertypes of [element] in [checkedArguments] and add checks // and precompute the substitutions for them. assert(invariant(element, element.allSupertypes != null, message: 'Supertypes have not been computed for $element.')); for (DartType supertype in element.allSupertypes) { ClassElement superelement = supertype.element; if (checked.contains(superelement)) { Substitution substitution = computeSubstitution(element, superelement); result.add(element, superelement, substitution); } } } return result; } void computeRequiredChecks() { Set isChecks = compiler.codegenWorld.isChecks; // These types are needed for is-checks against function types. Set instantiatedTypesAndClosures = computeInstantiatedTypesAndClosures(compiler.codegenWorld); computeInstantiatedArguments(instantiatedTypesAndClosures, isChecks); computeCheckedArguments(instantiatedTypesAndClosures, isChecks); cachedRequiredChecks = computeChecks(allInstantiatedArguments, checkedArguments); } Set computeInstantiatedTypesAndClosures(Universe universe) { Set instantiatedTypes = new Set.from(universe.instantiatedTypes); for (DartType instantiatedType in universe.instantiatedTypes) { if (instantiatedType.isInterfaceType) { InterfaceType interface = instantiatedType; FunctionType callType = interface.callType; if (callType != null) { instantiatedTypes.add(callType); } } } for (FunctionElement element in universe.staticFunctionsNeedingGetter) { instantiatedTypes.add(element.type); } // 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 (TypedElement element in compiler.resolverWorld.closurizedMembers) { instantiatedTypes.add(element.type); } return instantiatedTypes; } /** * Collects all types used in type arguments of instantiated types. * * This includes type arguments used in supertype relations, because we may * have a type check against this supertype that includes a check against * the type arguments. */ void computeInstantiatedArguments(Set instantiatedTypes, Set isChecks) { ArgumentCollector superCollector = new ArgumentCollector(backend); ArgumentCollector directCollector = new ArgumentCollector(backend); FunctionArgumentCollector functionArgumentCollector = new FunctionArgumentCollector(backend); // We need to add classes occuring in function type arguments, like for // instance 'I' for [: o is C :] where f is [: typedef I f(); :]. void collectFunctionTypeArguments(Iterable types) { for (DartType type in types) { functionArgumentCollector.collect(type); } } collectFunctionTypeArguments(isChecks); collectFunctionTypeArguments(checkedBounds); void collectTypeArguments(Iterable types, {bool isTypeArgument: false}) { for (DartType type in types) { directCollector.collect(type, isTypeArgument: isTypeArgument); if (type.isInterfaceType) { ClassElement cls = type.element; for (DartType supertype in cls.allSupertypes) { superCollector.collect(supertype, isTypeArgument: isTypeArgument); } } } } collectTypeArguments(instantiatedTypes); collectTypeArguments(checkedTypeArguments, isTypeArgument: true); for (ClassElement cls in superCollector.classes.toList()) { for (DartType supertype in cls.allSupertypes) { superCollector.collect(supertype); } } directlyInstantiatedArguments = directCollector.classes..addAll(functionArgumentCollector.classes); allInstantiatedArguments = superCollector.classes..addAll(directlyInstantiatedArguments); } /// Collects all type arguments used in is-checks. void computeCheckedArguments(Set instantiatedTypes, Set isChecks) { ArgumentCollector collector = new ArgumentCollector(backend); 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'. void collectFunctionTypeArguments(Iterable types) { for (DartType type in types) { functionArgumentCollector.collect(type); } } collectFunctionTypeArguments(instantiatedTypes); collectFunctionTypeArguments(checkedTypeArguments); void collectTypeArguments(Iterable types, {bool isTypeArgument: false}) { for (DartType type in types) { collector.collect(type, isTypeArgument: isTypeArgument); } } collectTypeArguments(isChecks); collectTypeArguments(checkedBounds, isTypeArgument: true); checkedArguments = collector.classes..addAll(functionArgumentCollector.classes); } Set getClassesUsedInSubstitutions(JavaScriptBackend backend, TypeChecks checks) { Set instantiated = new Set(); ArgumentCollector collector = new ArgumentCollector(backend); for (ClassElement target in checks) { instantiated.add(target); for (TypeCheck check in checks[target]) { Substitution substitution = check.substitution; if (substitution != null) { collector.collectAll(substitution.arguments); } } } return instantiated..addAll(collector.classes); } Set getRequiredArgumentClasses(JavaScriptBackend backend) { Set requiredArgumentClasses = new Set.from( getClassesUsedInSubstitutions(backend, requiredChecks)); return requiredArgumentClasses ..addAll(directlyInstantiatedArguments) ..addAll(checkedArguments); } String getTypeRepresentationForTypeConstant(DartType type) { JavaScriptBackend backend = compiler.backend; Namer namer = backend.namer; if (type.isDynamic) return namer.runtimeTypeName(null); String name = namer.uniqueNameForTypeConstantElement(type.element); if (!type.element.isClass) return name; InterfaceType interface = type; List variables = interface.element.typeVariables; // Type constants can currently only be raw types, so there is no point // adding ground-term type parameters, as they would just be 'dynamic'. // TODO(sra): Since the result string is used only in constructing constant // names, it would result in more readable names if the final string was a // legal JavaScript identifer. if (variables.isEmpty) return name; String arguments = new List.filled(variables.length, 'dynamic').join(', '); return '$name<$arguments>'; } // TODO(karlklose): maybe precompute this value and store it in typeChecks? bool isTrivialSubstitution(ClassElement cls, ClassElement check) { if (cls.isClosure) { // TODO(karlklose): handle closures. return true; } // If there are no type variables or the type is the same, we do not need // a substitution. if (check.typeVariables.isEmpty || cls == check) { return true; } InterfaceType originalType = cls.thisType; InterfaceType type = originalType.asInstanceOf(check); // [type] is not a subtype of [check]. we do not generate a check and do not // need a substitution. if (type == null) return true; // Run through both lists of type variables and check if the type variables // are identical at each position. If they are not, we need to calculate a // substitution function. List variables = cls.typeVariables; List arguments = type.typeArguments; if (variables.length != arguments.length) { return false; } for (int index = 0; index < variables.length; index++) { if (variables[index].element != arguments[index].element) { return false; } } return true; } /** * Compute a JavaScript expression that describes the necessary substitution * for type arguments in a subtype test. * * The result can be: * 1) `null`, if no substituted check is necessary, because the * type variables are the same or there are no type variables in the class * that is checked for. * 2) A list expression describing the type arguments to be used in the * subtype check, if the type arguments to be used in the check do not * depend on the type arguments of the object. * 3) A function mapping the type variables of the object to be checked to * a list expression. */ jsAst.Expression getSupertypeSubstitution( ClassElement cls, ClassElement check) { Substitution substitution = getSubstitution(cls, check); if (substitution != null) { return substitution.getCode(this); } else { return null; } } Substitution getSubstitution(ClassElement cls, ClassElement other) { // Look for a precomputed check. for (TypeCheck check in cachedRequiredChecks[cls]) { if (check.cls == other) { return check.substitution; } } // There is no precomputed check for this pair (because the check is not // done on type arguments only. Compute a new substitution. return computeSubstitution(cls, other); } Substitution computeSubstitution(ClassElement cls, ClassElement check, { bool alwaysGenerateFunction: false }) { if (isTrivialSubstitution(cls, check)) return null; // Unnamed mixin application classes do not need substitutions, because they // are never instantiated and their checks are overwritten by the class that // they are mixed into. InterfaceType type = cls.thisType; InterfaceType target = type.asInstanceOf(check); List typeVariables = cls.typeVariables; if (typeVariables.isEmpty && !alwaysGenerateFunction) { return new Substitution.list(target.typeArguments); } else { return new Substitution.function(target.typeArguments, typeVariables); } } jsAst.Expression getSubstitutionRepresentation( List types, OnVariableCallback onVariable) { List elements = types .map((DartType type) => getTypeRepresentation(type, onVariable)) .toList(growable: false); return new jsAst.ArrayInitializer(elements); } jsAst.Expression getTypeEncoding(DartType type, {bool alwaysGenerateFunction: false}) { ClassElement contextClass = Types.getClassContext(type); jsAst.Expression onVariable(TypeVariableType v) { return new jsAst.VariableUse(v.name); }; jsAst.Expression encoding = getTypeRepresentation(type, onVariable); if (contextClass == null && !alwaysGenerateFunction) { return encoding; } else { List parameters = const []; if (contextClass != null) { parameters = contextClass.typeVariables.map((type) { return type.toString(); }).toList(); } return js('function(#) { return # }', [parameters, encoding]); } } jsAst.Expression getSignatureEncoding(DartType type, jsAst.Expression this_) { ClassElement contextClass = Types.getClassContext(type); jsAst.Expression encoding = getTypeEncoding(type, alwaysGenerateFunction: true); if (contextClass != null) { JavaScriptBackend backend = compiler.backend; String contextName = backend.namer.className(contextClass); return js('function () { return #(#, #, #); }', [ backend.emitter.staticFunctionAccess(backend.getComputeSignature()), encoding, this_, js.string(contextName) ]); } else { return encoding; } } String getTypeRepresentationWithHashes(DartType type, OnVariableCallback onVariable) { // Create a type representation. For type variables call the original // callback for side effects and return a template placeholder. jsAst.Expression representation = getTypeRepresentation(type, (variable) { onVariable(variable); return new jsAst.LiteralString('#'); }); return jsAst.prettyPrint(representation, compiler).buffer.toString(); } jsAst.Expression getTypeRepresentation( DartType type, OnVariableCallback onVariable, [ShouldEncodeTypedefCallback shouldEncodeTypedef]) { return representationGenerator.getTypeRepresentation( type, onVariable, shouldEncodeTypedef); } bool isSimpleFunctionType(FunctionType type) { if (!type.returnType.isDynamic) return false; if (!type.optionalParameterTypes.isEmpty) return false; if (!type.namedParameterTypes.isEmpty) return false; for (DartType parameter in type.parameterTypes ) { if (!parameter.isDynamic) return false; } return true; } static bool hasTypeArguments(DartType type) { if (type is InterfaceType) { InterfaceType interfaceType = type; return !interfaceType.treatAsRaw; } return false; } static int getTypeVariableIndex(TypeVariableElement variable) { ClassElement classElement = variable.enclosingClass; List variables = classElement.typeVariables; for (int index = 0; index < variables.length; index++) { if (variables[index].element == variable) return index; } throw invariant(variable, false, message: "Couldn't find type-variable index"); } /// Return all classes that are referenced in the type of the function, i.e., /// in the return type or the argument types. Set getReferencedClasses(FunctionType type) { FunctionArgumentCollector collector = new FunctionArgumentCollector(backend); collector.collect(type); return collector.classes; } } class TypeRepresentationGenerator extends DartTypeVisitor { final Compiler compiler; OnVariableCallback onVariable; ShouldEncodeTypedefCallback shouldEncodeTypedef; JavaScriptBackend get backend => compiler.backend; Namer get namer => backend.namer; TypeRepresentationGenerator(Compiler this.compiler); /** * Creates a type representation for [type]. [onVariable] is called to provide * the type representation for type variables. */ jsAst.Expression getTypeRepresentation( DartType type, OnVariableCallback onVariable, ShouldEncodeTypedefCallback encodeTypedef) { this.onVariable = onVariable; this.shouldEncodeTypedef = (encodeTypedef != null) ? encodeTypedef : (TypedefType type) => false; jsAst.Expression representation = visit(type); this.onVariable = null; this.shouldEncodeTypedef = null; return representation; } jsAst.Expression getJavaScriptClassName(Element element) { return backend.emitter.typeAccess(element); } visit(DartType type) { return type.accept(this, null); } visitTypeVariableType(TypeVariableType type, _) { return onVariable(type); } visitDynamicType(DynamicType type, _) { return js('null'); } visitInterfaceType(InterfaceType type, _) { jsAst.Expression name = getJavaScriptClassName(type.element); return type.treatAsRaw ? name : visitList(type.typeArguments, head: name); } jsAst.Expression visitList(List types, {jsAst.Expression head}) { int index = 0; List elements = []; if (head != null) { elements.add(head); index++; } for (DartType type in types) { jsAst.Expression element = visit(type); if (element is jsAst.LiteralNull) { elements.add(new jsAst.ArrayHole()); } else { elements.add(element); } } return new jsAst.ArrayInitializer(elements); } visitFunctionType(FunctionType type, _) { List properties = []; void addProperty(String name, jsAst.Expression value) { properties.add(new jsAst.Property(js.string(name), value)); } addProperty(namer.functionTypeTag, js.string('')); if (type.returnType.isVoid) { addProperty(namer.functionTypeVoidReturnTag, js('true')); } else if (!type.returnType.treatAsDynamic) { addProperty(namer.functionTypeReturnTypeTag, visit(type.returnType)); } if (!type.parameterTypes.isEmpty) { addProperty(namer.functionTypeRequiredParametersTag, visitList(type.parameterTypes)); } if (!type.optionalParameterTypes.isEmpty) { addProperty(namer.functionTypeOptionalParametersTag, visitList(type.optionalParameterTypes)); } if (!type.namedParameterTypes.isEmpty) { List namedArguments = []; List names = type.namedParameters; List types = type.namedParameterTypes; assert(types.length == names.length); for (int index = 0; index < types.length; index++) { jsAst.Expression name = js.string(names[index]); namedArguments.add(new jsAst.Property(name, visit(types[index]))); } addProperty(namer.functionTypeNamedParametersTag, new jsAst.ObjectInitializer(namedArguments)); } return new jsAst.ObjectInitializer(properties); } visitMalformedType(MalformedType type, _) { // Treat malformed types as dynamic at runtime. return js('null'); } visitVoidType(VoidType type, _) { // TODO(ahe): Reify void type ("null" means "dynamic"). return js('null'); } visitTypedefType(TypedefType type, _) { bool shouldEncode = shouldEncodeTypedef(type); DartType unaliasedType = type.unalias(compiler); if (shouldEncode) { jsAst.ObjectInitializer initializer = unaliasedType.accept(this, null); // We have to encode the aliased type. jsAst.Expression name = getJavaScriptClassName(type.element); jsAst.Expression encodedTypedef = type.treatAsRaw ? name : visitList(type.typeArguments, head: name); // Add it to the function-type object. jsAst.LiteralString tag = js.string(namer.typedefTag); initializer.properties.add(new jsAst.Property(tag, encodedTypedef)); return initializer; } else { return unaliasedType.accept(this, null); } } visitType(DartType type, _) { compiler.internalError(NO_LOCATION_SPANNABLE, 'Unexpected type: $type (${type.kind}).'); } } class TypeCheckMapping implements TypeChecks { final Map> map = new Map>(); Iterable operator[](ClassElement element) { Set result = map[element]; return result != null ? result : const []; } void add(ClassElement cls, ClassElement check, Substitution substitution) { map.putIfAbsent(cls, () => new Set()); map[cls].add(new TypeCheck(check, substitution)); } Iterator get iterator => map.keys.iterator; String toString() { StringBuffer sb = new StringBuffer(); for (ClassElement holder in this) { for (ClassElement check in [holder]) { sb.write('${holder.name}.' '${check.name}, '); } } return '[$sb]'; } } class ArgumentCollector extends DartTypeVisitor { final JavaScriptBackend backend; final Set classes = new Set(); ArgumentCollector(this.backend); collect(DartType type, {bool isTypeArgument: false}) { type.accept(this, isTypeArgument); } /// Collect all types in the list as if they were arguments of an /// InterfaceType. collectAll(List types) { for (DartType type in types) { type.accept(this, true); } } visitType(DartType type, _) { // Do nothing. } visitDynamicType(DynamicType type, _) { // 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(type.element); type.visitChildren(this, true); } visitFunctionType(FunctionType type, _) { type.visitChildren(this, true); } } class FunctionArgumentCollector extends DartTypeVisitor { final JavaScriptBackend backend; final Set classes = new Set(); 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 types) { for (Link 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(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. * * For [:class B extends A, int>:], the substitution is * the representation of [: (T) => [, int] :]. For more details * of the representation consult the documentation of * [getSupertypeSubstitution]. */ //TODO(floitsch): Remove support for non-function substitutions. class Substitution { final bool isFunction; final List arguments; final List parameters; Substitution.list(this.arguments) : isFunction = false, parameters = const []; Substitution.function(this.arguments, this.parameters) : isFunction = true; jsAst.Expression getCode(RuntimeTypes rti) { jsAst.Expression declaration(TypeVariableType variable) { return new jsAst.Parameter( rti.backend.namer.safeVariableName(variable.name)); } jsAst.Expression use(TypeVariableType variable) { return new jsAst.VariableUse( rti.backend.namer.safeVariableName(variable.name)); } if (arguments.every((DartType type) => type.isDynamic)) { return rti.backend.emitter.emitter.generateFunctionThatReturnsNull(); } else { jsAst.Expression value = rti.getSubstitutionRepresentation(arguments, use); if (isFunction) { Iterable formals = parameters.map(declaration); return js('function(#) { return # }', [formals, value]); } else { return js('function() { return # }', value); } } } jsAst.Expression getCodeForVariable(int index, RuntimeTypes rti) { jsAst.Expression declaration(TypeVariableType variable) { return new jsAst.Parameter( rti.backend.namer.safeVariableName(variable.name)); } jsAst.Expression use(TypeVariableType variable) { return new jsAst.VariableUse( rti.backend.namer.safeVariableName(variable.name)); } if (arguments[index].isDynamic) { return rti.backend.emitter.emitter.generateFunctionThatReturnsNull(); } else { jsAst.Expression value = rti.getTypeRepresentation(arguments[index], use); Iterable formals = parameters.map(declaration); return js('function(#) { return # }', [formals, value]); } } } /** * A pair of a class that we need a check against and the type argument * substition for this check. */ class TypeCheck { final ClassElement cls; final Substitution substitution; final int hashCode = (nextHash++) & 0x3fffffff; static int nextHash = 49; TypeCheck(this.cls, this.substitution); }