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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart

Issue 1220333003: dart2js: Rename emitters. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 5 months 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
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 void emitClass(Class cls, ClassBuilder enclosingBuilder, Fragment fragment) {
16 ClassElement classElement = cls.element;
17
18 assert(invariant(classElement, classElement.isDeclaration));
19
20 emitter.needsClassSupport = true;
21
22 ClassElement superclass = classElement.superclass;
23 jsAst.Name superName;
24 if (superclass != null) {
25 superName = namer.className(superclass);
26 }
27
28 if (cls.isMixinApplication) {
29 MixinApplication mixinApplication = cls;
30 jsAst.Name mixinName = mixinApplication.mixinClass.name;
31 superName =
32 new CompoundName([superName, Namer.literalPlus, mixinName]);
33 emitter.needsMixinSupport = true;
34 }
35
36 ClassBuilder builder = new ClassBuilder.forClass(classElement, namer);
37 builder.superName = superName;
38 emitConstructorsForCSP(cls);
39 emitFields(cls, builder);
40 emitCheckedClassSetters(cls, builder);
41 emitClassGettersSettersForCSP(cls, builder);
42 emitInstanceMembers(cls, builder);
43 emitStubs(cls.callStubs, builder);
44 emitStubs(cls.typeVariableReaderStubs, builder);
45 emitRuntimeTypeInformation(cls, builder);
46 emitNativeInfo(cls, builder);
47
48 if (classElement == backend.closureClass) {
49 // We add a special getter here to allow for tearing off a closure from
50 // itself.
51 jsAst.Fun function = js('function() { return this; }');
52 jsAst.Name name = namer.getterForMember(Selector.CALL_NAME);
53 builder.addProperty(name, function);
54 }
55
56 emitClassBuilderWithReflectionData(cls, builder, enclosingBuilder,
57 fragment);
58 }
59 /**
60 * Emits the precompiled constructor when in CSP mode.
61 */
62 void emitConstructorsForCSP(Class cls) {
63 List<jsAst.Name> fieldNames = <jsAst.Name>[];
64
65 if (!compiler.useContentSecurityPolicy) return;
66
67 if (!cls.onlyForRti && !cls.isNative) {
68 fieldNames = cls.fields.map((Field field) => field.name).toList();
69 }
70
71 ClassElement classElement = cls.element;
72
73 jsAst.Expression constructorAst =
74 _stubGenerator.generateClassConstructor(classElement, fieldNames);
75
76 jsAst.Name constructorName = namer.className(classElement);
77 OutputUnit outputUnit =
78 compiler.deferredLoadTask.outputUnitForElement(classElement);
79 emitter.assemblePrecompiledConstructor(
80 outputUnit, constructorName, constructorAst, fieldNames);
81 }
82
83 /// Returns `true` if fields added.
84 bool emitFields(FieldContainer container,
85 ClassBuilder builder,
86 { bool classIsNative: false,
87 bool emitStatics: false }) {
88 Iterable<Field> fields;
89 if (container is Class) {
90 if (emitStatics) {
91 fields = container.staticFieldsForReflection;
92 } else if (container.onlyForRti) {
93 return false;
94 } else {
95 fields = container.fields;
96 }
97 } else {
98 assert(container is Library);
99 assert(emitStatics);
100 fields = container.staticFieldsForReflection;
101 }
102
103 var fieldMetadata = [];
104 bool hasMetadata = false;
105 bool fieldsAdded = false;
106
107 for (Field field in fields) {
108 FieldElement fieldElement = field.element;
109 jsAst.Name name = field.name;
110 jsAst.Name accessorName = field.accessorName;
111 bool needsGetter = field.needsGetter;
112 bool needsSetter = field.needsUncheckedSetter;
113
114 // Ignore needsCheckedSetter - that is handled below.
115 bool needsAccessor = (needsGetter || needsSetter);
116 // We need to output the fields for non-native classes so we can auto-
117 // generate the constructor. For native classes there are no
118 // constructors, so we don't need the fields unless we are generating
119 // accessors at runtime.
120 bool needsFieldsForConstructor = !emitStatics && !classIsNative;
121 if (needsFieldsForConstructor || needsAccessor) {
122 var metadata =
123 task.metadataCollector.buildMetadataFunction(fieldElement);
124 if (metadata != null) {
125 hasMetadata = true;
126 } else {
127 metadata = new jsAst.LiteralNull();
128 }
129 fieldMetadata.add(metadata);
130 recordMangledField(fieldElement, accessorName,
131 namer.privateName(fieldElement.memberName));
132 List<jsAst.Literal> fieldNameParts = <jsAst.Literal>[];
133 if (!needsAccessor) {
134 // Emit field for constructor generation.
135 assert(!classIsNative);
136 fieldNameParts.add(name);
137 } else {
138 // Emit (possibly renaming) field name so we can add accessors at
139 // runtime.
140 if (name != accessorName) {
141 fieldNameParts.add(accessorName);
142 fieldNameParts.add(js.stringPart(':'));
143 }
144 fieldNameParts.add(name);
145 if (field.needsInterceptedGetter) {
146 emitter.interceptorEmitter.interceptorInvocationNames.add(
147 namer.getterForElement(fieldElement));
148 }
149 // TODO(16168): The setter creator only looks at the getter-name.
150 // Even though the setter could avoid the interceptor convention we
151 // currently still need to add the additional argument.
152 if (field.needsInterceptedGetter || field.needsInterceptedSetter) {
153 emitter.interceptorEmitter.interceptorInvocationNames.add(
154 namer.setterForElement(fieldElement));
155 }
156
157 int code = field.getterFlags + (field.setterFlags << 2);
158 if (code == 0) {
159 compiler.internalError(fieldElement,
160 'Field code is 0 ($fieldElement).');
161 }
162 fieldNameParts.add(
163 js.stringPart(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]));
164 }
165 // Fields can only be reflected if their declaring class is reflectable
166 // (as they are only accessible via [ClassMirror.declarations]).
167 // However, set/get operations can be performed on them, so they are
168 // reflectable in some sense, which leads to [isAccessibleByReflection]
169 // reporting `true`.
170 if (backend.isAccessibleByReflection(fieldElement)) {
171 fieldNameParts.add(new jsAst.LiteralString('-'));
172 if (fieldElement.isTopLevel ||
173 backend.isAccessibleByReflection(fieldElement.enclosingClass)) {
174 DartType type = fieldElement.type;
175 fieldNameParts.add(task.metadataCollector.reifyType(type));
176 }
177 }
178 jsAst.Literal fieldNameAst = js.concatenateStrings(fieldNameParts);
179 builder.addField(fieldNameAst);
180 // Add 1 because adding a field to the class also requires a comma
181 compiler.dumpInfoTask.registerElementAst(fieldElement, fieldNameAst);
182 fieldsAdded = true;
183 }
184 }
185
186 if (hasMetadata) {
187 builder.fieldMetadata = fieldMetadata;
188 }
189 return fieldsAdded;
190 }
191
192 /// Emits checked setters for fields.
193 void emitCheckedClassSetters(Class cls, ClassBuilder builder) {
194 if (cls.onlyForRti) return;
195
196 for (Field field in cls.fields) {
197 if (field.needsCheckedSetter) {
198 assert(!field.needsUncheckedSetter);
199 compiler.withCurrentElement(field.element, () {
200 generateCheckedSetter(
201 field.element, field.name, field.accessorName, builder);
202 });
203 }
204 }
205 }
206
207 /// Emits getters/setters for fields if compiling in CSP mode.
208 void emitClassGettersSettersForCSP(Class cls, ClassBuilder builder) {
209
210 if (!compiler.useContentSecurityPolicy || cls.onlyForRti) return;
211
212 for (Field field in cls.fields) {
213 Element member = field.element;
214 compiler.withCurrentElement(member, () {
215 if (field.needsGetter) {
216 emitGetterForCSP(member, field.name, field.accessorName, builder);
217 }
218 if (field.needsUncheckedSetter) {
219 emitSetterForCSP(member, field.name, field.accessorName, builder);
220 }
221 });
222 }
223 }
224
225 void emitStubs(Iterable<StubMethod> stubs, ClassBuilder builder) {
226 for (Method method in stubs) {
227 jsAst.Property property = builder.addProperty(method.name, method.code);
228 compiler.dumpInfoTask.registerElementAst(method.element, property);
229 }
230 }
231
232 /**
233 * Documentation wanted -- johnniwinther
234 *
235 * Invariant: [classElement] must be a declaration element.
236 */
237 void emitInstanceMembers(Class cls,
238 ClassBuilder builder) {
239 ClassElement classElement = cls.element;
240 assert(invariant(classElement, classElement.isDeclaration));
241
242 if (cls.onlyForRti || cls.isMixinApplication) return;
243
244 // TODO(herhut): This is a no-op. Should it be removed?
245 for (Field field in cls.fields) {
246 emitter.containerBuilder.addMemberField(field, builder);
247 }
248
249 for (Method method in cls.methods) {
250 assert(invariant(classElement, method.element.isDeclaration));
251 assert(invariant(classElement, method.element.isInstanceMember));
252 emitter.containerBuilder.addMemberMethod(method, builder);
253 }
254
255 if (identical(classElement, compiler.objectClass)
256 && backend.enabledNoSuchMethod) {
257 // Emit the noSuchMethod handlers on the Object prototype now,
258 // so that the code in the dynamicFunction helper can find
259 // them. Note that this helper is invoked before analyzing the
260 // full JS script.
261 emitter.nsmEmitter.emitNoSuchMethodHandlers(builder.addProperty);
262 }
263 }
264
265 /// Emits the members from the model.
266 void emitRuntimeTypeInformation(Class cls, ClassBuilder builder) {
267 assert(builder.functionType == null);
268 if (cls.functionTypeIndex != null) {
269 builder.functionType = cls.functionTypeIndex;
270 }
271
272 for (Method method in cls.isChecks) {
273 builder.addProperty(method.name, method.code);
274 }
275 }
276
277 void emitNativeInfo(Class cls, ClassBuilder builder) {
278 jsAst.Expression nativeInfo = NativeGenerator.encodeNativeInfo(cls);
279 if (nativeInfo != null) {
280 builder.addPropertyByName(namer.nativeSpecProperty, nativeInfo);
281 }
282 }
283
284 void emitClassBuilderWithReflectionData(Class cls,
285 ClassBuilder classBuilder,
286 ClassBuilder enclosingBuilder,
287 Fragment fragment) {
288 ClassElement classElement = cls.element;
289 jsAst.Name className = cls.name;
290
291 var metadata = task.metadataCollector.buildMetadataFunction(classElement);
292 if (metadata != null) {
293 classBuilder.addPropertyByName("@", metadata);
294 }
295
296 if (backend.isAccessibleByReflection(classElement)) {
297 List<DartType> typeVars = classElement.typeVariables;
298 Iterable typeVariableProperties = emitter.typeVariableHandler
299 .typeVariablesOf(classElement);
300
301 ClassElement superclass = classElement.superclass;
302 bool hasSuper = superclass != null;
303 if ((!typeVariableProperties.isEmpty && !hasSuper) ||
304 (hasSuper && !equalElements(superclass.typeVariables, typeVars))) {
305 classBuilder.addPropertyByName('<>',
306 new jsAst.ArrayInitializer(typeVariableProperties.toList()));
307 }
308 }
309
310 List<jsAst.Property> statics = new List<jsAst.Property>();
311 ClassBuilder staticsBuilder =
312 new ClassBuilder.forStatics(classElement, namer);
313 if (emitFields(cls, staticsBuilder, emitStatics: true)) {
314 jsAst.ObjectInitializer initializer =
315 staticsBuilder.toObjectInitializer();
316 compiler.dumpInfoTask.registerElementAst(classElement,
317 initializer);
318 jsAst.Node property = initializer.properties.single;
319 compiler.dumpInfoTask.registerElementAst(classElement, property);
320 statics.add(property);
321 }
322
323 // TODO(herhut): Do not grab statics out of the properties.
324 ClassBuilder classProperties =
325 emitter.elementDescriptors[fragment].remove(classElement);
326 if (classProperties != null) {
327 statics.addAll(classProperties.properties);
328 }
329
330 if (!statics.isEmpty) {
331 classBuilder.addPropertyByName('static',
332 new jsAst.ObjectInitializer(statics));
333 }
334
335 // TODO(ahe): This method (generateClass) should return a jsAst.Expression.
336 jsAst.ObjectInitializer propertyValue =
337 classBuilder.toObjectInitializer();
338 compiler.dumpInfoTask.registerElementAst(classBuilder.element, propertyValue );
339 enclosingBuilder.addProperty(className, propertyValue);
340
341 String reflectionName = emitter.getReflectionName(classElement, className);
342 if (reflectionName != null) {
343 if (!backend.isAccessibleByReflection(classElement)) {
344 // TODO(herhut): Fix use of reflection name here.
345 enclosingBuilder.addPropertyByName("+$reflectionName", js.number(0));
346 } else {
347 List<jsAst.Expression> types = <jsAst.Expression>[];
348 if (classElement.supertype != null) {
349 types.add(task.metadataCollector.reifyType(classElement.supertype));
350 }
351 for (DartType interface in classElement.interfaces) {
352 types.add(task.metadataCollector.reifyType(interface));
353 }
354 // TODO(herhut): Fix use of reflection name here.
355 enclosingBuilder.addPropertyByName("+$reflectionName",
356 new jsAst.ArrayInitializer(types));
357 }
358 }
359 }
360
361 /**
362 * Invokes [f] for each of the fields of [element].
363 *
364 * [element] must be a [ClassElement] or a [LibraryElement].
365 *
366 * If [element] is a [ClassElement], the static fields of the class are
367 * visited if [visitStatics] is true and the instance fields are visited if
368 * [visitStatics] is false.
369 *
370 * If [element] is a [LibraryElement], [visitStatics] must be true.
371 *
372 * When visiting the instance fields of a class, the fields of its superclass
373 * are also visited if the class is instantiated.
374 *
375 * Invariant: [element] must be a declaration element.
376 */
377 void visitFields(Element element, bool visitStatics, AcceptField f) {
378 assert(invariant(element, element.isDeclaration));
379
380 bool isClass = false;
381 bool isLibrary = false;
382 if (element.isClass) {
383 isClass = true;
384 } else if (element.isLibrary) {
385 isLibrary = true;
386 assert(invariant(element, visitStatics));
387 } else {
388 throw new SpannableAssertionFailure(
389 element, 'Expected a ClassElement or a LibraryElement.');
390 }
391
392 // If the class is never instantiated we still need to set it up for
393 // inheritance purposes, but we can simplify its JavaScript constructor.
394 bool isInstantiated =
395 compiler.codegenWorld.directlyInstantiatedClasses.contains(element);
396
397 void visitField(Element holder, FieldElement field) {
398 assert(invariant(element, field.isDeclaration));
399
400 // Keep track of whether or not we're dealing with a field mixin
401 // into a native class.
402 bool isMixinNativeField =
403 isClass && element.isNative && holder.isMixinApplication;
404
405 // See if we can dynamically create getters and setters.
406 // We can only generate getters and setters for [element] since
407 // the fields of super classes could be overwritten with getters or
408 // setters.
409 bool needsGetter = false;
410 bool needsSetter = false;
411 if (isLibrary || isMixinNativeField || holder == element) {
412 needsGetter = fieldNeedsGetter(field);
413 needsSetter = fieldNeedsSetter(field);
414 }
415
416 if ((isInstantiated && !holder.isNative)
417 || needsGetter
418 || needsSetter) {
419 jsAst.Name accessorName = namer.fieldAccessorName(field);
420 jsAst.Name fieldName = namer.fieldPropertyName(field);
421 bool needsCheckedSetter = false;
422 if (compiler.enableTypeAssertions
423 && needsSetter
424 && !canAvoidGeneratedCheckedSetter(field)) {
425 needsCheckedSetter = true;
426 needsSetter = false;
427 }
428 // Getters and setters with suffixes will be generated dynamically.
429 f(field, fieldName, accessorName, needsGetter, needsSetter,
430 needsCheckedSetter);
431 }
432 }
433
434 if (isLibrary) {
435 LibraryElement library = element;
436 library.implementation.forEachLocalMember((Element member) {
437 if (member.isField) visitField(library, member);
438 });
439 } else if (visitStatics) {
440 ClassElement cls = element;
441 cls.implementation.forEachStaticField(visitField);
442 } else {
443 ClassElement cls = element;
444 // TODO(kasperl): We should make sure to only emit one version of
445 // overridden fields. Right now, we rely on the ordering so the
446 // fields pulled in from mixins are replaced with the fields from
447 // the class definition.
448
449 // If a class is not instantiated then we add the field just so we can
450 // generate the field getter/setter dynamically. Since this is only
451 // allowed on fields that are in [element] we don't need to visit
452 // superclasses for non-instantiated classes.
453 cls.implementation.forEachInstanceField(
454 visitField, includeSuperAndInjectedMembers: isInstantiated);
455 }
456 }
457
458 void recordMangledField(Element member,
459 jsAst.Name accessorName,
460 String memberName) {
461 if (!backend.shouldRetainGetter(member)) return;
462 String previousName;
463 if (member.isInstanceMember) {
464 previousName = emitter.mangledFieldNames.putIfAbsent(
465 namer.deriveGetterName(accessorName),
466 () => memberName);
467 } else {
468 previousName = emitter.mangledGlobalFieldNames.putIfAbsent(
469 accessorName,
470 () => memberName);
471 }
472 assert(invariant(member, previousName == memberName,
473 message: '$previousName != ${memberName}'));
474 }
475
476 bool fieldNeedsGetter(VariableElement field) {
477 assert(field.isField);
478 if (fieldAccessNeverThrows(field)) return false;
479 if (backend.shouldRetainGetter(field)) return true;
480 return field.isClassMember &&
481 compiler.codegenWorld.hasInvokedGetter(field, compiler.world);
482 }
483
484 bool fieldNeedsSetter(VariableElement field) {
485 assert(field.isField);
486 if (fieldAccessNeverThrows(field)) return false;
487 if (field.isFinal || field.isConst) return false;
488 if (backend.shouldRetainSetter(field)) return true;
489 return field.isClassMember &&
490 compiler.codegenWorld.hasInvokedSetter(field, compiler.world);
491 }
492
493 static bool fieldAccessNeverThrows(VariableElement field) {
494 return
495 // We never access a field in a closure (a captured variable) without
496 // knowing that it is there. Therefore we don't need to use a getter
497 // (that will throw if the getter method is missing), but can always
498 // access the field directly.
499 field is ClosureFieldElement;
500 }
501
502 bool canAvoidGeneratedCheckedSetter(VariableElement member) {
503 // We never generate accessors for top-level/static fields.
504 if (!member.isInstanceMember) return true;
505 DartType type = member.type;
506 return type.treatAsDynamic || (type.element == compiler.objectClass);
507 }
508
509 void generateCheckedSetter(Element member,
510 jsAst.Name fieldName,
511 jsAst.Name accessorName,
512 ClassBuilder builder) {
513 jsAst.Expression code = backend.generatedCode[member];
514 assert(code != null);
515 jsAst.Name setterName = namer.deriveSetterName(accessorName);
516 compiler.dumpInfoTask.registerElementAst(member,
517 builder.addProperty(setterName, code));
518 generateReflectionDataForFieldGetterOrSetter(
519 member, setterName, builder, isGetter: false);
520 }
521
522 void emitGetterForCSP(Element member, jsAst.Name fieldName,
523 jsAst.Name accessorName,
524 ClassBuilder builder) {
525 jsAst.Expression function =
526 _stubGenerator.generateGetter(member, fieldName);
527
528 jsAst.Name getterName = namer.deriveGetterName(accessorName);
529 ClassElement cls = member.enclosingClass;
530 jsAst.Name className = namer.className(cls);
531 OutputUnit outputUnit =
532 compiler.deferredLoadTask.outputUnitForElement(member);
533 emitter.cspPrecompiledFunctionFor(outputUnit).add(
534 js('#.prototype.# = #', [className, getterName, function]));
535 if (backend.isAccessibleByReflection(member)) {
536 emitter.cspPrecompiledFunctionFor(outputUnit).add(
537 js('#.prototype.#.${namer.reflectableField} = 1',
538 [className, getterName]));
539 }
540 }
541
542 void emitSetterForCSP(Element member, jsAst.Name fieldName,
543 jsAst.Name accessorName,
544 ClassBuilder builder) {
545 jsAst.Expression function =
546 _stubGenerator.generateSetter(member, fieldName);
547
548 jsAst.Name setterName = namer.deriveSetterName(accessorName);
549 ClassElement cls = member.enclosingClass;
550 jsAst.Name className = namer.className(cls);
551 OutputUnit outputUnit =
552 compiler.deferredLoadTask.outputUnitForElement(member);
553 emitter.cspPrecompiledFunctionFor(outputUnit).add(
554 js('#.prototype.# = #', [className, setterName, function]));
555 if (backend.isAccessibleByReflection(member)) {
556 emitter.cspPrecompiledFunctionFor(outputUnit).add(
557 js('#.prototype.#.${namer.reflectableField} = 1',
558 [className, setterName]));
559 }
560 }
561
562 void generateReflectionDataForFieldGetterOrSetter(Element member,
563 jsAst.Name name,
564 ClassBuilder builder,
565 {bool isGetter}) {
566 Selector selector = isGetter
567 ? new Selector.getter(member.name, member.library)
568 : new Selector.setter(member.name, member.library);
569 String reflectionName = emitter.getReflectionName(selector, name);
570 if (reflectionName != null) {
571 var reflectable =
572 js(backend.isAccessibleByReflection(member) ? '1' : '0');
573 builder.addPropertyByName('+$reflectionName', reflectable);
574 }
575 }
576 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698