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 |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e176fcfc59d29049dd6dfc3b64e9d393d9244611 |
| --- /dev/null |
| +++ b/sdk/lib/_internal/compiler/implementation/dump_info.dart |
| @@ -0,0 +1,356 @@ |
| +// 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_types; |
| + |
| +import 'elements/elements.dart'; |
| +import 'elements/visitor.dart'; |
| +import 'dart:convert' show HtmlEscape; |
| +import 'dart2jslib.dart' |
| + show Compiler, |
| + CompilerTask, |
| + CodeBuffer; |
| +import 'dart_types.dart' show DartType; |
| + |
| +// 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. |
| + |
| +class CodeSizeCounter { |
| + final Map<Element, int> generatedSize = new Map<Element, int>(); |
| + |
| + int getGeneratedSizeOf(Element element) { |
| + int result = generatedSize[element]; |
| + return result == null ? 0 : result; |
| + } |
| + |
| + void countCode(Element element, int added) { |
| + int before = generatedSize.putIfAbsent(element, () => 0); |
| + generatedSize[element] = before + added; |
| + } |
| +} |
| + |
| +class ElementInfo { |
| + String name; |
|
kasperl
2013/11/29 12:03:13
Can some of these fields be final?
sigurdm
2013/12/02 14:09:50
all
|
| + String kind; |
| + String type; |
| + String modifiers; |
| + String generatedCode; |
| + // How to present this piece of information |
| + // As "element" or "code" |
| + String presentation; |
| + int size; // How many bytes does this take in the output |
| + List<ElementInfo> contents; |
| + ElementInfo({String this.name: "", |
|
kasperl
2013/11/29 12:03:13
No need to add the types for the 'this.name' style
sigurdm
2013/12/02 14:09:50
Great!
|
| + String this.kind: "", |
| + String this.type: "", |
| + String this.modifiers: "", |
| + int this.size, |
| + String this.generatedCode, |
| + String this.presentation: "element", |
| + List<ElementInfo> this.contents}); |
| +} |
| + |
| +class ProgramInfo { |
|
kasperl
2013/11/29 12:03:13
Use final fields and no types for 'this.name' styl
sigurdm
2013/12/02 14:09:50
Done.
|
| + String name; |
| + String presentation; |
| + List<ElementInfo> libraries; |
| + int size; // How many bytes is the output |
| + DateTime compilationMoment; |
| + int compilationDuration; |
| + String dart2jsVersion; |
| + |
| + ProgramInfo({String this.name, |
| + String this.presentation, |
| + List<ElementInfo> this.libraries, |
| + int this.size, DateTime this.compilationMoment, |
| + int this.compilationDuration, String this.dart2jsVersion}); |
| +} |
| + |
| +class InfoDumpVisitor extends ElementVisitor<ElementInfo> { |
| + |
| + Compiler compiler; |
| + |
| + InfoDumpVisitor(Compiler this.compiler); |
| + |
| + ElementInfo visitElement(Element element) { |
| + compiler.internalError("This element of kind ${element.kind} " |
| + "does not support dumping of types", |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
|
| + token: element.position()); |
| + } |
| + |
| + ElementInfo visitLibraryElement(LibraryElement element) { |
| + List<ElementInfo> contents = new List<ElementInfo>(); |
| + int size = compiler.dumpInfoTask.codeSizeCounter |
|
kasperl
2013/11/29 12:03:13
Doesn't this fit on one line?
sigurdm
2013/12/02 14:09:50
No
|
| + .getGeneratedSizeOf(element); |
| + if (size == 0) return null; |
| + element.forEachLocalMember((Element member) { |
| + ElementInfo info = member.accept(this); |
| + if (info != null) { |
| + contents.add(info); |
| + } |
| + }); |
| + |
| + String nameString = element.getLibraryName() == "" ? |
|
kasperl
2013/11/29 12:03:13
I suggest breaking before ? and formatting this al
sigurdm
2013/12/02 14:09:50
Done.
|
| + "<unnamed>" : element.getLibraryName(); |
| + contents.sort((ElementInfo m1, ElementInfo m2) { |
| + return m1.name.compareTo(m2.name); |
| + }); |
| + return new ElementInfo( |
| + type: element.canonicalUri.toString(), |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
|
| + kind: "library", |
| + name: nameString, |
| + size: size, |
| + modifiers: "", |
| + contents: contents); |
| + } |
| + |
| + ElementInfo visitTypedefElement(TypedefElement element) { |
| + return element.thisType == null ? null : |
| + new ElementInfo( |
| + type: element.thisType.toString(), |
| + kind: "typedef", |
| + name: element.name); |
| + } |
| + |
| + ElementInfo visitVariableElement(VariableElement element) { |
| + DartType type = element.computeType(compiler); |
| + if (type == null) return null; |
| + String modifiersString = element.modifiers.toString() == "" ? |
|
kasperl
2013/11/29 12:03:13
Consider the alternative ?: formatting with respec
sigurdm
2013/12/02 14:09:50
Done.
|
| + "" : element.modifiers.toString()+" "; |
| + List inferredType = [new ElementInfo( |
| + kind: "inferred type", |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
|
| + name: "", |
| + type: compiler.typesTask.getGuaranteedTypeOfElement(element).toString(), |
| + modifiers: "")]; |
| + return new ElementInfo( |
| + kind: "field", |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
|
| + type: type.toString(), |
| + name: element.name, |
| + modifiers: modifiersString, |
| + contents: inferredType); |
| + } |
| + |
| + ElementInfo visitClassElement(ClassElement element) { |
| + String modifiersString = element.modifiers.toString() == "" ? |
| + "" : element.modifiers.toString()+" "; |
| + if (!element.isResolved) return null; |
| + String supersString = element.allSupertypes == null ? "" : |
| + "implements ${element.allSupertypes}"; |
| + List contents = []; |
| + element.forEachLocalMember((Element member) { |
| + ElementInfo info = member.accept(this); |
| + if (info != null) { |
| + contents.add(info); |
| + } |
| + }); |
| + if (contents.isEmpty) { |
| + return null; |
| + } |
| + contents.sort((ElementInfo m1, ElementInfo m2) { |
|
kasperl
2013/11/29 12:03:13
Why are these called m1 and m2? e1 and e2 seems mo
sigurdm
2013/12/02 14:09:50
Looong story...
fixed
|
| + return m1.name.compareTo(m2.name); |
| + }); |
| + return new ElementInfo( |
| + kind: "class", |
|
kasperl
2013/11/29 12:03:13
4 space indent (lots of occurrences).
sigurdm
2013/12/02 14:09:50
On 2013/11/29 12:03:13, kasperl wrote:
> 4 space i
|
| + name: element.name, |
| + type: supersString, |
| + modifiers: modifiersString, |
| + contents: contents); |
| + } |
| + |
| + ElementInfo visitFunctionElement(FunctionElement element) { |
| + CodeBuffer emittedCode = compiler.backend.codeOf(element); |
| + if (emittedCode == null) { |
| + return null; |
| + } |
| + String modifiersString = element.modifiers.toString() == "" ? |
| + "" : element.modifiers.toString()+" "; |
| + String kindString = "function"; |
| + String nameString = element.name; |
| + if (element.isConstructor()) { |
| + nameString = element.name == "" ? "${element.enclosingElement.name}()" : |
| + "${element.enclosingElement.name}.${element.name}"; |
| + kindString = "constructor"; |
| + } |
| + List contents = []; |
| + FunctionSignature signature = element.computeSignature(compiler); |
| + signature.forEachParameter((parameter) { |
| + contents.add(new ElementInfo( |
| + kind: "inferred", |
| + name: parameter.name, |
| + modifiers: "parameter type", |
| + type: compiler.typesTask |
|
kasperl
2013/11/29 12:03:13
Maybe the type parameter to the ElementInfo constr
sigurdm
2013/12/02 14:09:50
Hmm - maybe - right now the "type" is some explana
|
| + .getGuaranteedTypeOfElement(parameter).toString())); |
| + }); |
| + contents.add(new ElementInfo( |
| + kind: "inferred", |
| + modifiers: "return type", |
| + type: compiler.typesTask |
| + .getGuaranteedReturnTypeOfElement(element).toString())); |
| + contents.add(new ElementInfo( |
| + kind: "inferred", |
| + modifiers: "side effects", |
| + type: compiler.world.getSideEffectsOfElement(element).toString())); |
| + contents.add(new ElementInfo( |
| + name: "Generated code", |
| + presentation: "code", |
| + generatedCode: emittedCode.getText())); |
| + return new ElementInfo( |
| + type: element.type.toString(), |
| + kind: kindString, |
| + name: nameString, |
| + size: emittedCode.length, |
| + modifiers: modifiersString, |
| + contents: contents); |
| + } |
| +} |
| + |
| +class DumpInfoTask extends CompilerTask { |
| + DumpInfoTask(Compiler compiler) : |
| + super(compiler), |
| + infoDumpVisitor = new InfoDumpVisitor(compiler); |
| + |
| + String name = "Dump Info"; |
| + |
| + final CodeSizeCounter codeSizeCounter = new CodeSizeCounter(); |
| + |
| + final InfoDumpVisitor infoDumpVisitor; |
| + |
| + void dumpInfo() { |
| + measure(() { |
| + ProgramInfo info = collectDumpInfo(); |
| + StringBuffer buffer = new StringBuffer(); |
| + dumpInfoHtml(info, buffer); |
| + compiler.outputProvider('', 'info.html') |
| + ..add(buffer.toString()) |
|
kasperl
2013/11/29 12:03:13
4 space indent (match up to the next line).
sigurdm
2013/12/02 14:09:50
Done.
|
| + ..close(); |
| + }); |
| + } |
| + |
| + ProgramInfo collectDumpInfo() { |
| + List<LibraryElement> sortedLibraries = compiler.libraries.values.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()); |
| + }); |
| + |
| + List<ElementInfo> libraryInfos = new List<ElementInfo>.from(sortedLibraries |
|
kasperl
2013/11/29 12:03:13
Maybe compute the Iterable outside to make that co
sigurdm
2013/12/02 14:09:50
Done.
|
| + .map((library) => infoDumpVisitor.visit(library)) |
| + .where((library) => library != null)); |
| + |
| + return new ProgramInfo( |
| + compilationDuration: compiler.totalCompileTime.elapsedTicks, |
|
kasperl
2013/11/29 12:03:13
Weird indentation. Should be 4 space relative to t
sigurdm
2013/12/02 14:09:50
Done.
|
| + // 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); |
| + } |
| + |
| + void dumpInfoHtml(ProgramInfo info, StringSink buffer) { |
| + tag(String element) { |
| + return (String content, {String cls}) { |
| + return '<$element${cls == null ? '' : ' class="$cls"'}>' |
|
kasperl
2013/11/29 12:03:13
I'd compute the (cls == null ? '' : ' class ...')
sigurdm
2013/12/02 14:09:50
Done.
|
| + '$content</$element>'; |
| + }; |
| + } |
| + var div = tag('div'); |
| + var span = tag('span'); |
| + var code = tag('code'); |
| + var h2 = tag('h2'); |
| + int totalSize = info.size; |
| + void dumpElement(ElementInfo description) { |
| + var esc = const HtmlEscape().convert; |
| + if (description.presentation == 'code') { |
| + buffer.write(div(description.name, cls: 'kind') + |
| + code(esc(description.generatedCode))); |
| + } else { |
| + String kind = span(esc(description.kind), cls: 'kind'); |
| + String modifiers = span(esc(description.modifiers), cls: "modifiers"); |
| + String size = ''; |
| + if (description.size != null) { |
| + size = 'Size: ' + |
| + span('${description.size} bytes ' |
| + '(${description.size * 100 ~/ totalSize})%', |
| + cls: "size"); |
| + } |
| + String name = span(esc(description.name), cls: 'name'); |
| + String type = span(esc(description.type), cls: 'type'); |
| + String describe = [kind, modifiers, name, size, type].join(' '); |
| + |
| + if (description.contents != null) { |
| + buffer.write(div("+$describe", cls: "container")); |
| + String contents = "No Members"; |
| + buffer.write('<div class="contained">'); |
| + if (description.contents.isEmpty) { |
| + buffer.writeln("No members"); |
| + } |
| + for (Object subElementDescription in description.contents) { |
| + dumpElement(subElementDescription); |
| + } |
| + buffer.write("</div>"); |
| + } else { |
| + buffer.writeln(describe); |
| + } |
| + } |
| + } |
| + buffer.writeln(""" |
| + <html> |
| + <head> |
| + <title>Dart2JS compilation information</title> |
| + <style> |
| + div.show {display:block;} |
| + code {margin-left: 20px; display: block;} |
| + div.contained {margin-left: 20px;} |
| + div {margin-top:0px; |
| + margin-bottom: 0px; |
| + white-space: pre; /*border: 1px solid;*/} |
| + span.kind {font-weight:bold;} |
| + span.modifiers {font-weight:bold;} |
| + span.name {font-style:italic} |
| + span.type {color:blue;} |
| + </style> |
| + </head> |
| + <body> |
| + <h1>Dart2js compilation information</h1>"""); |
| + buffer.writeln(h2('Compilation took place: ' |
| + '${info.compilationMoment}')); |
| + buffer.writeln(h2('Compilation took: ' |
| + '${info.compilationDuration/1000000} seconds')); |
| + buffer.writeln(h2('Output size: ${info.size} bytes')); |
| + if (info.dart2jsVersion != null) { |
| + buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}')); |
| + } |
| + |
| + info.libraries.forEach(dumpElement); |
| + |
| + // TODO (sigurdm) this script should be written in dart |
|
kasperl
2013/11/29 12:03:13
Usually we format these TODO comment like this:
sigurdm
2013/12/02 14:09:50
Done.
|
| + buffer.writeln(r""" |
| + <script type="text/javascript"> |
| + function toggler(element) { return function(e) { |
|
kasperl
2013/11/29 12:03:13
Newline before return and more indentation.
sigurdm
2013/12/02 14:09:50
Done.
|
| + element.hidden = ! element.hidden; |
|
kasperl
2013/11/29 12:03:13
No space after !.
sigurdm
2013/12/02 14:09:50
Done.
|
| + console.log(element); |
|
kasperl
2013/11/29 12:03:13
Do you really want to log this to the console? Loo
sigurdm
2013/12/02 14:09:50
Done.
|
| + } |
| + } |
| + var containers = document.getElementsByClassName('container'); |
| + for(var i = 0; i < containers.length; i++) { |
|
kasperl
2013/11/29 12:03:13
for( -> for (
sigurdm
2013/12/02 14:09:50
Done.
|
| + var container = containers[i]; |
| + var hidden = 0; |
| + container.addEventListener('click', |
| + toggler(container.nextElementSibling), false); |
| + container.nextElementSibling.hidden=true; |
|
kasperl
2013/11/29 12:03:13
Spaces around =.
sigurdm
2013/12/02 14:09:50
Done.
|
| + }; |
| + </script> |
| + </body> |
| + </html>"""); |
| + } |
| +} |