Index: pkg/compiler/lib/src/dump_info.dart |
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart |
deleted file mode 100644 |
index c59ea596ab48213d27935bc65f222db80e3eed4b..0000000000000000000000000000000000000000 |
--- a/pkg/compiler/lib/src/dump_info.dart |
+++ /dev/null |
@@ -1,667 +0,0 @@ |
-// Copyright (c) 2013, 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. |
- |
-library dump_info; |
- |
-import 'dart:convert' show |
- HtmlEscape, |
- JsonEncoder, |
- StringConversionSink, |
- ChunkedConversionSink; |
- |
-import 'elements/elements.dart'; |
-import 'elements/visitor.dart'; |
-import 'dart2jslib.dart' show |
- Backend, |
- CodeBuffer, |
- Compiler, |
- CompilerTask, |
- MessageKind; |
-import 'types/types.dart' show TypeMask; |
-import 'deferred_load.dart' show OutputUnit; |
-import 'js_backend/js_backend.dart' show JavaScriptBackend; |
-import 'js/js.dart' as jsAst; |
-import 'universe/universe.dart' show Selector; |
-import 'util/util.dart' show NO_LOCATION_SPANNABLE; |
- |
-/// Maps objects to an id. Supports lookups in |
-/// both directions. |
-class IdMapper<T>{ |
- Map<int, T> _idToElement = {}; |
- Map<T, int> _elementToId = {}; |
- int _idCounter = 0; |
- String name; |
- |
- IdMapper(this.name); |
- |
- Iterable<T> get elements => _elementToId.keys; |
- |
- String add(T e) { |
- if (_elementToId.containsKey(e)) { |
- return name + "/${_elementToId[e]}"; |
- } |
- |
- _idToElement[_idCounter] = e; |
- _elementToId[e] = _idCounter; |
- _idCounter += 1; |
- return name + "/${_idCounter - 1}"; |
- } |
-} |
- |
-class GroupedIdMapper { |
- // Mappers for specific kinds of elements. |
- IdMapper<LibraryElement> _library = new IdMapper('library'); |
- IdMapper<TypedefElement> _typedef = new IdMapper('typedef'); |
- IdMapper<FieldElement> _field = new IdMapper('field'); |
- IdMapper<ClassElement> _class = new IdMapper('class'); |
- IdMapper<FunctionElement> _function = new IdMapper('function'); |
- IdMapper<OutputUnit> _outputUnit = new IdMapper('outputUnit'); |
- |
- Iterable<Element> get functions => _function.elements; |
- |
- // Convert this database of elements into JSON for rendering |
- Map<String, dynamic> _toJson(ElementToJsonVisitor elementToJson) { |
- Map<String, dynamic> json = {}; |
- var m = [_library, _typedef, _field, _class, _function]; |
- for (IdMapper mapper in m) { |
- Map<String, dynamic> innerMapper = {}; |
- mapper._idToElement.forEach((k, v) { |
- // All these elements are already cached in the |
- // jsonCache, so this is just an access. |
- var elementJson = elementToJson.process(v); |
- if (elementJson != null) { |
- innerMapper["$k"] = elementJson; |
- } |
- }); |
- json[mapper.name] = innerMapper; |
- } |
- return json; |
- } |
-} |
- |
-class ElementToJsonVisitor extends ElementVisitor<Map<String, dynamic>> { |
- final GroupedIdMapper mapper = new GroupedIdMapper(); |
- final Compiler compiler; |
- |
- final Map<Element, Map<String, dynamic>> jsonCache = {}; |
- |
- int programSize; |
- String dart2jsVersion; |
- |
- ElementToJsonVisitor(this.compiler); |
- |
- void run() { |
- Backend backend = compiler.backend; |
- if (backend is JavaScriptBackend) { |
- // Add up the sizes of all output-buffers. |
- programSize = backend.emitter.oldEmitter.outputBuffers.values.fold(0, |
- (a, b) => a + b.length); |
- } else { |
- programSize = compiler.assembledCode.length; |
- } |
- |
- dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null; |
- |
- for (var library in compiler.libraryLoader.libraries.toList()) { |
- library.accept(this); |
- } |
- } |
- |
- // If keeping the element is in question (like if a function has a size |
- // of zero), only keep it if it holds dependencies to elsewhere. |
- bool shouldKeep(Element element) { |
- return compiler.dumpInfoTask.selectorsFromElement.containsKey(element) |
- || compiler.dumpInfoTask.inlineCount.containsKey(element); |
- } |
- |
- Map<String, dynamic> toJson() { |
- return mapper._toJson(this); |
- } |
- |
- // Memoization of the JSON creating process. |
- Map<String, dynamic> process(Element element) { |
- return jsonCache.putIfAbsent(element, () => element.accept(this)); |
- } |
- |
- // Returns the id of an [element] if it has already been processed. |
- // If the element has not been processed, this function does not |
- // process it, and simply returns null instead. |
- String idOf(Element element) { |
- if (jsonCache.containsKey(element) && jsonCache[element] != null) { |
- return jsonCache[element]['id']; |
- } else { |
- return null; |
- } |
- } |
- |
- Map<String, dynamic> visitElement(Element element) { |
- return null; |
- } |
- |
- Map<String, dynamic> visitConstructorBodyElement(ConstructorBodyElement e) { |
- return visitFunctionElement(e.constructor); |
- } |
- |
- Map<String, dynamic> visitLibraryElement(LibraryElement element) { |
- var id = mapper._library.add(element); |
- List<String> children = <String>[]; |
- |
- String libname = element.getLibraryName(); |
- libname = libname == "" ? "<unnamed>" : libname; |
- |
- int size = compiler.dumpInfoTask.sizeOf(element); |
- |
- LibraryElement contentsOfLibrary = element.isPatched |
- ? element.patch : element; |
- contentsOfLibrary.forEachLocalMember((Element member) { |
- Map<String, dynamic> childJson = this.process(member); |
- if (childJson == null) return; |
- children.add(childJson['id']); |
- }); |
- |
- if (children.length == 0 && !shouldKeep(element)) { |
- return null; |
- } |
- |
- return { |
- 'kind': 'library', |
- 'name': libname, |
- 'size': size, |
- 'id': id, |
- 'children': children |
- }; |
- } |
- |
- Map<String, dynamic> visitTypedefElement(TypedefElement element) { |
- String id = mapper._typedef.add(element); |
- return element.alias == null |
- ? null |
- : { |
- 'id': id, |
- 'type': element.alias.toString(), |
- 'kind': 'typedef', |
- 'name': element.name |
- }; |
- } |
- |
- Map<String, dynamic> visitFieldElement(FieldElement element) { |
- String id = mapper._field.add(element); |
- List<String> children = []; |
- StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); |
- |
- TypeMask inferredType = |
- compiler.typesTask.getGuaranteedTypeOfElement(element); |
- // If a field has an empty inferred type it is never used. |
- if (inferredType == null || inferredType.isEmpty || element.isConst) { |
- return null; |
- } |
- |
- int size = compiler.dumpInfoTask.sizeOf(element); |
- String code; |
- |
- if (emittedCode != null) { |
- size += emittedCode.length; |
- code = emittedCode.toString(); |
- } |
- |
- for (Element closure in element.nestedClosures) { |
- var childJson = this.process(closure); |
- if (childJson != null) { |
- children.add(childJson['id']); |
- if (childJson.containsKey('size')) { |
- size += childJson['size']; |
- } |
- } |
- } |
- |
- OutputUnit outputUnit = |
- compiler.deferredLoadTask.outputUnitForElement(element); |
- |
- return { |
- 'id': id, |
- 'kind': 'field', |
- 'type': element.type.toString(), |
- 'inferredType': inferredType.toString(), |
- 'name': element.name, |
- 'children': children, |
- 'size': size, |
- 'code': code, |
- 'outputUnit': mapper._outputUnit.add(outputUnit) |
- }; |
- } |
- |
- Map<String, dynamic> visitClassElement(ClassElement element) { |
- String id = mapper._class.add(element); |
- List<String> children = []; |
- |
- int size = compiler.dumpInfoTask.sizeOf(element); |
- JavaScriptBackend backend = compiler.backend; |
- |
- Map<String, dynamic> modifiers = { 'abstract': element.isAbstract }; |
- |
- element.forEachLocalMember((Element member) { |
- Map<String, dynamic> childJson = this.process(member); |
- if (childJson != null) { |
- children.add(childJson['id']); |
- |
- // Closures are placed in the library namespace, but |
- // we want to attribute them to a function, and by |
- // extension, this class. Process and add the sizes |
- // here. |
- if (member is MemberElement) { |
- for (Element closure in member.nestedClosures) { |
- Map<String, dynamic> child = this.process(closure); |
- |
- // Look for the parent element of this closure which should |
- // be a class. If it exists, set the display name to |
- // the name of the class + the name of the closure function. |
- Element parent = closure.enclosingElement; |
- Map<String, dynamic> processedParent = this.process(parent); |
- if (processedParent != null) { |
- child['name'] = "${processedParent['name']}.${child['name']}"; |
- } |
- |
- if (child != null) { |
- size += child['size']; |
- } |
- } |
- } |
- } |
- }); |
- |
- // Omit element if it is not needed. |
- if (!backend.emitter.neededClasses.contains(element) && |
- children.length == 0) { |
- return null; |
- } |
- |
- OutputUnit outputUnit = |
- compiler.deferredLoadTask.outputUnitForElement(element); |
- |
- return { |
- 'name': element.name, |
- 'size': size, |
- 'kind': 'class', |
- 'modifiers': modifiers, |
- 'children': children, |
- 'id': id, |
- 'outputUnit': mapper._outputUnit.add(outputUnit) |
- }; |
- } |
- |
- Map<String, dynamic> visitFunctionElement(FunctionElement element) { |
- String id = mapper._function.add(element); |
- String name = element.name; |
- String kind = "function"; |
- List<String> children = []; |
- List<Map<String, dynamic>> parameters = []; |
- String inferredReturnType = null; |
- String returnType = null; |
- String sideEffects = null; |
- String code = ""; |
- |
- StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); |
- int size = compiler.dumpInfoTask.sizeOf(element); |
- |
- Map<String, dynamic> modifiers = { |
- 'static': element.isStatic, |
- 'const': element.isConst, |
- 'factory': element.isFactoryConstructor, |
- 'external': element.isPatched |
- }; |
- |
- var enclosingElement = element.enclosingElement; |
- if (enclosingElement.isField || |
- enclosingElement.isFunction || |
- element.isClosure || |
- enclosingElement.isConstructor) { |
- kind = "closure"; |
- name = "<unnamed>"; |
- } else if (modifiers['static']) { |
- kind = 'function'; |
- } else if (enclosingElement.isClass) { |
- kind = 'method'; |
- } |
- |
- if (element.isConstructor) { |
- name == "" |
- ? "${element.enclosingElement.name}" |
- : "${element.enclosingElement.name}.${element.name}"; |
- kind = "constructor"; |
- } |
- |
- if (emittedCode != null) { |
- FunctionSignature signature = element.functionSignature; |
- returnType = signature.type.returnType.toString(); |
- signature.forEachParameter((parameter) { |
- parameters.add({ |
- 'name': parameter.name, |
- 'type': compiler.typesTask |
- .getGuaranteedTypeOfElement(parameter).toString(), |
- 'declaredType': parameter.node.type.toString() |
- }); |
- }); |
- inferredReturnType = compiler.typesTask |
- .getGuaranteedReturnTypeOfElement(element).toString(); |
- sideEffects = compiler.world.getSideEffectsOfElement(element).toString(); |
- code = emittedCode.toString(); |
- } |
- |
- if (element is MemberElement) { |
- MemberElement member = element as MemberElement; |
- for (Element closure in member.nestedClosures) { |
- Map<String, dynamic> child = this.process(closure); |
- if (child != null) { |
- child['kind'] = 'closure'; |
- children.add(child['id']); |
- size += child['size']; |
- } |
- } |
- } |
- |
- if (size == 0 && !shouldKeep(element)) { |
- return null; |
- } |
- |
- int inlinedCount = compiler.dumpInfoTask.inlineCount[element]; |
- if (inlinedCount == null) { |
- inlinedCount = 0; |
- } |
- |
- OutputUnit outputUnit = |
- compiler.deferredLoadTask.outputUnitForElement(element); |
- |
- return { |
- 'kind': kind, |
- 'name': name, |
- 'id': id, |
- 'modifiers': modifiers, |
- 'children': children, |
- 'size': size, |
- 'returnType': returnType, |
- 'inferredReturnType': inferredReturnType, |
- 'parameters': parameters, |
- 'sideEffects': sideEffects, |
- 'inlinedCount': inlinedCount, |
- 'code': code, |
- 'type': element.type.toString(), |
- 'outputUnit': mapper._outputUnit.add(outputUnit) |
- }; |
- } |
-} |
- |
-class Selection { |
- final Element selectedElement; |
- final Selector selector; |
- Selection(this.selectedElement, this.selector); |
-} |
- |
-class DumpInfoTask extends CompilerTask { |
- DumpInfoTask(Compiler compiler) |
- : super(compiler); |
- |
- String name = "Dump Info"; |
- |
- ElementToJsonVisitor infoCollector; |
- |
- // A set of javascript AST nodes that we care about the size of. |
- // This set is automatically populated when registerElementAst() |
- // is called. |
- final Set<jsAst.Node> _tracking = new Set<jsAst.Node>(); |
- // A mapping from Dart Elements to Javascript AST Nodes. |
- final Map<Element, List<jsAst.Node>> _elementToNodes = |
- <Element, List<jsAst.Node>>{}; |
- // A mapping from Javascript AST Nodes to the size of their |
- // pretty-printed contents. |
- final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{}; |
- final Map<jsAst.Node, int> _nodeBeforeSize = <jsAst.Node, int>{}; |
- final Map<Element, int> _fieldNameToSize = <Element, int>{}; |
- |
- final Map<Element, Set<Selector>> selectorsFromElement = {}; |
- final Map<Element, int> inlineCount = <Element, int>{}; |
- // A mapping from an element to a list of elements that are |
- // inlined inside of it. |
- final Map<Element, List<Element>> inlineMap = <Element, List<Element>>{}; |
- |
- void registerInlined(Element element, Element inlinedFrom) { |
- inlineCount.putIfAbsent(element, () => 0); |
- inlineCount[element] += 1; |
- inlineMap.putIfAbsent(inlinedFrom, () => new List<Element>()); |
- inlineMap[inlinedFrom].add(element); |
- } |
- |
- /** |
- * Registers that a function uses a selector in the |
- * function body |
- */ |
- void elementUsesSelector(Element element, Selector selector) { |
- if (compiler.dumpInfo) { |
- selectorsFromElement |
- .putIfAbsent(element, () => new Set<Selector>()) |
- .add(selector); |
- } |
- } |
- |
- /** |
- * Returns an iterable of [Selection]s that are used by |
- * [element]. Each [Selection] contains an element that is |
- * used and the selector that selected the element. |
- */ |
- Iterable<Selection> getRetaining(Element element) { |
- if (!selectorsFromElement.containsKey(element)) { |
- return const <Selection>[]; |
- } else { |
- return selectorsFromElement[element].expand( |
- (selector) { |
- return compiler.world.allFunctions.filter(selector).map((element) { |
- return new Selection(element, selector); |
- }); |
- }); |
- } |
- } |
- |
- /** |
- * A callback that can be called before a jsAst [node] is |
- * pretty-printed. The size of the code buffer ([aftersize]) |
- * is also passed. |
- */ |
- void enteringAst(jsAst.Node node, int beforeSize) { |
- if (isTracking(node)) { |
- _nodeBeforeSize[node] = beforeSize; |
- } |
- } |
- |
- /** |
- * A callback that can be called after a jsAst [node] is |
- * pretty-printed. The size of the code buffer ([aftersize]) |
- * is also passed. |
- */ |
- void exitingAst(jsAst.Node node, int afterSize) { |
- if (isTracking(node)) { |
- int diff = afterSize - _nodeBeforeSize[node]; |
- recordAstSize(node, diff); |
- } |
- } |
- |
- // Returns true if we care about tracking the size of |
- // this node. |
- bool isTracking(jsAst.Node code) { |
- if (compiler.dumpInfo) { |
- return _tracking.contains(code); |
- } else { |
- return false; |
- } |
- } |
- |
- // Registers that a javascript AST node `code` was produced by the |
- // dart Element `element`. |
- void registerElementAst(Element element, jsAst.Node code) { |
- if (compiler.dumpInfo) { |
- _elementToNodes |
- .putIfAbsent(element, () => new List<jsAst.Node>()) |
- .add(code); |
- _tracking.add(code); |
- } |
- } |
- |
- // Records the size of a dart AST node after it has been |
- // pretty-printed into the output buffer. |
- void recordAstSize(jsAst.Node code, int size) { |
- if (compiler.dumpInfo) { |
- //TODO: should I be incrementing here instead? |
- _nodeToSize[code] = size; |
- } |
- } |
- |
- // Field names are treated differently by the dart compiler |
- // so they must be recorded seperately. |
- void recordFieldNameSize(Element element, int size) { |
- _fieldNameToSize[element] = size; |
- } |
- |
- // Returns the size of the source code that |
- // was generated for an element. If no source |
- // code was produced, return 0. |
- int sizeOf(Element element) { |
- if (_fieldNameToSize.containsKey(element)) { |
- return _fieldNameToSize[element]; |
- } |
- if (_elementToNodes.containsKey(element)) { |
- return _elementToNodes[element] |
- .map(sizeOfNode) |
- .fold(0, (a, b) => a + b); |
- } else { |
- return 0; |
- } |
- } |
- |
- int sizeOfNode(jsAst.Node node) { |
- if (_nodeToSize.containsKey(node)) { |
- return _nodeToSize[node]; |
- } else { |
- return 0; |
- } |
- } |
- |
- StringBuffer codeOf(Element element) { |
- List<jsAst.Node> code = _elementToNodes[element]; |
- if (code == null) return null; |
- // Concatenate rendered ASTs. |
- StringBuffer sb = new StringBuffer(); |
- for (jsAst.Node ast in code) { |
- sb.writeln(jsAst.prettyPrint(ast, compiler).getText()); |
- } |
- return sb; |
- } |
- |
- void collectInfo() { |
- infoCollector = new ElementToJsonVisitor(compiler)..run(); |
- } |
- |
- void dumpInfo() { |
- measure(() { |
- if (infoCollector == null) { |
- collectInfo(); |
- } |
- |
- StringBuffer jsonBuffer = new StringBuffer(); |
- dumpInfoJson(jsonBuffer); |
- compiler.outputProvider('', 'info.json') |
- ..add(jsonBuffer.toString()) |
- ..close(); |
- }); |
- } |
- |
- |
- void dumpInfoJson(StringSink buffer) { |
- JsonEncoder encoder = const JsonEncoder(); |
- DateTime startToJsonTime = new DateTime.now(); |
- |
- Map<String, List<Map<String, String>>> holding = |
- <String, List<Map<String, String>>>{}; |
- for (Element fn in infoCollector.mapper.functions) { |
- Iterable<Selection> pulling = getRetaining(fn); |
- // Don't bother recording an empty list of dependencies. |
- if (pulling.length > 0) { |
- String fnId = infoCollector.idOf(fn); |
- // Some dart2js builtin functions are not |
- // recorded. Don't register these. |
- if (fnId != null) { |
- holding[fnId] = pulling |
- .map((selection) { |
- return <String, String>{ |
- "id": infoCollector.idOf(selection.selectedElement), |
- "mask": selection.selector.mask.toString() |
- }; |
- }) |
- // Filter non-null ids for the same reason as above. |
- .where((a) => a['id'] != null) |
- .toList(); |
- } |
- } |
- } |
- |
- // Track dependencies that come from inlining. |
- for (Element element in inlineMap.keys) { |
- String keyId = infoCollector.idOf(element); |
- if (keyId != null) { |
- for (Element held in inlineMap[element]) { |
- String valueId = infoCollector.idOf(held); |
- if (valueId != null) { |
- holding.putIfAbsent(keyId, () => new List<Map<String, String>>()) |
- .add(<String, String>{ |
- "id": valueId, |
- "mask": "inlined" |
- }); |
- } |
- } |
- } |
- } |
- |
- List<Map<String, dynamic>> outputUnits = |
- new List<Map<String, dynamic>>(); |
- |
- JavaScriptBackend backend = compiler.backend; |
- |
- for (OutputUnit outputUnit in |
- infoCollector.mapper._outputUnit._elementToId.keys) { |
- String id = infoCollector.mapper._outputUnit.add(outputUnit); |
- outputUnits.add(<String, dynamic> { |
- 'id': id, |
- 'name': outputUnit.name, |
- 'size': backend.emitter.oldEmitter.outputBuffers[outputUnit].length, |
- }); |
- } |
- |
- Map<String, dynamic> outJson = { |
- 'elements': infoCollector.toJson(), |
- 'holding': holding, |
- 'outputUnits': outputUnits, |
- 'dump_version': 3, |
- }; |
- |
- Duration toJsonDuration = new DateTime.now().difference(startToJsonTime); |
- |
- Map<String, dynamic> generalProgramInfo = <String, dynamic> { |
- 'size': infoCollector.programSize, |
- 'dart2jsVersion': infoCollector.dart2jsVersion, |
- 'compilationMoment': new DateTime.now().toString(), |
- 'compilationDuration': compiler.totalCompileTime.elapsed.toString(), |
- 'toJsonDuration': 0, |
- 'dumpInfoDuration': this.timing.toString(), |
- 'noSuchMethodEnabled': compiler.enabledNoSuchMethod |
- }; |
- |
- outJson['program'] = generalProgramInfo; |
- |
- ChunkedConversionSink<Object> sink = |
- encoder.startChunkedConversion( |
- new StringConversionSink.fromStringSink(buffer)); |
- sink.add(outJson); |
- compiler.reportInfo(NO_LOCATION_SPANNABLE, |
- const MessageKind( |
- "View the dumped .info.json file at " |
- "https://dart-lang.github.io/dump-info-visualizer")); |
- } |
-} |