Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1184)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698