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(this.ast, this.treeElements); | |
15 } | |
16 | |
17 class DartBackend extends Backend { | |
18 final List<CompilerTask> tasks; | |
19 final bool stripAsserts; | |
20 | |
21 bool get supportsReflection => true; | |
22 | |
23 // TODO(zarah) Maybe change this to a command-line option. | |
24 // Right now, it is set by the tests. | |
25 bool useMirrorHelperLibrary = false; | |
26 | |
27 /// Updated to a [MirrorRenamerImpl] instance if the [useMirrorHelperLibrary] | |
28 /// field is set and mirror are needed. | |
29 MirrorRenamer mirrorRenamer = const MirrorRenamer(); | |
30 | |
31 final DartOutputter outputter; | |
32 | |
33 // Used in test. | |
34 PlaceholderRenamer get placeholderRenamer => outputter.renamer; | |
35 Map<ClassNode, List<Node>> get memberNodes => outputter.output.memberNodes; | |
36 | |
37 ConstantSystem get constantSystem { | |
38 return constantCompilerTask.constantCompiler.constantSystem; | |
39 } | |
40 | |
41 BackendConstantEnvironment get constants => constantCompilerTask; | |
42 | |
43 DartConstantTask constantCompilerTask; | |
44 | |
45 DartImpactTransformer impactTransformer; | |
46 | |
47 final Set<ClassElement> usedTypeLiterals = new Set<ClassElement>(); | |
48 | |
49 /// The set of visible platform classes that are implemented by instantiated | |
50 /// user classes. | |
51 final Set<ClassElement> _userImplementedPlatformClasses = | |
52 new Set<ClassElement>(); | |
53 | |
54 bool enableCodegenWithErrorsIfSupported(Spannable node) { | |
55 reporter.reportHintMessage(node, MessageKind.GENERIC, { | |
56 'text': "Generation of code with compile time errors is not " | |
57 "supported for dart2dart." | |
58 }); | |
59 return false; | |
60 } | |
61 | |
62 /** | |
63 * Tells whether it is safe to remove type declarations from variables, | |
64 * functions parameters. It becomes not safe if: | |
65 * 1) TypeError is used somewhere in the code, | |
66 * 2) The code has typedefs in right hand side of IS checks, | |
67 * 3) The code has classes which extend typedefs, have type arguments typedefs | |
68 * or type variable bounds typedefs. | |
69 * These restrictions can be less strict. | |
70 */ | |
71 bool isSafeToRemoveTypeDeclarations( | |
72 Map<ClassElement, Iterable<Element>> classMembers) { | |
73 ClassElement typeErrorElement = compiler.coreLibrary.find('TypeError'); | |
74 if (classMembers.containsKey(typeErrorElement) || | |
75 compiler.resolverWorld.isChecks | |
76 .any((DartType type) => type.element == typeErrorElement)) { | |
77 return false; | |
78 } | |
79 Set<DartType> processedTypes = new Set<DartType>(); | |
80 List<DartType> workQueue = new List<DartType>(); | |
81 workQueue | |
82 .addAll(classMembers.keys.map((classElement) => classElement.thisType)); | |
83 workQueue.addAll(compiler.resolverWorld.isChecks); | |
84 | |
85 while (!workQueue.isEmpty) { | |
86 DartType type = workQueue.removeLast(); | |
87 if (processedTypes.contains(type)) continue; | |
88 processedTypes.add(type); | |
89 if (type is FunctionType) return false; | |
90 if (type is TypedefType) return false; | |
91 if (type is InterfaceType) { | |
92 InterfaceType interfaceType = type; | |
93 // Check all type arguments. | |
94 interfaceType.typeArguments.forEach(workQueue.add); | |
95 ClassElement element = type.element; | |
96 // Check all supertypes. | |
97 if (element.allSupertypes != null) { | |
98 element.allSupertypes.forEach(workQueue.add); | |
99 } | |
100 } | |
101 } | |
102 return true; | |
103 } | |
104 | |
105 DartBackend(Compiler compiler, List<String> strips, {bool multiFile}) | |
106 : tasks = <CompilerTask>[], | |
107 stripAsserts = strips.indexOf('asserts') != -1, | |
108 constantCompilerTask = new DartConstantTask(compiler), | |
109 outputter = new DartOutputter( | |
110 compiler.reporter, compiler.outputProvider, | |
111 forceStripTypes: strips.indexOf('types') != -1, | |
112 multiFile: multiFile, | |
113 enableMinification: compiler.options.enableMinification), | |
114 super(compiler) { | |
115 impactTransformer = new DartImpactTransformer(this); | |
116 } | |
117 | |
118 DiagnosticReporter get reporter => compiler.reporter; | |
119 | |
120 Resolution get resolution => compiler.resolution; | |
121 | |
122 bool classNeedsRti(ClassElement cls) => false; | |
123 bool methodNeedsRti(FunctionElement function) => false; | |
124 | |
125 void enqueueHelpers(ResolutionEnqueuer world, Registry registry) { | |
126 // Right now resolver doesn't always resolve interfaces needed | |
127 // for literals, so force them. TODO(antonm): fix in the resolver. | |
128 final LITERAL_TYPE_NAMES = const [ | |
129 'Map', | |
130 'List', | |
131 'num', | |
132 'int', | |
133 'double', | |
134 'bool' | |
135 ]; | |
136 final coreLibrary = compiler.coreLibrary; | |
137 for (final name in LITERAL_TYPE_NAMES) { | |
138 ClassElement classElement = coreLibrary.findLocal(name); | |
139 classElement.ensureResolved(resolution); | |
140 } | |
141 // Enqueue the methods that the VM might invoke on user objects because | |
142 // we don't trust the resolution to always get these included. | |
143 world.registerDynamicUse(new DynamicUse(Selectors.toString_, null)); | |
144 world.registerDynamicUse(new DynamicUse(Selectors.hashCode_, null)); | |
145 world.registerDynamicUse( | |
146 new DynamicUse(new Selector.binaryOperator('=='), null)); | |
147 world.registerDynamicUse(new DynamicUse(Selectors.compareTo, null)); | |
148 } | |
149 | |
150 WorldImpact codegen(CodegenWorkItem work) { | |
151 return const WorldImpact(); | |
152 } | |
153 | |
154 /** | |
155 * Tells whether we should output given element. Corelib classes like | |
156 * Object should not be in the resulting code. | |
157 */ | |
158 @override | |
159 bool shouldOutput(Element element) { | |
160 return (!element.library.isPlatformLibrary && | |
161 !element.isSynthesized && | |
162 element is! AbstractFieldElement) || | |
163 mirrorRenamer.isMirrorHelperLibrary(element.library); | |
164 } | |
165 | |
166 int assembleProgram() { | |
167 ElementAst computeElementAst(AstElement element) { | |
168 return new ElementAst( | |
169 element.resolvedAst.node, element.resolvedAst.elements); | |
170 } | |
171 | |
172 // TODO(johnniwinther): Remove the need for this method. | |
173 void postProcessElementAst(AstElement element, ElementAst elementAst, | |
174 newTypedefElementCallback, newClassElementCallback) { | |
175 ReferencedElementCollector collector = new ReferencedElementCollector( | |
176 reporter, | |
177 element, | |
178 elementAst, | |
179 newTypedefElementCallback, | |
180 newClassElementCallback); | |
181 collector.collect(); | |
182 } | |
183 | |
184 int totalSize = outputter.assembleProgram( | |
185 libraries: compiler.libraryLoader.libraries, | |
186 instantiatedClasses: compiler.resolverWorld.directlyInstantiatedClasses, | |
187 resolvedElements: compiler.enqueuer.resolution.processedElements, | |
188 usedTypeLiterals: usedTypeLiterals, | |
189 postProcessElementAst: postProcessElementAst, | |
190 computeElementAst: computeElementAst, | |
191 shouldOutput: shouldOutput, | |
192 isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations, | |
193 sortElements: Elements.sortedByPosition, | |
194 mirrorRenamer: mirrorRenamer, | |
195 mainFunction: compiler.mainFunction, | |
196 outputUri: compiler.options.outputUri); | |
197 | |
198 // Output verbose info about size ratio of resulting bundle to all | |
199 // referenced non-platform sources. | |
200 logResultBundleSizeInfo(outputter.libraryInfo.userLibraries, | |
201 outputter.elementInfo.topLevelElements, totalSize); | |
202 | |
203 return totalSize; | |
204 } | |
205 | |
206 void logResultBundleSizeInfo(Iterable<LibraryElement> userLibraries, | |
207 Iterable<Element> topLevelElements, int totalOutputSize) { | |
208 // Sum total size of scripts in each referenced library. | |
209 int nonPlatformSize = 0; | |
210 for (LibraryElement lib in userLibraries) { | |
211 for (CompilationUnitElement compilationUnit in lib.compilationUnits) { | |
212 nonPlatformSize += compilationUnit.script.file.length; | |
213 } | |
214 } | |
215 int percentage = totalOutputSize * 100 ~/ nonPlatformSize; | |
216 log('Total used non-platform files size: ${nonPlatformSize} bytes, ' | |
217 'Output total size: $totalOutputSize bytes (${percentage}%)'); | |
218 } | |
219 | |
220 log(String message) => reporter.log('[DartBackend] $message'); | |
221 | |
222 @override | |
223 Future onLibrariesLoaded(LoadedLibraries loadedLibraries) { | |
224 // All platform classes must be resolved to ensure that their member names | |
225 // are preserved. | |
226 loadedLibraries.forEachLibrary((LibraryElement library) { | |
227 if (library.isPlatformLibrary) { | |
228 library.forEachLocalMember((Element element) { | |
229 if (element.isClass) { | |
230 ClassElement classElement = element; | |
231 classElement.ensureResolved(resolution); | |
232 } | |
233 }); | |
234 } | |
235 }); | |
236 if (useMirrorHelperLibrary && | |
237 loadedLibraries.containsLibrary(Uris.dart_mirrors)) { | |
238 return compiler.libraryLoader | |
239 .loadLibrary(compiler.resolvedUriTranslator.translate( | |
240 loadedLibraries.getLibrary(Uris.dart_mirrors), | |
241 MirrorRenamerImpl.DART_MIRROR_HELPER, | |
242 null)) | |
243 .then((LibraryElement library) { | |
244 mirrorRenamer = new MirrorRenamerImpl(compiler, this, library); | |
245 }); | |
246 } | |
247 return new Future.value(); | |
248 } | |
249 | |
250 @override | |
251 void registerStaticUse(Element element, Enqueuer enqueuer) { | |
252 if (element == compiler.mirrorSystemGetNameFunction) { | |
253 FunctionElement getNameFunction = mirrorRenamer.getNameFunction; | |
254 if (getNameFunction != null) { | |
255 enqueuer.addToWorkList(getNameFunction); | |
256 } | |
257 } | |
258 } | |
259 | |
260 @override | |
261 void registerInstantiatedType( | |
262 InterfaceType type, Enqueuer enqueuer, Registry registry, | |
263 {bool mirrorUsage: false}) { | |
264 registerPlatformMembers(type, registerUse: registry.registerDynamicUse); | |
265 super.registerInstantiatedType(type, enqueuer, registry, | |
266 mirrorUsage: mirrorUsage); | |
267 } | |
268 | |
269 /// Register dynamic access of members of [type] that implement members | |
270 /// of types defined in the platform libraries. | |
271 void registerPlatformMembers(InterfaceType type, | |
272 {void registerUse(DynamicUse dynamicUse)}) { | |
273 // Without patching, dart2dart has no way of performing sound tree-shaking | |
274 // in face external functions. Therefore we employ another scheme: | |
275 // | |
276 // Based on the assumption that the platform code only relies on the | |
277 // interfaces of it's own classes, we can approximate the semantics of | |
278 // external functions by eagerly registering dynamic invocation of instance | |
279 // members defined the platform interfaces. | |
280 // | |
281 // Since we only need to generate code for non-platform classes we can | |
282 // restrict this registration to platform interfaces implemented by | |
283 // instantiated non-platform classes. | |
284 // | |
285 // Consider for instance this program: | |
286 // | |
287 // import 'dart:math' show Random; | |
288 // | |
289 // class MyRandom implements Random { | |
290 // int nextInt() => 0; | |
291 // } | |
292 // | |
293 // main() { | |
294 // print([0, 1, 2].shuffle(new MyRandom())); | |
295 // } | |
296 // | |
297 // Here `MyRandom` is a subtype if `Random` defined in 'dart:math'. By the | |
298 // assumption, all methods defined `Random` are potentially called, and | |
299 // therefore, though there are no visible call sites from the user node, | |
300 // dynamic invocation of for instance `nextInt` should be registered. In | |
301 // this case, `nextInt` is actually called by the standard implementation of | |
302 // `shuffle`. | |
303 | |
304 ClassElement cls = type.element; | |
305 if (!cls.library.isPlatformLibrary) { | |
306 for (Link<DartType> link = cls.allSupertypes; | |
307 !link.isEmpty; | |
308 link = link.tail) { | |
309 InterfaceType supertype = link.head; | |
310 ClassElement superclass = supertype.element; | |
311 LibraryElement library = superclass.library; | |
312 if (library.isPlatformLibrary) { | |
313 if (_userImplementedPlatformClasses.add(superclass)) { | |
314 // Register selectors for all instance methods since these might | |
315 // be called on user classes from within the platform | |
316 // implementation. | |
317 superclass.forEachLocalMember((MemberElement element) { | |
318 if (element.isConstructor || element.isStatic) return; | |
319 | |
320 element.computeType(resolution); | |
321 Selector selector = new Selector.fromElement(element); | |
322 registerUse(new DynamicUse(selector, null)); | |
323 }); | |
324 } | |
325 } | |
326 } | |
327 } | |
328 } | |
329 | |
330 @override | |
331 bool enableDeferredLoadingIfSupported(Spannable node, Registry registry) { | |
332 // TODO(sigurdm): Implement deferred loading for dart2dart. | |
333 reporter.reportWarningMessage( | |
334 node, MessageKind.DEFERRED_LIBRARY_DART_2_DART); | |
335 return false; | |
336 } | |
337 | |
338 @override | |
339 Uri resolvePatchUri(String libraryName, Uri) { | |
340 // Dart2dart does not use patches. | |
341 return null; | |
342 } | |
343 } | |
344 | |
345 class DartImpactTransformer extends ImpactTransformer { | |
346 final DartBackend backend; | |
347 | |
348 DartImpactTransformer(this.backend); | |
349 | |
350 @override | |
351 WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) { | |
352 TransformedWorldImpact transformed = | |
353 new TransformedWorldImpact(worldImpact); | |
354 for (TypeUse typeUse in worldImpact.typeUses) { | |
355 if (typeUse.kind == TypeUseKind.TYPE_LITERAL && | |
356 typeUse.type.isInterfaceType) { | |
357 backend.usedTypeLiterals.add(typeUse.type.element); | |
358 } | |
359 if (typeUse.kind == TypeUseKind.INSTANTIATION) { | |
360 backend.registerPlatformMembers(typeUse.type, | |
361 registerUse: transformed.registerDynamicUse); | |
362 } | |
363 } | |
364 return transformed; | |
365 } | |
366 } | |
367 | |
368 class EmitterUnparser extends Unparser { | |
369 final Map<Node, String> renames; | |
370 | |
371 EmitterUnparser(this.renames, {bool minify, bool stripTypes}) | |
372 : super(minify: minify, stripTypes: stripTypes); | |
373 | |
374 visit(Node node) { | |
375 if (node != null && renames.containsKey(node)) { | |
376 write(renames[node]); | |
377 } else { | |
378 super.visit(node); | |
379 } | |
380 } | |
381 | |
382 unparseSendReceiver(Send node, {bool spacesNeeded: false}) { | |
383 // TODO(smok): Remove ugly hack for library prefices. | |
384 if (node.receiver != null && renames[node.receiver] == '') return; | |
385 super.unparseSendReceiver(node, spacesNeeded: spacesNeeded); | |
386 } | |
387 | |
388 unparseFunctionName(Node name) { | |
389 if (name != null && renames.containsKey(name)) { | |
390 write(renames[name]); | |
391 } else { | |
392 super.unparseFunctionName(name); | |
393 } | |
394 } | |
395 } | |
396 | |
397 /** | |
398 * Some elements are not recorded by resolver now, | |
399 * for example, typedefs or classes which are only | |
400 * used in signatures, as/is operators or in super clauses | |
401 * (just to name a few). Retraverse AST to pick those up. | |
402 */ | |
403 class ReferencedElementCollector extends Visitor { | |
404 final DiagnosticReporter reporter; | |
405 final Element element; | |
406 final ElementAst elementAst; | |
407 final newTypedefElementCallback; | |
408 final newClassElementCallback; | |
409 | |
410 ReferencedElementCollector(this.reporter, this.element, this.elementAst, | |
411 this.newTypedefElementCallback, this.newClassElementCallback); | |
412 | |
413 visitNode(Node node) { | |
414 node.visitChildren(this); | |
415 } | |
416 | |
417 visitTypeAnnotation(TypeAnnotation typeAnnotation) { | |
418 TreeElements treeElements = elementAst.treeElements; | |
419 final DartType type = treeElements.getType(typeAnnotation); | |
420 assert(invariant(typeAnnotation, type != null, | |
421 message: "Missing type for type annotation: $treeElements.")); | |
422 if (type.isTypedef) newTypedefElementCallback(type.element); | |
423 if (type.isInterfaceType) newClassElementCallback(type.element); | |
424 typeAnnotation.visitChildren(this); | |
425 } | |
426 | |
427 void collect() { | |
428 reporter.withCurrentElement(element, () { | |
429 elementAst.ast.accept(this); | |
430 }); | |
431 } | |
432 } | |
433 | |
434 Comparator compareBy(f) => (x, y) => f(x).compareTo(f(y)); | |
435 | |
436 List sorted(Iterable l, comparison) { | |
437 final result = new List.from(l); | |
438 result.sort(comparison); | |
439 return result; | |
440 } | |
441 | |
442 compareElements(e0, e1) { | |
443 int result = compareBy((e) => e.library.canonicalUri.toString())(e0, e1); | |
444 if (result != 0) return result; | |
445 return compareBy((e) => e.position.charOffset)(e0, e1); | |
446 } | |
447 | |
448 /// [ConstantCompilerTask] for compilation of constants for the Dart backend. | |
449 /// | |
450 /// Since this task needs no distinction between frontend and backend constants | |
451 /// it also serves as the [BackendConstantEnvironment]. | |
452 class DartConstantTask extends ConstantCompilerTask | |
453 implements BackendConstantEnvironment { | |
454 final DartConstantCompiler constantCompiler; | |
455 | |
456 DartConstantTask(Compiler compiler) | |
457 : this.constantCompiler = new DartConstantCompiler(compiler), | |
458 super(compiler.measurer); | |
459 | |
460 String get name => 'ConstantHandler'; | |
461 | |
462 @override | |
463 ConstantSystem get constantSystem => constantCompiler.constantSystem; | |
464 | |
465 @override | |
466 bool hasConstantValue(ConstantExpression expression) { | |
467 return constantCompiler.hasConstantValue(expression); | |
468 } | |
469 | |
470 @override | |
471 ConstantValue getConstantValue(ConstantExpression expression) { | |
472 return constantCompiler.getConstantValue(expression); | |
473 } | |
474 | |
475 @override | |
476 ConstantValue getConstantValueForVariable(VariableElement element) { | |
477 return constantCompiler.getConstantValueForVariable(element); | |
478 } | |
479 | |
480 @override | |
481 ConstantExpression getConstantForNode(Node node, TreeElements elements) { | |
482 return constantCompiler.getConstantForNode(node, elements); | |
483 } | |
484 | |
485 @override | |
486 ConstantValue getConstantValueForNode(Node node, TreeElements elements) { | |
487 return getConstantValue( | |
488 constantCompiler.getConstantForNode(node, elements)); | |
489 } | |
490 | |
491 @override | |
492 ConstantValue getConstantValueForMetadata(MetadataAnnotation metadata) { | |
493 return getConstantValue(metadata.constant); | |
494 } | |
495 | |
496 @override | |
497 ConstantExpression compileConstant(VariableElement element) { | |
498 return measure(() { | |
499 return constantCompiler.compileConstant(element); | |
500 }); | |
501 } | |
502 | |
503 @override | |
504 void evaluate(ConstantExpression constant) { | |
505 return measure(() { | |
506 return constantCompiler.evaluate(constant); | |
507 }); | |
508 } | |
509 | |
510 @override | |
511 ConstantExpression compileVariable(VariableElement element) { | |
512 return measure(() { | |
513 return constantCompiler.compileVariable(element); | |
514 }); | |
515 } | |
516 | |
517 @override | |
518 ConstantExpression compileNode(Node node, TreeElements elements, | |
519 {bool enforceConst: true}) { | |
520 return measure(() { | |
521 return constantCompiler.compileNodeWithDefinitions(node, elements, | |
522 isConst: enforceConst); | |
523 }); | |
524 } | |
525 | |
526 @override | |
527 ConstantExpression compileMetadata( | |
528 MetadataAnnotation metadata, Node node, TreeElements elements) { | |
529 return measure(() { | |
530 return constantCompiler.compileMetadata(metadata, node, elements); | |
531 }); | |
532 } | |
533 | |
534 // TODO(johnniwinther): Remove this when values are computed from the | |
535 // expressions. | |
536 @override | |
537 void copyConstantValues(DartConstantTask task) { | |
538 constantCompiler.constantValueMap | |
539 .addAll(task.constantCompiler.constantValueMap); | |
540 } | |
541 | |
542 @override | |
543 void registerLazyStatic(FieldElement element) { | |
544 // Do nothing. | |
545 } | |
546 } | |
OLD | NEW |