Index: pkg/compiler/lib/src/dart_backend/outputter.dart |
diff --git a/pkg/compiler/lib/src/dart_backend/outputter.dart b/pkg/compiler/lib/src/dart_backend/outputter.dart |
deleted file mode 100644 |
index f705c527427c039e7ceb20e6014db99794b2a4bf..0000000000000000000000000000000000000000 |
--- a/pkg/compiler/lib/src/dart_backend/outputter.dart |
+++ /dev/null |
@@ -1,562 +0,0 @@ |
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of dart_backend; |
- |
-typedef bool IsSafeToRemoveTypeDeclarations( |
- Map<ClassElement, Iterable<Element>> classMembers); |
-typedef void ElementCallback<E>(E element); |
-typedef void ElementPostProcessFunction( |
- AstElement element, ElementAst elementAst, |
- ElementCallback<TypedefElement> typedefCallback, |
- ElementCallback<ClassElement> classCallback); |
-typedef ElementAst ComputeElementAstFunction(AstElement element); |
-typedef bool ElementFilter(Element element); |
-typedef List<Element> ElementSorter(Iterable<Element> elements); |
- |
-/// Output engine for dart2dart that is shared between the dart2js and the |
-/// analyzer implementations of dart2dart. |
-class DartOutputter { |
- final DiagnosticListener listener; |
- final CompilerOutputProvider outputProvider; |
- final bool forceStripTypes; |
- |
- // TODO(antonm): make available from command-line options. |
- final bool outputAst = false; |
- final bool enableMinification; |
- |
- /// If `true`, libraries are generated into separate files. |
- final bool multiFile; |
- |
- /// Internal structures accessible for tests and logging. |
- // TODO(johnniwinther): Clean this up. |
- PlaceholderRenamer renamer; |
- MainOutputGenerator output; |
- LibraryInfo libraryInfo; |
- ElementInfo elementInfo; |
- |
- // TODO(johnniwinther): Support recompilation. |
- DartOutputter(this.listener, this.outputProvider, |
- {bool this.forceStripTypes: false, |
- bool this.enableMinification: false, |
- bool this.multiFile: false}); |
- |
- /// Generate Dart code for the program starting at [mainFunction]. |
- /// |
- /// [libraries] is the set of all libraries (user/package/sdk) that are |
- /// referenced in the program. |
- /// |
- /// [instantiatedClasses] is the set of classes that are potentially |
- /// instantiated in the program. |
- /// |
- /// [resolvedElements] is the set of methods, constructors, and fields that |
- /// are potentially accessed/called in the program. |
- /// |
- /// The [sortElements] function is used to sort [instantiatedClasses] and |
- /// [resolvedElements] in the generated output. |
- String assembleProgram({ |
- MirrorRenamer mirrorRenamer: const MirrorRenamer(), |
- Iterable<LibraryElement> libraries, |
- Iterable<Element> instantiatedClasses, |
- Iterable<Element> resolvedElements, |
- Iterable<ClassElement> usedTypeLiterals: const <ClassElement>[], |
- FunctionElement mainFunction, |
- Uri outputUri, |
- ElementPostProcessFunction postProcessElementAst, |
- ComputeElementAstFunction computeElementAst, |
- ElementFilter shouldOutput, |
- IsSafeToRemoveTypeDeclarations isSafeToRemoveTypeDeclarations, |
- ElementSorter sortElements}) { |
- |
- assert(invariant(NO_LOCATION_SPANNABLE, libraries != null, |
- message: "'libraries' must be non-null.")); |
- assert(invariant(NO_LOCATION_SPANNABLE, instantiatedClasses != null, |
- message: "'instantiatedClasses' must be non-null.")); |
- assert(invariant(NO_LOCATION_SPANNABLE, resolvedElements != null, |
- message: "'resolvedElements' must be non-null.")); |
- assert(invariant(NO_LOCATION_SPANNABLE, mainFunction != null, |
- message: "'mainFunction' must be non-null.")); |
- assert(invariant(NO_LOCATION_SPANNABLE, computeElementAst != null, |
- message: "'computeElementAst' must be non-null.")); |
- assert(invariant(NO_LOCATION_SPANNABLE, shouldOutput != null, |
- message: "'shouldOutput' must be non-null.")); |
- assert(invariant(NO_LOCATION_SPANNABLE, |
- isSafeToRemoveTypeDeclarations != null, |
- message: "'isSafeToRemoveTypeDeclarations' must be non-null.")); |
- |
- if (sortElements == null) { |
- // Ensure deterministic output order. |
- sortElements = (Iterable<Element> elements) { |
- List<Element> list = elements.toList(); |
- list.sort((Element a, Element b) => a.name.compareTo(b.name)); |
- return list; |
- }; |
- } |
- |
- libraryInfo = LibraryInfo.processLibraries(libraries, resolvedElements); |
- |
- elementInfo = ElementInfoProcessor.createElementInfo( |
- instantiatedClasses, |
- resolvedElements, |
- usedTypeLiterals, |
- postProcessElementAst: postProcessElementAst, |
- parseElementAst: computeElementAst, |
- shouldOutput: shouldOutput, |
- sortElements: sortElements); |
- |
- PlaceholderCollector collector = collectPlaceholders( |
- listener, |
- mirrorRenamer, |
- mainFunction, |
- libraryInfo, |
- elementInfo); |
- |
- renamer = createRenamer( |
- collector, |
- libraryInfo, |
- elementInfo, |
- enableMinification: enableMinification, |
- forceStripTypes: forceStripTypes, |
- isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations); |
- |
- String assembledCode; |
- if (outputAst) { |
- assembledCode = astOutput(listener, elementInfo); |
- } else { |
- output = new MainOutputGenerator(); |
- assembledCode = output.generateCode( |
- libraryInfo, |
- elementInfo, |
- collector, |
- renamer, |
- mainFunction, |
- outputUri, |
- outputProvider, |
- mirrorRenamer, |
- multiFile: multiFile, |
- forceStripTypes: forceStripTypes, |
- enableMinification: enableMinification); |
- } |
- return assembledCode; |
- } |
- |
- static PlaceholderCollector collectPlaceholders( |
- DiagnosticListener listener, |
- MirrorRenamer mirrorRenamer, |
- FunctionElement mainFunction, |
- LibraryInfo libraryInfo, |
- ElementInfo elementInfo) { |
- // Create all necessary placeholders. |
- PlaceholderCollector collector = new PlaceholderCollector( |
- listener, |
- mirrorRenamer, |
- libraryInfo.fixedMemberNames, |
- elementInfo.elementAsts, |
- mainFunction); |
- |
- makePlaceholders(element) { |
- collector.collect(element); |
- |
- if (element.isClass) { |
- elementInfo.classMembers[element].forEach(makePlaceholders); |
- } |
- } |
- elementInfo.topLevelElements.forEach(makePlaceholders); |
- return collector; |
- } |
- |
- static PlaceholderRenamer createRenamer( |
- PlaceholderCollector collector, |
- LibraryInfo libraryInfo, |
- ElementInfo elementInfo, |
- {bool enableMinification: false, |
- bool forceStripTypes: false, |
- isSafeToRemoveTypeDeclarations}) { |
- // Create renames. |
- bool shouldCutDeclarationTypes = forceStripTypes |
- || (enableMinification |
- && isSafeToRemoveTypeDeclarations(elementInfo.classMembers)); |
- |
- PlaceholderRenamer placeholderRenamer = new PlaceholderRenamer( |
- libraryInfo.fixedMemberNames, libraryInfo.reexportingLibraries, |
- cutDeclarationTypes: shouldCutDeclarationTypes, |
- enableMinification: enableMinification); |
- |
- placeholderRenamer.computeRenames(collector); |
- return placeholderRenamer; |
- } |
- |
- static String astOutput(DiagnosticListener listener, |
- ElementInfo elementInfo) { |
- // TODO(antonm): Ideally XML should be a separate backend. |
- // TODO(antonm): obey renames and minification, at least as an option. |
- StringBuffer sb = new StringBuffer(); |
- outputElement(element) { |
- sb.write(element.parseNode(listener).toDebugString()); |
- } |
- |
- // Emit XML for AST instead of the program. |
- for (Element topLevel in elementInfo.topLevelElements) { |
- if (topLevel.isClass && |
- !elementInfo.emitNoMembersFor.contains(topLevel)) { |
- // TODO(antonm): add some class info. |
- elementInfo.classMembers[topLevel].forEach(outputElement); |
- } else { |
- outputElement(topLevel); |
- } |
- } |
- return '<Program>\n$sb</Program>\n'; |
- } |
-} |
- |
-class LibraryInfo { |
- final Set<String> fixedMemberNames; |
- final Map<Element, LibraryElement> reexportingLibraries; |
- final List<LibraryElement> userLibraries; |
- |
- LibraryInfo(this.fixedMemberNames, |
- this.reexportingLibraries, |
- this.userLibraries); |
- |
- static LibraryInfo processLibraries( |
- Iterable<LibraryElement> libraries, |
- Iterable<AstElement> resolvedElements) { |
- Set<String> fixedMemberNames = new Set<String>(); |
- Map<Element, LibraryElement> reexportingLibraries = |
- <Element, LibraryElement>{}; |
- List<LibraryElement> userLibraries = <LibraryElement>[]; |
- // Conservatively traverse all platform libraries and collect member names. |
- // TODO(antonm): ideally we should only collect names of used members, |
- // however as of today there are problems with names of some core library |
- // interfaces, most probably for interfaces of literals. |
- |
- for (LibraryElement library in libraries) { |
- if (!library.isPlatformLibrary) { |
- userLibraries.add(library); |
- continue; |
- } |
- library.forEachLocalMember((Element element) { |
- if (element.isClass) { |
- ClassElement classElement = element; |
- assert(invariant(classElement, classElement.isResolved, |
- message: "Unresolved platform class.")); |
- classElement.forEachLocalMember((member) { |
- String name = member.name; |
- // Skip operator names. |
- if (!name.startsWith(r'operator$')) { |
- // Fetch name of named constructors and factories if any, |
- // otherwise store regular name. |
- // TODO(antonm): better way to analyze the name. |
- fixedMemberNames.add(name.split(r'$').last); |
- } |
- }); |
- } |
- // Even class names are added due to a delicate problem we have: |
- // if one imports dart:core with a prefix, we cannot tell prefix.name |
- // from dynamic invocation (alas!). So we'd better err on preserving |
- // those names. |
- fixedMemberNames.add(element.name); |
- }); |
- |
- for (Element export in library.exports) { |
- if (!library.isInternalLibrary && |
- export.library.isInternalLibrary) { |
- // If an element of an internal library is reexported by a platform |
- // library, we have to import the reexporting library instead of the |
- // internal library, because the internal library is an |
- // implementation detail of dart2js. |
- reexportingLibraries[export] = library; |
- } |
- } |
- } |
- // As of now names of named optionals are not renamed. Therefore add all |
- // field names used as named optionals into [fixedMemberNames]. |
- for (final element in resolvedElements) { |
- if (!element.isConstructor) continue; |
- Link<Element> optionalParameters = |
- element.functionSignature.optionalParameters; |
- for (final optional in optionalParameters) { |
- if (!optional.isInitializingFormal) continue; |
- fixedMemberNames.add(optional.name); |
- } |
- } |
- // The VM will automatically invoke the call method of objects |
- // that are invoked as functions. Make sure to not rename that. |
- fixedMemberNames.add('call'); |
- // TODO(antonm): TypeError.srcType and TypeError.dstType are defined in |
- // runtime/lib/error.dart. Overall, all DartVM specific libs should be |
- // accounted for. |
- fixedMemberNames.add('srcType'); |
- fixedMemberNames.add('dstType'); |
- |
- return new LibraryInfo( |
- fixedMemberNames, reexportingLibraries, userLibraries); |
- } |
-} |
- |
-class ElementInfo { |
- final Map<Element, ElementAst> elementAsts; |
- final Iterable<Element> topLevelElements; |
- final Map<ClassElement, Iterable<Element>> classMembers; |
- final Iterable<ClassElement> emitNoMembersFor; |
- |
- ElementInfo(this.elementAsts, |
- this.topLevelElements, |
- this.classMembers, |
- this.emitNoMembersFor); |
-} |
- |
-class ElementInfoProcessor implements ElementInfo { |
- final Map<Element, ElementAst> elementAsts = new Map<Element, ElementAst>(); |
- final Set<Element> topLevelElements = new Set<Element>(); |
- final Map<ClassElement, Set<Element>> classMembers = |
- new Map<ClassElement, Set<Element>>(); |
- final Set<ClassElement> emitNoMembersFor = new Set<ClassElement>(); |
- final ElementPostProcessFunction postProcessElementAst; |
- final ComputeElementAstFunction parseElementAst; |
- final ElementFilter shouldOutput; |
- |
- ElementInfoProcessor( |
- {this.postProcessElementAst, |
- this.parseElementAst, |
- this.shouldOutput}); |
- |
- static ElementInfo createElementInfo( |
- Iterable<ClassElement> instantiatedClasses, |
- Iterable<AstElement> resolvedElements, |
- Iterable<ClassElement> usedTypeLiterals, |
- {ElementPostProcessFunction postProcessElementAst, |
- ComputeElementAstFunction parseElementAst, |
- ElementFilter shouldOutput, |
- ElementSorter sortElements}) { |
- ElementInfoProcessor processor = new ElementInfoProcessor( |
- postProcessElementAst: postProcessElementAst, |
- parseElementAst: parseElementAst, |
- shouldOutput: shouldOutput); |
- return processor.process( |
- instantiatedClasses, resolvedElements, usedTypeLiterals, |
- sortElements: sortElements); |
- } |
- |
- ElementInfo process(Iterable<ClassElement> instantiatedClasses, |
- Iterable<AstElement> resolvedElements, |
- Iterable<ClassElement> usedTypeLiterals, |
- {ElementSorter sortElements}) { |
- // Build all top level elements to emit and necessary class members. |
- instantiatedClasses.where(shouldOutput).forEach(addClass); |
- resolvedElements.where(shouldOutput).forEach(addMember); |
- usedTypeLiterals.forEach((ClassElement element) { |
- if (shouldOutput(element)) { |
- if (!topLevelElements.contains(element)) { |
- // The class is only referenced by type literals. |
- emitNoMembersFor.add(element); |
- } |
- addClass(element); |
- } |
- }); |
- |
- // Sort elements. |
- List<Element> sortedTopLevels = sortElements(topLevelElements); |
- Map<ClassElement, List<Element>> sortedClassMembers = |
- new Map<ClassElement, List<Element>>(); |
- classMembers.forEach((classElement, members) { |
- sortedClassMembers[classElement] = sortElements(members); |
- }); |
- |
- return new ElementInfo( |
- elementAsts, sortedTopLevels, sortedClassMembers, emitNoMembersFor); |
- } |
- |
- void processElement(Element element, ElementAst elementAst) { |
- if (postProcessElementAst != null) { |
- postProcessElementAst(element, elementAst, |
- newTypedefElementCallback, |
- newClassElementCallback); |
- } |
- elementAsts[element] = elementAst; |
- } |
- |
- void addTopLevel(AstElement element, ElementAst elementAst) { |
- if (topLevelElements.contains(element)) return; |
- topLevelElements.add(element); |
- processElement(element, elementAst); |
- } |
- |
- void addClass(ClassElement classElement) { |
- TreeElements treeElements = new TreeElementMapping(classElement); |
- backend2frontend.TreePrinter treePrinter = |
- new backend2frontend.TreePrinter(treeElements); |
- Node node = treePrinter.makeNodeForClassElement(classElement); |
- addTopLevel(classElement, new ElementAst.internal(node, treeElements)); |
- classMembers.putIfAbsent(classElement, () => new Set()); |
- } |
- |
- void newTypedefElementCallback(TypedefElement element) { |
- if (!shouldOutput(element)) return; |
- addTopLevel(element, new ElementAst(element)); |
- } |
- |
- void newClassElementCallback(ClassElement classElement) { |
- if (!shouldOutput(classElement)) return; |
- addClass(classElement); |
- } |
- |
- void addMember(element) { |
- ElementAst elementAst = parseElementAst(element); |
- if (element.isClassMember) { |
- ClassElement enclosingClass = element.enclosingClass; |
- assert(enclosingClass.isClass); |
- assert(enclosingClass.isTopLevel); |
- assert(shouldOutput(enclosingClass)); |
- addClass(enclosingClass); |
- classMembers[enclosingClass].add(element); |
- processElement(element, elementAst); |
- } else { |
- if (element.isTopLevel) { |
- addTopLevel(element, elementAst); |
- } |
- } |
- } |
-} |
- |
-/// Main output generator for [DartOutputter] that emits dart code through a |
-/// [CompilerOutputProvider]. |
-class MainOutputGenerator { |
- final Map<ClassNode, List<Node>> memberNodes = |
- new Map<ClassNode, List<Node>>(); |
- final List<Node> topLevelNodes = <Node>[]; |
- |
- String generateCode( |
- LibraryInfo libraryInfo, |
- ElementInfo elementInfo, |
- PlaceholderCollector collector, |
- PlaceholderRenamer placeholderRenamer, |
- FunctionElement mainFunction, |
- Uri outputUri, |
- CompilerOutputProvider outputProvider, |
- MirrorRenamer mirrorRenamer, |
- {bool multiFile: false, |
- bool forceStripTypes: false, |
- bool enableMinification: false}) { |
- for (Element element in elementInfo.topLevelElements) { |
- topLevelNodes.add(elementInfo.elementAsts[element].ast); |
- if (element.isClass && !element.isMixinApplication) { |
- final members = <Node>[]; |
- for (Element member in elementInfo.classMembers[element]) { |
- members.add(elementInfo.elementAsts[member].ast); |
- } |
- memberNodes[elementInfo.elementAsts[element].ast] = members; |
- } |
- } |
- |
- mirrorRenamer.addRenames(placeholderRenamer.renames, |
- topLevelNodes, collector); |
- |
- Map<LibraryElement, String> outputPaths = new Map<LibraryElement, String>(); |
- Map<LibraryElement, EmitterUnparser> unparsers = |
- new Map<LibraryElement, EmitterUnparser>(); |
- |
- // The single unparser used if we collect all the output in one file. |
- EmitterUnparser mainUnparser = multiFile |
- ? null |
- : new EmitterUnparser(placeholderRenamer.renames, |
- stripTypes: forceStripTypes, |
- minify: enableMinification); |
- |
- if (multiFile) { |
- // TODO(sigurdm): Factor handling of library-paths out from emitting. |
- String mainName = outputUri.pathSegments.last; |
- String mainBaseName = mainName.endsWith(".dart") |
- ? mainName.substring(0, mainName.length - 5) |
- : mainName; |
- // Map each library to a path based on the uri of the original |
- // library and [compiler.outputUri]. |
- Set<String> usedLibraryPaths = new Set<String>(); |
- for (LibraryElement library in libraryInfo.userLibraries) { |
- if (library == mainFunction.library) { |
- outputPaths[library] = mainBaseName; |
- } else { |
- List<String> names = |
- library.canonicalUri.pathSegments.last.split("."); |
- if (names.last == "dart") { |
- names = names.sublist(0, names.length - 1); |
- } |
- outputPaths[library] = |
- "$mainBaseName.${makeUnique(names.join("."), usedLibraryPaths)}"; |
- } |
- } |
- |
- /// Rewrites imports/exports to refer to the paths given in [outputPaths]. |
- for(LibraryElement outputLibrary in libraryInfo.userLibraries) { |
- EmitterUnparser unparser = new EmitterUnparser( |
- placeholderRenamer.renames, |
- stripTypes: forceStripTypes, |
- minify: enableMinification); |
- unparsers[outputLibrary] = unparser; |
- LibraryName libraryName = outputLibrary.libraryTag; |
- if (libraryName != null) { |
- unparser.visitLibraryName(libraryName); |
- } |
- for (LibraryTag tag in outputLibrary.tags) { |
- if (tag is! LibraryDependency) continue; |
- LibraryDependency dependency = tag; |
- LibraryElement libraryElement = |
- outputLibrary.getLibraryFromTag(dependency); |
- String uri = outputPaths.containsKey(libraryElement) |
- ? "${outputPaths[libraryElement]}.dart" |
- : libraryElement.canonicalUri.toString(); |
- if (dependency is Import) { |
- unparser.unparseImportTag(uri); |
- } else { |
- unparser.unparseExportTag(uri); |
- } |
- } |
- } |
- } else { |
- for(LibraryElement library in placeholderRenamer.platformImports) { |
- if (library.isPlatformLibrary && !library.isInternalLibrary) { |
- mainUnparser.unparseImportTag(library.canonicalUri.toString()); |
- } |
- } |
- } |
- |
- for (int i = 0; i < elementInfo.topLevelElements.length; i++) { |
- Element element = elementInfo.topLevelElements.elementAt(i); |
- Node node = topLevelNodes[i]; |
- Unparser unparser = multiFile ? unparsers[element.library] : mainUnparser; |
- if (node is ClassNode) { |
- // TODO(smok): Filter out default constructors here. |
- unparser.unparseClassWithBody(node, memberNodes[node]); |
- } else { |
- unparser.unparse(node); |
- } |
- unparser.newline(); |
- } |
- |
- int totalSize = 0; |
- String assembledCode; |
- if (multiFile) { |
- for(LibraryElement outputLibrary in libraryInfo.userLibraries) { |
- // TODO(sigurdm): Make the unparser output directly into the buffer |
- // instead of caching in `.result`. |
- String code = unparsers[outputLibrary].result; |
- totalSize += code.length; |
- outputProvider(outputPaths[outputLibrary], "dart") |
- ..add(code) |
- ..close(); |
- } |
- // TODO(sigurdm): We should get rid of compiler.assembledCode. |
- assembledCode = unparsers[mainFunction.library].result; |
- } else { |
- assembledCode = mainUnparser.result; |
- outputProvider("", "dart") |
- ..add(assembledCode) |
- ..close(); |
- |
- totalSize = assembledCode.length; |
- } |
- |
- return assembledCode; |
- } |
-} |