OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 library dart2js.js_emitter.program_builder; | |
6 | |
7 import 'model.dart'; | |
8 import '../common.dart'; | |
9 import '../js/js.dart' as js; | |
10 | |
11 import '../js_backend/js_backend.dart' show | |
12 Namer, | |
13 JavaScriptBackend, | |
14 JavaScriptConstantCompiler; | |
15 | |
16 import '../closure.dart' show ClosureFieldElement; | |
17 | |
18 import 'js_emitter.dart' as emitterTask show | |
19 CodeEmitterTask, | |
20 Emitter, | |
21 InterceptorStubGenerator; | |
22 | |
23 import '../universe/universe.dart' show Universe; | |
24 import '../deferred_load.dart' show DeferredLoadTask, OutputUnit; | |
25 | |
26 part 'registry.dart'; | |
27 | |
28 class ProgramBuilder { | |
29 final Compiler _compiler; | |
30 final Namer namer; | |
31 final emitterTask.CodeEmitterTask _task; | |
32 | |
33 final Registry _registry; | |
34 | |
35 ProgramBuilder(Compiler compiler, | |
36 this.namer, | |
37 this._task) | |
38 : this._compiler = compiler, | |
39 this._registry = new Registry(compiler); | |
40 | |
41 JavaScriptBackend get backend => _compiler.backend; | |
42 Universe get universe => _compiler.codegenWorld; | |
43 | |
44 /// Mapping from [ClassElement] to constructed [Class]. We need this to | |
45 /// update the superclass in the [Class]. | |
46 final Map<ClassElement, Class> _classes = <ClassElement, Class>{}; | |
47 | |
48 /// Mapping from [OutputUnit] to constructed [Output]. We need this to | |
49 /// generate the deferredLoadingMap (to know which hunks to load). | |
50 final Map<OutputUnit, Output> _outputs = <OutputUnit, Output>{}; | |
51 | |
52 /// Mapping from [ConstantValue] to constructed [Constant]. We need this to | |
53 /// update field-initializers to point to the ConstantModel. | |
54 final Map<ConstantValue, Constant> _constants = <ConstantValue, Constant>{}; | |
55 | |
56 Program buildProgram() { | |
57 _task.outputClassLists.forEach(_registry.registerElements); | |
58 _task.outputStaticLists.forEach(_registry.registerElements); | |
59 _task.outputConstantLists.forEach(_registerConstants); | |
60 | |
61 // TODO(kasperl): There's code that implicitly needs access to the special | |
62 // $ holder so we have to register that. Can we track if we have to? | |
63 _registry.registerHolder(r'$'); | |
64 | |
65 MainOutput mainOutput = _buildMainOutput(_registry.mainFragment); | |
66 Iterable<Output> deferredOutputs = _registry.deferredFragments | |
67 .map((fragment) => _buildDeferredOutput(mainOutput, fragment)); | |
68 | |
69 List<Output> outputs = new List<Output>(_registry.fragmentCount); | |
70 outputs[0] = mainOutput; | |
71 outputs.setAll(1, deferredOutputs); | |
72 | |
73 Program result = | |
74 new Program(outputs, _task.outputContainsConstantList, _buildLoadMap()); | |
75 | |
76 // Resolve the superclass references after we've processed all the classes. | |
77 _classes.forEach((ClassElement element, Class c) { | |
78 if (element.superclass != null) { | |
79 c.setSuperclass(_classes[element.superclass]); | |
80 } | |
81 }); | |
82 | |
83 _markEagerClasses(); | |
84 | |
85 return result; | |
86 } | |
87 | |
88 void _markEagerClasses() { | |
89 _markEagerInterceptorClasses(); | |
90 } | |
91 | |
92 /// Builds a map from loadId to outputs-to-load. | |
93 Map<String, List<Output>> _buildLoadMap() { | |
94 List<OutputUnit> convertHunks(List<OutputUnit> hunks) { | |
95 return hunks.map((OutputUnit unit) => _outputs[unit]) | |
96 .toList(growable: false); | |
97 } | |
98 | |
99 Map<String, List<Output>> loadMap = <String, List<Output>>{}; | |
100 _compiler.deferredLoadTask.hunksToLoad | |
101 .forEach((String loadId, List<OutputUnit> outputUnits) { | |
102 loadMap[loadId] = outputUnits | |
103 .map((OutputUnit unit) => _outputs[unit]) | |
104 .toList(growable: false); | |
105 }); | |
106 return loadMap; | |
107 } | |
108 | |
109 MainOutput _buildMainOutput(Fragment fragment) { | |
110 // Construct the main output from the libraries and the registered holders. | |
111 MainOutput result = new MainOutput( | |
112 "", // The empty string is the name for the main output file. | |
113 namer.elementAccess(_compiler.mainFunction), | |
114 _buildLibraries(fragment), | |
115 _buildStaticNonFinalFields(fragment), | |
116 _buildStaticLazilyInitializedFields(fragment), | |
117 _buildConstants(fragment), | |
118 _registry.holders.toList(growable: false)); | |
119 _outputs[fragment.outputUnit] = result; | |
120 return result; | |
121 } | |
122 | |
123 /// Returns a name composed of the main output file name and [name]. | |
124 String _outputFileName(String name) { | |
125 assert(name != ""); | |
126 String outPath = _compiler.outputUri != null | |
127 ? _compiler.outputUri.path | |
128 : "out"; | |
129 String outName = outPath.substring(outPath.lastIndexOf('/') + 1); | |
130 return "${outName}_$name"; | |
131 } | |
132 | |
133 DeferredOutput _buildDeferredOutput(MainOutput mainOutput, | |
134 Fragment fragment) { | |
135 DeferredOutput result = new DeferredOutput( | |
136 _outputFileName(fragment.name), fragment.name, | |
137 mainOutput, | |
138 _buildLibraries(fragment), | |
139 _buildStaticNonFinalFields(fragment), | |
140 _buildStaticLazilyInitializedFields(fragment), | |
141 _buildConstants(fragment)); | |
142 _outputs[fragment.outputUnit] = result; | |
143 return result; | |
144 } | |
145 | |
146 List<Constant> _buildConstants(Fragment fragment) { | |
147 List<ConstantValue> constantValues = | |
148 _task.outputConstantLists[fragment.outputUnit]; | |
149 if (constantValues == null) return const <Constant>[]; | |
150 return constantValues.map((ConstantValue value) => _constants[value]) | |
151 .toList(growable: false); | |
152 } | |
153 | |
154 List<StaticField> _buildStaticNonFinalFields(Fragment fragment) { | |
155 // TODO(floitsch): handle static non-final fields correctly with deferred | |
156 // libraries. | |
157 if (fragment != _registry.mainFragment) return const <StaticField>[]; | |
158 Iterable<VariableElement> staticNonFinalFields = | |
159 backend.constants.getStaticNonFinalFieldsForEmission(); | |
160 return Elements.sortedByPosition(staticNonFinalFields) | |
161 .map(_buildStaticField) | |
162 .toList(growable: false); | |
163 } | |
164 | |
165 StaticField _buildStaticField(Element element) { | |
166 JavaScriptConstantCompiler handler = backend.constants; | |
167 ConstantValue initialValue = handler.getInitialValueFor(element).value; | |
168 js.Expression code = _task.emitter.constantReference(initialValue); | |
169 String name = namer.getNameOfGlobalField(element); | |
170 bool isFinal = false; | |
171 bool isLazy = false; | |
172 return new StaticField(name, _registry.registerHolder(r'$'), code, | |
173 isFinal, isLazy); | |
174 } | |
175 | |
176 List<StaticField> _buildStaticLazilyInitializedFields(Fragment fragment) { | |
177 // TODO(floitsch): lazy fields should just be in their respective | |
178 // libraries. | |
179 if (fragment != _registry.mainFragment) return const <StaticField>[]; | |
180 | |
181 JavaScriptConstantCompiler handler = backend.constants; | |
182 List<VariableElement> lazyFields = | |
183 handler.getLazilyInitializedFieldsForEmission(); | |
184 return Elements.sortedByPosition(lazyFields) | |
185 .map(_buildLazyField) | |
186 .where((field) => field != null) // Happens when the field was unused. | |
187 .toList(growable: false); | |
188 } | |
189 | |
190 StaticField _buildLazyField(Element element) { | |
191 JavaScriptConstantCompiler handler = backend.constants; | |
192 js.Expression code = backend.generatedCode[element]; | |
193 // The code is null if we ended up not needing the lazily | |
194 // initialized field after all because of constant folding | |
195 // before code generation. | |
196 if (code == null) return null; | |
197 | |
198 String name = namer.getNameOfGlobalField(element); | |
199 bool isFinal = element.isFinal; | |
200 bool isLazy = true; | |
201 return new StaticField(name, _registry.registerHolder(r'$'), code, | |
202 isFinal, isLazy); | |
203 } | |
204 | |
205 List<Library> _buildLibraries(Fragment fragment) { | |
206 List<Library> libraries = new List<Library>(fragment.length); | |
207 int count = 0; | |
208 fragment.forEach((LibraryElement library, List<Element> elements) { | |
209 libraries[count++] = _buildLibrary(library, elements); | |
210 }); | |
211 return libraries; | |
212 } | |
213 | |
214 // Note that a library-element may have multiple [Library]s, if it is split | |
215 // into multiple output units. | |
216 Library _buildLibrary(LibraryElement library, List<Element> elements) { | |
217 String uri = library.canonicalUri.toString(); | |
218 | |
219 List<StaticMethod> statics = elements | |
220 .where((e) => e is FunctionElement).map(_buildStaticMethod).toList(); | |
221 | |
222 statics.addAll(elements | |
223 .where((e) => e is FunctionElement) | |
224 .where((e) => universe.staticFunctionsNeedingGetter.contains(e)) | |
225 .map(_buildStaticMethodTearOff)); | |
226 | |
227 if (library == backend.interceptorsLibrary) { | |
228 statics.addAll(_generateGetInterceptorMethods()); | |
229 statics.addAll(_generateOneShotInterceptors()); | |
230 } | |
231 | |
232 List<Class> classes = elements | |
233 .where((e) => e is ClassElement) | |
234 .map(_buildClass) | |
235 .toList(growable: false); | |
236 | |
237 return new Library(uri, statics, classes); | |
238 } | |
239 | |
240 Class _buildClass(ClassElement element) { | |
241 bool isInstantiated = | |
242 _compiler.codegenWorld.directlyInstantiatedClasses.contains(element); | |
243 | |
244 List<Method> methods = []; | |
245 List<InstanceField> fields = []; | |
246 | |
247 void visitMember(ClassElement enclosing, Element member) { | |
248 assert(invariant(element, member.isDeclaration)); | |
249 assert(invariant(element, element == enclosing)); | |
250 | |
251 if (Elements.isNonAbstractInstanceMember(member)) { | |
252 js.Expression code = backend.generatedCode[member]; | |
253 // TODO(kasperl): Figure out under which conditions code is null. | |
254 if (code != null) methods.add(_buildMethod(member, code)); | |
255 } else if (member.isField && !member.isStatic) { | |
256 fields.add(_buildInstanceField(member, enclosing)); | |
257 } | |
258 } | |
259 | |
260 ClassElement implementation = element.implementation; | |
261 if (isInstantiated) { | |
262 implementation.forEachMember(visitMember, includeBackendMembers: true); | |
263 } | |
264 String name = namer.getNameOfClass(element); | |
265 String holderName = namer.globalObjectFor(element); | |
266 Holder holder = _registry.registerHolder(holderName); | |
267 Class result = new Class(name, holder, methods, fields); | |
268 _classes[element] = result; | |
269 return result; | |
270 } | |
271 | |
272 Method _buildMethod(FunctionElement element, js.Expression code) { | |
273 String name = namer.getNameOfInstanceMember(element); | |
274 return new Method(name, code); | |
275 } | |
276 | |
277 // The getInterceptor methods directly access the prototype of classes. | |
278 // We must evaluate these classes eagerly so that the prototype is | |
279 // accessible. | |
280 void _markEagerInterceptorClasses() { | |
281 Map<String, Set<ClassElement>> specializedGetInterceptors = | |
282 backend.specializedGetInterceptors; | |
283 for (Set<ClassElement> classes in specializedGetInterceptors.values) { | |
284 for (ClassElement element in classes) { | |
285 Class cls = _classes[element]; | |
286 if (cls != null) cls.isEager = true; | |
287 } | |
288 } | |
289 } | |
290 | |
291 Iterable<StaticMethod> _generateGetInterceptorMethods() { | |
292 emitterTask.InterceptorStubGenerator stubGenerator = | |
293 new emitterTask.InterceptorStubGenerator(_compiler, namer, backend); | |
294 | |
295 String holderName = namer.globalObjectFor(backend.interceptorsLibrary); | |
296 Holder holder = _registry.registerHolder(holderName); | |
297 | |
298 Map<String, Set<ClassElement>> specializedGetInterceptors = | |
299 backend.specializedGetInterceptors; | |
300 List<String> names = specializedGetInterceptors.keys.toList()..sort(); | |
301 return names.map((String name) { | |
302 Set<ClassElement> classes = specializedGetInterceptors[name]; | |
303 js.Expression code = stubGenerator.generateGetInterceptorMethod(classes); | |
304 return new StaticMethod(name, holder, code); | |
305 }); | |
306 } | |
307 | |
308 bool _fieldNeedsGetter(VariableElement field) { | |
309 assert(field.isField); | |
310 if (_fieldAccessNeverThrows(field)) return false; | |
311 return backend.shouldRetainGetter(field) | |
312 || _compiler.codegenWorld.hasInvokedGetter(field, _compiler.world); | |
313 } | |
314 | |
315 bool _fieldNeedsSetter(VariableElement field) { | |
316 assert(field.isField); | |
317 if (_fieldAccessNeverThrows(field)) return false; | |
318 return (!field.isFinal && !field.isConst) | |
319 && (backend.shouldRetainSetter(field) | |
320 || _compiler.codegenWorld.hasInvokedSetter(field, _compiler.world)); | |
321 } | |
322 | |
323 // We never access a field in a closure (a captured variable) without knowing | |
324 // that it is there. Therefore we don't need to use a getter (that will throw | |
325 // if the getter method is missing), but can always access the field directly. | |
326 bool _fieldAccessNeverThrows(VariableElement field) { | |
327 return field is ClosureFieldElement; | |
328 } | |
329 | |
330 InstanceField _buildInstanceField(VariableElement field, | |
331 ClassElement holder) { | |
332 assert(invariant(field, field.isDeclaration)); | |
333 String name = namer.fieldPropertyName(field); | |
334 | |
335 int getterFlags = 0; | |
336 if (_fieldNeedsGetter(field)) { | |
337 bool isIntercepted = backend.fieldHasInterceptedGetter(field); | |
338 if (isIntercepted) { | |
339 getterFlags += 2; | |
340 if (backend.isInterceptorClass(holder)) { | |
341 getterFlags += 1; | |
342 } | |
343 } else { | |
344 getterFlags = 1; | |
345 } | |
346 } | |
347 | |
348 int setterFlags = 0; | |
349 if (_fieldNeedsSetter(field)) { | |
350 bool isIntercepted = backend.fieldHasInterceptedSetter(field); | |
351 if (isIntercepted) { | |
352 setterFlags += 2; | |
353 if (backend.isInterceptorClass(holder)) { | |
354 setterFlags += 1; | |
355 } | |
356 } else { | |
357 setterFlags = 1; | |
358 } | |
359 } | |
360 | |
361 return new InstanceField(name, getterFlags, setterFlags); | |
362 } | |
363 | |
364 Iterable<StaticMethod> _generateOneShotInterceptors() { | |
365 emitterTask.InterceptorStubGenerator stubGenerator = | |
366 new emitterTask.InterceptorStubGenerator(_compiler, namer, backend); | |
367 | |
368 String holderName = namer.globalObjectFor(backend.interceptorsLibrary); | |
369 Holder holder = _registry.registerHolder(holderName); | |
370 | |
371 List<String> names = backend.oneShotInterceptors.keys.toList()..sort(); | |
372 return names.map((String name) { | |
373 js.Expression code = stubGenerator.generateOneShotInterceptor(name); | |
374 return new StaticMethod(name, holder, code); | |
375 }); | |
376 } | |
377 | |
378 StaticMethod _buildStaticMethod(FunctionElement element) { | |
379 String name = namer.getNameOfMember(element); | |
380 String holder = namer.globalObjectFor(element); | |
381 js.Expression code = backend.generatedCode[element]; | |
382 return new StaticMethod(name, _registry.registerHolder(holder), code); | |
383 } | |
384 | |
385 StaticMethod _buildStaticMethodTearOff(FunctionElement element) { | |
386 String name = namer.getStaticClosureName(element); | |
387 String holder = namer.globalObjectFor(element); | |
388 // TODO(kasperl): This clearly doesn't work yet. | |
389 js.Expression code = js.string("<<unimplemented>>"); | |
390 return new StaticMethod(name, _registry.registerHolder(holder), code); | |
391 } | |
392 | |
393 void _registerConstants(OutputUnit outputUnit, | |
394 List<ConstantValue> constantValues) { | |
395 if (constantValues == null) return; | |
396 for (ConstantValue constantValue in constantValues) { | |
397 assert(!_constants.containsKey(constantValue)); | |
398 String name = namer.constantName(constantValue); | |
399 String constantObject = namer.globalObjectForConstant(constantValue); | |
400 Holder holder = _registry.registerHolder(constantObject); | |
401 Constant constant = new Constant(name, holder, constantValue); | |
402 _constants[constantValue] = constant; | |
403 }; | |
404 } | |
405 } | |
OLD | NEW |