Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/dart_backend/outputter.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 part of dart_backend;
6
7 typedef bool IsSafeToRemoveTypeDeclarations(
8 Map<ClassElement, Iterable<Element>> classMembers);
9 typedef void ElementCallback<E>(E element);
10 typedef void ElementPostProcessFunction(
11 AstElement element, ElementAst elementAst,
12 ElementCallback<TypedefElement> typedefCallback,
13 ElementCallback<ClassElement> classCallback);
14 typedef ElementAst ComputeElementAstFunction(AstElement element);
15 typedef bool ElementFilter(Element element);
16 typedef List<Element> ElementSorter(Iterable<Element> elements);
17
18 /// Output engine for dart2dart that is shared between the dart2js and the
19 /// analyzer implementations of dart2dart.
20 class DartOutputter {
21 final DiagnosticListener listener;
22 final CompilerOutputProvider outputProvider;
23 final bool forceStripTypes;
24
25 // TODO(antonm): make available from command-line options.
26 final bool outputAst = false;
27 final bool enableMinification;
28
29 /// If `true`, libraries are generated into separate files.
30 final bool multiFile;
31
32 /// Internal structures accessible for tests and logging.
33 // TODO(johnniwinther): Clean this up.
34 PlaceholderRenamer renamer;
35 MainOutputGenerator output;
36 LibraryInfo libraryInfo;
37 ElementInfo elementInfo;
38
39 // TODO(johnniwinther): Support recompilation.
40 DartOutputter(this.listener, this.outputProvider,
41 {bool this.forceStripTypes: false,
42 bool this.enableMinification: false,
43 bool this.multiFile: false});
44
45 /// Generate Dart code for the program starting at [mainFunction].
46 ///
47 /// [libraries] is the set of all libraries (user/package/sdk) that are
48 /// referenced in the program.
49 ///
50 /// [instantiatedClasses] is the set of classes that are potentially
51 /// instantiated in the program.
52 ///
53 /// [resolvedElements] is the set of methods, constructors, and fields that
54 /// are potentially accessed/called in the program.
55 ///
56 /// The [sortElements] function is used to sort [instantiatedClasses] and
57 /// [resolvedElements] in the generated output.
58 String assembleProgram({
59 MirrorRenamer mirrorRenamer: const MirrorRenamer(),
60 Iterable<LibraryElement> libraries,
61 Iterable<Element> instantiatedClasses,
62 Iterable<Element> resolvedElements,
63 Iterable<ClassElement> usedTypeLiterals: const <ClassElement>[],
64 FunctionElement mainFunction,
65 Uri outputUri,
66 ElementPostProcessFunction postProcessElementAst,
67 ComputeElementAstFunction computeElementAst,
68 ElementFilter shouldOutput,
69 IsSafeToRemoveTypeDeclarations isSafeToRemoveTypeDeclarations,
70 ElementSorter sortElements}) {
71
72 assert(invariant(NO_LOCATION_SPANNABLE, libraries != null,
73 message: "'libraries' must be non-null."));
74 assert(invariant(NO_LOCATION_SPANNABLE, instantiatedClasses != null,
75 message: "'instantiatedClasses' must be non-null."));
76 assert(invariant(NO_LOCATION_SPANNABLE, resolvedElements != null,
77 message: "'resolvedElements' must be non-null."));
78 assert(invariant(NO_LOCATION_SPANNABLE, mainFunction != null,
79 message: "'mainFunction' must be non-null."));
80 assert(invariant(NO_LOCATION_SPANNABLE, computeElementAst != null,
81 message: "'computeElementAst' must be non-null."));
82 assert(invariant(NO_LOCATION_SPANNABLE, shouldOutput != null,
83 message: "'shouldOutput' must be non-null."));
84 assert(invariant(NO_LOCATION_SPANNABLE,
85 isSafeToRemoveTypeDeclarations != null,
86 message: "'isSafeToRemoveTypeDeclarations' must be non-null."));
87
88 if (sortElements == null) {
89 // Ensure deterministic output order.
90 sortElements = (Iterable<Element> elements) {
91 List<Element> list = elements.toList();
92 list.sort((Element a, Element b) => a.name.compareTo(b.name));
93 return list;
94 };
95 }
96
97 libraryInfo = LibraryInfo.processLibraries(libraries, resolvedElements);
98
99 elementInfo = ElementInfoProcessor.createElementInfo(
100 instantiatedClasses,
101 resolvedElements,
102 usedTypeLiterals,
103 postProcessElementAst: postProcessElementAst,
104 parseElementAst: computeElementAst,
105 shouldOutput: shouldOutput,
106 sortElements: sortElements);
107
108 PlaceholderCollector collector = collectPlaceholders(
109 listener,
110 mirrorRenamer,
111 mainFunction,
112 libraryInfo,
113 elementInfo);
114
115 renamer = createRenamer(
116 collector,
117 libraryInfo,
118 elementInfo,
119 enableMinification: enableMinification,
120 forceStripTypes: forceStripTypes,
121 isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations);
122
123 String assembledCode;
124 if (outputAst) {
125 assembledCode = astOutput(listener, elementInfo);
126 } else {
127 output = new MainOutputGenerator();
128 assembledCode = output.generateCode(
129 libraryInfo,
130 elementInfo,
131 collector,
132 renamer,
133 mainFunction,
134 outputUri,
135 outputProvider,
136 mirrorRenamer,
137 multiFile: multiFile,
138 forceStripTypes: forceStripTypes,
139 enableMinification: enableMinification);
140 }
141 return assembledCode;
142 }
143
144 static PlaceholderCollector collectPlaceholders(
145 DiagnosticListener listener,
146 MirrorRenamer mirrorRenamer,
147 FunctionElement mainFunction,
148 LibraryInfo libraryInfo,
149 ElementInfo elementInfo) {
150 // Create all necessary placeholders.
151 PlaceholderCollector collector = new PlaceholderCollector(
152 listener,
153 mirrorRenamer,
154 libraryInfo.fixedMemberNames,
155 elementInfo.elementAsts,
156 mainFunction);
157
158 makePlaceholders(element) {
159 collector.collect(element);
160
161 if (element.isClass) {
162 elementInfo.classMembers[element].forEach(makePlaceholders);
163 }
164 }
165 elementInfo.topLevelElements.forEach(makePlaceholders);
166 return collector;
167 }
168
169 static PlaceholderRenamer createRenamer(
170 PlaceholderCollector collector,
171 LibraryInfo libraryInfo,
172 ElementInfo elementInfo,
173 {bool enableMinification: false,
174 bool forceStripTypes: false,
175 isSafeToRemoveTypeDeclarations}) {
176 // Create renames.
177 bool shouldCutDeclarationTypes = forceStripTypes
178 || (enableMinification
179 && isSafeToRemoveTypeDeclarations(elementInfo.classMembers));
180
181 PlaceholderRenamer placeholderRenamer = new PlaceholderRenamer(
182 libraryInfo.fixedMemberNames, libraryInfo.reexportingLibraries,
183 cutDeclarationTypes: shouldCutDeclarationTypes,
184 enableMinification: enableMinification);
185
186 placeholderRenamer.computeRenames(collector);
187 return placeholderRenamer;
188 }
189
190 static String astOutput(DiagnosticListener listener,
191 ElementInfo elementInfo) {
192 // TODO(antonm): Ideally XML should be a separate backend.
193 // TODO(antonm): obey renames and minification, at least as an option.
194 StringBuffer sb = new StringBuffer();
195 outputElement(element) {
196 sb.write(element.parseNode(listener).toDebugString());
197 }
198
199 // Emit XML for AST instead of the program.
200 for (Element topLevel in elementInfo.topLevelElements) {
201 if (topLevel.isClass &&
202 !elementInfo.emitNoMembersFor.contains(topLevel)) {
203 // TODO(antonm): add some class info.
204 elementInfo.classMembers[topLevel].forEach(outputElement);
205 } else {
206 outputElement(topLevel);
207 }
208 }
209 return '<Program>\n$sb</Program>\n';
210 }
211 }
212
213 class LibraryInfo {
214 final Set<String> fixedMemberNames;
215 final Map<Element, LibraryElement> reexportingLibraries;
216 final List<LibraryElement> userLibraries;
217
218 LibraryInfo(this.fixedMemberNames,
219 this.reexportingLibraries,
220 this.userLibraries);
221
222 static LibraryInfo processLibraries(
223 Iterable<LibraryElement> libraries,
224 Iterable<AstElement> resolvedElements) {
225 Set<String> fixedMemberNames = new Set<String>();
226 Map<Element, LibraryElement> reexportingLibraries =
227 <Element, LibraryElement>{};
228 List<LibraryElement> userLibraries = <LibraryElement>[];
229 // Conservatively traverse all platform libraries and collect member names.
230 // TODO(antonm): ideally we should only collect names of used members,
231 // however as of today there are problems with names of some core library
232 // interfaces, most probably for interfaces of literals.
233
234 for (LibraryElement library in libraries) {
235 if (!library.isPlatformLibrary) {
236 userLibraries.add(library);
237 continue;
238 }
239 library.forEachLocalMember((Element element) {
240 if (element.isClass) {
241 ClassElement classElement = element;
242 assert(invariant(classElement, classElement.isResolved,
243 message: "Unresolved platform class."));
244 classElement.forEachLocalMember((member) {
245 String name = member.name;
246 // Skip operator names.
247 if (!name.startsWith(r'operator$')) {
248 // Fetch name of named constructors and factories if any,
249 // otherwise store regular name.
250 // TODO(antonm): better way to analyze the name.
251 fixedMemberNames.add(name.split(r'$').last);
252 }
253 });
254 }
255 // Even class names are added due to a delicate problem we have:
256 // if one imports dart:core with a prefix, we cannot tell prefix.name
257 // from dynamic invocation (alas!). So we'd better err on preserving
258 // those names.
259 fixedMemberNames.add(element.name);
260 });
261
262 for (Element export in library.exports) {
263 if (!library.isInternalLibrary &&
264 export.library.isInternalLibrary) {
265 // If an element of an internal library is reexported by a platform
266 // library, we have to import the reexporting library instead of the
267 // internal library, because the internal library is an
268 // implementation detail of dart2js.
269 reexportingLibraries[export] = library;
270 }
271 }
272 }
273 // As of now names of named optionals are not renamed. Therefore add all
274 // field names used as named optionals into [fixedMemberNames].
275 for (final element in resolvedElements) {
276 if (!element.isConstructor) continue;
277 Link<Element> optionalParameters =
278 element.functionSignature.optionalParameters;
279 for (final optional in optionalParameters) {
280 if (!optional.isInitializingFormal) continue;
281 fixedMemberNames.add(optional.name);
282 }
283 }
284 // The VM will automatically invoke the call method of objects
285 // that are invoked as functions. Make sure to not rename that.
286 fixedMemberNames.add('call');
287 // TODO(antonm): TypeError.srcType and TypeError.dstType are defined in
288 // runtime/lib/error.dart. Overall, all DartVM specific libs should be
289 // accounted for.
290 fixedMemberNames.add('srcType');
291 fixedMemberNames.add('dstType');
292
293 return new LibraryInfo(
294 fixedMemberNames, reexportingLibraries, userLibraries);
295 }
296 }
297
298 class ElementInfo {
299 final Map<Element, ElementAst> elementAsts;
300 final Iterable<Element> topLevelElements;
301 final Map<ClassElement, Iterable<Element>> classMembers;
302 final Iterable<ClassElement> emitNoMembersFor;
303
304 ElementInfo(this.elementAsts,
305 this.topLevelElements,
306 this.classMembers,
307 this.emitNoMembersFor);
308 }
309
310 class ElementInfoProcessor implements ElementInfo {
311 final Map<Element, ElementAst> elementAsts = new Map<Element, ElementAst>();
312 final Set<Element> topLevelElements = new Set<Element>();
313 final Map<ClassElement, Set<Element>> classMembers =
314 new Map<ClassElement, Set<Element>>();
315 final Set<ClassElement> emitNoMembersFor = new Set<ClassElement>();
316 final ElementPostProcessFunction postProcessElementAst;
317 final ComputeElementAstFunction parseElementAst;
318 final ElementFilter shouldOutput;
319
320 ElementInfoProcessor(
321 {this.postProcessElementAst,
322 this.parseElementAst,
323 this.shouldOutput});
324
325 static ElementInfo createElementInfo(
326 Iterable<ClassElement> instantiatedClasses,
327 Iterable<AstElement> resolvedElements,
328 Iterable<ClassElement> usedTypeLiterals,
329 {ElementPostProcessFunction postProcessElementAst,
330 ComputeElementAstFunction parseElementAst,
331 ElementFilter shouldOutput,
332 ElementSorter sortElements}) {
333 ElementInfoProcessor processor = new ElementInfoProcessor(
334 postProcessElementAst: postProcessElementAst,
335 parseElementAst: parseElementAst,
336 shouldOutput: shouldOutput);
337 return processor.process(
338 instantiatedClasses, resolvedElements, usedTypeLiterals,
339 sortElements: sortElements);
340 }
341
342 ElementInfo process(Iterable<ClassElement> instantiatedClasses,
343 Iterable<AstElement> resolvedElements,
344 Iterable<ClassElement> usedTypeLiterals,
345 {ElementSorter sortElements}) {
346 // Build all top level elements to emit and necessary class members.
347 instantiatedClasses.where(shouldOutput).forEach(addClass);
348 resolvedElements.where(shouldOutput).forEach(addMember);
349 usedTypeLiterals.forEach((ClassElement element) {
350 if (shouldOutput(element)) {
351 if (!topLevelElements.contains(element)) {
352 // The class is only referenced by type literals.
353 emitNoMembersFor.add(element);
354 }
355 addClass(element);
356 }
357 });
358
359 // Sort elements.
360 List<Element> sortedTopLevels = sortElements(topLevelElements);
361 Map<ClassElement, List<Element>> sortedClassMembers =
362 new Map<ClassElement, List<Element>>();
363 classMembers.forEach((classElement, members) {
364 sortedClassMembers[classElement] = sortElements(members);
365 });
366
367 return new ElementInfo(
368 elementAsts, sortedTopLevels, sortedClassMembers, emitNoMembersFor);
369 }
370
371 void processElement(Element element, ElementAst elementAst) {
372 if (postProcessElementAst != null) {
373 postProcessElementAst(element, elementAst,
374 newTypedefElementCallback,
375 newClassElementCallback);
376 }
377 elementAsts[element] = elementAst;
378 }
379
380 void addTopLevel(AstElement element, ElementAst elementAst) {
381 if (topLevelElements.contains(element)) return;
382 topLevelElements.add(element);
383 processElement(element, elementAst);
384 }
385
386 void addClass(ClassElement classElement) {
387 TreeElements treeElements = new TreeElementMapping(classElement);
388 backend2frontend.TreePrinter treePrinter =
389 new backend2frontend.TreePrinter(treeElements);
390 Node node = treePrinter.makeNodeForClassElement(classElement);
391 addTopLevel(classElement, new ElementAst.internal(node, treeElements));
392 classMembers.putIfAbsent(classElement, () => new Set());
393 }
394
395 void newTypedefElementCallback(TypedefElement element) {
396 if (!shouldOutput(element)) return;
397 addTopLevel(element, new ElementAst(element));
398 }
399
400 void newClassElementCallback(ClassElement classElement) {
401 if (!shouldOutput(classElement)) return;
402 addClass(classElement);
403 }
404
405 void addMember(element) {
406 ElementAst elementAst = parseElementAst(element);
407 if (element.isClassMember) {
408 ClassElement enclosingClass = element.enclosingClass;
409 assert(enclosingClass.isClass);
410 assert(enclosingClass.isTopLevel);
411 assert(shouldOutput(enclosingClass));
412 addClass(enclosingClass);
413 classMembers[enclosingClass].add(element);
414 processElement(element, elementAst);
415 } else {
416 if (element.isTopLevel) {
417 addTopLevel(element, elementAst);
418 }
419 }
420 }
421 }
422
423 /// Main output generator for [DartOutputter] that emits dart code through a
424 /// [CompilerOutputProvider].
425 class MainOutputGenerator {
426 final Map<ClassNode, List<Node>> memberNodes =
427 new Map<ClassNode, List<Node>>();
428 final List<Node> topLevelNodes = <Node>[];
429
430 String generateCode(
431 LibraryInfo libraryInfo,
432 ElementInfo elementInfo,
433 PlaceholderCollector collector,
434 PlaceholderRenamer placeholderRenamer,
435 FunctionElement mainFunction,
436 Uri outputUri,
437 CompilerOutputProvider outputProvider,
438 MirrorRenamer mirrorRenamer,
439 {bool multiFile: false,
440 bool forceStripTypes: false,
441 bool enableMinification: false}) {
442 for (Element element in elementInfo.topLevelElements) {
443 topLevelNodes.add(elementInfo.elementAsts[element].ast);
444 if (element.isClass && !element.isMixinApplication) {
445 final members = <Node>[];
446 for (Element member in elementInfo.classMembers[element]) {
447 members.add(elementInfo.elementAsts[member].ast);
448 }
449 memberNodes[elementInfo.elementAsts[element].ast] = members;
450 }
451 }
452
453 mirrorRenamer.addRenames(placeholderRenamer.renames,
454 topLevelNodes, collector);
455
456 Map<LibraryElement, String> outputPaths = new Map<LibraryElement, String>();
457 Map<LibraryElement, EmitterUnparser> unparsers =
458 new Map<LibraryElement, EmitterUnparser>();
459
460 // The single unparser used if we collect all the output in one file.
461 EmitterUnparser mainUnparser = multiFile
462 ? null
463 : new EmitterUnparser(placeholderRenamer.renames,
464 stripTypes: forceStripTypes,
465 minify: enableMinification);
466
467 if (multiFile) {
468 // TODO(sigurdm): Factor handling of library-paths out from emitting.
469 String mainName = outputUri.pathSegments.last;
470 String mainBaseName = mainName.endsWith(".dart")
471 ? mainName.substring(0, mainName.length - 5)
472 : mainName;
473 // Map each library to a path based on the uri of the original
474 // library and [compiler.outputUri].
475 Set<String> usedLibraryPaths = new Set<String>();
476 for (LibraryElement library in libraryInfo.userLibraries) {
477 if (library == mainFunction.library) {
478 outputPaths[library] = mainBaseName;
479 } else {
480 List<String> names =
481 library.canonicalUri.pathSegments.last.split(".");
482 if (names.last == "dart") {
483 names = names.sublist(0, names.length - 1);
484 }
485 outputPaths[library] =
486 "$mainBaseName.${makeUnique(names.join("."), usedLibraryPaths)}";
487 }
488 }
489
490 /// Rewrites imports/exports to refer to the paths given in [outputPaths].
491 for(LibraryElement outputLibrary in libraryInfo.userLibraries) {
492 EmitterUnparser unparser = new EmitterUnparser(
493 placeholderRenamer.renames,
494 stripTypes: forceStripTypes,
495 minify: enableMinification);
496 unparsers[outputLibrary] = unparser;
497 LibraryName libraryName = outputLibrary.libraryTag;
498 if (libraryName != null) {
499 unparser.visitLibraryName(libraryName);
500 }
501 for (LibraryTag tag in outputLibrary.tags) {
502 if (tag is! LibraryDependency) continue;
503 LibraryDependency dependency = tag;
504 LibraryElement libraryElement =
505 outputLibrary.getLibraryFromTag(dependency);
506 String uri = outputPaths.containsKey(libraryElement)
507 ? "${outputPaths[libraryElement]}.dart"
508 : libraryElement.canonicalUri.toString();
509 if (dependency is Import) {
510 unparser.unparseImportTag(uri);
511 } else {
512 unparser.unparseExportTag(uri);
513 }
514 }
515 }
516 } else {
517 for(LibraryElement library in placeholderRenamer.platformImports) {
518 if (library.isPlatformLibrary && !library.isInternalLibrary) {
519 mainUnparser.unparseImportTag(library.canonicalUri.toString());
520 }
521 }
522 }
523
524 for (int i = 0; i < elementInfo.topLevelElements.length; i++) {
525 Element element = elementInfo.topLevelElements.elementAt(i);
526 Node node = topLevelNodes[i];
527 Unparser unparser = multiFile ? unparsers[element.library] : mainUnparser;
528 if (node is ClassNode) {
529 // TODO(smok): Filter out default constructors here.
530 unparser.unparseClassWithBody(node, memberNodes[node]);
531 } else {
532 unparser.unparse(node);
533 }
534 unparser.newline();
535 }
536
537 int totalSize = 0;
538 String assembledCode;
539 if (multiFile) {
540 for(LibraryElement outputLibrary in libraryInfo.userLibraries) {
541 // TODO(sigurdm): Make the unparser output directly into the buffer
542 // instead of caching in `.result`.
543 String code = unparsers[outputLibrary].result;
544 totalSize += code.length;
545 outputProvider(outputPaths[outputLibrary], "dart")
546 ..add(code)
547 ..close();
548 }
549 // TODO(sigurdm): We should get rid of compiler.assembledCode.
550 assembledCode = unparsers[mainFunction.library].result;
551 } else {
552 assembledCode = mainUnparser.result;
553 outputProvider("", "dart")
554 ..add(assembledCode)
555 ..close();
556
557 totalSize = assembledCode.length;
558 }
559
560 return assembledCode;
561 }
562 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698