Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/dump_info.dart |
| diff --git a/sdk/lib/_internal/compiler/implementation/dump_info.dart b/sdk/lib/_internal/compiler/implementation/dump_info.dart |
| index 06195fea2c0f15403eb33925c9f10ef69d144abf..5e6e39342904ad297a512e11994ba285ef57fb05 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/dump_info.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/dump_info.dart |
| @@ -22,37 +22,14 @@ import 'util/util.dart' show modifiersToString; |
| import 'deferred_load.dart' show OutputUnit; |
| import 'js_backend/js_backend.dart' show JavaScriptBackend; |
| import 'js/js.dart' as jsAst; |
| - |
| -// TODO (sigurdm): A search function. |
| -// TODO (sigurdm): Output size of classes. |
| -// TODO (sigurdm): Print that we dumped the HTML-file. |
| -// TODO (sigurdm): Include why a given element was included in the output. |
| -// TODO (sigurdm): Include how much output grew because of mirror support. |
| -// TODO (sigurdm): Write each function with parameter names. |
| -// TODO (sigurdm): Write how much space the boilerplate takes. |
| -// TODO (sigurdm): Include javascript names of entities in the output. |
| - |
| -const List<String> COLORS = const [ |
| - "#fff", |
| - "#8dd3c7", |
| - "#ffffb3", |
| - "#bebada", |
| - "#fb8072", |
| - "#80b1d3", |
| - "#fdb462", |
| - "#b3de69", |
| - "#fccde5", |
| - "#d9d9d9", |
| - "#bc80bd", |
| - "#ccebc5", |
| - "#ffed6f"]; |
| +import 'compilation_info.dart' show CompilationInformation; |
| class CodeSizeCounter { |
| final Map<Element, int> generatedSize = new Map<Element, int>(); |
| int getGeneratedSizeOf(Element element) { |
| int result = generatedSize[element]; |
| - return result == null ? 0 : result; |
| + return result == null ? 0: result; |
| } |
| void countCode(Element element, int added) { |
| @@ -61,709 +38,402 @@ class CodeSizeCounter { |
| } |
| } |
| -tag(String element) { |
| - return (String content, {String cls}) { |
| - String classString = cls == null ? '' : ' class="$cls"'; |
| - return '<$element$classString>$content</$element>'; |
| - }; |
| -} |
| - |
| -var div = tag('div'); |
| -var span = tag('span'); |
| -var code = tag('code'); |
| -var h2 = tag('h2'); |
| +/// Maps elements to an id. Supports lookups in |
| +/// both directions. |
| +class ElementMapper { |
| + Map<int, Element> _idToElement = {}; |
| + Map<Element, int> _elementToId = {}; |
| + int _idCounter = 0; |
| + String name; |
| -var esc = const HtmlEscape().convert; |
| - |
| -String sizeDescription(int size, ProgramInfo programInfo) { |
| - if (size == null) { |
| - return ''; |
| - } |
| - return span(span(size.toString(), cls: 'value') + |
| - ' bytes (${size * 100 ~/ programInfo.size}%)', cls: 'size'); |
| -} |
| - |
| -String sizePercent(int size, ProgramInfo programInfo) { |
| - if (size == null) { |
| - return "0.000%"; |
| - } else { |
| - return (100 * size / programInfo.size).toStringAsFixed(3) + "%"; |
| - } |
| -} |
| + ElementMapper(this.name); |
| -/// An [InfoNode] holds information about a part the program. |
| -abstract class InfoNode { |
| - String get name; |
| - |
| - int get size; |
| - |
| - void emitHtml(ProgramInfo programInfo, StringSink buffer, |
| - [String indentation = '']); |
| - |
| - Map<String, dynamic> toJson(ProgramInfo programInfo); |
| -} |
| - |
| -/// An [ElementNode] holds information about an [Element] |
| -class ElementInfoNode implements InfoNode { |
| - /// The name of the represented [Element]. |
| - final String name; |
| - |
| - /// The kind of the [Element] represented. This is presented to the |
| - /// user, so it might be more specific than [element.kind]. |
| - final String kind; |
| - |
| - /// The static type of the represented [Element]. |
| - /// [:null:] if this kind of element has no type. |
| - final String type; |
| - |
| - /// Any extra information to display about the represented [Element]. |
| - final String extra; |
| - |
| - /// A textual description of the modifiers (such as "static", "abstract") of |
| - /// the represented [Element]. |
| - final String modifiers; |
| - |
| - /// Describes how many bytes the code for the represented [Element] takes up |
| - /// in the output. |
| - final int size; |
| - |
| - /// Subnodes containing more detailed information about the represented |
| - /// [Element], and its members. |
| - List<InfoNode> contents; |
| - |
| - /// Subnodes containing more detailed information about the represented |
| - /// [Element], and its members. |
| - int outputUnitId; |
| - |
| - ElementInfoNode({this.name: "", |
| - this.kind: "", |
| - this.type, |
| - this.modifiers: "", |
| - this.size, |
| - this.contents, |
| - this.extra: "", |
| - this.outputUnitId}); |
| - |
| - Map<String, dynamic> toJson(ProgramInfo programInfo) { |
| - Map<String, dynamic> json = <String, dynamic>{ |
| - 'kind': this.kind, |
| - 'modifiers': this.modifiers, |
| - 'name': this.name, |
| - 'type': this.type, |
| - 'size': this.size, |
| - 'sizePercent': sizePercent(this.size, programInfo), |
| - 'extra': this.extra |
| - }; |
| - |
| - if (this.contents != null) { |
| - json['children'] = |
| - this.contents.map((c) => c.toJson(programInfo)).toList(); |
| + String add(Element e) { |
| + if (_elementToId.containsKey(e)) { |
| + return name + "/${_elementToId[e]}"; |
| } |
| - return json; |
| + _idToElement[_idCounter] = e; |
| + _elementToId[e] = _idCounter; |
| + _idCounter += 1; |
| + return name + "/${_idCounter - 1}"; |
| } |
| +} |
| - void emitHtml(ProgramInfo programInfo, StringSink buffer, |
| - [String indentation = '']) { |
| - String kindString = span(esc(kind), cls: 'kind'); |
| - String modifiersString = span(esc(modifiers), cls: "modifiers"); |
| - |
| - String nameString = span(esc(name), cls: 'name'); |
| - String typeString = type == null |
| - ? '' |
| - : span('/* ' + esc(type) + ' */', cls: 'type'); |
| - String extraString = span(esc(extra), cls: 'type'); |
| - String describe = [ |
| - kindString, |
| - typeString, |
| - modifiersString, |
| - nameString, |
| - sizeDescription(size, programInfo), |
| - extraString].join(' '); |
| - |
| - if (contents != null) { |
| - String outputUnitClass = outputUnitId == null |
| - ? "" |
| - : " outputUnit${outputUnitId % COLORS.length}"; |
| - buffer.write(indentation); |
| - buffer.write('<div class="container$outputUnitClass">\n'); |
| - buffer.write('$indentation '); |
| - buffer.write(div('+$describe', cls: "details")); |
| - buffer.write('\n'); |
| - buffer.write('$indentation <div class="contents">'); |
| - if (contents.isEmpty) { |
| - buffer.write('No members</div>'); |
| - } else { |
| - buffer.write('\n'); |
| - for (InfoNode subElementDescription in contents) { |
| - subElementDescription.emitHtml(programInfo, buffer, |
| - indentation + ' '); |
| +class DividedElementMapper { |
| + // Mappers for specific kinds of elements. |
| + ElementMapper _library = new ElementMapper('library'); |
| + ElementMapper _typedef = new ElementMapper('typedef'); |
| + ElementMapper _field = new ElementMapper('field'); |
| + ElementMapper _class = new ElementMapper('class'); |
| + ElementMapper _function = new ElementMapper('function'); |
| + |
| + // 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 (ElementMapper 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; |
| } |
| - buffer.write("\n$indentation </div>"); |
| - } |
| - buffer.write("\n$indentation</div>\n"); |
| - } else { |
| - buffer.writeln(div('$describe', cls: "element")); |
| + }); |
| + json[mapper.name] = innerMapper; |
| } |
| + return json; |
| } |
| } |
| -/// A [CodeInfoNode] holds information about a piece of code. |
| -class CodeInfoNode implements InfoNode { |
| - /// A short description of the code. |
| - final String description; |
| - |
| - final String generatedCode; |
| - |
| - get size => generatedCode.length; |
| - |
| - get name => ""; |
| - |
| - CodeInfoNode({this.description: "", this.generatedCode}); |
| +class ElementToJsonVisitor extends ElementVisitor<Map<String, dynamic>> { |
| + DividedElementMapper mapper = new DividedElementMapper(); |
| + Compiler compiler; |
| - void emitHtml(ProgramInfo programInfo, StringBuffer buffer, |
| - [String indentation = '']) { |
| - buffer.write(indentation); |
| - buffer.write(div(description + ' ' + |
| - sizeDescription(generatedCode.length, programInfo), |
| - cls: 'kind') + |
| - code(esc(generatedCode))); |
| - buffer.write('\n'); |
| - } |
| - |
| - Map<String, dynamic> toJson(ProgramInfo programInfo) { |
| - return <String, dynamic>{ |
| - 'kind': 'code', |
| - 'description': description, |
| - 'code': generatedCode, |
| - 'size': generatedCode.length, |
| - 'sizePercent': sizePercent(generatedCode.length, programInfo) |
| - }; |
| - } |
| -} |
| + CompilationInformation compilationInfo; |
| -/// Instances represent information inferred about the program such as |
| -/// inferred type information or inferred side effects. |
| -class InferredInfoNode implements InfoNode { |
| - /// Text describing the represented information. |
| - final String description; |
| + Map<Element, Map<String, dynamic>> jsonCache = {}; |
| + Map<Element, jsAst.Expression> codeCache; |
| - /// The name of the entity this information is inferred about (for example the |
| - /// name of a parameter). |
| - final String name; |
| + int programSize; |
| + DateTime compilationMoment; |
| + String dart2jsVersion; |
| + Duration compilationDuration; |
| + Duration dumpInfoDuration; |
| - /// The inferred type/side effect. |
| - final String type; |
| + ElementToJsonVisitor(Compiler compiler) { |
| + this.compiler = compiler; |
|
ahe
2014/08/18 15:31:14
Please use Dart constructor syntax. For example:
Ty Overby (Google)
2014/08/19 19:44:13
Done.
|
| + this.compilationInfo = compiler.enqueuer.codegen.compilationInfo; |
| - get size => 0; |
| + programSize = compiler.assembledCode.length; |
| + compilationMoment = new DateTime.now(); |
| + dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null; |
| + compilationDuration = compiler.totalCompileTime.elapsed; |
| - InferredInfoNode({this.name: "", this.description, this.type}); |
| + for (var library in compiler.libraryLoader.libraries.toList()) { |
| + library.accept(this); |
| + } |
| - Map<String, dynamic> toJson(ProgramInfo programInfo) { |
| - return <String, dynamic>{ |
| - 'kind': 'inferred', |
| - 'name': name, |
| - 'type': type, |
| - 'desc': description |
| - }; |
| + dumpInfoDuration = new DateTime.now().difference(compilationMoment); |
|
ahe
2014/08/18 15:31:14
We have CompilerTask to measure timing.
Ty Overby (Google)
2014/08/19 19:44:13
I can't seem to find out how to extract time measu
ahe
2014/08/20 08:33:19
Create a subclass of CompilerTask and add it to co
|
| } |
| - void emitHtml(ProgramInfo programInfo, StringBuffer buffer, |
| - [String indentation = '']) { |
| - buffer.write(indentation); |
| - buffer.write( |
| - div('${span("Inferred " + description, cls: "kind")} ' |
| - '${span(esc(name), cls: "name")} ' |
| - '${span(esc(type), cls: "type")} ', |
| - cls: "attr")); |
| - buffer.write('\n'); |
| + // 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 compilationInfo.relations['addsToWorklist'].containsKey(element) || |
| + compilationInfo.relations['enqueues'].containsKey(element); |
| } |
| -} |
| - |
| -/// Instances represent information about a program. |
| -class ProgramInfo { |
| - /// A list of all the libraries in the program to show information about. |
| - final List<InfoNode> libraries; |
| - |
| - /// The size of the whole program in bytes. |
| - final int size; |
| - |
| - /// The time the compilation took place. |
| - final DateTime compilationMoment; |
| - |
| - /// The time the compilation took to complete. |
| - final Duration compilationDuration; |
| - |
| - /// The version of dart2js used to compile the program. |
| - final String dart2jsVersion; |
| - |
| - final Map<OutputUnit, int> outputUnitNumbering; |
| - |
| - ProgramInfo({this.libraries, |
| - this.size, |
| - this.compilationMoment, |
| - this.compilationDuration, |
| - this.dart2jsVersion, |
| - this.outputUnitNumbering: null}); |
| Map<String, dynamic> toJson() { |
| - return <String, dynamic>{ |
| - 'program_size': size, |
| - 'compile_time': compilationMoment.toString(), |
| - 'compile_duration': compilationDuration.toString(), |
| - 'dart2js_version': dart2jsVersion |
| - }; |
| + return mapper._toJson(this); |
| } |
| -} |
| -class InfoDumpVisitor extends ElementVisitor<InfoNode> { |
| - final Compiler compiler; |
| - |
| - /// Contains the elements visited on the path from the library to here. |
| - final List<Element> stack = new List<Element>(); |
| + // Memoization of the JSON creating process. |
| + Map<String, dynamic> process(Element element) { |
| + return jsonCache.putIfAbsent(element, () => element.accept(this)); |
| + } |
| - final Map<OutputUnit, int> outputUnitNumbering = new Map<OutputUnit, int>(); |
| + Map<String, dynamic> visitElement(Element element) { |
| + return null; |
| + } |
| - Element get currentElement => stack.last; |
| + Map<String, dynamic> visitConstructorBodyElement(ConstructorBodyElement e) { |
| + return visitFunctionElement(e.constructor); |
| + } |
| - InfoDumpVisitor(Compiler this.compiler); |
| + Map<String, dynamic> visitLibraryElement(LibraryElement element) { |
| + var id = mapper._library.add(element); |
| + List<String> children = <String>[]; |
| - ProgramInfo collectDumpInfo() { |
| - JavaScriptBackend backend = compiler.backend; |
| + String libname = element.getLibraryName(); |
| + libname = libname == "" ? "<unnamed>" : libname; |
| - int counter = 0; |
| - for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { |
| - outputUnitNumbering[outputUnit] = counter; |
| - counter += 1; |
| - } |
| + int size = |
| + compiler.dumpInfoTask.codeSizeCounter.getGeneratedSizeOf(element); |
| - List<LibraryElement> sortedLibraries = |
| - compiler.libraryLoader.libraries.toList(); |
| - sortedLibraries.sort((LibraryElement l1, LibraryElement l2) { |
| - if (l1.isPlatformLibrary && !l2.isPlatformLibrary) { |
| - return 1; |
| - } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) { |
| - return -1; |
| - } |
| - return l1.getLibraryName().compareTo(l2.getLibraryName()); |
| + 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']); |
| }); |
| - List<InfoNode> libraryInfos = new List<InfoNode>(); |
| - libraryInfos.addAll(sortedLibraries |
| - .map((library) => visit(library)) |
| - .where((info) => info != null)); |
| - |
| - return new ProgramInfo( |
| - compilationDuration: compiler.totalCompileTime.elapsed, |
| - // TODO (sigurdm): Also count the size of deferred code |
| - size: compiler.assembledCode.length, |
| - libraries: libraryInfos, |
| - compilationMoment: new DateTime.now(), |
| - dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null, |
| - outputUnitNumbering: outputUnitNumbering); |
| - } |
| - |
| - InfoNode visitElement(Element element) { |
| - compiler.internalError(element, |
| - "This element of kind ${element.kind} " |
| - "does not support --dump-info"); |
| - return null; |
| - } |
| + if (children.length == 0 && !shouldKeep(element)) { |
| + return null; |
| + } |
| - InfoNode visitLibraryElement(LibraryElement element) { |
| - List<InfoNode> contents = new List<InfoNode>(); |
| - int size = compiler.dumpInfoTask.codeSizeCounter |
| - .getGeneratedSizeOf(element); |
| - if (size == 0) return null; |
| - stack.add(element); |
| - // For some reason the patch library contains the origin libraries members, |
| - // but the origin library does not contain the patch members. |
| - LibraryElement contentsLibrary = element.isPatched |
| - ? element.patch |
| - : element; |
| - contentsLibrary.forEachLocalMember((Element member) { |
| - InfoNode info = member.accept(this); |
| - if (info != null) { |
| - contents.add(info); |
| - } |
| - }); |
| - stack.removeLast(); |
| - String nameString = element.getLibraryName() == "" |
| - ? "<unnamed>" |
| - : element.getLibraryName(); |
| - contents.sort((InfoNode e1, InfoNode e2) { |
| - return e1.name.compareTo(e2.name); |
| - }); |
| - return new ElementInfoNode( |
| - extra: "${element.canonicalUri}", |
| - kind: "library", |
| - name: nameString, |
| - size: size, |
| - modifiers: "", |
| - contents: contents); |
| + return { |
| + 'kind': 'library', |
| + 'name': libname, |
| + 'size': size, |
| + 'id': id, |
| + 'children': children |
| + }; |
| } |
| - InfoNode visitTypedefElement(TypedefElement element) { |
| + Map<String, dynamic> visitTypedefElement(TypedefElement element) { |
| + String id = mapper._typedef.add(element); |
| return element.alias == null |
| - ? null |
| - : new ElementInfoNode( |
| - type: element.alias.toString(), |
| - kind: "typedef", |
| - name: element.name); |
| + ? null |
| + : { |
| + 'id': id, |
| + 'type': element.alias.toString(), |
| + 'kind': 'typedef', |
| + 'name': element.name |
| + }; |
| } |
| - InfoNode visitFieldElement(FieldElement element) { |
| + Map<String, dynamic> visitFieldElement(FieldElement element) { |
| + String id = mapper._field.add(element); |
| + List<String> children = []; |
| CodeBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); |
| - TypeMask inferredType = compiler.typesTask |
| - .getGuaranteedTypeOfElement(element); |
| + |
| // If a field has an empty inferred type it is never used. |
| - // Also constant fields do not get output as fields. |
| + TypeMask inferredType = |
| + compiler.typesTask.getGuaranteedTypeOfElement(element); |
| if (inferredType == null || inferredType.isEmpty || element.isConst) { |
| return null; |
| } |
| + |
| int size = 0; |
| - DartType type = element.type; |
| - List<InfoNode> contents = new List<InfoNode>(); |
| + String code; |
| + |
| if (emittedCode != null) { |
| - contents.add(new CodeInfoNode( |
| - description: "Generated initializer", |
| - generatedCode: emittedCode.getText())); |
| - size = emittedCode.length; |
| - } |
| - if (inferredType != null) { |
| - contents.add(new InferredInfoNode( |
| - description: "type", |
| - type: inferredType.toString())); |
| - stack.add(element); |
| + size += emittedCode.length; |
| + code = emittedCode.getText(); |
| } |
| + |
| for (Element closure in element.nestedClosures) { |
| - InfoNode info = closure.accept(this); |
| - if (info != null) { |
| - contents.add(info); |
| - size += info.size; |
| + var childJson = this.process(closure); |
| + if (childJson != null) { |
| + children.add(childJson['id']); |
| + if (childJson.containsKey('size')) { |
| + size += childJson['size']; |
| + } |
| } |
| } |
| - stack.removeLast(); |
| - |
| - return new ElementInfoNode( |
| - kind: "field", |
| - type: "$type", |
| - name: element.name, |
| - size: size, |
| - modifiers: modifiersToString(isStatic: element.isStatic, |
| - isFinal: element.isFinal, |
| - isConst: element.isConst), |
| - contents: contents, |
| - outputUnitId: outputUnitId(element)); |
| - } |
| - int outputUnitId(Element element) { |
| - OutputUnit outputUnit = |
| - compiler.deferredLoadTask.outputUnitForElement(element); |
| - return outputUnitNumbering[outputUnit]; |
| + return { |
| + 'id': id, |
| + 'kind': 'field', |
| + 'name': element.name, |
| + 'children': children, |
| + 'size': size, |
| + 'code': code |
| + }; |
| } |
| - InfoNode visitClassElement(ClassElement element) { |
| - // If the element is not emitted in the program, we omit it from the output. |
| + Map<String, dynamic> visitClassElement(ClassElement element) { |
| + String id = mapper._class.add(element); |
| + List<String> children = []; |
| + |
| + int size = compiler.dumpInfoTask.codeSizeCounter.getGeneratedSizeOf(element); |
| + |
| + // Omit element if it is not needed. |
| JavaScriptBackend backend = compiler.backend; |
| if (!backend.emitter.neededClasses.contains(element)) return null; |
| - String modifiersString = modifiersToString(isAbstract: element.isAbstract); |
| - String supersString = element.allSupertypes == null ? "" : |
| - "implements ${element.allSupertypes}"; |
| - List contents = []; |
| - stack.add(element); |
| + Map<String, dynamic> modifiers = { 'abstract': element.isAbstract }; |
| + |
| element.forEachLocalMember((Element member) { |
| - InfoNode info = member.accept(this); |
| - if (info != null) { |
| - contents.add(info); |
| + Map<String, dynamic> childJson = this.process(member); |
| + if (childJson != null) { |
| + children.add(childJson['id']); |
| } |
| }); |
| - stack.removeLast(); |
| - contents.sort((InfoNode n1, InfoNode n2) { |
| - return n1.name.compareTo(n2.name); |
| - }); |
| - return new ElementInfoNode( |
| - kind: "class", |
| - name: element.name, |
| - extra: supersString, |
| - modifiers: modifiersString, |
| - contents: contents, |
| - outputUnitId: outputUnitId(element)); |
| + |
| + return { |
| + 'name': element.name, |
| + 'size': size, |
| + 'kind': 'class', |
| + 'modifiers': modifiers, |
| + 'children': children, |
| + 'id': id |
| + }; |
| } |
| - InfoNode visitFunctionElement(FunctionElement element) { |
| + 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 returnType = null; |
| + String sideEffects = null; |
| + String code = ""; |
| + |
| CodeBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); |
| int size = 0; |
| - String nameString = element.name; |
| - String modifiersString = modifiersToString( |
| - isStatic: element.isStatic, |
| - isConst: element.isConst, |
| - isFactory: element.isFactoryConstructor, |
| - isExternal: element.isPatched); |
| - String kindString = "function"; |
| - if (currentElement.isClass) { |
| - kindString = "method"; |
| - } else if (currentElement.isField || |
| - currentElement.isFunction || |
| - currentElement.isConstructor) { |
| - kindString = "closure"; |
| - nameString = "<unnamed>"; |
| + |
| + 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 (enclosingElement.isClass) { |
| + kind = 'method'; |
| } |
| + |
| if (element.isConstructor) { |
| - nameString = element.name == "" |
| - ? "${element.enclosingClass.name}" |
| - : "${element.enclosingClass.name}.${element.name}"; |
| - kindString = "constructor"; |
| + name == "" |
| + ? "${element.enclosingElement.name}" |
| + : "${element.enclosingElement.name}.${element.name}"; |
| + kind = "constructor"; |
| } |
| - List contents = []; |
| + |
| if (emittedCode != null) { |
| FunctionSignature signature = element.functionSignature; |
| signature.forEachParameter((parameter) { |
| - contents.add(new InferredInfoNode( |
| - description: "parameter", |
| - name: parameter.name, |
| - type: compiler.typesTask |
| - .getGuaranteedTypeOfElement(parameter).toString())); |
| + parameters.add({ |
| + 'name': parameter.name, |
| + 'type': compiler.typesTask |
| + .getGuaranteedTypeOfElement(parameter).toString() |
| + }); |
| }); |
| - contents.add(new InferredInfoNode( |
| - description: "return type", |
| - type: compiler.typesTask |
| - .getGuaranteedReturnTypeOfElement(element).toString())); |
| - contents.add(new InferredInfoNode( |
| - description: "side effects", |
| - type: compiler.world |
| - .getSideEffectsOfElement(element).toString())); |
| - contents.add(new CodeInfoNode( |
| - description: "Generated code", |
| - generatedCode: emittedCode.getText())); |
| - size += emittedCode.length; |
| + returnType = compiler.typesTask |
| + .getGuaranteedReturnTypeOfElement(element).toString(); |
| + sideEffects = compiler.world.getSideEffectsOfElement(element).toString(); |
| + code = emittedCode.getText(); |
| + size += code.length; |
| } |
| - stack.add(element); |
| + |
| for (Element closure in element.nestedClosures) { |
| - InfoNode info = closure.accept(this); |
| - if (info != null) { |
| - contents.add(info); |
| - size += info.size; |
| + Map<String, dynamic> child = this.process(closure); |
| + if (child != null) { |
| + children.add(child['id']); |
| + size += child['size']; |
| } |
| } |
| - stack.removeLast(); |
| - if (size == 0) { |
| + |
| + if (size == 0 && !shouldKeep(element)) { |
| return null; |
| } |
| - return new ElementInfoNode( |
| - type: element.computeType(compiler).toString(), |
| - kind: kindString, |
| - name: nameString, |
| - size: size, |
| - modifiers: modifiersString, |
| - contents: contents, |
| - outputUnitId: outputUnitId(element)); |
| + return { |
| + 'kind': kind, |
| + 'name': name, |
| + 'id': id, |
| + 'modifiers': modifiers, |
| + 'children': children, |
| + 'size': size, |
| + 'returnType': returnType, |
| + 'parameters': parameters, |
| + 'sideEffects': sideEffects, |
| + 'code': code, |
| + 'type': element.computeType(compiler).toString() |
| + }; |
| } |
| } |
| + |
| class DumpInfoTask extends CompilerTask { |
| DumpInfoTask(Compiler compiler) |
| - : infoDumpVisitor = new InfoDumpVisitor(compiler), |
| - super(compiler); |
| + : super(compiler); |
| String name = "Dump Info"; |
| final CodeSizeCounter codeSizeCounter = new CodeSizeCounter(); |
| - final InfoDumpVisitor infoDumpVisitor; |
| + ElementToJsonVisitor infoCollector; |
| - final Map<Element, jsAst.Expression>_generatedCode = |
| - new Map<Element, jsAst.Expression>(); |
| + final Map<Element, jsAst.Expression> _generatedCode = {}; |
| - /// Registers that [code] has been generated for [element] so that it can be |
| - /// emitted in the info.html. |
| void registerGeneratedCode(Element element, jsAst.Expression code) { |
| if (compiler.dumpInfo) { |
| _generatedCode[element] = code; |
| } |
| } |
| - CodeBuffer codeOf(Element element) { |
| - jsAst.Expression code = _generatedCode[element]; |
| - return code != null |
| - ? jsAst.prettyPrint(code, compiler) |
| - : compiler.backend.codeOf(element); |
| + |
| + void collectInfo() { |
| + infoCollector = new ElementToJsonVisitor(compiler); |
| } |
| void dumpInfo() { |
| measure(() { |
| - ProgramInfo info = infoDumpVisitor.collectDumpInfo(); |
| - |
| - StringBuffer htmlBuffer = new StringBuffer(); |
| - dumpInfoHtml(info, htmlBuffer); |
| - compiler.outputProvider('', 'info.html') |
| - ..add(htmlBuffer.toString()) |
| - ..close(); |
| + if (infoCollector == null) { |
| + collectInfo(); |
| + } |
| StringBuffer jsonBuffer = new StringBuffer(); |
| - dumpInfoJson(info, jsonBuffer); |
| + dumpInfoJson(jsonBuffer); |
| compiler.outputProvider('', 'info.json') |
| ..add(jsonBuffer.toString()) |
| ..close(); |
| }); |
| } |
| - void dumpInfoJson(ProgramInfo info, StringSink buffer) { |
| - Map<String, dynamic> entire = <String, dynamic>{ |
| - 'program': info.toJson(), |
| - 'libs': info.libraries.map((lib) => lib.toJson(info)).toList() |
| - }; |
| + CodeBuffer codeOf(Element element) { |
| + jsAst.Expression code = _generatedCode[element]; |
| + return code != null |
| + ? jsAst.prettyPrint(code, compiler) |
| + : compiler.backend.codeOf(element); |
| + } |
| + void dumpInfoJson(StringSink buffer) { |
| JsonEncoder encoder = const JsonEncoder(); |
| - ChunkedConversionSink<Object> sink = |
| - encoder.startChunkedConversion( |
| - new StringConversionSink.fromStringSink(buffer)); |
| - sink.add(entire); |
| - } |
| - void dumpInfoHtml(ProgramInfo info, StringSink buffer) { |
| - int totalSize = info.size; |
| - |
| - buffer.writeln(""" |
| -<html> |
| - <head> |
| - <title>Dart2JS compilation information</title> |
| - <style> |
| - code {margin-left: 20px; display: block; white-space: pre; } |
| - div.container, div.contained, div.element, div.attr { |
| - margin-top:0px; |
| - margin-bottom: 0px; |
| - } |
| - div.container, div.element, div.attr { |
| - white-space: nowrap; |
| - } |
| - .contents { |
| - margin-left: 20px; |
| + // `A` uses and depends on the functions `Bs`. |
| + // A Bs |
| + Map<String, List<String>> holding = <String, List<String>>{}; |
| + |
| + DateTime startToJsonTime = new DateTime.now(); |
| + |
| + CompilationInformation compilationInfo = |
| + infoCollector.compiler.enqueuer.codegen.compilationInfo; |
| + var relations = compilationInfo.relations; |
| + relations['addsToWorklist'].forEach((func, deps) { |
| + if (func != null) { |
| + var funcJson = infoCollector.process(func); |
| + if (funcJson != null) { |
| + var funcId = funcJson['id']; |
| + |
| + List<String> heldList = <String>[]; |
| + |
| + for (var held in deps) { |
| + // "process" to get the ids of the elements. |
| + var heldJson = infoCollector.process(held); |
| + if (heldJson != null) { |
| + var heldId = heldJson['id']; |
| + heldList.add(heldId); |
| + } |
| + } |
| + holding[funcId] = heldList; |
| } |
| - div.contained {margin-left: 20px;} |
| - div {/*border: 1px solid;*/} |
| - span.kind {} |
| - span.modifiers {font-weight:bold;} |
| - span.name {font-weight:bold; font-family: monospace;} |
| - span.type {font-family: monospace; color:blue;} |
| -"""); |
| - for (int i = 0; i < COLORS.length; i++) { |
| - buffer.writeln(" .outputUnit$i " |
| - "{border-left: 4px solid ${COLORS[i]}}"); |
| - } |
| - buffer.writeln(""" |
| - </style> |
| - </head> |
| - <body> |
| - <h1>Dart2js compilation information</h1>"""); |
| - if (info.outputUnitNumbering.length > 1) { |
| - for (OutputUnit outputUnit in info.outputUnitNumbering.keys) { |
| - String color = COLORS[info.outputUnitNumbering[outputUnit] |
| - % COLORS.length]; |
| - JavaScriptBackend backend = compiler.backend; |
| - int size = backend.emitter.outputBuffers[outputUnit].length; |
| - buffer.writeln('<div style=' |
| - '"background:$color;">' |
| - '${outputUnit.partFileName(compiler)} $size bytes</div>'); |
| } |
| - } |
| - buffer.writeln(h2('Compilation took place: ' |
| - '${info.compilationMoment}')); |
| - buffer.writeln(h2('Compilation took: ' |
| - '${info.compilationDuration.inSeconds} seconds')); |
| - buffer.writeln(h2('Output size: ${info.size} bytes')); |
| - if (info.dart2jsVersion != null) { |
| - buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}')); |
| - } |
| + }); |
| - buffer.writeln('<a href="#" class="sort_by_size">Sort by size</a>\n'); |
| + Map<String, dynamic> outJson = {}; |
| + outJson['elements'] = infoCollector.toJson(); |
| + outJson['holding'] = holding; |
| + outJson['dump_version'] = 1; |
| - buffer.writeln('<div class="contents">'); |
| - info.libraries.forEach((InfoNode node) { |
| - node.emitHtml(info, buffer); |
| - }); |
| - buffer.writeln('</div>'); |
| - |
| - // TODO (sigurdm): This script should be written in dart |
| - buffer.writeln(r""" |
| - <script type="text/javascript"> |
| - function toggler(element) { |
| - return function(e) { |
| - element.hidden = !element.hidden; |
| - }; |
| - } |
| - var containers = document.getElementsByClassName('container'); |
| - for (var i = 0; i < containers.length; i++) { |
| - var container = containers[i]; |
| - container.querySelector('.details').addEventListener('click', |
| - toggler(container.querySelector('.contents')), false); |
| - container.querySelector('.contents').hidden = true; |
| - } |
| + Duration toJsonDuration = new DateTime.now().difference(startToJsonTime); |
| - function sortBySize() { |
| - var toSort = document.querySelectorAll('.contents'); |
| - for (var i = 0; i < toSort.length; ++i) { |
| - sortNodes(toSort[i], function(a, b) { |
| - if (a[1] !== b[1]) { |
| - return a[1] > b[1] ? -1 : 1; |
| - } |
| - return a[2] === b[2] ? 0 : a[2] > b[2] ? 1 : -1; |
| - }); |
| - } |
| - } |
| + Map<String, dynamic> generalProgramInfo = <String, dynamic>{}; |
| + generalProgramInfo['size'] = infoCollector.programSize; |
| + generalProgramInfo['dart2jsVersion'] = infoCollector.dart2jsVersion; |
| + generalProgramInfo['compilationMoment'] = infoCollector.compilationMoment.toString(); |
| + generalProgramInfo['compilationDuration'] = infoCollector.compilationDuration.toString(); |
| + generalProgramInfo['toJsonDuration'] = toJsonDuration.toString(); |
| + generalProgramInfo['dumpInfoDuration'] = infoCollector.dumpInfoDuration.toString(); |
| - function findSize(node) { |
| - var size = 0; |
| - var details = node.querySelector('.details'); |
| - if (details) { |
| - var sizeElement = details.querySelector('.size'); |
| - if (sizeElement) { |
| - size = parseInt(sizeElement.textContent); |
| - } else { |
| - // For classes, sum up the contents for sorting purposes. |
| - var kind = details.querySelector('.kind'); |
| - if (kind && kind.textContent === 'class') { |
| - var contents = node.querySelector('.contents'); |
| - if (contents) { |
| - var child = contents.firstElementChild; |
| - while (child) { |
| - size += findSize(child); |
| - child = child.nextElementSibling; |
| - } |
| - } |
| - } |
| - } |
| - } |
| - return size; |
| - } |
| + outJson['program'] = generalProgramInfo; |
| - function findName(node) { |
| - var name = ''; |
| - var nameNode = node.querySelector('.name'); |
| - if (nameNode) { |
| - return nameNode.textContent; |
| - } |
| - return node.textContent; |
| - } |
| - function sortNodes(node, fn) { |
| - var items = []; |
| - var child = node.firstElementChild; |
| - while (child) { |
| - items.push([child, findSize(child), findName(child)]); |
| - child = child.nextElementSibling; |
| - } |
| - items.sort(fn); |
| - for (var i = 0; i < items.length; ++i) { |
| - node.appendChild(items[i][0]); |
| - } |
| - } |
| - document.querySelector('.sort_by_size').addEventListener('click', |
| - function() { |
| - sortBySize(); |
| - }, false); |
| - </script> |
| - </body> |
| -</html>"""); |
| + ChunkedConversionSink<Object> sink = |
| + encoder.startChunkedConversion( |
| + new StringConversionSink.fromStringSink(buffer)); |
| + sink.add(outJson); |
| } |
| } |