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