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); |
} |
} |