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

Side by Side Diff: pkg/compiler/lib/src/dart_backend/outputter.dart

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

Powered by Google App Engine
This is Rietveld 408576698