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 |