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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_emitter/old_emitter/class_emitter.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 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698