Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library dump_types; | |
| 6 | |
| 7 import 'elements/elements.dart'; | |
| 8 import 'elements/visitor.dart'; | |
| 9 import 'dart:convert' show HtmlEscape; | |
| 10 import 'dart2jslib.dart' | |
| 11 show Compiler, | |
| 12 CompilerTask, | |
| 13 CodeBuffer; | |
| 14 import 'dart_types.dart' show DartType; | |
| 15 | |
| 16 // TODO (sigurdm) A search function. | |
| 17 // TODO (sigurdm) Output size of classes. | |
| 18 // TODO (sigurdm) Print that we dumped the HTML-file. | |
| 19 // TODO (sigurdm) Include why a given element was included in the output. | |
| 20 // TODO (sigurdm) Include how much output grew because of mirror support. | |
| 21 // TODO (sigurdm) Write each function with parameter names. | |
| 22 // TODO (sigurdm) Write how much space the boilerplate takes. | |
| 23 | |
| 24 class CodeSizeCounter { | |
| 25 final Map<Element, int> generatedSize = new Map<Element, int>(); | |
| 26 | |
| 27 int getGeneratedSizeOf(Element element) { | |
| 28 int result = generatedSize[element]; | |
| 29 return result == null ? 0 : result; | |
| 30 } | |
| 31 | |
| 32 void countCode(Element element, int added) { | |
| 33 int before = generatedSize.putIfAbsent(element, () => 0); | |
| 34 generatedSize[element] = before + added; | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 class ElementInfo { | |
| 39 String name; | |
|
kasperl
2013/11/29 12:03:13
Can some of these fields be final?
sigurdm
2013/12/02 14:09:50
all
| |
| 40 String kind; | |
| 41 String type; | |
| 42 String modifiers; | |
| 43 String generatedCode; | |
| 44 // How to present this piece of information | |
| 45 // As "element" or "code" | |
| 46 String presentation; | |
| 47 int size; // How many bytes does this take in the output | |
| 48 List<ElementInfo> contents; | |
| 49 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!
| |
| 50 String this.kind: "", | |
| 51 String this.type: "", | |
| 52 String this.modifiers: "", | |
| 53 int this.size, | |
| 54 String this.generatedCode, | |
| 55 String this.presentation: "element", | |
| 56 List<ElementInfo> this.contents}); | |
| 57 } | |
| 58 | |
| 59 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.
| |
| 60 String name; | |
| 61 String presentation; | |
| 62 List<ElementInfo> libraries; | |
| 63 int size; // How many bytes is the output | |
| 64 DateTime compilationMoment; | |
| 65 int compilationDuration; | |
| 66 String dart2jsVersion; | |
| 67 | |
| 68 ProgramInfo({String this.name, | |
| 69 String this.presentation, | |
| 70 List<ElementInfo> this.libraries, | |
| 71 int this.size, DateTime this.compilationMoment, | |
| 72 int this.compilationDuration, String this.dart2jsVersion}); | |
| 73 } | |
| 74 | |
| 75 class InfoDumpVisitor extends ElementVisitor<ElementInfo> { | |
| 76 | |
| 77 Compiler compiler; | |
| 78 | |
| 79 InfoDumpVisitor(Compiler this.compiler); | |
| 80 | |
| 81 ElementInfo visitElement(Element element) { | |
| 82 compiler.internalError("This element of kind ${element.kind} " | |
| 83 "does not support dumping of types", | |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
| |
| 84 token: element.position()); | |
| 85 } | |
| 86 | |
| 87 ElementInfo visitLibraryElement(LibraryElement element) { | |
| 88 List<ElementInfo> contents = new List<ElementInfo>(); | |
| 89 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
| |
| 90 .getGeneratedSizeOf(element); | |
| 91 if (size == 0) return null; | |
| 92 element.forEachLocalMember((Element member) { | |
| 93 ElementInfo info = member.accept(this); | |
| 94 if (info != null) { | |
| 95 contents.add(info); | |
| 96 } | |
| 97 }); | |
| 98 | |
| 99 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.
| |
| 100 "<unnamed>" : element.getLibraryName(); | |
| 101 contents.sort((ElementInfo m1, ElementInfo m2) { | |
| 102 return m1.name.compareTo(m2.name); | |
| 103 }); | |
| 104 return new ElementInfo( | |
| 105 type: element.canonicalUri.toString(), | |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
| |
| 106 kind: "library", | |
| 107 name: nameString, | |
| 108 size: size, | |
| 109 modifiers: "", | |
| 110 contents: contents); | |
| 111 } | |
| 112 | |
| 113 ElementInfo visitTypedefElement(TypedefElement element) { | |
| 114 return element.thisType == null ? null : | |
| 115 new ElementInfo( | |
| 116 type: element.thisType.toString(), | |
| 117 kind: "typedef", | |
| 118 name: element.name); | |
| 119 } | |
| 120 | |
| 121 ElementInfo visitVariableElement(VariableElement element) { | |
| 122 DartType type = element.computeType(compiler); | |
| 123 if (type == null) return null; | |
| 124 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.
| |
| 125 "" : element.modifiers.toString()+" "; | |
| 126 List inferredType = [new ElementInfo( | |
| 127 kind: "inferred type", | |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
| |
| 128 name: "", | |
| 129 type: compiler.typesTask.getGuaranteedTypeOfElement(element).toString(), | |
| 130 modifiers: "")]; | |
| 131 return new ElementInfo( | |
| 132 kind: "field", | |
|
kasperl
2013/11/29 12:03:13
4 space indent.
sigurdm
2013/12/02 14:09:50
Done.
| |
| 133 type: type.toString(), | |
| 134 name: element.name, | |
| 135 modifiers: modifiersString, | |
| 136 contents: inferredType); | |
| 137 } | |
| 138 | |
| 139 ElementInfo visitClassElement(ClassElement element) { | |
| 140 String modifiersString = element.modifiers.toString() == "" ? | |
| 141 "" : element.modifiers.toString()+" "; | |
| 142 if (!element.isResolved) return null; | |
| 143 String supersString = element.allSupertypes == null ? "" : | |
| 144 "implements ${element.allSupertypes}"; | |
| 145 List contents = []; | |
| 146 element.forEachLocalMember((Element member) { | |
| 147 ElementInfo info = member.accept(this); | |
| 148 if (info != null) { | |
| 149 contents.add(info); | |
| 150 } | |
| 151 }); | |
| 152 if (contents.isEmpty) { | |
| 153 return null; | |
| 154 } | |
| 155 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
| |
| 156 return m1.name.compareTo(m2.name); | |
| 157 }); | |
| 158 return new ElementInfo( | |
| 159 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
| |
| 160 name: element.name, | |
| 161 type: supersString, | |
| 162 modifiers: modifiersString, | |
| 163 contents: contents); | |
| 164 } | |
| 165 | |
| 166 ElementInfo visitFunctionElement(FunctionElement element) { | |
| 167 CodeBuffer emittedCode = compiler.backend.codeOf(element); | |
| 168 if (emittedCode == null) { | |
| 169 return null; | |
| 170 } | |
| 171 String modifiersString = element.modifiers.toString() == "" ? | |
| 172 "" : element.modifiers.toString()+" "; | |
| 173 String kindString = "function"; | |
| 174 String nameString = element.name; | |
| 175 if (element.isConstructor()) { | |
| 176 nameString = element.name == "" ? "${element.enclosingElement.name}()" : | |
| 177 "${element.enclosingElement.name}.${element.name}"; | |
| 178 kindString = "constructor"; | |
| 179 } | |
| 180 List contents = []; | |
| 181 FunctionSignature signature = element.computeSignature(compiler); | |
| 182 signature.forEachParameter((parameter) { | |
| 183 contents.add(new ElementInfo( | |
| 184 kind: "inferred", | |
| 185 name: parameter.name, | |
| 186 modifiers: "parameter type", | |
| 187 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
| |
| 188 .getGuaranteedTypeOfElement(parameter).toString())); | |
| 189 }); | |
| 190 contents.add(new ElementInfo( | |
| 191 kind: "inferred", | |
| 192 modifiers: "return type", | |
| 193 type: compiler.typesTask | |
| 194 .getGuaranteedReturnTypeOfElement(element).toString())); | |
| 195 contents.add(new ElementInfo( | |
| 196 kind: "inferred", | |
| 197 modifiers: "side effects", | |
| 198 type: compiler.world.getSideEffectsOfElement(element).toString())); | |
| 199 contents.add(new ElementInfo( | |
| 200 name: "Generated code", | |
| 201 presentation: "code", | |
| 202 generatedCode: emittedCode.getText())); | |
| 203 return new ElementInfo( | |
| 204 type: element.type.toString(), | |
| 205 kind: kindString, | |
| 206 name: nameString, | |
| 207 size: emittedCode.length, | |
| 208 modifiers: modifiersString, | |
| 209 contents: contents); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 class DumpInfoTask extends CompilerTask { | |
| 214 DumpInfoTask(Compiler compiler) : | |
| 215 super(compiler), | |
| 216 infoDumpVisitor = new InfoDumpVisitor(compiler); | |
| 217 | |
| 218 String name = "Dump Info"; | |
| 219 | |
| 220 final CodeSizeCounter codeSizeCounter = new CodeSizeCounter(); | |
| 221 | |
| 222 final InfoDumpVisitor infoDumpVisitor; | |
| 223 | |
| 224 void dumpInfo() { | |
| 225 measure(() { | |
| 226 ProgramInfo info = collectDumpInfo(); | |
| 227 StringBuffer buffer = new StringBuffer(); | |
| 228 dumpInfoHtml(info, buffer); | |
| 229 compiler.outputProvider('', 'info.html') | |
| 230 ..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.
| |
| 231 ..close(); | |
| 232 }); | |
| 233 } | |
| 234 | |
| 235 ProgramInfo collectDumpInfo() { | |
| 236 List<LibraryElement> sortedLibraries = compiler.libraries.values.toList(); | |
| 237 sortedLibraries.sort((LibraryElement l1, LibraryElement l2) { | |
| 238 if (l1.isPlatformLibrary && !l2.isPlatformLibrary) { | |
| 239 return 1; | |
| 240 } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) { | |
| 241 return -1; | |
| 242 } | |
| 243 return l1.getLibraryName().compareTo(l2.getLibraryName()); | |
| 244 }); | |
| 245 | |
| 246 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.
| |
| 247 .map((library) => infoDumpVisitor.visit(library)) | |
| 248 .where((library) => library != null)); | |
| 249 | |
| 250 return new ProgramInfo( | |
| 251 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.
| |
| 252 // TODO (sigurdm) Also count the size of deferred code | |
| 253 size: compiler.assembledCode.length, | |
| 254 libraries: libraryInfos, | |
| 255 compilationMoment: new DateTime.now(), | |
| 256 dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null); | |
| 257 } | |
| 258 | |
| 259 void dumpInfoHtml(ProgramInfo info, StringSink buffer) { | |
| 260 tag(String element) { | |
| 261 return (String content, {String cls}) { | |
| 262 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.
| |
| 263 '$content</$element>'; | |
| 264 }; | |
| 265 } | |
| 266 var div = tag('div'); | |
| 267 var span = tag('span'); | |
| 268 var code = tag('code'); | |
| 269 var h2 = tag('h2'); | |
| 270 int totalSize = info.size; | |
| 271 void dumpElement(ElementInfo description) { | |
| 272 var esc = const HtmlEscape().convert; | |
| 273 if (description.presentation == 'code') { | |
| 274 buffer.write(div(description.name, cls: 'kind') + | |
| 275 code(esc(description.generatedCode))); | |
| 276 } else { | |
| 277 String kind = span(esc(description.kind), cls: 'kind'); | |
| 278 String modifiers = span(esc(description.modifiers), cls: "modifiers"); | |
| 279 String size = ''; | |
| 280 if (description.size != null) { | |
| 281 size = 'Size: ' + | |
| 282 span('${description.size} bytes ' | |
| 283 '(${description.size * 100 ~/ totalSize})%', | |
| 284 cls: "size"); | |
| 285 } | |
| 286 String name = span(esc(description.name), cls: 'name'); | |
| 287 String type = span(esc(description.type), cls: 'type'); | |
| 288 String describe = [kind, modifiers, name, size, type].join(' '); | |
| 289 | |
| 290 if (description.contents != null) { | |
| 291 buffer.write(div("+$describe", cls: "container")); | |
| 292 String contents = "No Members"; | |
| 293 buffer.write('<div class="contained">'); | |
| 294 if (description.contents.isEmpty) { | |
| 295 buffer.writeln("No members"); | |
| 296 } | |
| 297 for (Object subElementDescription in description.contents) { | |
| 298 dumpElement(subElementDescription); | |
| 299 } | |
| 300 buffer.write("</div>"); | |
| 301 } else { | |
| 302 buffer.writeln(describe); | |
| 303 } | |
| 304 } | |
| 305 } | |
| 306 buffer.writeln(""" | |
| 307 <html> | |
| 308 <head> | |
| 309 <title>Dart2JS compilation information</title> | |
| 310 <style> | |
| 311 div.show {display:block;} | |
| 312 code {margin-left: 20px; display: block;} | |
| 313 div.contained {margin-left: 20px;} | |
| 314 div {margin-top:0px; | |
| 315 margin-bottom: 0px; | |
| 316 white-space: pre; /*border: 1px solid;*/} | |
| 317 span.kind {font-weight:bold;} | |
| 318 span.modifiers {font-weight:bold;} | |
| 319 span.name {font-style:italic} | |
| 320 span.type {color:blue;} | |
| 321 </style> | |
| 322 </head> | |
| 323 <body> | |
| 324 <h1>Dart2js compilation information</h1>"""); | |
| 325 buffer.writeln(h2('Compilation took place: ' | |
| 326 '${info.compilationMoment}')); | |
| 327 buffer.writeln(h2('Compilation took: ' | |
| 328 '${info.compilationDuration/1000000} seconds')); | |
| 329 buffer.writeln(h2('Output size: ${info.size} bytes')); | |
| 330 if (info.dart2jsVersion != null) { | |
| 331 buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}')); | |
| 332 } | |
| 333 | |
| 334 info.libraries.forEach(dumpElement); | |
| 335 | |
| 336 // 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.
| |
| 337 buffer.writeln(r""" | |
| 338 <script type="text/javascript"> | |
| 339 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.
| |
| 340 element.hidden = ! element.hidden; | |
|
kasperl
2013/11/29 12:03:13
No space after !.
sigurdm
2013/12/02 14:09:50
Done.
| |
| 341 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.
| |
| 342 } | |
| 343 } | |
| 344 var containers = document.getElementsByClassName('container'); | |
| 345 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.
| |
| 346 var container = containers[i]; | |
| 347 var hidden = 0; | |
| 348 container.addEventListener('click', | |
| 349 toggler(container.nextElementSibling), false); | |
| 350 container.nextElementSibling.hidden=true; | |
|
kasperl
2013/11/29 12:03:13
Spaces around =.
sigurdm
2013/12/02 14:09:50
Done.
| |
| 351 }; | |
| 352 </script> | |
| 353 </body> | |
| 354 </html>"""); | |
| 355 } | |
| 356 } | |
| OLD | NEW |