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 dart_backend; | |
6 | |
7 // TODO(ahe): This class is simply wrong. This backend should use | |
8 // elements when it can, not AST nodes. Perhaps a [Map<Element, | |
9 // TreeElements>] is what is needed. | |
10 class ElementAst { | |
11 final Node ast; | |
12 final TreeElements treeElements; | |
13 | |
14 ElementAst(AstElement element) | |
15 : this.internal(element.resolvedAst.node, element.resolvedAst.elements); | |
16 | |
17 ElementAst.internal(this.ast, this.treeElements); | |
18 } | |
19 | |
20 class DartBackend extends Backend { | |
21 final List<CompilerTask> tasks; | |
22 final bool stripAsserts; | |
23 | |
24 // TODO(zarah) Maybe change this to a command-line option. | |
25 // Right now, it is set by the tests. | |
26 bool useMirrorHelperLibrary = false; | |
27 | |
28 /// Updated to a [MirrorRenamerImpl] instance if the [useMirrorHelperLibrary] | |
29 /// field is set and mirror are needed. | |
30 MirrorRenamer mirrorRenamer = const MirrorRenamer(); | |
31 | |
32 final DartOutputter outputter; | |
33 | |
34 // Used in test. | |
35 PlaceholderRenamer get placeholderRenamer => outputter.renamer; | |
36 Map<ClassNode, List<Node>> get memberNodes => outputter.output.memberNodes; | |
37 | |
38 ConstantSystem get constantSystem { | |
39 return constantCompilerTask.constantCompiler.constantSystem; | |
40 } | |
41 | |
42 BackendConstantEnvironment get constants => constantCompilerTask; | |
43 | |
44 DartConstantTask constantCompilerTask; | |
45 | |
46 DartResolutionCallbacks resolutionCallbacks; | |
47 | |
48 final Set<ClassElement> usedTypeLiterals = new Set<ClassElement>(); | |
49 | |
50 /** | |
51 * Tells whether it is safe to remove type declarations from variables, | |
52 * functions parameters. It becomes not safe if: | |
53 * 1) TypeError is used somewhere in the code, | |
54 * 2) The code has typedefs in right hand side of IS checks, | |
55 * 3) The code has classes which extend typedefs, have type arguments typedefs | |
56 * or type variable bounds typedefs. | |
57 * These restrictions can be less strict. | |
58 */ | |
59 bool isSafeToRemoveTypeDeclarations( | |
60 Map<ClassElement, Iterable<Element>> classMembers) { | |
61 ClassElement typeErrorElement = compiler.coreLibrary.find('TypeError'); | |
62 if (classMembers.containsKey(typeErrorElement) || | |
63 compiler.resolverWorld.isChecks.any( | |
64 (DartType type) => type.element == typeErrorElement)) { | |
65 return false; | |
66 } | |
67 Set<DartType> processedTypes = new Set<DartType>(); | |
68 List<DartType> workQueue = new List<DartType>(); | |
69 workQueue.addAll( | |
70 classMembers.keys.map((classElement) => classElement.thisType)); | |
71 workQueue.addAll(compiler.resolverWorld.isChecks); | |
72 | |
73 while (!workQueue.isEmpty) { | |
74 DartType type = workQueue.removeLast(); | |
75 if (processedTypes.contains(type)) continue; | |
76 processedTypes.add(type); | |
77 if (type is FunctionType) return false; | |
78 if (type is TypedefType) return false; | |
79 if (type is InterfaceType) { | |
80 InterfaceType interfaceType = type; | |
81 // Check all type arguments. | |
82 interfaceType.typeArguments.forEach(workQueue.add); | |
83 ClassElement element = type.element; | |
84 // Check all supertypes. | |
85 if (element.allSupertypes != null) { | |
86 element.allSupertypes.forEach(workQueue.add); | |
87 } | |
88 } | |
89 } | |
90 return true; | |
91 } | |
92 | |
93 DartBackend(Compiler compiler, List<String> strips, {bool multiFile}) | |
94 : tasks = <CompilerTask>[], | |
95 stripAsserts = strips.indexOf('asserts') != -1, | |
96 constantCompilerTask = new DartConstantTask(compiler), | |
97 outputter = new DartOutputter( | |
98 compiler, compiler.outputProvider, | |
99 forceStripTypes: strips.indexOf('types') != -1, | |
100 multiFile: multiFile, | |
101 enableMinification: compiler.enableMinification), | |
102 super(compiler) { | |
103 resolutionCallbacks = new DartResolutionCallbacks(this); | |
104 } | |
105 | |
106 bool classNeedsRti(ClassElement cls) => false; | |
107 bool methodNeedsRti(FunctionElement function) => false; | |
108 | |
109 void enqueueHelpers(ResolutionEnqueuer world, Registry registry) { | |
110 // Right now resolver doesn't always resolve interfaces needed | |
111 // for literals, so force them. TODO(antonm): fix in the resolver. | |
112 final LITERAL_TYPE_NAMES = const [ | |
113 'Map', 'List', 'num', 'int', 'double', 'bool' | |
114 ]; | |
115 final coreLibrary = compiler.coreLibrary; | |
116 for (final name in LITERAL_TYPE_NAMES) { | |
117 ClassElement classElement = coreLibrary.findLocal(name); | |
118 classElement.ensureResolved(compiler); | |
119 } | |
120 // Enqueue the methods that the VM might invoke on user objects because | |
121 // we don't trust the resolution to always get these included. | |
122 world.registerInvocation(new Selector.call("toString", null, 0)); | |
123 world.registerInvokedGetter(new Selector.getter("hashCode", null)); | |
124 world.registerInvocation(new Selector.binaryOperator("==")); | |
125 world.registerInvocation(new Selector.call("compareTo", null, 1)); | |
126 } | |
127 | |
128 void codegen(CodegenWorkItem work) { } | |
129 | |
130 /// Create an [ElementAst] from the CPS IR. | |
131 static ElementAst createElementAst(Compiler compiler, | |
132 Tracer tracer, | |
133 ConstantSystem constantSystem, | |
134 Element element, | |
135 cps_ir.FunctionDefinition function) { | |
136 // Transformations on the CPS IR. | |
137 if (tracer != null) { | |
138 tracer.traceCompilation(element.name, null); | |
139 } | |
140 | |
141 void traceGraph(String title, var irObject) { | |
142 if (tracer != null) { | |
143 tracer.traceGraph(title, irObject); | |
144 } | |
145 } | |
146 | |
147 new ConstantPropagator(compiler, constantSystem).rewrite(function); | |
148 traceGraph("Sparse constant propagation", function); | |
149 new RedundantPhiEliminator().rewrite(function); | |
150 traceGraph("Redundant phi elimination", function); | |
151 new ShrinkingReducer().rewrite(function); | |
152 traceGraph("Shrinking reductions", function); | |
153 | |
154 // Do not rewrite the IR after variable allocation. Allocation | |
155 // makes decisions based on an approximation of IR variable live | |
156 // ranges that can be invalidated by transforming the IR. | |
157 new cps_ir.RegisterAllocator().visit(function); | |
158 | |
159 tree_builder.Builder builder = new tree_builder.Builder(compiler); | |
160 tree_ir.FunctionDefinition definition = builder.build(function); | |
161 assert(definition != null); | |
162 traceGraph('Tree builder', definition); | |
163 | |
164 // Transformations on the Tree IR. | |
165 new StatementRewriter().rewrite(definition); | |
166 traceGraph('Statement rewriter', definition); | |
167 new CopyPropagator().rewrite(definition); | |
168 traceGraph('Copy propagation', definition); | |
169 new LoopRewriter().rewrite(definition); | |
170 traceGraph('Loop rewriter', definition); | |
171 new LogicalRewriter().rewrite(definition); | |
172 traceGraph('Logical rewriter', definition); | |
173 new backend_ast_emitter.UnshadowParameters().unshadow(definition); | |
174 traceGraph('Unshadow parameters', definition); | |
175 | |
176 TreeElementMapping treeElements = new TreeElementMapping(element); | |
177 backend_ast.Node backendAst = | |
178 backend_ast_emitter.emit(definition); | |
179 Node frontend_ast = backend2frontend.emit(treeElements, backendAst); | |
180 return new ElementAst.internal(frontend_ast, treeElements); | |
181 | |
182 } | |
183 | |
184 /** | |
185 * Tells whether we should output given element. Corelib classes like | |
186 * Object should not be in the resulting code. | |
187 */ | |
188 @override | |
189 bool shouldOutput(Element element) { | |
190 return (!element.library.isPlatformLibrary && | |
191 !element.isSynthesized && | |
192 element is! AbstractFieldElement) | |
193 || mirrorRenamer.isMirrorHelperLibrary(element.library); | |
194 } | |
195 | |
196 void assembleProgram() { | |
197 | |
198 ElementAst computeElementAst(AstElement element) { | |
199 if (!compiler.irBuilder.hasIr(element)) { | |
200 return new ElementAst(element); | |
201 } else { | |
202 cps_ir.FunctionDefinition function = compiler.irBuilder.getIr(element); | |
203 return createElementAst(compiler, | |
204 compiler.tracer, constantSystem, element, function); | |
205 } | |
206 } | |
207 | |
208 // TODO(johnniwinther): Remove the need for this method. | |
209 void postProcessElementAst( | |
210 AstElement element, ElementAst elementAst, | |
211 newTypedefElementCallback, | |
212 newClassElementCallback) { | |
213 ReferencedElementCollector collector = | |
214 new ReferencedElementCollector(compiler, | |
215 element, | |
216 elementAst, | |
217 newTypedefElementCallback, | |
218 newClassElementCallback); | |
219 collector.collect(); | |
220 } | |
221 | |
222 String assembledCode = outputter.assembleProgram( | |
223 libraries: compiler.libraryLoader.libraries, | |
224 instantiatedClasses: compiler.resolverWorld.directlyInstantiatedClasses, | |
225 resolvedElements: compiler.enqueuer.resolution.resolvedElements, | |
226 usedTypeLiterals: usedTypeLiterals, | |
227 postProcessElementAst: postProcessElementAst, | |
228 computeElementAst: computeElementAst, | |
229 shouldOutput: shouldOutput, | |
230 isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations, | |
231 sortElements: sortElements, | |
232 mirrorRenamer: mirrorRenamer, | |
233 mainFunction: compiler.mainFunction, | |
234 outputUri: compiler.outputUri); | |
235 compiler.assembledCode = assembledCode; | |
236 | |
237 int totalSize = assembledCode.length; | |
238 | |
239 // Output verbose info about size ratio of resulting bundle to all | |
240 // referenced non-platform sources. | |
241 logResultBundleSizeInfo( | |
242 outputter.libraryInfo.userLibraries, | |
243 outputter.elementInfo.topLevelElements, | |
244 assembledCode.length); | |
245 } | |
246 | |
247 void logResultBundleSizeInfo(Iterable<LibraryElement> userLibraries, | |
248 Iterable<Element> topLevelElements, | |
249 int totalOutputSize) { | |
250 // Sum total size of scripts in each referenced library. | |
251 int nonPlatformSize = 0; | |
252 for (LibraryElement lib in userLibraries) { | |
253 for (CompilationUnitElement compilationUnit in lib.compilationUnits) { | |
254 nonPlatformSize += compilationUnit.script.file.length; | |
255 } | |
256 } | |
257 int percentage = totalOutputSize * 100 ~/ nonPlatformSize; | |
258 log('Total used non-platform files size: ${nonPlatformSize} bytes, ' | |
259 'Output total size: $totalOutputSize bytes (${percentage}%)'); | |
260 } | |
261 | |
262 log(String message) => compiler.log('[DartBackend] $message'); | |
263 | |
264 Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) { | |
265 // All platform classes must be resolved to ensure that their member names | |
266 // are preserved. | |
267 loadedLibraries.values.forEach((LibraryElement library) { | |
268 if (library.isPlatformLibrary) { | |
269 library.forEachLocalMember((Element element) { | |
270 if (element.isClass) { | |
271 ClassElement classElement = element; | |
272 classElement.ensureResolved(compiler); | |
273 } | |
274 }); | |
275 } | |
276 }); | |
277 if (useMirrorHelperLibrary && | |
278 loadedLibraries.containsKey(Compiler.DART_MIRRORS)) { | |
279 return compiler.libraryLoader.loadLibrary( | |
280 compiler.translateResolvedUri( | |
281 loadedLibraries[Compiler.DART_MIRRORS], | |
282 MirrorRenamerImpl.DART_MIRROR_HELPER, null)). | |
283 then((LibraryElement library) { | |
284 mirrorRenamer = new MirrorRenamerImpl(compiler, this, library); | |
285 }); | |
286 } | |
287 return new Future.value(); | |
288 } | |
289 | |
290 void registerStaticUse(Element element, Enqueuer enqueuer) { | |
291 if (element == compiler.mirrorSystemGetNameFunction) { | |
292 FunctionElement getNameFunction = mirrorRenamer.getNameFunction; | |
293 if (getNameFunction != null) { | |
294 enqueuer.addToWorkList(getNameFunction); | |
295 } | |
296 } | |
297 } | |
298 } | |
299 | |
300 class DartResolutionCallbacks extends ResolutionCallbacks { | |
301 final DartBackend backend; | |
302 | |
303 DartResolutionCallbacks(this.backend); | |
304 | |
305 void onTypeLiteral(DartType type, Registry registry) { | |
306 if (type.isInterfaceType) { | |
307 backend.usedTypeLiterals.add(type.element); | |
308 } | |
309 } | |
310 } | |
311 | |
312 class EmitterUnparser extends Unparser { | |
313 final Map<Node, String> renames; | |
314 | |
315 EmitterUnparser(this.renames, {bool minify, bool stripTypes}) | |
316 : super(minify: minify, stripTypes: stripTypes); | |
317 | |
318 visit(Node node) { | |
319 if (node != null && renames.containsKey(node)) { | |
320 write(renames[node]); | |
321 } else { | |
322 super.visit(node); | |
323 } | |
324 } | |
325 | |
326 unparseSendReceiver(Send node, {bool spacesNeeded: false}) { | |
327 // TODO(smok): Remove ugly hack for library prefices. | |
328 if (node.receiver != null && renames[node.receiver] == '') return; | |
329 super.unparseSendReceiver(node, spacesNeeded: spacesNeeded); | |
330 } | |
331 | |
332 unparseFunctionName(Node name) { | |
333 if (name != null && renames.containsKey(name)) { | |
334 write(renames[name]); | |
335 } else { | |
336 super.unparseFunctionName(name); | |
337 } | |
338 } | |
339 } | |
340 | |
341 | |
342 /** | |
343 * Some elements are not recorded by resolver now, | |
344 * for example, typedefs or classes which are only | |
345 * used in signatures, as/is operators or in super clauses | |
346 * (just to name a few). Retraverse AST to pick those up. | |
347 */ | |
348 class ReferencedElementCollector extends Visitor { | |
349 final Compiler compiler; | |
350 final Element element; | |
351 final ElementAst elementAst; | |
352 final newTypedefElementCallback; | |
353 final newClassElementCallback; | |
354 | |
355 ReferencedElementCollector(this.compiler, | |
356 this.element, | |
357 this.elementAst, | |
358 this.newTypedefElementCallback, | |
359 this.newClassElementCallback); | |
360 | |
361 visitNode(Node node) { | |
362 node.visitChildren(this); | |
363 } | |
364 | |
365 visitTypeAnnotation(TypeAnnotation typeAnnotation) { | |
366 TreeElements treeElements = elementAst.treeElements; | |
367 final DartType type = treeElements.getType(typeAnnotation); | |
368 assert(invariant(typeAnnotation, type != null, | |
369 message: "Missing type for type annotation: $treeElements.")); | |
370 if (type.isTypedef) newTypedefElementCallback(type.element); | |
371 if (type.isInterfaceType) newClassElementCallback(type.element); | |
372 typeAnnotation.visitChildren(this); | |
373 } | |
374 | |
375 void collect() { | |
376 compiler.withCurrentElement(element, () { | |
377 elementAst.ast.accept(this); | |
378 }); | |
379 } | |
380 } | |
381 | |
382 Comparator compareBy(f) => (x, y) => f(x).compareTo(f(y)); | |
383 | |
384 List sorted(Iterable l, comparison) { | |
385 final result = new List.from(l); | |
386 result.sort(comparison); | |
387 return result; | |
388 } | |
389 | |
390 compareElements(e0, e1) { | |
391 int result = compareBy((e) => e.library.canonicalUri.toString())(e0, e1); | |
392 if (result != 0) return result; | |
393 return compareBy((e) => e.position.charOffset)(e0, e1); | |
394 } | |
395 | |
396 List<Element> sortElements(Iterable<Element> elements) => | |
397 sorted(elements, compareElements); | |
398 | |
399 /// [ConstantCompilerTask] for compilation of constants for the Dart backend. | |
400 /// | |
401 /// Since this task needs no distinction between frontend and backend constants | |
402 /// it also serves as the [BackendConstantEnvironment]. | |
403 class DartConstantTask extends ConstantCompilerTask | |
404 implements BackendConstantEnvironment { | |
405 final DartConstantCompiler constantCompiler; | |
406 | |
407 DartConstantTask(Compiler compiler) | |
408 : this.constantCompiler = new DartConstantCompiler(compiler), | |
409 super(compiler); | |
410 | |
411 String get name => 'ConstantHandler'; | |
412 | |
413 ConstantExpression getConstantForVariable(VariableElement element) { | |
414 return constantCompiler.getConstantForVariable(element); | |
415 } | |
416 | |
417 ConstantExpression getConstantForNode(Node node, TreeElements elements) { | |
418 return constantCompiler.getConstantForNode(node, elements); | |
419 } | |
420 | |
421 ConstantExpression getConstantForMetadata(MetadataAnnotation metadata) { | |
422 return metadata.constant; | |
423 } | |
424 | |
425 ConstantExpression compileConstant(VariableElement element) { | |
426 return measure(() { | |
427 return constantCompiler.compileConstant(element); | |
428 }); | |
429 } | |
430 | |
431 void compileVariable(VariableElement element) { | |
432 measure(() { | |
433 constantCompiler.compileVariable(element); | |
434 }); | |
435 } | |
436 | |
437 ConstantExpression compileNode(Node node, TreeElements elements) { | |
438 return measure(() { | |
439 return constantCompiler.compileNodeWithDefinitions(node, elements); | |
440 }); | |
441 } | |
442 | |
443 ConstantExpression compileMetadata(MetadataAnnotation metadata, | |
444 Node node, | |
445 TreeElements elements) { | |
446 return measure(() { | |
447 return constantCompiler.compileMetadata(metadata, node, elements); | |
448 }); | |
449 } | |
450 } | |
OLD | NEW |