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 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 } | |
OLD | NEW |