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, | |
|
ahe
2013/12/02 15:20:26
Put show on previous line and indent by four.
sigurdm
2013/12/05 09:19:44
Done.
| |
| 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 { | |
|
ahe
2013/12/02 15:20:26
I'd put a line between each member.
sigurdm
2013/12/05 09:19:44
Done.
| |
| 39 final String name; | |
| 40 final String kind; | |
| 41 final String type; | |
| 42 final String modifiers; | |
| 43 final String generatedCode; | |
| 44 // How to present this piece of information | |
|
ahe
2013/12/02 15:20:26
What does this comment apply to? Is it a TODO?
sigurdm
2013/12/05 09:19:44
Done.
| |
| 45 // As "element" or "code" | |
|
ahe
2013/12/02 15:20:26
Is this a sentence by itself or a continuation of
| |
| 46 final String presentation; | |
| 47 final int size; // How many bytes does this take in the output | |
|
ahe
2013/12/02 15:20:26
Is this supposed to be a documentation comment?
sigurdm
2013/12/05 09:19:44
yes
| |
| 48 List<ElementInfo> contents; | |
| 49 ElementInfo({this.name: "", | |
| 50 this.kind: "", | |
| 51 this.type: "", | |
| 52 this.modifiers: "", | |
| 53 this.size, | |
| 54 this.generatedCode, | |
| 55 this.presentation: "element", | |
| 56 this.contents}); | |
| 57 } | |
| 58 | |
| 59 class ProgramInfo { | |
|
ahe
2013/12/02 15:20:26
I'd put a line between each member.
sigurdm
2013/12/05 09:19:44
Done.
| |
| 60 final String name; | |
| 61 final String presentation; | |
| 62 final List<ElementInfo> libraries; | |
| 63 final int size; // How many bytes is the output | |
|
ahe
2013/12/02 15:20:26
Documentation comment?
sigurdm
2013/12/05 09:19:44
Done.
| |
| 64 final DateTime compilationMoment; | |
| 65 final int compilationDuration; | |
| 66 final String dart2jsVersion; | |
| 67 | |
| 68 ProgramInfo({String this.name, | |
| 69 this.presentation, | |
| 70 this.libraries, | |
| 71 this.size, DateTime this.compilationMoment, | |
| 72 this.compilationDuration, String this.dart2jsVersion}); | |
| 73 } | |
| 74 | |
| 75 class InfoDumpVisitor extends ElementVisitor<ElementInfo> { | |
| 76 | |
|
ahe
2013/12/02 15:20:26
Extra line.
sigurdm
2013/12/05 09:19:44
Done.
| |
| 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", | |
| 84 token: element.position()); | |
| 85 } | |
| 86 | |
| 87 ElementInfo visitLibraryElement(LibraryElement element) { | |
| 88 List<ElementInfo> contents = new List<ElementInfo>(); | |
| 89 int size = compiler.dumpInfoTask.codeSizeCounter | |
| 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() == "" | |
| 100 ? "<unnamed>" | |
| 101 : element.getLibraryName(); | |
| 102 contents.sort((ElementInfo e1, ElementInfo e2) { | |
| 103 return e1.name.compareTo(e2.name); | |
| 104 }); | |
| 105 return new ElementInfo( | |
| 106 type: element.canonicalUri.toString(), | |
| 107 kind: "library", | |
| 108 name: nameString, | |
| 109 size: size, | |
| 110 modifiers: "", | |
| 111 contents: contents); | |
| 112 } | |
| 113 | |
| 114 ElementInfo visitTypedefElement(TypedefElement element) { | |
| 115 return element.thisType == null | |
| 116 ? null | |
| 117 : new ElementInfo( | |
| 118 type: element.thisType.toString(), | |
| 119 kind: "typedef", | |
| 120 name: element.name); | |
| 121 } | |
| 122 | |
| 123 ElementInfo visitVariableElement(VariableElement element) { | |
| 124 DartType type = element.computeType(compiler); | |
| 125 if (type == null) return null; | |
| 126 String modifiersString = element.modifiers.toString() == "" | |
| 127 ? "" | |
| 128 : element.modifiers.toString()+" "; | |
| 129 List inferredType = [new ElementInfo( | |
|
ahe
2013/12/02 15:20:26
I don't understand why an inferredType is a list.
sigurdm
2013/12/05 09:19:44
Done.
| |
| 130 kind: "inferred type", | |
| 131 name: "", | |
| 132 type: compiler.typesTask.getGuaranteedTypeOfElement(element).toString(), | |
| 133 modifiers: "")]; | |
| 134 return new ElementInfo( | |
| 135 kind: "field", | |
| 136 type: type.toString(), | |
| 137 name: element.name, | |
| 138 modifiers: modifiersString, | |
| 139 contents: inferredType); | |
| 140 } | |
| 141 | |
| 142 ElementInfo visitClassElement(ClassElement element) { | |
| 143 String modifiersString = element.modifiers.toString() == "" | |
| 144 ? "" | |
| 145 : element.modifiers.toString()+" "; | |
| 146 if (!element.isResolved) return null; | |
| 147 String supersString = element.allSupertypes == null ? "" : | |
| 148 "implements ${element.allSupertypes}"; | |
| 149 List contents = []; | |
| 150 element.forEachLocalMember((Element member) { | |
| 151 ElementInfo info = member.accept(this); | |
| 152 if (info != null) { | |
| 153 contents.add(info); | |
| 154 } | |
| 155 }); | |
| 156 if (contents.isEmpty) { | |
| 157 return null; | |
| 158 } | |
| 159 contents.sort((ElementInfo m1, ElementInfo m2) { | |
| 160 return m1.name.compareTo(m2.name); | |
| 161 }); | |
| 162 return new ElementInfo( | |
| 163 kind: "class", | |
| 164 name: element.name, | |
| 165 type: supersString, | |
| 166 modifiers: modifiersString, | |
| 167 contents: contents); | |
| 168 } | |
| 169 | |
| 170 ElementInfo visitFunctionElement(FunctionElement element) { | |
| 171 CodeBuffer emittedCode = compiler.backend.codeOf(element); | |
| 172 if (emittedCode == null) { | |
| 173 return null; | |
| 174 } | |
| 175 String modifiersString = element.modifiers.toString() == "" | |
| 176 ? "" | |
| 177 : element.modifiers.toString()+" "; | |
| 178 String kindString = "function"; | |
| 179 String nameString = element.name; | |
| 180 if (element.isConstructor()) { | |
| 181 nameString = element.name == "" ? "${element.enclosingElement.name}()" : | |
| 182 "${element.enclosingElement.name}.${element.name}"; | |
| 183 kindString = "constructor"; | |
| 184 } | |
| 185 List contents = []; | |
| 186 FunctionSignature signature = element.computeSignature(compiler); | |
| 187 signature.forEachParameter((parameter) { | |
| 188 contents.add(new ElementInfo( | |
| 189 kind: "inferred", | |
| 190 name: parameter.name, | |
| 191 modifiers: "parameter type", | |
| 192 type: compiler.typesTask | |
| 193 .getGuaranteedTypeOfElement(parameter).toString())); | |
| 194 }); | |
| 195 contents.add(new ElementInfo( | |
| 196 kind: "inferred", | |
| 197 modifiers: "return type", | |
| 198 type: compiler.typesTask | |
| 199 .getGuaranteedReturnTypeOfElement(element).toString())); | |
| 200 contents.add(new ElementInfo( | |
| 201 kind: "inferred", | |
| 202 modifiers: "side effects", | |
| 203 type: compiler.world.getSideEffectsOfElement(element).toString())); | |
| 204 contents.add(new ElementInfo( | |
| 205 name: "Generated code", | |
| 206 presentation: "code", | |
| 207 generatedCode: emittedCode.getText())); | |
| 208 return new ElementInfo( | |
| 209 type: element.type.toString(), | |
| 210 kind: kindString, | |
| 211 name: nameString, | |
| 212 size: emittedCode.length, | |
| 213 modifiers: modifiersString, | |
| 214 contents: contents); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 class DumpInfoTask extends CompilerTask { | |
| 219 DumpInfoTask(Compiler compiler) : | |
| 220 super(compiler), | |
| 221 infoDumpVisitor = new InfoDumpVisitor(compiler); | |
| 222 | |
| 223 String name = "Dump Info"; | |
| 224 | |
| 225 final CodeSizeCounter codeSizeCounter = new CodeSizeCounter(); | |
| 226 | |
| 227 final InfoDumpVisitor infoDumpVisitor; | |
| 228 | |
| 229 void dumpInfo() { | |
| 230 measure(() { | |
| 231 ProgramInfo info = collectDumpInfo(); | |
| 232 StringBuffer buffer = new StringBuffer(); | |
| 233 dumpInfoHtml(info, buffer); | |
| 234 compiler.outputProvider('', 'info.html') | |
| 235 ..add(buffer.toString()) | |
| 236 ..close(); | |
| 237 }); | |
| 238 } | |
| 239 | |
| 240 ProgramInfo collectDumpInfo() { | |
| 241 List<LibraryElement> sortedLibraries = compiler.libraries.values.toList(); | |
| 242 sortedLibraries.sort((LibraryElement l1, LibraryElement l2) { | |
| 243 if (l1.isPlatformLibrary && !l2.isPlatformLibrary) { | |
| 244 return 1; | |
| 245 } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) { | |
| 246 return -1; | |
| 247 } | |
| 248 return l1.getLibraryName().compareTo(l2.getLibraryName()); | |
| 249 }); | |
| 250 | |
| 251 List<ElementInfo> libraryInfos = new List<ElementInfo>(); | |
| 252 libraryInfos.addAll(sortedLibraries | |
| 253 .map((library) => infoDumpVisitor.visit(library)) | |
| 254 .where((library) => library != null)); | |
| 255 | |
| 256 return new ProgramInfo( | |
| 257 compilationDuration: compiler.totalCompileTime.elapsedTicks, | |
| 258 // TODO (sigurdm): Also count the size of deferred code | |
| 259 size: compiler.assembledCode.length, | |
| 260 libraries: libraryInfos, | |
| 261 compilationMoment: new DateTime.now(), | |
| 262 dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null); | |
| 263 } | |
| 264 | |
| 265 void dumpInfoHtml(ProgramInfo info, StringSink buffer) { | |
| 266 tag(String element) { | |
| 267 return (String content, {String cls}) { | |
| 268 String classString = cls == null ? '' : ' class="$cls"'; | |
| 269 return '<$element$classString>$content</$element>'; | |
| 270 }; | |
| 271 } | |
| 272 var div = tag('div'); | |
| 273 var span = tag('span'); | |
| 274 var code = tag('code'); | |
| 275 var h2 = tag('h2'); | |
| 276 int totalSize = info.size; | |
| 277 void dumpElement(ElementInfo description) { | |
| 278 var esc = const HtmlEscape().convert; | |
| 279 if (description.presentation == 'code') { | |
| 280 buffer.write(div(description.name, cls: 'kind') + | |
| 281 code(esc(description.generatedCode))); | |
| 282 } else { | |
| 283 String kind = span(esc(description.kind), cls: 'kind'); | |
| 284 String modifiers = span(esc(description.modifiers), cls: "modifiers"); | |
| 285 String size = ''; | |
| 286 if (description.size != null) { | |
| 287 size = 'Size: ' + | |
| 288 span('${description.size} bytes ' | |
| 289 '(${description.size * 100 ~/ totalSize})%', | |
| 290 cls: "size"); | |
| 291 } | |
| 292 String name = span(esc(description.name), cls: 'name'); | |
| 293 String type = span(esc(description.type), cls: 'type'); | |
| 294 String describe = [kind, modifiers, name, size, type].join(' '); | |
| 295 | |
| 296 if (description.contents != null) { | |
| 297 buffer.write(div("+$describe", cls: "container")); | |
| 298 String contents = "No Members"; | |
| 299 buffer.write('<div class="contained">'); | |
| 300 if (description.contents.isEmpty) { | |
| 301 buffer.writeln("No members"); | |
| 302 } | |
| 303 for (Object subElementDescription in description.contents) { | |
| 304 dumpElement(subElementDescription); | |
| 305 } | |
| 306 buffer.write("</div>"); | |
| 307 } else { | |
| 308 buffer.writeln(describe); | |
| 309 } | |
| 310 } | |
| 311 } | |
| 312 buffer.writeln(""" | |
| 313 <html> | |
| 314 <head> | |
| 315 <title>Dart2JS compilation information</title> | |
| 316 <style> | |
| 317 div.show {display:block;} | |
| 318 code {margin-left: 20px; display: block;} | |
| 319 div.contained {margin-left: 20px;} | |
| 320 div {margin-top:0px; | |
| 321 margin-bottom: 0px; | |
| 322 white-space: pre; /*border: 1px solid;*/} | |
| 323 span.kind {font-weight:bold;} | |
| 324 span.modifiers {font-weight:bold;} | |
| 325 span.name {font-style:italic} | |
| 326 span.type {color:blue;} | |
| 327 </style> | |
| 328 </head> | |
| 329 <body> | |
| 330 <h1>Dart2js compilation information</h1>"""); | |
| 331 buffer.writeln(h2('Compilation took place: ' | |
| 332 '${info.compilationMoment}')); | |
| 333 buffer.writeln(h2('Compilation took: ' | |
| 334 '${info.compilationDuration/1000000} seconds')); | |
| 335 buffer.writeln(h2('Output size: ${info.size} bytes')); | |
| 336 if (info.dart2jsVersion != null) { | |
| 337 buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}')); | |
| 338 } | |
| 339 | |
| 340 info.libraries.forEach(dumpElement); | |
| 341 | |
| 342 // TODO (sigurdm): This script should be written in dart | |
| 343 buffer.writeln(r""" | |
| 344 <script type="text/javascript"> | |
| 345 function toggler(element) { | |
| 346 return function(e) { | |
| 347 element.hidden = !element.hidden; | |
| 348 }; | |
| 349 } | |
| 350 var containers = document.getElementsByClassName('container'); | |
| 351 for (var i = 0; i < containers.length; i++) { | |
| 352 var container = containers[i]; | |
| 353 container.addEventListener('click', | |
| 354 toggler(container.nextElementSibling), false); | |
| 355 container.nextElementSibling.hidden = true; | |
| 356 }; | |
| 357 </script> | |
| 358 </body> | |
| 359 </html>"""); | |
| 360 } | |
| 361 } | |
| OLD | NEW |