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 dart2js.js_emitter; | |
6 | |
7 class ClassEmitter extends CodeEmitterHelper { | |
8 | |
9 ClassStubGenerator get _stubGenerator => | |
10 new ClassStubGenerator(compiler, namer, backend); | |
11 | |
12 /** | |
13 * Documentation wanted -- johnniwinther | |
14 * | |
15 * Invariant: [classElement] must be a declaration element. | |
16 */ | |
17 void generateClass(ClassElement classElement, | |
18 ClassBuilder properties, | |
19 Map<String, jsAst.Expression> additionalProperties) { | |
20 final onlyForRti = | |
21 emitter.typeTestEmitter.rtiNeededClasses.contains(classElement); | |
22 | |
23 assert(invariant(classElement, classElement.isDeclaration)); | |
24 assert(invariant(classElement, !classElement.isNative || onlyForRti)); | |
25 | |
26 emitter.needsDefineClass = true; | |
27 String className = namer.getNameOfClass(classElement); | |
28 | |
29 ClassElement superclass = classElement.superclass; | |
30 String superName = ""; | |
31 if (superclass != null) { | |
32 superName = namer.getNameOfClass(superclass); | |
33 } | |
34 | |
35 if (classElement.isMixinApplication) { | |
36 String mixinName = namer.getNameOfClass(computeMixinClass(classElement)); | |
37 superName = '$superName+$mixinName'; | |
38 emitter.needsMixinSupport = true; | |
39 } | |
40 | |
41 ClassBuilder builder = new ClassBuilder(classElement, namer); | |
42 emitClassConstructor(classElement, builder, onlyForRti: onlyForRti); | |
43 emitFields(classElement, builder, superName, onlyForRti: onlyForRti); | |
44 emitClassGettersSetters(classElement, builder, onlyForRti: onlyForRti); | |
45 emitInstanceMembers(classElement, builder, onlyForRti: onlyForRti); | |
46 emitter.typeTestEmitter.emitIsTests(classElement, builder); | |
47 if (additionalProperties != null) { | |
48 additionalProperties.forEach(builder.addProperty); | |
49 } | |
50 | |
51 if (classElement == backend.closureClass) { | |
52 // We add a special getter here to allow for tearing off a closure from | |
53 // itself. | |
54 String name = namer.getMappedInstanceName(Compiler.CALL_OPERATOR_NAME); | |
55 jsAst.Fun function = js('function() { return this; }'); | |
56 builder.addProperty(namer.getterNameFromAccessorName(name), function); | |
57 } | |
58 | |
59 emitTypeVariableReaders(classElement, builder); | |
60 | |
61 emitClassBuilderWithReflectionData( | |
62 className, classElement, builder, properties); | |
63 } | |
64 | |
65 void emitClassConstructor(ClassElement classElement, | |
66 ClassBuilder builder, | |
67 {bool onlyForRti: false}) { | |
68 List<String> fields = <String>[]; | |
69 if (!onlyForRti && !classElement.isNative) { | |
70 visitFields(classElement, false, | |
71 (Element member, | |
72 String name, | |
73 String accessorName, | |
74 bool needsGetter, | |
75 bool needsSetter, | |
76 bool needsCheckedSetter) { | |
77 fields.add(name); | |
78 }); | |
79 } | |
80 | |
81 jsAst.Expression constructorAst = | |
82 _stubGenerator.generateClassConstructor(classElement, fields); | |
83 | |
84 String constructorName = namer.getNameOfClass(classElement); | |
85 OutputUnit outputUnit = | |
86 compiler.deferredLoadTask.outputUnitForElement(classElement); | |
87 emitter.emitPrecompiledConstructor( | |
88 outputUnit, constructorName, constructorAst); | |
89 } | |
90 | |
91 /// Returns `true` if fields added. | |
92 bool emitFields(Element element, | |
93 ClassBuilder builder, | |
94 String superName, | |
95 { bool classIsNative: false, | |
96 bool emitStatics: false, | |
97 bool onlyForRti: false }) { | |
98 assert(!emitStatics || !onlyForRti); | |
99 if (element.isLibrary) { | |
100 assert(invariant(element, emitStatics)); | |
101 } else if (!element.isClass) { | |
102 throw new SpannableAssertionFailure( | |
103 element, 'Must be a ClassElement or a LibraryElement'); | |
104 } | |
105 if (emitStatics) { | |
106 assert(invariant(element, superName == null, message: superName)); | |
107 } else { | |
108 assert(invariant(element, superName != null)); | |
109 builder.superName = superName; | |
110 } | |
111 var fieldMetadata = []; | |
112 bool hasMetadata = false; | |
113 bool fieldsAdded = false; | |
114 | |
115 if (!onlyForRti) { | |
116 visitFields(element, emitStatics, | |
117 (VariableElement field, | |
118 String name, | |
119 String accessorName, | |
120 bool needsGetter, | |
121 bool needsSetter, | |
122 bool needsCheckedSetter) { | |
123 // Ignore needsCheckedSetter - that is handled below. | |
124 bool needsAccessor = (needsGetter || needsSetter); | |
125 // We need to output the fields for non-native classes so we can auto- | |
126 // generate the constructor. For native classes there are no | |
127 // constructors, so we don't need the fields unless we are generating | |
128 // accessors at runtime. | |
129 if (!classIsNative || needsAccessor) { | |
130 var metadata = emitter.metadataEmitter.buildMetadataFunction(field); | |
131 if (metadata != null) { | |
132 hasMetadata = true; | |
133 } else { | |
134 metadata = new jsAst.LiteralNull(); | |
135 } | |
136 fieldMetadata.add(metadata); | |
137 recordMangledField(field, accessorName, | |
138 namer.privateName(field.library, field.name)); | |
139 String fieldName = name; | |
140 String fieldCode = ''; | |
141 String reflectionMarker = ''; | |
142 if (!needsAccessor) { | |
143 // Emit field for constructor generation. | |
144 assert(!classIsNative); | |
145 } else { | |
146 // Emit (possibly renaming) field name so we can add accessors at | |
147 // runtime. | |
148 if (name != accessorName) { | |
149 fieldName = '$accessorName:$name'; | |
150 } | |
151 | |
152 int getterCode = 0; | |
153 if (needsAccessor && backend.fieldHasInterceptedGetter(field)) { | |
154 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
155 namer.getterName(field)); | |
156 } | |
157 if (needsAccessor && backend.fieldHasInterceptedGetter(field)) { | |
158 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
159 namer.setterName(field)); | |
160 } | |
161 if (needsGetter) { | |
162 if (field.isInstanceMember) { | |
163 // 01: function() { return this.field; } | |
164 // 10: function(receiver) { return receiver.field; } | |
165 // 11: function(receiver) { return this.field; } | |
166 bool isIntercepted = backend.fieldHasInterceptedGetter(field); | |
167 getterCode += isIntercepted ? 2 : 0; | |
168 getterCode += backend.isInterceptorClass(element) ? 0 : 1; | |
169 // TODO(sra): 'isInterceptorClass' might not be the correct test | |
170 // for methods forced to use the interceptor convention because | |
171 // the method's class was elsewhere mixed-in to an interceptor. | |
172 assert(!field.isInstanceMember || getterCode != 0); | |
173 if (isIntercepted) { | |
174 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
175 namer.getterName(field)); | |
176 } | |
177 } else { | |
178 getterCode = 1; | |
179 } | |
180 } | |
181 int setterCode = 0; | |
182 if (needsSetter) { | |
183 if (field.isInstanceMember) { | |
184 // 01: function(value) { this.field = value; } | |
185 // 10: function(receiver, value) { receiver.field = value; } | |
186 // 11: function(receiver, value) { this.field = value; } | |
187 bool isIntercepted = backend.fieldHasInterceptedSetter(field); | |
188 setterCode += isIntercepted ? 2 : 0; | |
189 setterCode += backend.isInterceptorClass(element) ? 0 : 1; | |
190 assert(!field.isInstanceMember || setterCode != 0); | |
191 if (isIntercepted) { | |
192 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
193 namer.setterName(field)); | |
194 } | |
195 } else { | |
196 setterCode = 1; | |
197 } | |
198 } | |
199 int code = getterCode + (setterCode << 2); | |
200 if (code == 0) { | |
201 compiler.internalError(field, | |
202 'Field code is 0 ($element/$field).'); | |
203 } else { | |
204 fieldCode = FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]; | |
205 } | |
206 } | |
207 if (backend.isAccessibleByReflection(field)) { | |
208 DartType type = field.type; | |
209 reflectionMarker = '-${emitter.metadataEmitter.reifyType(type)}'; | |
210 } | |
211 String builtFieldname = '$fieldName$fieldCode$reflectionMarker'; | |
212 builder.addField(builtFieldname); | |
213 // Add 1 because adding a field to the class also requires a comma | |
214 compiler.dumpInfoTask.recordFieldNameSize(field, | |
215 builtFieldname.length + 1); | |
216 fieldsAdded = true; | |
217 } | |
218 }); | |
219 } | |
220 | |
221 if (hasMetadata) { | |
222 builder.fieldMetadata = fieldMetadata; | |
223 } | |
224 return fieldsAdded; | |
225 } | |
226 | |
227 void emitClassGettersSetters(ClassElement classElement, | |
228 ClassBuilder builder, | |
229 {bool onlyForRti: false}) { | |
230 if (onlyForRti) return; | |
231 | |
232 visitFields(classElement, false, | |
233 (VariableElement member, | |
234 String name, | |
235 String accessorName, | |
236 bool needsGetter, | |
237 bool needsSetter, | |
238 bool needsCheckedSetter) { | |
239 compiler.withCurrentElement(member, () { | |
240 if (needsCheckedSetter) { | |
241 assert(!needsSetter); | |
242 generateCheckedSetter(member, name, accessorName, builder); | |
243 } | |
244 if (needsGetter) { | |
245 generateGetter(member, name, accessorName, builder); | |
246 } | |
247 if (needsSetter) { | |
248 generateSetter(member, name, accessorName, builder); | |
249 } | |
250 }); | |
251 }); | |
252 } | |
253 | |
254 /** | |
255 * Documentation wanted -- johnniwinther | |
256 * | |
257 * Invariant: [classElement] must be a declaration element. | |
258 */ | |
259 void emitInstanceMembers(ClassElement classElement, | |
260 ClassBuilder builder, | |
261 {bool onlyForRti: false}) { | |
262 assert(invariant(classElement, classElement.isDeclaration)); | |
263 | |
264 if (onlyForRti || classElement.isMixinApplication) return; | |
265 | |
266 void visitMember(ClassElement enclosing, Element member) { | |
267 assert(invariant(classElement, member.isDeclaration)); | |
268 if (member.isInstanceMember) { | |
269 emitter.containerBuilder.addMember(member, builder); | |
270 } | |
271 } | |
272 | |
273 classElement.implementation.forEachMember( | |
274 visitMember, | |
275 includeBackendMembers: true); | |
276 | |
277 if (identical(classElement, compiler.objectClass) | |
278 && compiler.enabledNoSuchMethod) { | |
279 // Emit the noSuchMethod handlers on the Object prototype now, | |
280 // so that the code in the dynamicFunction helper can find | |
281 // them. Note that this helper is invoked before analyzing the | |
282 // full JS script. | |
283 if (!emitter.nativeEmitter.handleNoSuchMethod) { | |
284 emitter.nsmEmitter.emitNoSuchMethodHandlers(builder.addProperty); | |
285 } | |
286 } | |
287 } | |
288 | |
289 void emitClassBuilderWithReflectionData(String className, | |
290 ClassElement classElement, | |
291 ClassBuilder classBuilder, | |
292 ClassBuilder enclosingBuilder) { | |
293 var metadata = emitter.metadataEmitter.buildMetadataFunction(classElement); | |
294 if (metadata != null) { | |
295 classBuilder.addProperty("@", metadata); | |
296 } | |
297 | |
298 if (backend.isAccessibleByReflection(classElement)) { | |
299 List<DartType> typeVars = classElement.typeVariables; | |
300 Iterable typeVariableProperties = emitter.typeVariableHandler | |
301 .typeVariablesOf(classElement).map(js.number); | |
302 | |
303 ClassElement superclass = classElement.superclass; | |
304 bool hasSuper = superclass != null; | |
305 if ((!typeVariableProperties.isEmpty && !hasSuper) || | |
306 (hasSuper && !equalElements(superclass.typeVariables, typeVars))) { | |
307 classBuilder.addProperty('<>', | |
308 new jsAst.ArrayInitializer.from(typeVariableProperties)); | |
309 } | |
310 } | |
311 | |
312 List<jsAst.Property> statics = new List<jsAst.Property>(); | |
313 ClassBuilder staticsBuilder = new ClassBuilder(classElement, namer); | |
314 if (emitFields(classElement, staticsBuilder, null, emitStatics: true)) { | |
315 jsAst.ObjectInitializer initializer = | |
316 staticsBuilder.toObjectInitializer(); | |
317 compiler.dumpInfoTask.registerElementAst(classElement, | |
318 initializer); | |
319 jsAst.Node property = initializer.properties.single; | |
320 compiler.dumpInfoTask.registerElementAst(classElement, property); | |
321 statics.add(property); | |
322 } | |
323 | |
324 ClassBuilder classProperties = | |
325 emitter.elementDescriptors.remove(classElement); | |
326 if (classProperties != null) { | |
327 statics.addAll(classProperties.properties); | |
328 } | |
329 | |
330 if (!statics.isEmpty) { | |
331 classBuilder.addProperty('static', new jsAst.ObjectInitializer(statics)); | |
332 } | |
333 | |
334 // TODO(ahe): This method (generateClass) should return a jsAst.Expression. | |
335 jsAst.ObjectInitializer propertyValue = classBuilder.toObjectInitializer(); | |
336 compiler.dumpInfoTask.registerElementAst(classBuilder.element, propertyValue
); | |
337 enclosingBuilder.addProperty(className, propertyValue); | |
338 | |
339 String reflectionName = emitter.getReflectionName(classElement, className); | |
340 if (reflectionName != null) { | |
341 if (!backend.isAccessibleByReflection(classElement)) { | |
342 enclosingBuilder.addProperty("+$reflectionName", js.number(0)); | |
343 } else { | |
344 List<int> types = <int>[]; | |
345 if (classElement.supertype != null) { | |
346 types.add(emitter.metadataEmitter.reifyType(classElement.supertype)); | |
347 } | |
348 for (DartType interface in classElement.interfaces) { | |
349 types.add(emitter.metadataEmitter.reifyType(interface)); | |
350 } | |
351 enclosingBuilder.addProperty("+$reflectionName", | |
352 new jsAst.ArrayInitializer.from(types.map(js.number))); | |
353 } | |
354 } | |
355 } | |
356 | |
357 /** | |
358 * Invokes [f] for each of the fields of [element]. | |
359 * | |
360 * [element] must be a [ClassElement] or a [LibraryElement]. | |
361 * | |
362 * If [element] is a [ClassElement], the static fields of the class are | |
363 * visited if [visitStatics] is true and the instance fields are visited if | |
364 * [visitStatics] is false. | |
365 * | |
366 * If [element] is a [LibraryElement], [visitStatics] must be true. | |
367 * | |
368 * When visiting the instance fields of a class, the fields of its superclass | |
369 * are also visited if the class is instantiated. | |
370 * | |
371 * Invariant: [element] must be a declaration element. | |
372 */ | |
373 void visitFields(Element element, bool visitStatics, AcceptField f) { | |
374 assert(invariant(element, element.isDeclaration)); | |
375 | |
376 bool isClass = false; | |
377 bool isLibrary = false; | |
378 if (element.isClass) { | |
379 isClass = true; | |
380 } else if (element.isLibrary) { | |
381 isLibrary = true; | |
382 assert(invariant(element, visitStatics)); | |
383 } else { | |
384 throw new SpannableAssertionFailure( | |
385 element, 'Expected a ClassElement or a LibraryElement.'); | |
386 } | |
387 | |
388 // If the class is never instantiated we still need to set it up for | |
389 // inheritance purposes, but we can simplify its JavaScript constructor. | |
390 bool isInstantiated = | |
391 compiler.codegenWorld.directlyInstantiatedClasses.contains(element); | |
392 | |
393 void visitField(Element holder, VariableElement field) { | |
394 assert(invariant(element, field.isDeclaration)); | |
395 String name = field.name; | |
396 | |
397 // Keep track of whether or not we're dealing with a field mixin | |
398 // into a native class. | |
399 bool isMixinNativeField = | |
400 isClass && element.isNative && holder.isMixinApplication; | |
401 | |
402 // See if we can dynamically create getters and setters. | |
403 // We can only generate getters and setters for [element] since | |
404 // the fields of super classes could be overwritten with getters or | |
405 // setters. | |
406 bool needsGetter = false; | |
407 bool needsSetter = false; | |
408 if (isLibrary || isMixinNativeField || holder == element) { | |
409 needsGetter = fieldNeedsGetter(field); | |
410 needsSetter = fieldNeedsSetter(field); | |
411 } | |
412 | |
413 if ((isInstantiated && !holder.isNative) | |
414 || needsGetter | |
415 || needsSetter) { | |
416 String accessorName = namer.fieldAccessorName(field); | |
417 String fieldName = namer.fieldPropertyName(field); | |
418 bool needsCheckedSetter = false; | |
419 if (compiler.enableTypeAssertions | |
420 && needsSetter | |
421 && !canAvoidGeneratedCheckedSetter(field)) { | |
422 needsCheckedSetter = true; | |
423 needsSetter = false; | |
424 } | |
425 // Getters and setters with suffixes will be generated dynamically. | |
426 f(field, fieldName, accessorName, needsGetter, needsSetter, | |
427 needsCheckedSetter); | |
428 } | |
429 } | |
430 | |
431 if (isLibrary) { | |
432 LibraryElement library = element; | |
433 library.implementation.forEachLocalMember((Element member) { | |
434 if (member.isField) visitField(library, member); | |
435 }); | |
436 } else if (visitStatics) { | |
437 ClassElement cls = element; | |
438 cls.implementation.forEachStaticField(visitField); | |
439 } else { | |
440 ClassElement cls = element; | |
441 // TODO(kasperl): We should make sure to only emit one version of | |
442 // overridden fields. Right now, we rely on the ordering so the | |
443 // fields pulled in from mixins are replaced with the fields from | |
444 // the class definition. | |
445 | |
446 // If a class is not instantiated then we add the field just so we can | |
447 // generate the field getter/setter dynamically. Since this is only | |
448 // allowed on fields that are in [element] we don't need to visit | |
449 // superclasses for non-instantiated classes. | |
450 cls.implementation.forEachInstanceField( | |
451 visitField, includeSuperAndInjectedMembers: isInstantiated); | |
452 } | |
453 } | |
454 | |
455 void recordMangledField(Element member, | |
456 String accessorName, | |
457 String memberName) { | |
458 if (!backend.shouldRetainGetter(member)) return; | |
459 String previousName; | |
460 if (member.isInstanceMember) { | |
461 previousName = emitter.mangledFieldNames.putIfAbsent( | |
462 '${namer.getterPrefix}$accessorName', | |
463 () => memberName); | |
464 } else { | |
465 previousName = emitter.mangledGlobalFieldNames.putIfAbsent( | |
466 accessorName, | |
467 () => memberName); | |
468 } | |
469 assert(invariant(member, previousName == memberName, | |
470 message: '$previousName != ${memberName}')); | |
471 } | |
472 | |
473 bool fieldNeedsGetter(VariableElement field) { | |
474 assert(field.isField); | |
475 if (fieldAccessNeverThrows(field)) return false; | |
476 return backend.shouldRetainGetter(field) | |
477 || compiler.codegenWorld.hasInvokedGetter(field, compiler.world); | |
478 } | |
479 | |
480 bool fieldNeedsSetter(VariableElement field) { | |
481 assert(field.isField); | |
482 if (fieldAccessNeverThrows(field)) return false; | |
483 return (!field.isFinal && !field.isConst) | |
484 && (backend.shouldRetainSetter(field) | |
485 || compiler.codegenWorld.hasInvokedSetter(field, compiler.world)); | |
486 } | |
487 | |
488 // We never access a field in a closure (a captured variable) without knowing | |
489 // that it is there. Therefore we don't need to use a getter (that will throw | |
490 // if the getter method is missing), but can always access the field directly. | |
491 static bool fieldAccessNeverThrows(VariableElement field) { | |
492 return field is ClosureFieldElement; | |
493 } | |
494 | |
495 bool canAvoidGeneratedCheckedSetter(VariableElement member) { | |
496 // We never generate accessors for top-level/static fields. | |
497 if (!member.isInstanceMember) return true; | |
498 DartType type = member.type; | |
499 return type.treatAsDynamic || (type.element == compiler.objectClass); | |
500 } | |
501 | |
502 void generateCheckedSetter(Element member, | |
503 String fieldName, | |
504 String accessorName, | |
505 ClassBuilder builder) { | |
506 jsAst.Expression code = backend.generatedCode[member]; | |
507 assert(code != null); | |
508 String setterName = namer.setterNameFromAccessorName(accessorName); | |
509 compiler.dumpInfoTask.registerElementAst(member, | |
510 builder.addProperty(setterName, code)); | |
511 generateReflectionDataForFieldGetterOrSetter( | |
512 member, setterName, builder, isGetter: false); | |
513 } | |
514 | |
515 void generateGetter(Element member, String fieldName, String accessorName, | |
516 ClassBuilder builder) { | |
517 jsAst.Expression function = | |
518 _stubGenerator.generateGetter(member, fieldName); | |
519 | |
520 String getterName = namer.getterNameFromAccessorName(accessorName); | |
521 ClassElement cls = member.enclosingClass; | |
522 String className = namer.getNameOfClass(cls); | |
523 OutputUnit outputUnit = | |
524 compiler.deferredLoadTask.outputUnitForElement(member); | |
525 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
526 js('#.prototype.# = #', [className, getterName, function])); | |
527 if (backend.isAccessibleByReflection(member)) { | |
528 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
529 js('#.prototype.#.${namer.reflectableField} = 1', | |
530 [className, getterName])); | |
531 } | |
532 } | |
533 | |
534 void generateSetter(Element member, String fieldName, String accessorName, | |
535 ClassBuilder builder) { | |
536 jsAst.Expression function = | |
537 _stubGenerator.generateSetter(member, fieldName); | |
538 | |
539 String setterName = namer.setterNameFromAccessorName(accessorName); | |
540 ClassElement cls = member.enclosingClass; | |
541 String className = namer.getNameOfClass(cls); | |
542 OutputUnit outputUnit = | |
543 compiler.deferredLoadTask.outputUnitForElement(member); | |
544 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
545 js('#.prototype.# = #', [className, setterName, function])); | |
546 if (backend.isAccessibleByReflection(member)) { | |
547 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
548 js('#.prototype.#.${namer.reflectableField} = 1', | |
549 [className, setterName])); | |
550 } | |
551 } | |
552 | |
553 void generateReflectionDataForFieldGetterOrSetter(Element member, | |
554 String name, | |
555 ClassBuilder builder, | |
556 {bool isGetter}) { | |
557 Selector selector = isGetter | |
558 ? new Selector.getter(member.name, member.library) | |
559 : new Selector.setter(member.name, member.library); | |
560 String reflectionName = emitter.getReflectionName(selector, name); | |
561 if (reflectionName != null) { | |
562 var reflectable = | |
563 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | |
564 builder.addProperty('+$reflectionName', reflectable); | |
565 } | |
566 } | |
567 | |
568 void emitTypeVariableReaders(ClassElement cls, ClassBuilder builder) { | |
569 List typeVariables = []; | |
570 ClassElement superclass = cls; | |
571 while (superclass != null) { | |
572 for (TypeVariableType parameter in superclass.typeVariables) { | |
573 if (backend.emitter.readTypeVariables.contains(parameter.element)) { | |
574 emitTypeVariableReader(cls, builder, parameter.element); | |
575 } | |
576 } | |
577 superclass = superclass.superclass; | |
578 } | |
579 } | |
580 | |
581 void emitTypeVariableReader(ClassElement cls, | |
582 ClassBuilder builder, | |
583 TypeVariableElement element) { | |
584 String name = namer.readTypeVariableName(element); | |
585 jsAst.Expression index = | |
586 js.number(RuntimeTypes.getTypeVariableIndex(element)); | |
587 jsAst.Expression computeTypeVariable; | |
588 | |
589 Substitution substitution = | |
590 backend.rti.computeSubstitution( | |
591 cls, element.typeDeclaration, alwaysGenerateFunction: true); | |
592 if (substitution != null) { | |
593 jsAst.Expression typeArguments = | |
594 js(r'#.apply(null, this.$builtinTypeInfo)', | |
595 substitution.getCode(backend.rti, true)); | |
596 computeTypeVariable = js('#[#]', [typeArguments, index]); | |
597 } else { | |
598 // TODO(ahe): These can be generated dynamically. | |
599 computeTypeVariable = | |
600 js(r'this.$builtinTypeInfo && this.$builtinTypeInfo[#]', index); | |
601 } | |
602 jsAst.Expression convertRtiToRuntimeType = | |
603 namer.elementAccess(backend.findHelper('convertRtiToRuntimeType')); | |
604 compiler.dumpInfoTask.registerElementAst(element, | |
605 builder.addProperty(name, | |
606 js('function () { return #(#) }', | |
607 [convertRtiToRuntimeType, computeTypeVariable]))); | |
608 } | |
609 } | |
OLD | NEW |