| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of js_backend; | |
| 6 | |
| 7 /// For each class, stores the possible class subtype tests that could succeed. | |
| 8 abstract class TypeChecks { | |
| 9 /// Get the set of checks required for class [element]. | |
| 10 Iterable<TypeCheck> operator[](ClassElement element); | |
| 11 /// Get the iterator for all classes that need type checks. | |
| 12 Iterator<ClassElement> get iterator; | |
| 13 } | |
| 14 | |
| 15 typedef jsAst.Expression OnVariableCallback(TypeVariableType variable); | |
| 16 typedef bool ShouldEncodeTypedefCallback(TypedefType variable); | |
| 17 | |
| 18 class RuntimeTypes { | |
| 19 final Compiler compiler; | |
| 20 final TypeRepresentationGenerator representationGenerator; | |
| 21 | |
| 22 final Map<ClassElement, Set<ClassElement>> rtiDependencies; | |
| 23 final Set<ClassElement> classesNeedingRti; | |
| 24 final Set<Element> methodsNeedingRti; | |
| 25 // The set of classes that use one of their type variables as expressions | |
| 26 // to get the runtime type. | |
| 27 final Set<ClassElement> classesUsingTypeVariableExpression; | |
| 28 // The set of type arguments tested against type variable bounds. | |
| 29 final Set<DartType> checkedTypeArguments; | |
| 30 // The set of tested type variable bounds. | |
| 31 final Set<DartType> checkedBounds; | |
| 32 | |
| 33 JavaScriptBackend get backend => compiler.backend; | |
| 34 | |
| 35 RuntimeTypes(Compiler compiler) | |
| 36 : this.compiler = compiler, | |
| 37 representationGenerator = new TypeRepresentationGenerator(compiler), | |
| 38 classesNeedingRti = new Set<ClassElement>(), | |
| 39 methodsNeedingRti = new Set<Element>(), | |
| 40 rtiDependencies = new Map<ClassElement, Set<ClassElement>>(), | |
| 41 classesUsingTypeVariableExpression = new Set<ClassElement>(), | |
| 42 checkedTypeArguments = new Set<DartType>(), | |
| 43 checkedBounds = new Set<DartType>(); | |
| 44 | |
| 45 Set<ClassElement> directlyInstantiatedArguments; | |
| 46 Set<ClassElement> allInstantiatedArguments; | |
| 47 Set<ClassElement> checkedArguments; | |
| 48 | |
| 49 void registerRtiDependency(Element element, Element dependency) { | |
| 50 // We're not dealing with typedef for now. | |
| 51 if (!element.isClass || !dependency.isClass) return; | |
| 52 Set<ClassElement> classes = | |
| 53 rtiDependencies.putIfAbsent(element, () => new Set<ClassElement>()); | |
| 54 classes.add(dependency); | |
| 55 } | |
| 56 | |
| 57 void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, | |
| 58 DartType bound) { | |
| 59 checkedTypeArguments.add(typeArgument); | |
| 60 checkedBounds.add(bound); | |
| 61 } | |
| 62 | |
| 63 bool usingFactoryWithTypeArguments = false; | |
| 64 | |
| 65 /** | |
| 66 * Compute type arguments of classes that use one of their type variables in | |
| 67 * is-checks and add the is-checks that they imply. | |
| 68 * | |
| 69 * This function must be called after all is-checks have been registered. | |
| 70 * | |
| 71 * TODO(karlklose): move these computations into a function producing an | |
| 72 * immutable datastructure. | |
| 73 */ | |
| 74 void addImplicitChecks(Universe universe, | |
| 75 Iterable<ClassElement> classesUsingChecks) { | |
| 76 // If there are no classes that use their variables in checks, there is | |
| 77 // nothing to do. | |
| 78 if (classesUsingChecks.isEmpty) return; | |
| 79 Set<DartType> instantiatedTypes = universe.instantiatedTypes; | |
| 80 if (universe.usingFactoryWithTypeArguments) { | |
| 81 for (DartType type in instantiatedTypes) { | |
| 82 if (type.kind != TypeKind.INTERFACE) continue; | |
| 83 InterfaceType interface = type; | |
| 84 do { | |
| 85 for (DartType argument in interface.typeArguments) { | |
| 86 universe.registerIsCheck(argument, compiler); | |
| 87 } | |
| 88 interface = interface.element.supertype; | |
| 89 } while (interface != null && !instantiatedTypes.contains(interface)); | |
| 90 } | |
| 91 } else { | |
| 92 // Find all instantiated types that are a subtype of a class that uses | |
| 93 // one of its type arguments in an is-check and add the arguments to the | |
| 94 // set of is-checks. | |
| 95 // TODO(karlklose): replace this with code that uses a subtype lookup | |
| 96 // datastructure in the world. | |
| 97 for (DartType type in instantiatedTypes) { | |
| 98 if (type.kind != TypeKind.INTERFACE) continue; | |
| 99 InterfaceType classType = type; | |
| 100 for (ClassElement cls in classesUsingChecks) { | |
| 101 InterfaceType current = classType; | |
| 102 do { | |
| 103 // We need the type as instance of its superclass anyway, so we just | |
| 104 // try to compute the substitution; if the result is [:null:], the | |
| 105 // classes are not related. | |
| 106 InterfaceType instance = current.asInstanceOf(cls); | |
| 107 if (instance == null) break; | |
| 108 for (DartType argument in instance.typeArguments) { | |
| 109 universe.registerIsCheck(argument, compiler); | |
| 110 } | |
| 111 current = current.element.supertype; | |
| 112 } while (current != null && !instantiatedTypes.contains(current)); | |
| 113 } | |
| 114 } | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 void computeClassesNeedingRti() { | |
| 119 // Find the classes that need runtime type information. Such | |
| 120 // classes are: | |
| 121 // (1) used in a is check with type variables, | |
| 122 // (2) dependencies of classes in (1), | |
| 123 // (3) subclasses of (2) and (3). | |
| 124 void potentiallyAddForRti(ClassElement cls) { | |
| 125 assert(invariant(cls, cls.isDeclaration)); | |
| 126 if (cls.typeVariables.isEmpty) return; | |
| 127 if (classesNeedingRti.contains(cls)) return; | |
| 128 classesNeedingRti.add(cls); | |
| 129 | |
| 130 // TODO(ngeoffray): This should use subclasses, not subtypes. | |
| 131 Iterable<ClassElement> classes = compiler.world.subtypesOf(cls); | |
| 132 classes.forEach((ClassElement sub) { | |
| 133 potentiallyAddForRti(sub); | |
| 134 }); | |
| 135 | |
| 136 Set<ClassElement> dependencies = rtiDependencies[cls]; | |
| 137 if (dependencies != null) { | |
| 138 dependencies.forEach((ClassElement other) { | |
| 139 potentiallyAddForRti(other); | |
| 140 }); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 Set<ClassElement> classesUsingTypeVariableTests = new Set<ClassElement>(); | |
| 145 compiler.resolverWorld.isChecks.forEach((DartType type) { | |
| 146 if (type.isTypeVariable) { | |
| 147 TypeVariableElement variable = type.element; | |
| 148 classesUsingTypeVariableTests.add(variable.typeDeclaration); | |
| 149 } | |
| 150 }); | |
| 151 // Add is-checks that result from classes using type variables in checks. | |
| 152 addImplicitChecks(compiler.resolverWorld, classesUsingTypeVariableTests); | |
| 153 // Add the rti dependencies that are implicit in the way the backend | |
| 154 // generates code: when we create a new [List], we actually create | |
| 155 // a JSArray in the backend and we need to add type arguments to | |
| 156 // the calls of the list constructor whenever we determine that | |
| 157 // JSArray needs type arguments. | |
| 158 // TODO(karlklose): make this dependency visible from code. | |
| 159 if (backend.jsArrayClass != null) { | |
| 160 registerRtiDependency(backend.jsArrayClass, compiler.listClass); | |
| 161 } | |
| 162 // Compute the set of all classes and methods that need runtime type | |
| 163 // information. | |
| 164 compiler.resolverWorld.isChecks.forEach((DartType type) { | |
| 165 if (type.isInterfaceType) { | |
| 166 InterfaceType itf = type; | |
| 167 if (!itf.treatAsRaw) { | |
| 168 potentiallyAddForRti(itf.element); | |
| 169 } | |
| 170 } else { | |
| 171 ClassElement contextClass = Types.getClassContext(type); | |
| 172 if (contextClass != null) { | |
| 173 // [type] contains type variables (declared in [contextClass]) if | |
| 174 // [contextClass] is non-null. This handles checks against type | |
| 175 // variables and function types containing type variables. | |
| 176 potentiallyAddForRti(contextClass); | |
| 177 } | |
| 178 if (type.isFunctionType) { | |
| 179 void analyzeMethod(TypedElement method) { | |
| 180 DartType memberType = method.type; | |
| 181 ClassElement contextClass = Types.getClassContext(memberType); | |
| 182 if (contextClass != null && | |
| 183 compiler.types.isPotentialSubtype(memberType, type)) { | |
| 184 potentiallyAddForRti(contextClass); | |
| 185 methodsNeedingRti.add(method); | |
| 186 } | |
| 187 } | |
| 188 compiler.resolverWorld.closuresWithFreeTypeVariables.forEach( | |
| 189 analyzeMethod); | |
| 190 compiler.resolverWorld.callMethodsWithFreeTypeVariables.forEach( | |
| 191 analyzeMethod); | |
| 192 } | |
| 193 } | |
| 194 }); | |
| 195 if (compiler.enableTypeAssertions) { | |
| 196 void analyzeMethod(TypedElement method) { | |
| 197 DartType memberType = method.type; | |
| 198 ClassElement contextClass = Types.getClassContext(memberType); | |
| 199 if (contextClass != null) { | |
| 200 potentiallyAddForRti(contextClass); | |
| 201 methodsNeedingRti.add(method); | |
| 202 } | |
| 203 } | |
| 204 compiler.resolverWorld.closuresWithFreeTypeVariables.forEach( | |
| 205 analyzeMethod); | |
| 206 compiler.resolverWorld.callMethodsWithFreeTypeVariables.forEach( | |
| 207 analyzeMethod); | |
| 208 } | |
| 209 // Add the classes that need RTI because they use a type variable as | |
| 210 // expression. | |
| 211 classesUsingTypeVariableExpression.forEach(potentiallyAddForRti); | |
| 212 } | |
| 213 | |
| 214 TypeChecks cachedRequiredChecks; | |
| 215 | |
| 216 TypeChecks get requiredChecks { | |
| 217 if (cachedRequiredChecks == null) { | |
| 218 computeRequiredChecks(); | |
| 219 } | |
| 220 assert(cachedRequiredChecks != null); | |
| 221 return cachedRequiredChecks; | |
| 222 } | |
| 223 | |
| 224 /// Compute the required type checkes and substitutions for the given | |
| 225 /// instantitated and checked classes. | |
| 226 TypeChecks computeChecks(Set<ClassElement> instantiated, | |
| 227 Set<ClassElement> checked) { | |
| 228 // Run through the combination of instantiated and checked | |
| 229 // arguments and record all combination where the element of a checked | |
| 230 // argument is a superclass of the element of an instantiated type. | |
| 231 TypeCheckMapping result = new TypeCheckMapping(); | |
| 232 for (ClassElement element in instantiated) { | |
| 233 if (checked.contains(element)) { | |
| 234 result.add(element, element, null); | |
| 235 } | |
| 236 // Find all supertypes of [element] in [checkedArguments] and add checks | |
| 237 // and precompute the substitutions for them. | |
| 238 assert(invariant(element, element.allSupertypes != null, | |
| 239 message: 'Supertypes have not been computed for $element.')); | |
| 240 for (DartType supertype in element.allSupertypes) { | |
| 241 ClassElement superelement = supertype.element; | |
| 242 if (checked.contains(superelement)) { | |
| 243 Substitution substitution = | |
| 244 computeSubstitution(element, superelement); | |
| 245 result.add(element, superelement, substitution); | |
| 246 } | |
| 247 } | |
| 248 } | |
| 249 return result; | |
| 250 } | |
| 251 | |
| 252 void computeRequiredChecks() { | |
| 253 Set<DartType> isChecks = compiler.codegenWorld.isChecks; | |
| 254 bool hasFunctionTypeCheck = | |
| 255 isChecks.any((type) => identical(type.kind, TypeKind.FUNCTION)); | |
| 256 Set<DartType> instantiatedTypesAndClosures = hasFunctionTypeCheck | |
| 257 ? computeInstantiatedTypesAndClosures(compiler.codegenWorld) | |
| 258 : compiler.codegenWorld.instantiatedTypes; | |
| 259 computeInstantiatedArguments(instantiatedTypesAndClosures, isChecks); | |
| 260 computeCheckedArguments(instantiatedTypesAndClosures, isChecks); | |
| 261 cachedRequiredChecks = | |
| 262 computeChecks(allInstantiatedArguments, checkedArguments); | |
| 263 } | |
| 264 | |
| 265 Set<DartType> computeInstantiatedTypesAndClosures(Universe universe) { | |
| 266 Set<DartType> instantiatedTypes = | |
| 267 new Set<DartType>.from(universe.instantiatedTypes); | |
| 268 for (DartType instantiatedType in universe.instantiatedTypes) { | |
| 269 if (instantiatedType.isInterfaceType) { | |
| 270 InterfaceType interface = instantiatedType; | |
| 271 FunctionType callType = interface.callType; | |
| 272 if (callType != null) { | |
| 273 instantiatedTypes.add(callType); | |
| 274 } | |
| 275 } | |
| 276 } | |
| 277 for (FunctionElement element in universe.staticFunctionsNeedingGetter) { | |
| 278 instantiatedTypes.add(element.type); | |
| 279 } | |
| 280 // TODO(johnniwinther): We should get this information through the | |
| 281 // [neededClasses] computed in the emitter instead of storing it and pulling | |
| 282 // it from resolution, but currently it would introduce a cyclic dependency | |
| 283 // between [computeRequiredChecks] and [computeNeededClasses]. | |
| 284 for (TypedElement element in compiler.resolverWorld.closurizedMembers) { | |
| 285 instantiatedTypes.add(element.type); | |
| 286 } | |
| 287 return instantiatedTypes; | |
| 288 } | |
| 289 | |
| 290 /** | |
| 291 * Collects all types used in type arguments of instantiated types. | |
| 292 * | |
| 293 * This includes type arguments used in supertype relations, because we may | |
| 294 * have a type check against this supertype that includes a check against | |
| 295 * the type arguments. | |
| 296 */ | |
| 297 void computeInstantiatedArguments(Set<DartType> instantiatedTypes, | |
| 298 Set<DartType> isChecks) { | |
| 299 ArgumentCollector superCollector = new ArgumentCollector(backend); | |
| 300 ArgumentCollector directCollector = new ArgumentCollector(backend); | |
| 301 FunctionArgumentCollector functionArgumentCollector = | |
| 302 new FunctionArgumentCollector(backend); | |
| 303 | |
| 304 // We need to add classes occuring in function type arguments, like for | |
| 305 // instance 'I' for [: o is C<f> :] where f is [: typedef I f(); :]. | |
| 306 void collectFunctionTypeArguments(Iterable<DartType> types) { | |
| 307 for (DartType type in types) { | |
| 308 functionArgumentCollector.collect(type); | |
| 309 } | |
| 310 } | |
| 311 collectFunctionTypeArguments(isChecks); | |
| 312 collectFunctionTypeArguments(checkedBounds); | |
| 313 | |
| 314 void collectTypeArguments(Iterable<DartType> types, | |
| 315 {bool isTypeArgument: false}) { | |
| 316 for (DartType type in types) { | |
| 317 directCollector.collect(type, isTypeArgument: isTypeArgument); | |
| 318 if (type.isInterfaceType) { | |
| 319 ClassElement cls = type.element; | |
| 320 for (DartType supertype in cls.allSupertypes) { | |
| 321 superCollector.collect(supertype, isTypeArgument: isTypeArgument); | |
| 322 } | |
| 323 } | |
| 324 } | |
| 325 } | |
| 326 collectTypeArguments(instantiatedTypes); | |
| 327 collectTypeArguments(checkedTypeArguments, isTypeArgument: true); | |
| 328 | |
| 329 for (ClassElement cls in superCollector.classes.toList()) { | |
| 330 for (DartType supertype in cls.allSupertypes) { | |
| 331 superCollector.collect(supertype); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 directlyInstantiatedArguments = | |
| 336 directCollector.classes..addAll(functionArgumentCollector.classes); | |
| 337 allInstantiatedArguments = | |
| 338 superCollector.classes..addAll(directlyInstantiatedArguments); | |
| 339 } | |
| 340 | |
| 341 /// Collects all type arguments used in is-checks. | |
| 342 void computeCheckedArguments(Set<DartType> instantiatedTypes, | |
| 343 Set<DartType> isChecks) { | |
| 344 ArgumentCollector collector = new ArgumentCollector(backend); | |
| 345 FunctionArgumentCollector functionArgumentCollector = | |
| 346 new FunctionArgumentCollector(backend); | |
| 347 | |
| 348 // We need to add types occuring in function type arguments, like for | |
| 349 // instance 'J' for [: (J j) {} is f :] where f is | |
| 350 // [: typedef void f(I i); :] and 'J' is a subtype of 'I'. | |
| 351 void collectFunctionTypeArguments(Iterable<DartType> types) { | |
| 352 for (DartType type in types) { | |
| 353 functionArgumentCollector.collect(type); | |
| 354 } | |
| 355 } | |
| 356 collectFunctionTypeArguments(instantiatedTypes); | |
| 357 collectFunctionTypeArguments(checkedTypeArguments); | |
| 358 | |
| 359 void collectTypeArguments(Iterable<DartType> types, | |
| 360 {bool isTypeArgument: false}) { | |
| 361 for (DartType type in types) { | |
| 362 collector.collect(type, isTypeArgument: isTypeArgument); | |
| 363 } | |
| 364 } | |
| 365 collectTypeArguments(isChecks); | |
| 366 collectTypeArguments(checkedBounds, isTypeArgument: true); | |
| 367 | |
| 368 checkedArguments = | |
| 369 collector.classes..addAll(functionArgumentCollector.classes); | |
| 370 } | |
| 371 | |
| 372 Set<ClassElement> getClassesUsedInSubstitutions(JavaScriptBackend backend, | |
| 373 TypeChecks checks) { | |
| 374 Set<ClassElement> instantiated = new Set<ClassElement>(); | |
| 375 ArgumentCollector collector = new ArgumentCollector(backend); | |
| 376 for (ClassElement target in checks) { | |
| 377 instantiated.add(target); | |
| 378 for (TypeCheck check in checks[target]) { | |
| 379 Substitution substitution = check.substitution; | |
| 380 if (substitution != null) { | |
| 381 collector.collectAll(substitution.arguments); | |
| 382 } | |
| 383 } | |
| 384 } | |
| 385 return instantiated..addAll(collector.classes); | |
| 386 } | |
| 387 | |
| 388 Set<ClassElement> getRequiredArgumentClasses(JavaScriptBackend backend) { | |
| 389 Set<ClassElement> requiredArgumentClasses = | |
| 390 new Set<ClassElement>.from( | |
| 391 getClassesUsedInSubstitutions(backend, requiredChecks)); | |
| 392 return requiredArgumentClasses | |
| 393 ..addAll(directlyInstantiatedArguments) | |
| 394 ..addAll(checkedArguments); | |
| 395 } | |
| 396 | |
| 397 String getTypeRepresentationForTypeConstant(DartType type) { | |
| 398 JavaScriptBackend backend = compiler.backend; | |
| 399 Namer namer = backend.namer; | |
| 400 if (type.isDynamic) return namer.getRuntimeTypeName(null); | |
| 401 String name = namer.uniqueNameForTypeConstantElement(type.element); | |
| 402 if (!type.element.isClass) return name; | |
| 403 InterfaceType interface = type; | |
| 404 List<DartType> variables = interface.element.typeVariables; | |
| 405 // Type constants can currently only be raw types, so there is no point | |
| 406 // adding ground-term type parameters, as they would just be 'dynamic'. | |
| 407 // TODO(sra): Since the result string is used only in constructing constant | |
| 408 // names, it would result in more readable names if the final string was a | |
| 409 // legal JavaScript identifer. | |
| 410 if (variables.isEmpty) return name; | |
| 411 String arguments = | |
| 412 new List.filled(variables.length, 'dynamic').join(', '); | |
| 413 return '$name<$arguments>'; | |
| 414 } | |
| 415 | |
| 416 // TODO(karlklose): maybe precompute this value and store it in typeChecks? | |
| 417 bool isTrivialSubstitution(ClassElement cls, ClassElement check) { | |
| 418 if (cls.isClosure) { | |
| 419 // TODO(karlklose): handle closures. | |
| 420 return true; | |
| 421 } | |
| 422 | |
| 423 // If there are no type variables or the type is the same, we do not need | |
| 424 // a substitution. | |
| 425 if (check.typeVariables.isEmpty || cls == check) { | |
| 426 return true; | |
| 427 } | |
| 428 | |
| 429 InterfaceType originalType = cls.thisType; | |
| 430 InterfaceType type = originalType.asInstanceOf(check); | |
| 431 // [type] is not a subtype of [check]. we do not generate a check and do not | |
| 432 // need a substitution. | |
| 433 if (type == null) return true; | |
| 434 | |
| 435 // Run through both lists of type variables and check if the type variables | |
| 436 // are identical at each position. If they are not, we need to calculate a | |
| 437 // substitution function. | |
| 438 List<DartType> variables = cls.typeVariables; | |
| 439 List<DartType> arguments = type.typeArguments; | |
| 440 if (variables.length != arguments.length) { | |
| 441 return false; | |
| 442 } | |
| 443 for (int index = 0; index < variables.length; index++) { | |
| 444 if (variables[index].element != arguments[index].element) { | |
| 445 return false; | |
| 446 } | |
| 447 } | |
| 448 return true; | |
| 449 } | |
| 450 | |
| 451 /** | |
| 452 * Compute a JavaScript expression that describes the necessary substitution | |
| 453 * for type arguments in a subtype test. | |
| 454 * | |
| 455 * The result can be: | |
| 456 * 1) [:null:], if no substituted check is necessary, because the | |
| 457 * type variables are the same or there are no type variables in the class | |
| 458 * that is checked for. | |
| 459 * 2) A list expression describing the type arguments to be used in the | |
| 460 * subtype check, if the type arguments to be used in the check do not | |
| 461 * depend on the type arguments of the object. | |
| 462 * 3) A function mapping the type variables of the object to be checked to | |
| 463 * a list expression. | |
| 464 */ | |
| 465 jsAst.Expression getSupertypeSubstitution( | |
| 466 ClassElement cls, | |
| 467 ClassElement check, | |
| 468 {bool alwaysGenerateFunction: false}) { | |
| 469 Substitution substitution = getSubstitution(cls, check); | |
| 470 if (substitution != null) { | |
| 471 return substitution.getCode(this, alwaysGenerateFunction); | |
| 472 } else { | |
| 473 return null; | |
| 474 } | |
| 475 } | |
| 476 | |
| 477 Substitution getSubstitution(ClassElement cls, ClassElement other) { | |
| 478 // Look for a precomputed check. | |
| 479 for (TypeCheck check in cachedRequiredChecks[cls]) { | |
| 480 if (check.cls == other) { | |
| 481 return check.substitution; | |
| 482 } | |
| 483 } | |
| 484 // There is no precomputed check for this pair (because the check is not | |
| 485 // done on type arguments only. Compute a new substitution. | |
| 486 return computeSubstitution(cls, other); | |
| 487 } | |
| 488 | |
| 489 Substitution computeSubstitution(ClassElement cls, ClassElement check, | |
| 490 { bool alwaysGenerateFunction: false }) { | |
| 491 if (isTrivialSubstitution(cls, check)) return null; | |
| 492 | |
| 493 // Unnamed mixin application classes do not need substitutions, because they | |
| 494 // are never instantiated and their checks are overwritten by the class that | |
| 495 // they are mixed into. | |
| 496 InterfaceType type = cls.thisType; | |
| 497 InterfaceType target = type.asInstanceOf(check); | |
| 498 List<DartType> typeVariables = cls.typeVariables; | |
| 499 if (typeVariables.isEmpty && !alwaysGenerateFunction) { | |
| 500 return new Substitution.list(target.typeArguments); | |
| 501 } else { | |
| 502 return new Substitution.function(target.typeArguments, typeVariables); | |
| 503 } | |
| 504 } | |
| 505 | |
| 506 jsAst.Expression getSubstitutionRepresentation( | |
| 507 List<DartType> types, | |
| 508 OnVariableCallback onVariable) { | |
| 509 List<jsAst.ArrayElement> elements = <jsAst.ArrayElement>[]; | |
| 510 int index = 0; | |
| 511 for (DartType type in types) { | |
| 512 jsAst.Expression representation = getTypeRepresentation(type, onVariable); | |
| 513 elements.add(new jsAst.ArrayElement(index++, representation)); | |
| 514 } | |
| 515 return new jsAst.ArrayInitializer(index, elements); | |
| 516 } | |
| 517 | |
| 518 jsAst.Expression getTypeEncoding(DartType type, | |
| 519 {bool alwaysGenerateFunction: false}) { | |
| 520 ClassElement contextClass = Types.getClassContext(type); | |
| 521 jsAst.Expression onVariable(TypeVariableType v) { | |
| 522 return new jsAst.VariableUse(v.name); | |
| 523 }; | |
| 524 jsAst.Expression encoding = getTypeRepresentation(type, onVariable); | |
| 525 if (contextClass == null && !alwaysGenerateFunction) { | |
| 526 return encoding; | |
| 527 } else { | |
| 528 List<String> parameters = const <String>[]; | |
| 529 if (contextClass != null) { | |
| 530 parameters = contextClass.typeVariables.map((type) { | |
| 531 return type.toString(); | |
| 532 }).toList(); | |
| 533 } | |
| 534 return js('function(#) { return # }', [parameters, encoding]); | |
| 535 } | |
| 536 } | |
| 537 | |
| 538 jsAst.Expression getSignatureEncoding(DartType type, jsAst.Expression this_) { | |
| 539 ClassElement contextClass = Types.getClassContext(type); | |
| 540 jsAst.Expression encoding = | |
| 541 getTypeEncoding(type, alwaysGenerateFunction: true); | |
| 542 if (contextClass != null) { | |
| 543 JavaScriptBackend backend = compiler.backend; | |
| 544 String contextName = backend.namer.getNameOfClass(contextClass); | |
| 545 return js('function () { return #(#, #, #); }', | |
| 546 [ backend.namer.elementAccess(backend.getComputeSignature()), | |
| 547 encoding, this_, js.string(contextName) ]); | |
| 548 } else { | |
| 549 return encoding; | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 String getTypeRepresentationWithHashes(DartType type, | |
| 554 OnVariableCallback onVariable) { | |
| 555 // Create a type representation. For type variables call the original | |
| 556 // callback for side effects and return a template placeholder. | |
| 557 jsAst.Expression representation = getTypeRepresentation(type, (variable) { | |
| 558 onVariable(variable); | |
| 559 return new jsAst.LiteralString('#'); | |
| 560 }); | |
| 561 return jsAst.prettyPrint(representation, compiler).buffer.toString(); | |
| 562 } | |
| 563 | |
| 564 jsAst.Expression getTypeRepresentation( | |
| 565 DartType type, | |
| 566 OnVariableCallback onVariable, | |
| 567 [ShouldEncodeTypedefCallback shouldEncodeTypedef]) { | |
| 568 return representationGenerator.getTypeRepresentation( | |
| 569 type, onVariable, shouldEncodeTypedef); | |
| 570 } | |
| 571 | |
| 572 bool isSimpleFunctionType(FunctionType type) { | |
| 573 if (!type.returnType.isDynamic) return false; | |
| 574 if (!type.optionalParameterTypes.isEmpty) return false; | |
| 575 if (!type.namedParameterTypes.isEmpty) return false; | |
| 576 for (DartType parameter in type.parameterTypes ) { | |
| 577 if (!parameter.isDynamic) return false; | |
| 578 } | |
| 579 return true; | |
| 580 } | |
| 581 | |
| 582 static bool hasTypeArguments(DartType type) { | |
| 583 if (type is InterfaceType) { | |
| 584 InterfaceType interfaceType = type; | |
| 585 return !interfaceType.treatAsRaw; | |
| 586 } | |
| 587 return false; | |
| 588 } | |
| 589 | |
| 590 static int getTypeVariableIndex(TypeVariableElement variable) { | |
| 591 ClassElement classElement = variable.enclosingClass; | |
| 592 List<DartType> variables = classElement.typeVariables; | |
| 593 for (int index = 0; index < variables.length; index++) { | |
| 594 if (variables[index].element == variable) return index; | |
| 595 } | |
| 596 throw invariant(variable, false, | |
| 597 message: "Couldn't find type-variable index"); | |
| 598 } | |
| 599 | |
| 600 /// Return all classes that are referenced in the type of the function, i.e., | |
| 601 /// in the return type or the argument types. | |
| 602 Set<ClassElement> getReferencedClasses(FunctionType type) { | |
| 603 FunctionArgumentCollector collector = | |
| 604 new FunctionArgumentCollector(backend); | |
| 605 collector.collect(type); | |
| 606 return collector.classes; | |
| 607 } | |
| 608 } | |
| 609 | |
| 610 class TypeRepresentationGenerator extends DartTypeVisitor { | |
| 611 final Compiler compiler; | |
| 612 OnVariableCallback onVariable; | |
| 613 ShouldEncodeTypedefCallback shouldEncodeTypedef; | |
| 614 | |
| 615 JavaScriptBackend get backend => compiler.backend; | |
| 616 Namer get namer => backend.namer; | |
| 617 | |
| 618 TypeRepresentationGenerator(Compiler this.compiler); | |
| 619 | |
| 620 /** | |
| 621 * Creates a type representation for [type]. [onVariable] is called to provide | |
| 622 * the type representation for type variables. | |
| 623 */ | |
| 624 jsAst.Expression getTypeRepresentation( | |
| 625 DartType type, | |
| 626 OnVariableCallback onVariable, | |
| 627 ShouldEncodeTypedefCallback encodeTypedef) { | |
| 628 this.onVariable = onVariable; | |
| 629 this.shouldEncodeTypedef = | |
| 630 (encodeTypedef != null) ? encodeTypedef : (TypedefType type) => false; | |
| 631 jsAst.Expression representation = visit(type); | |
| 632 this.onVariable = null; | |
| 633 this.shouldEncodeTypedef = null; | |
| 634 return representation; | |
| 635 } | |
| 636 | |
| 637 jsAst.Expression getJavaScriptClassName(Element element) { | |
| 638 return namer.elementAccess(element); | |
| 639 } | |
| 640 | |
| 641 visit(DartType type) { | |
| 642 return type.accept(this, null); | |
| 643 } | |
| 644 | |
| 645 visitTypeVariableType(TypeVariableType type, _) { | |
| 646 return onVariable(type); | |
| 647 } | |
| 648 | |
| 649 visitDynamicType(DynamicType type, _) { | |
| 650 return js('null'); | |
| 651 } | |
| 652 | |
| 653 visitInterfaceType(InterfaceType type, _) { | |
| 654 jsAst.Expression name = getJavaScriptClassName(type.element); | |
| 655 return type.treatAsRaw ? name : visitList(type.typeArguments, head: name); | |
| 656 } | |
| 657 | |
| 658 jsAst.Expression visitList(List<DartType> types, {jsAst.Expression head}) { | |
| 659 int index = 0; | |
| 660 List<jsAst.ArrayElement> elements = <jsAst.ArrayElement>[]; | |
| 661 if (head != null) { | |
| 662 elements.add(new jsAst.ArrayElement(0, head)); | |
| 663 index++; | |
| 664 } | |
| 665 for (DartType type in types) { | |
| 666 elements.add(new jsAst.ArrayElement(index++, visit(type))); | |
| 667 } | |
| 668 return new jsAst.ArrayInitializer(elements.length, elements); | |
| 669 } | |
| 670 | |
| 671 visitFunctionType(FunctionType type, _) { | |
| 672 List<jsAst.Property> properties = <jsAst.Property>[]; | |
| 673 | |
| 674 void addProperty(String name, jsAst.Expression value) { | |
| 675 properties.add(new jsAst.Property(js.string(name), value)); | |
| 676 } | |
| 677 | |
| 678 jsAst.LiteralString name = js.string(namer.getFunctionTypeName(type)); | |
| 679 addProperty(namer.functionTypeTag(), name); | |
| 680 if (type.returnType.isVoid) { | |
| 681 addProperty(namer.functionTypeVoidReturnTag(), js('true')); | |
| 682 } else if (!type.returnType.treatAsDynamic) { | |
| 683 addProperty(namer.functionTypeReturnTypeTag(), visit(type.returnType)); | |
| 684 } | |
| 685 if (!type.parameterTypes.isEmpty) { | |
| 686 addProperty(namer.functionTypeRequiredParametersTag(), | |
| 687 visitList(type.parameterTypes)); | |
| 688 } | |
| 689 if (!type.optionalParameterTypes.isEmpty) { | |
| 690 addProperty(namer.functionTypeOptionalParametersTag(), | |
| 691 visitList(type.optionalParameterTypes)); | |
| 692 } | |
| 693 if (!type.namedParameterTypes.isEmpty) { | |
| 694 List<jsAst.Property> namedArguments = <jsAst.Property>[]; | |
| 695 List<String> names = type.namedParameters; | |
| 696 List<DartType> types = type.namedParameterTypes; | |
| 697 assert(types.length == names.length); | |
| 698 for (int index = 0; index < types.length; index++) { | |
| 699 jsAst.Expression name = js.string(names[index]); | |
| 700 namedArguments.add(new jsAst.Property(name, visit(types[index]))); | |
| 701 } | |
| 702 addProperty(namer.functionTypeNamedParametersTag(), | |
| 703 new jsAst.ObjectInitializer(namedArguments)); | |
| 704 } | |
| 705 return new jsAst.ObjectInitializer(properties); | |
| 706 } | |
| 707 | |
| 708 visitMalformedType(MalformedType type, _) { | |
| 709 // Treat malformed types as dynamic at runtime. | |
| 710 return js('null'); | |
| 711 } | |
| 712 | |
| 713 visitVoidType(VoidType type, _) { | |
| 714 // TODO(ahe): Reify void type ("null" means "dynamic"). | |
| 715 return js('null'); | |
| 716 } | |
| 717 | |
| 718 visitTypedefType(TypedefType type, _) { | |
| 719 bool shouldEncode = shouldEncodeTypedef(type); | |
| 720 DartType unaliasedType = type.unalias(compiler); | |
| 721 if (shouldEncode) { | |
| 722 jsAst.ObjectInitializer initializer = unaliasedType.accept(this, null); | |
| 723 // We have to encode the aliased type. | |
| 724 jsAst.Expression name = getJavaScriptClassName(type.element); | |
| 725 jsAst.Expression encodedTypedef = | |
| 726 type.treatAsRaw ? name : visitList(type.typeArguments, head: name); | |
| 727 | |
| 728 // Add it to the function-type object. | |
| 729 jsAst.LiteralString tag = js.string(namer.typedefTag()); | |
| 730 initializer.properties.add(new jsAst.Property(tag, encodedTypedef)); | |
| 731 return initializer; | |
| 732 } else { | |
| 733 return unaliasedType.accept(this, null); | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 visitType(DartType type, _) { | |
| 738 compiler.internalError(NO_LOCATION_SPANNABLE, | |
| 739 'Unexpected type: $type (${type.kind}).'); | |
| 740 } | |
| 741 } | |
| 742 | |
| 743 | |
| 744 class TypeCheckMapping implements TypeChecks { | |
| 745 final Map<ClassElement, Set<TypeCheck>> map = | |
| 746 new Map<ClassElement, Set<TypeCheck>>(); | |
| 747 | |
| 748 Iterable<TypeCheck> operator[](ClassElement element) { | |
| 749 Set<TypeCheck> result = map[element]; | |
| 750 return result != null ? result : const <TypeCheck>[]; | |
| 751 } | |
| 752 | |
| 753 void add(ClassElement cls, ClassElement check, Substitution substitution) { | |
| 754 map.putIfAbsent(cls, () => new Set<TypeCheck>()); | |
| 755 map[cls].add(new TypeCheck(check, substitution)); | |
| 756 } | |
| 757 | |
| 758 Iterator<ClassElement> get iterator => map.keys.iterator; | |
| 759 | |
| 760 String toString() { | |
| 761 StringBuffer sb = new StringBuffer(); | |
| 762 for (ClassElement holder in this) { | |
| 763 for (ClassElement check in [holder]) { | |
| 764 sb.write('${holder.name}.' '${check.name}, '); | |
| 765 } | |
| 766 } | |
| 767 return '[$sb]'; | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 class ArgumentCollector extends DartTypeVisitor { | |
| 772 final JavaScriptBackend backend; | |
| 773 final Set<ClassElement> classes = new Set<ClassElement>(); | |
| 774 | |
| 775 ArgumentCollector(this.backend); | |
| 776 | |
| 777 collect(DartType type, {bool isTypeArgument: false}) { | |
| 778 type.accept(this, isTypeArgument); | |
| 779 } | |
| 780 | |
| 781 /// Collect all types in the list as if they were arguments of an | |
| 782 /// InterfaceType. | |
| 783 collectAll(List<DartType> types) { | |
| 784 for (DartType type in types) { | |
| 785 type.accept(this, true); | |
| 786 } | |
| 787 } | |
| 788 | |
| 789 visitType(DartType type, _) { | |
| 790 // Do nothing. | |
| 791 } | |
| 792 | |
| 793 visitDynamicType(DynamicType type, _) { | |
| 794 // Do not collect [:dynamic:]. | |
| 795 } | |
| 796 | |
| 797 visitTypedefType(TypedefType type, bool isTypeArgument) { | |
| 798 type.unalias(backend.compiler).accept(this, isTypeArgument); | |
| 799 } | |
| 800 | |
| 801 visitInterfaceType(InterfaceType type, bool isTypeArgument) { | |
| 802 if (isTypeArgument) classes.add(type.element); | |
| 803 type.visitChildren(this, true); | |
| 804 } | |
| 805 | |
| 806 visitFunctionType(FunctionType type, _) { | |
| 807 type.visitChildren(this, true); | |
| 808 } | |
| 809 } | |
| 810 | |
| 811 class FunctionArgumentCollector extends DartTypeVisitor { | |
| 812 final JavaScriptBackend backend; | |
| 813 final Set<ClassElement> classes = new Set<ClassElement>(); | |
| 814 | |
| 815 FunctionArgumentCollector(this.backend); | |
| 816 | |
| 817 collect(DartType type) { | |
| 818 type.accept(this, false); | |
| 819 } | |
| 820 | |
| 821 /// Collect all types in the list as if they were arguments of an | |
| 822 /// InterfaceType. | |
| 823 collectAll(Link<DartType> types) { | |
| 824 for (Link<DartType> link = types; !link.isEmpty; link = link.tail) { | |
| 825 link.head.accept(this, true); | |
| 826 } | |
| 827 } | |
| 828 | |
| 829 visitType(DartType type, _) { | |
| 830 // Do nothing. | |
| 831 } | |
| 832 | |
| 833 visitDynamicType(DynamicType type, _) { | |
| 834 // Do not collect [:dynamic:]. | |
| 835 } | |
| 836 | |
| 837 visitTypedefType(TypedefType type, bool inFunctionType) { | |
| 838 type.unalias(backend.compiler).accept(this, inFunctionType); | |
| 839 } | |
| 840 | |
| 841 visitInterfaceType(InterfaceType type, bool inFunctionType) { | |
| 842 if (inFunctionType) { | |
| 843 classes.add(type.element); | |
| 844 } | |
| 845 type.visitChildren(this, inFunctionType); | |
| 846 } | |
| 847 | |
| 848 visitFunctionType(FunctionType type, _) { | |
| 849 type.visitChildren(this, true); | |
| 850 } | |
| 851 } | |
| 852 | |
| 853 /** | |
| 854 * Representation of the substitution of type arguments | |
| 855 * when going from the type of a class to one of its supertypes. | |
| 856 * | |
| 857 * For [:class B<T> extends A<List<T>, int>:], the substitution is | |
| 858 * the representation of [: (T) => [<List, T>, int] :]. For more details | |
| 859 * of the representation consult the documentation of | |
| 860 * [getSupertypeSubstitution]. | |
| 861 */ | |
| 862 class Substitution { | |
| 863 final bool isFunction; | |
| 864 final List<DartType> arguments; | |
| 865 final List<DartType> parameters; | |
| 866 | |
| 867 Substitution.list(this.arguments) | |
| 868 : isFunction = false, | |
| 869 parameters = const <DartType>[]; | |
| 870 | |
| 871 Substitution.function(this.arguments, this.parameters) | |
| 872 : isFunction = true; | |
| 873 | |
| 874 jsAst.Expression getCode(RuntimeTypes rti, bool ensureIsFunction) { | |
| 875 jsAst.Expression declaration(TypeVariableType variable) { | |
| 876 return new jsAst.Parameter( | |
| 877 rti.backend.namer.safeVariableName(variable.name)); | |
| 878 } | |
| 879 | |
| 880 jsAst.Expression use(TypeVariableType variable) { | |
| 881 return new jsAst.VariableUse( | |
| 882 rti.backend.namer.safeVariableName(variable.name)); | |
| 883 } | |
| 884 | |
| 885 jsAst.Expression value = | |
| 886 rti.getSubstitutionRepresentation(arguments, use); | |
| 887 if (isFunction) { | |
| 888 Iterable<jsAst.Expression> formals = parameters.map(declaration); | |
| 889 return js('function(#) { return # }', [formals, value]); | |
| 890 } else if (ensureIsFunction) { | |
| 891 return js('function() { return # }', value); | |
| 892 } else { | |
| 893 return value; | |
| 894 } | |
| 895 } | |
| 896 } | |
| 897 | |
| 898 /** | |
| 899 * A pair of a class that we need a check against and the type argument | |
| 900 * substition for this check. | |
| 901 */ | |
| 902 class TypeCheck { | |
| 903 final ClassElement cls; | |
| 904 final Substitution substitution; | |
| 905 final int hashCode = (nextHash++) & 0x3fffffff; | |
| 906 static int nextHash = 49; | |
| 907 | |
| 908 TypeCheck(this.cls, this.substitution); | |
| 909 } | |
| OLD | NEW |