OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 const USE_NEW_EMITTER = const bool.fromEnvironment("dart2js.use.new.emitter"); | |
8 | |
9 /** | |
10 * Generates the code for all used classes in the program. Static fields (even | |
11 * in classes) are ignored, since they can be treated as non-class elements. | |
12 * | |
13 * The code for the containing (used) methods must exist in the [:universe:]. | |
14 */ | |
15 class CodeEmitterTask extends CompilerTask { | |
16 // TODO(floitsch): the code-emitter task should not need a namer. | |
17 final Namer namer; | |
18 final TypeTestEmitter typeTestEmitter = new TypeTestEmitter(); | |
19 NativeEmitter nativeEmitter; | |
20 OldEmitter oldEmitter; | |
21 Emitter emitter; | |
22 | |
23 final Set<ClassElement> neededClasses = new Set<ClassElement>(); | |
24 final Map<OutputUnit, List<ClassElement>> outputClassLists = | |
25 new Map<OutputUnit, List<ClassElement>>(); | |
26 final Map<OutputUnit, List<ConstantValue>> outputConstantLists = | |
27 new Map<OutputUnit, List<ConstantValue>>(); | |
28 final Map<OutputUnit, List<Element>> outputStaticLists = | |
29 new Map<OutputUnit, List<Element>>(); | |
30 final Map<OutputUnit, Set<LibraryElement>> outputLibraryLists = | |
31 new Map<OutputUnit, Set<LibraryElement>>(); | |
32 | |
33 /// True, if the output contains a constant list. | |
34 /// | |
35 /// This flag is updated in [computeNeededConstants]. | |
36 bool outputContainsConstantList = false; | |
37 | |
38 final List<ClassElement> nativeClasses = <ClassElement>[]; | |
39 | |
40 /// Records if a type variable is read dynamically for type tests. | |
41 final Set<TypeVariableElement> readTypeVariables = | |
42 new Set<TypeVariableElement>(); | |
43 | |
44 List<TypedefElement> typedefsNeededForReflection; | |
45 | |
46 JavaScriptBackend get backend => compiler.backend; | |
47 | |
48 CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap) | |
49 : super(compiler), | |
50 this.namer = namer { | |
51 oldEmitter = new OldEmitter(compiler, namer, generateSourceMap, this); | |
52 emitter = USE_NEW_EMITTER | |
53 ? new new_js_emitter.Emitter(compiler, namer) | |
54 : oldEmitter; | |
55 nativeEmitter = new NativeEmitter(this); | |
56 typeTestEmitter.emitter = this.oldEmitter; | |
57 } | |
58 | |
59 | |
60 jsAst.Expression generateEmbeddedGlobalAccess(String global) { | |
61 return emitter.generateEmbeddedGlobalAccess(global); | |
62 } | |
63 | |
64 jsAst.Expression constantReference(ConstantValue value) { | |
65 return emitter.constantReference(value); | |
66 } | |
67 | |
68 void registerReadTypeVariable(TypeVariableElement element) { | |
69 readTypeVariables.add(element); | |
70 } | |
71 | |
72 Set<ClassElement> interceptorsReferencedFromConstants() { | |
73 Set<ClassElement> classes = new Set<ClassElement>(); | |
74 JavaScriptConstantCompiler handler = backend.constants; | |
75 List<ConstantValue> constants = handler.getConstantsForEmission(); | |
76 for (ConstantValue constant in constants) { | |
77 if (constant is InterceptorConstantValue) { | |
78 InterceptorConstantValue interceptorConstant = constant; | |
79 classes.add(interceptorConstant.dispatchedType.element); | |
80 } | |
81 } | |
82 return classes; | |
83 } | |
84 | |
85 /** | |
86 * Return a function that returns true if its argument is a class | |
87 * that needs to be emitted. | |
88 */ | |
89 Function computeClassFilter() { | |
90 if (backend.isTreeShakingDisabled) return (ClassElement cls) => true; | |
91 | |
92 Set<ClassElement> unneededClasses = new Set<ClassElement>(); | |
93 // The [Bool] class is not marked as abstract, but has a factory | |
94 // constructor that always throws. We never need to emit it. | |
95 unneededClasses.add(compiler.boolClass); | |
96 | |
97 // Go over specialized interceptors and then constants to know which | |
98 // interceptors are needed. | |
99 Set<ClassElement> needed = new Set<ClassElement>(); | |
100 backend.specializedGetInterceptors.forEach( | |
101 (_, Iterable<ClassElement> elements) { | |
102 needed.addAll(elements); | |
103 } | |
104 ); | |
105 | |
106 // Add interceptors referenced by constants. | |
107 needed.addAll(interceptorsReferencedFromConstants()); | |
108 | |
109 // Add unneeded interceptors to the [unneededClasses] set. | |
110 for (ClassElement interceptor in backend.interceptedClasses) { | |
111 if (!needed.contains(interceptor) | |
112 && interceptor != compiler.objectClass) { | |
113 unneededClasses.add(interceptor); | |
114 } | |
115 } | |
116 | |
117 // These classes are just helpers for the backend's type system. | |
118 unneededClasses.add(backend.jsMutableArrayClass); | |
119 unneededClasses.add(backend.jsFixedArrayClass); | |
120 unneededClasses.add(backend.jsExtendableArrayClass); | |
121 unneededClasses.add(backend.jsUInt32Class); | |
122 unneededClasses.add(backend.jsUInt31Class); | |
123 unneededClasses.add(backend.jsPositiveIntClass); | |
124 | |
125 return (ClassElement cls) => !unneededClasses.contains(cls); | |
126 } | |
127 | |
128 /** | |
129 * Compute all the constants that must be emitted. | |
130 */ | |
131 void computeNeededConstants() { | |
132 // Make sure we retain all metadata of all elements. This could add new | |
133 // constants to the handler. | |
134 if (backend.mustRetainMetadata) { | |
135 // TODO(floitsch): verify that we don't run through the same elements | |
136 // multiple times. | |
137 for (Element element in backend.generatedCode.keys) { | |
138 if (backend.isAccessibleByReflection(element)) { | |
139 bool shouldRetainMetadata = backend.retainMetadataOf(element); | |
140 if (shouldRetainMetadata && element.isFunction) { | |
141 FunctionElement function = element; | |
142 function.functionSignature.forEachParameter( | |
143 backend.retainMetadataOf); | |
144 } | |
145 } | |
146 } | |
147 for (ClassElement cls in neededClasses) { | |
148 final onlyForRti = typeTestEmitter.rtiNeededClasses.contains(cls); | |
149 if (!onlyForRti) { | |
150 backend.retainMetadataOf(cls); | |
151 oldEmitter.classEmitter.visitFields(cls, false, | |
152 (Element member, | |
153 String name, | |
154 String accessorName, | |
155 bool needsGetter, | |
156 bool needsSetter, | |
157 bool needsCheckedSetter) { | |
158 bool needsAccessor = needsGetter || needsSetter; | |
159 if (needsAccessor && backend.isAccessibleByReflection(member)) { | |
160 backend.retainMetadataOf(member); | |
161 } | |
162 }); | |
163 } | |
164 } | |
165 typedefsNeededForReflection.forEach(backend.retainMetadataOf); | |
166 } | |
167 | |
168 JavaScriptConstantCompiler handler = backend.constants; | |
169 List<ConstantValue> constants = handler.getConstantsForEmission( | |
170 compiler.hasIncrementalSupport ? null : emitter.compareConstants); | |
171 for (ConstantValue constant in constants) { | |
172 if (emitter.isConstantInlinedOrAlreadyEmitted(constant)) continue; | |
173 | |
174 if (constant.isList) outputContainsConstantList = true; | |
175 | |
176 OutputUnit constantUnit = | |
177 compiler.deferredLoadTask.outputUnitForConstant(constant); | |
178 if (constantUnit == null) { | |
179 // The back-end introduces some constants, like "InterceptorConstant" or | |
180 // some list constants. They are emitted in the main output-unit. | |
181 // TODO(sigurdm): We should track those constants. | |
182 constantUnit = compiler.deferredLoadTask.mainOutputUnit; | |
183 } | |
184 outputConstantLists.putIfAbsent( | |
185 constantUnit, () => new List<ConstantValue>()).add(constant); | |
186 } | |
187 } | |
188 | |
189 /// Compute all the classes and typedefs that must be emitted. | |
190 void computeNeededDeclarations() { | |
191 // Compute needed typedefs. | |
192 typedefsNeededForReflection = Elements.sortedByPosition( | |
193 compiler.world.allTypedefs | |
194 .where(backend.isAccessibleByReflection) | |
195 .toList()); | |
196 | |
197 // Compute needed classes. | |
198 Set<ClassElement> instantiatedClasses = | |
199 compiler.codegenWorld.directlyInstantiatedClasses | |
200 .where(computeClassFilter()).toSet(); | |
201 | |
202 void addClassWithSuperclasses(ClassElement cls) { | |
203 neededClasses.add(cls); | |
204 for (ClassElement superclass = cls.superclass; | |
205 superclass != null; | |
206 superclass = superclass.superclass) { | |
207 neededClasses.add(superclass); | |
208 } | |
209 } | |
210 | |
211 void addClassesWithSuperclasses(Iterable<ClassElement> classes) { | |
212 for (ClassElement cls in classes) { | |
213 addClassWithSuperclasses(cls); | |
214 } | |
215 } | |
216 | |
217 // 1. We need to generate all classes that are instantiated. | |
218 addClassesWithSuperclasses(instantiatedClasses); | |
219 | |
220 // 2. Add all classes used as mixins. | |
221 Set<ClassElement> mixinClasses = neededClasses | |
222 .where((ClassElement element) => element.isMixinApplication) | |
223 .map(computeMixinClass) | |
224 .toSet(); | |
225 neededClasses.addAll(mixinClasses); | |
226 | |
227 // 3. If we need noSuchMethod support, we run through all needed | |
228 // classes to figure out if we need the support on any native | |
229 // class. If so, we let the native emitter deal with it. | |
230 if (compiler.enabledNoSuchMethod) { | |
231 String noSuchMethodName = Compiler.NO_SUCH_METHOD; | |
232 Selector noSuchMethodSelector = compiler.noSuchMethodSelector; | |
233 for (ClassElement element in neededClasses) { | |
234 if (!element.isNative) continue; | |
235 Element member = element.lookupLocalMember(noSuchMethodName); | |
236 if (member == null) continue; | |
237 if (noSuchMethodSelector.applies(member, compiler.world)) { | |
238 nativeEmitter.handleNoSuchMethod = true; | |
239 break; | |
240 } | |
241 } | |
242 } | |
243 | |
244 // 4. Find all classes needed for rti. | |
245 // It is important that this is the penultimate step, at this point, | |
246 // neededClasses must only contain classes that have been resolved and | |
247 // codegen'd. The rtiNeededClasses may contain additional classes, but | |
248 // these are thought to not have been instantiated, so we neeed to be able | |
249 // to identify them later and make sure we only emit "empty shells" without | |
250 // fields, etc. | |
251 typeTestEmitter.computeRtiNeededClasses(); | |
252 typeTestEmitter.rtiNeededClasses.removeAll(neededClasses); | |
253 // rtiNeededClasses now contains only the "empty shells". | |
254 neededClasses.addAll(typeTestEmitter.rtiNeededClasses); | |
255 | |
256 // TODO(18175, floitsch): remove once issue 18175 is fixed. | |
257 if (neededClasses.contains(backend.jsIntClass)) { | |
258 neededClasses.add(compiler.intClass); | |
259 } | |
260 if (neededClasses.contains(backend.jsDoubleClass)) { | |
261 neededClasses.add(compiler.doubleClass); | |
262 } | |
263 if (neededClasses.contains(backend.jsNumberClass)) { | |
264 neededClasses.add(compiler.numClass); | |
265 } | |
266 if (neededClasses.contains(backend.jsStringClass)) { | |
267 neededClasses.add(compiler.stringClass); | |
268 } | |
269 if (neededClasses.contains(backend.jsBoolClass)) { | |
270 neededClasses.add(compiler.boolClass); | |
271 } | |
272 if (neededClasses.contains(backend.jsArrayClass)) { | |
273 neededClasses.add(compiler.listClass); | |
274 } | |
275 | |
276 // 5. Finally, sort the classes. | |
277 List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses); | |
278 | |
279 for (ClassElement element in sortedClasses) { | |
280 if (Elements.isNativeOrExtendsNative(element) && | |
281 !typeTestEmitter.rtiNeededClasses.contains(element)) { | |
282 // For now, native classes and related classes cannot be deferred. | |
283 nativeClasses.add(element); | |
284 if (!element.isNative) { | |
285 assert(invariant(element, | |
286 !compiler.deferredLoadTask.isDeferred(element))); | |
287 outputClassLists.putIfAbsent(compiler.deferredLoadTask.mainOutputUnit, | |
288 () => new List<ClassElement>()).add(element); | |
289 } | |
290 } else { | |
291 outputClassLists.putIfAbsent( | |
292 compiler.deferredLoadTask.outputUnitForElement(element), | |
293 () => new List<ClassElement>()) | |
294 .add(element); | |
295 } | |
296 } | |
297 } | |
298 | |
299 void computeNeededStatics() { | |
300 bool isStaticFunction(Element element) => | |
301 !element.isInstanceMember && !element.isField; | |
302 | |
303 Iterable<Element> elements = | |
304 backend.generatedCode.keys.where(isStaticFunction); | |
305 | |
306 for (Element element in Elements.sortedByPosition(elements)) { | |
307 outputStaticLists.putIfAbsent( | |
308 compiler.deferredLoadTask.outputUnitForElement(element), | |
309 () => new List<Element>()) | |
310 .add(element); | |
311 } | |
312 } | |
313 | |
314 void computeNeededLibraries() { | |
315 void addSurroundingLibraryToSet(Element element) { | |
316 OutputUnit unit = compiler.deferredLoadTask.outputUnitForElement(element); | |
317 LibraryElement library = element.library; | |
318 outputLibraryLists.putIfAbsent(unit, () => new Set<LibraryElement>()) | |
319 .add(library); | |
320 } | |
321 | |
322 backend.generatedCode.keys.forEach(addSurroundingLibraryToSet); | |
323 neededClasses.forEach(addSurroundingLibraryToSet); | |
324 } | |
325 | |
326 void assembleProgram() { | |
327 measure(() { | |
328 emitter.invalidateCaches(); | |
329 | |
330 // Compute the required type checks to know which classes need a | |
331 // 'is$' method. | |
332 typeTestEmitter.computeRequiredTypeChecks(); | |
333 | |
334 computeNeededDeclarations(); | |
335 computeNeededConstants(); | |
336 computeNeededStatics(); | |
337 computeNeededLibraries(); | |
338 | |
339 | |
340 Program program; | |
341 if (USE_NEW_EMITTER) { | |
342 program = new ProgramBuilder(compiler, namer, this).buildProgram(); | |
343 } | |
344 emitter.emitProgram(program); | |
345 }); | |
346 } | |
347 } | |
348 | |
349 abstract class Emitter { | |
350 void emitProgram(Program program); | |
351 | |
352 jsAst.Expression generateEmbeddedGlobalAccess(String global); | |
353 jsAst.Expression constantReference(ConstantValue value); | |
354 | |
355 int compareConstants(ConstantValue a, ConstantValue b); | |
356 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant); | |
357 | |
358 void invalidateCaches(); | |
359 } | |
OLD | NEW |