Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/dump_info.dart

Issue 382063002: Redo "Information about which functions require other functions is gathered in the enqueuer." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: rebase Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dump_info; 5 library dump_info;
6 6
7 import 'dart:convert' show 7 import 'dart:convert' show
8 HtmlEscape, 8 HtmlEscape,
9 JsonEncoder, 9 JsonEncoder,
10 StringConversionSink, 10 StringConversionSink,
11 ChunkedConversionSink; 11 ChunkedConversionSink;
12 12
13 import 'elements/elements.dart'; 13 import 'elements/elements.dart';
14 import 'elements/visitor.dart'; 14 import 'elements/visitor.dart';
15 import 'dart2jslib.dart' show 15 import 'dart2jslib.dart' show
16 Compiler, 16 Compiler,
17 CompilerTask, 17 CompilerTask,
18 CodeBuffer; 18 CodeBuffer;
19 import 'dart_types.dart' show DartType; 19 import 'dart_types.dart' show DartType;
20 import 'types/types.dart' show TypeMask; 20 import 'types/types.dart' show TypeMask;
21 import 'util/util.dart' show modifiersToString; 21 import 'util/util.dart' show modifiersToString;
22 import 'deferred_load.dart' show OutputUnit; 22 import 'deferred_load.dart' show OutputUnit;
23 import 'js_backend/js_backend.dart' show JavaScriptBackend; 23 import 'js_backend/js_backend.dart' show JavaScriptBackend;
24 import 'js/js.dart' as jsAst; 24 import 'js/js.dart' as jsAst;
25 25 import 'compilation_info.dart' show CompilationInformation;
26 // TODO (sigurdm): A search function.
27 // TODO (sigurdm): Output size of classes.
28 // TODO (sigurdm): Print that we dumped the HTML-file.
29 // TODO (sigurdm): Include why a given element was included in the output.
30 // TODO (sigurdm): Include how much output grew because of mirror support.
31 // TODO (sigurdm): Write each function with parameter names.
32 // TODO (sigurdm): Write how much space the boilerplate takes.
33 // TODO (sigurdm): Include javascript names of entities in the output.
34
35 const List<String> COLORS = const [
36 "#fff",
37 "#8dd3c7",
38 "#ffffb3",
39 "#bebada",
40 "#fb8072",
41 "#80b1d3",
42 "#fdb462",
43 "#b3de69",
44 "#fccde5",
45 "#d9d9d9",
46 "#bc80bd",
47 "#ccebc5",
48 "#ffed6f"];
49 26
50 class CodeSizeCounter { 27 class CodeSizeCounter {
51 final Map<Element, int> generatedSize = new Map<Element, int>(); 28 final Map<Element, int> generatedSize = new Map<Element, int>();
52 29
53 int getGeneratedSizeOf(Element element) { 30 int getGeneratedSizeOf(Element element) {
54 int result = generatedSize[element]; 31 int result = generatedSize[element];
55 return result == null ? 0 : result; 32 return result == null ? 0: result;
56 } 33 }
57 34
58 void countCode(Element element, int added) { 35 void countCode(Element element, int added) {
59 int before = generatedSize.putIfAbsent(element, () => 0); 36 int before = generatedSize.putIfAbsent(element, () => 0);
60 generatedSize[element] = before + added; 37 generatedSize[element] = before + added;
61 } 38 }
62 } 39 }
63 40
64 tag(String element) { 41 /// Maps elements to an id. Supports lookups in
65 return (String content, {String cls}) { 42 /// both directions.
66 String classString = cls == null ? '' : ' class="$cls"'; 43 class ElementMapper {
67 return '<$element$classString>$content</$element>'; 44 Map<int, Element> _idToElement = {};
68 }; 45 Map<Element, int> _elementToId = {};
46 int _idCounter = 0;
47 String name;
48
49 ElementMapper(this.name);
50
51 String add(Element e) {
52 if (_elementToId.containsKey(e)) {
53 return name + "/${_elementToId[e]}";
54 }
55
56 _idToElement[_idCounter] = e;
57 _elementToId[e] = _idCounter;
58 _idCounter += 1;
59 return name + "/${_idCounter - 1}";
60 }
69 } 61 }
70 62
71 var div = tag('div'); 63 class DividedElementMapper {
72 var span = tag('span'); 64 // Mappers for specific kinds of elements.
73 var code = tag('code'); 65 ElementMapper _library = new ElementMapper('library');
74 var h2 = tag('h2'); 66 ElementMapper _typedef = new ElementMapper('typedef');
75 67 ElementMapper _field = new ElementMapper('field');
76 var esc = const HtmlEscape().convert; 68 ElementMapper _class = new ElementMapper('class');
77 69 ElementMapper _function = new ElementMapper('function');
78 String sizeDescription(int size, ProgramInfo programInfo) { 70
79 if (size == null) { 71 // Convert this database of elements into JSON for rendering
80 return ''; 72 Map<String, dynamic> _toJson(ElementToJsonVisitor elementToJson) {
81 } 73 Map<String, dynamic> json = {};
82 return span(span(size.toString(), cls: 'value') + 74 var m = [_library, _typedef, _field, _class, _function];
83 ' bytes (${size * 100 ~/ programInfo.size}%)', cls: 'size'); 75 for (ElementMapper mapper in m) {
76 Map<String, dynamic> innerMapper = {};
77 mapper._idToElement.forEach((k, v) {
78 // All these elements are already cached in the
79 // jsonCache, so this is just an access.
80 var elementJson = elementToJson.process(v);
81 if (elementJson != null) {
82 innerMapper["$k"] = elementJson;
83 }
84 });
85 json[mapper.name] = innerMapper;
86 }
87 return json;
88 }
84 } 89 }
85 90
86 String sizePercent(int size, ProgramInfo programInfo) { 91 class ElementToJsonVisitor extends ElementVisitor<Map<String, dynamic>> {
87 if (size == null) { 92 DividedElementMapper mapper = new DividedElementMapper();
88 return "0.000%"; 93 Compiler compiler;
89 } else { 94
90 return (100 * size / programInfo.size).toStringAsFixed(3) + "%"; 95 CompilationInformation compilationInfo;
91 } 96
92 } 97 Map<Element, Map<String, dynamic>> jsonCache = {};
93 98 Map<Element, jsAst.Expression> codeCache;
94 /// An [InfoNode] holds information about a part the program. 99
95 abstract class InfoNode { 100 int programSize;
96 String get name; 101 DateTime compilationMoment;
97 102 String dart2jsVersion;
98 int get size; 103 Duration compilationDuration;
99 104 Duration dumpInfoDuration;
100 void emitHtml(ProgramInfo programInfo, StringSink buffer, 105
101 [String indentation = '']); 106 ElementToJsonVisitor(Compiler compiler) {
102 107 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.
103 Map<String, dynamic> toJson(ProgramInfo programInfo); 108 this.compilationInfo = compiler.enqueuer.codegen.compilationInfo;
104 } 109
105 110 programSize = compiler.assembledCode.length;
106 /// An [ElementNode] holds information about an [Element] 111 compilationMoment = new DateTime.now();
107 class ElementInfoNode implements InfoNode { 112 dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null;
108 /// The name of the represented [Element]. 113 compilationDuration = compiler.totalCompileTime.elapsed;
109 final String name; 114
110 115 for (var library in compiler.libraryLoader.libraries.toList()) {
111 /// The kind of the [Element] represented. This is presented to the 116 library.accept(this);
112 /// user, so it might be more specific than [element.kind]. 117 }
113 final String kind; 118
114 119 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
115 /// The static type of the represented [Element]. 120 }
116 /// [:null:] if this kind of element has no type. 121
117 final String type; 122 // If keeping the element is in question (like if a function has a size
118 123 // of zero), only keep it if it holds dependencies to elsewhere.
119 /// Any extra information to display about the represented [Element]. 124 bool shouldKeep(Element element) {
120 final String extra; 125 return compilationInfo.relations['addsToWorklist'].containsKey(element) ||
121 126 compilationInfo.relations['enqueues'].containsKey(element);
122 /// A textual description of the modifiers (such as "static", "abstract") of 127 }
123 /// the represented [Element]. 128
124 final String modifiers; 129 Map<String, dynamic> toJson() {
125 130 return mapper._toJson(this);
126 /// Describes how many bytes the code for the represented [Element] takes up 131 }
127 /// in the output. 132
128 final int size; 133 // Memoization of the JSON creating process.
129 134 Map<String, dynamic> process(Element element) {
130 /// Subnodes containing more detailed information about the represented 135 return jsonCache.putIfAbsent(element, () => element.accept(this));
131 /// [Element], and its members. 136 }
132 List<InfoNode> contents; 137
133 138 Map<String, dynamic> visitElement(Element element) {
134 /// Subnodes containing more detailed information about the represented 139 return null;
135 /// [Element], and its members. 140 }
136 int outputUnitId; 141
137 142 Map<String, dynamic> visitConstructorBodyElement(ConstructorBodyElement e) {
138 ElementInfoNode({this.name: "", 143 return visitFunctionElement(e.constructor);
139 this.kind: "", 144 }
140 this.type, 145
141 this.modifiers: "", 146 Map<String, dynamic> visitLibraryElement(LibraryElement element) {
142 this.size, 147 var id = mapper._library.add(element);
143 this.contents, 148 List<String> children = <String>[];
144 this.extra: "", 149
145 this.outputUnitId}); 150 String libname = element.getLibraryName();
146 151 libname = libname == "" ? "<unnamed>" : libname;
147 Map<String, dynamic> toJson(ProgramInfo programInfo) { 152
148 Map<String, dynamic> json = <String, dynamic>{ 153 int size =
149 'kind': this.kind, 154 compiler.dumpInfoTask.codeSizeCounter.getGeneratedSizeOf(element);
150 'modifiers': this.modifiers, 155
151 'name': this.name, 156 LibraryElement contentsOfLibrary = element.isPatched
152 'type': this.type, 157 ? element.patch : element;
153 'size': this.size, 158 contentsOfLibrary.forEachLocalMember((Element member) {
154 'sizePercent': sizePercent(this.size, programInfo), 159 Map<String, dynamic> childJson = this.process(member);
155 'extra': this.extra 160 if (childJson == null) return;
161 children.add(childJson['id']);
162 });
163
164 if (children.length == 0 && !shouldKeep(element)) {
165 return null;
166 }
167
168 return {
169 'kind': 'library',
170 'name': libname,
171 'size': size,
172 'id': id,
173 'children': children
156 }; 174 };
157 175 }
158 if (this.contents != null) { 176
159 json['children'] = 177 Map<String, dynamic> visitTypedefElement(TypedefElement element) {
160 this.contents.map((c) => c.toJson(programInfo)).toList(); 178 String id = mapper._typedef.add(element);
161 } 179 return element.alias == null
162 180 ? null
163 return json; 181 : {
164 } 182 'id': id,
165 183 'type': element.alias.toString(),
166 void emitHtml(ProgramInfo programInfo, StringSink buffer, 184 'kind': 'typedef',
167 [String indentation = '']) { 185 'name': element.name
168 String kindString = span(esc(kind), cls: 'kind'); 186 };
169 String modifiersString = span(esc(modifiers), cls: "modifiers"); 187 }
170 188
171 String nameString = span(esc(name), cls: 'name'); 189 Map<String, dynamic> visitFieldElement(FieldElement element) {
172 String typeString = type == null 190 String id = mapper._field.add(element);
173 ? '' 191 List<String> children = [];
174 : span('/* ' + esc(type) + ' */', cls: 'type'); 192 CodeBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
175 String extraString = span(esc(extra), cls: 'type'); 193
176 String describe = [ 194 // If a field has an empty inferred type it is never used.
177 kindString, 195 TypeMask inferredType =
178 typeString, 196 compiler.typesTask.getGuaranteedTypeOfElement(element);
179 modifiersString, 197 if (inferredType == null || inferredType.isEmpty || element.isConst) {
180 nameString, 198 return null;
181 sizeDescription(size, programInfo), 199 }
182 extraString].join(' '); 200
183 201 int size = 0;
184 if (contents != null) { 202 String code;
185 String outputUnitClass = outputUnitId == null 203
186 ? "" 204 if (emittedCode != null) {
187 : " outputUnit${outputUnitId % COLORS.length}"; 205 size += emittedCode.length;
188 buffer.write(indentation); 206 code = emittedCode.getText();
189 buffer.write('<div class="container$outputUnitClass">\n'); 207 }
190 buffer.write('$indentation '); 208
191 buffer.write(div('+$describe', cls: "details")); 209 for (Element closure in element.nestedClosures) {
192 buffer.write('\n'); 210 var childJson = this.process(closure);
193 buffer.write('$indentation <div class="contents">'); 211 if (childJson != null) {
194 if (contents.isEmpty) { 212 children.add(childJson['id']);
195 buffer.write('No members</div>'); 213 if (childJson.containsKey('size')) {
196 } else { 214 size += childJson['size'];
197 buffer.write('\n');
198 for (InfoNode subElementDescription in contents) {
199 subElementDescription.emitHtml(programInfo, buffer,
200 indentation + ' ');
201 } 215 }
202 buffer.write("\n$indentation </div>");
203 } 216 }
204 buffer.write("\n$indentation</div>\n"); 217 }
205 } else { 218
206 buffer.writeln(div('$describe', cls: "element")); 219 return {
207 } 220 'id': id,
208 } 221 'kind': 'field',
209 } 222 'name': element.name,
210 223 'children': children,
211 /// A [CodeInfoNode] holds information about a piece of code. 224 'size': size,
212 class CodeInfoNode implements InfoNode { 225 'code': code
213 /// A short description of the code.
214 final String description;
215
216 final String generatedCode;
217
218 get size => generatedCode.length;
219
220 get name => "";
221
222 CodeInfoNode({this.description: "", this.generatedCode});
223
224 void emitHtml(ProgramInfo programInfo, StringBuffer buffer,
225 [String indentation = '']) {
226 buffer.write(indentation);
227 buffer.write(div(description + ' ' +
228 sizeDescription(generatedCode.length, programInfo),
229 cls: 'kind') +
230 code(esc(generatedCode)));
231 buffer.write('\n');
232 }
233
234 Map<String, dynamic> toJson(ProgramInfo programInfo) {
235 return <String, dynamic>{
236 'kind': 'code',
237 'description': description,
238 'code': generatedCode,
239 'size': generatedCode.length,
240 'sizePercent': sizePercent(generatedCode.length, programInfo)
241 }; 226 };
242 } 227 }
243 } 228
244 229 Map<String, dynamic> visitClassElement(ClassElement element) {
245 /// Instances represent information inferred about the program such as 230 String id = mapper._class.add(element);
246 /// inferred type information or inferred side effects. 231 List<String> children = [];
247 class InferredInfoNode implements InfoNode { 232
248 /// Text describing the represented information. 233 int size = compiler.dumpInfoTask.codeSizeCounter.getGeneratedSizeOf(element) ;
249 final String description; 234
250 235 // Omit element if it is not needed.
251 /// The name of the entity this information is inferred about (for example the
252 /// name of a parameter).
253 final String name;
254
255 /// The inferred type/side effect.
256 final String type;
257
258 get size => 0;
259
260 InferredInfoNode({this.name: "", this.description, this.type});
261
262 Map<String, dynamic> toJson(ProgramInfo programInfo) {
263 return <String, dynamic>{
264 'kind': 'inferred',
265 'name': name,
266 'type': type,
267 'desc': description
268 };
269 }
270
271 void emitHtml(ProgramInfo programInfo, StringBuffer buffer,
272 [String indentation = '']) {
273 buffer.write(indentation);
274 buffer.write(
275 div('${span("Inferred " + description, cls: "kind")} '
276 '${span(esc(name), cls: "name")} '
277 '${span(esc(type), cls: "type")} ',
278 cls: "attr"));
279 buffer.write('\n');
280 }
281 }
282
283 /// Instances represent information about a program.
284 class ProgramInfo {
285 /// A list of all the libraries in the program to show information about.
286 final List<InfoNode> libraries;
287
288 /// The size of the whole program in bytes.
289 final int size;
290
291 /// The time the compilation took place.
292 final DateTime compilationMoment;
293
294 /// The time the compilation took to complete.
295 final Duration compilationDuration;
296
297 /// The version of dart2js used to compile the program.
298 final String dart2jsVersion;
299
300 final Map<OutputUnit, int> outputUnitNumbering;
301
302 ProgramInfo({this.libraries,
303 this.size,
304 this.compilationMoment,
305 this.compilationDuration,
306 this.dart2jsVersion,
307 this.outputUnitNumbering: null});
308
309 Map<String, dynamic> toJson() {
310 return <String, dynamic>{
311 'program_size': size,
312 'compile_time': compilationMoment.toString(),
313 'compile_duration': compilationDuration.toString(),
314 'dart2js_version': dart2jsVersion
315 };
316 }
317 }
318
319 class InfoDumpVisitor extends ElementVisitor<InfoNode> {
320 final Compiler compiler;
321
322 /// Contains the elements visited on the path from the library to here.
323 final List<Element> stack = new List<Element>();
324
325 final Map<OutputUnit, int> outputUnitNumbering = new Map<OutputUnit, int>();
326
327 Element get currentElement => stack.last;
328
329 InfoDumpVisitor(Compiler this.compiler);
330
331 ProgramInfo collectDumpInfo() {
332 JavaScriptBackend backend = compiler.backend; 236 JavaScriptBackend backend = compiler.backend;
333 237 if (!backend.emitter.neededClasses.contains(element)) return null;
334 int counter = 0; 238 Map<String, dynamic> modifiers = { 'abstract': element.isAbstract };
335 for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { 239
336 outputUnitNumbering[outputUnit] = counter; 240 element.forEachLocalMember((Element member) {
337 counter += 1; 241 Map<String, dynamic> childJson = this.process(member);
338 } 242 if (childJson != null) {
339 243 children.add(childJson['id']);
340 List<LibraryElement> sortedLibraries =
341 compiler.libraryLoader.libraries.toList();
342 sortedLibraries.sort((LibraryElement l1, LibraryElement l2) {
343 if (l1.isPlatformLibrary && !l2.isPlatformLibrary) {
344 return 1;
345 } else if (!l1.isPlatformLibrary && l2.isPlatformLibrary) {
346 return -1;
347 }
348 return l1.getLibraryName().compareTo(l2.getLibraryName());
349 });
350
351 List<InfoNode> libraryInfos = new List<InfoNode>();
352 libraryInfos.addAll(sortedLibraries
353 .map((library) => visit(library))
354 .where((info) => info != null));
355
356 return new ProgramInfo(
357 compilationDuration: compiler.totalCompileTime.elapsed,
358 // TODO (sigurdm): Also count the size of deferred code
359 size: compiler.assembledCode.length,
360 libraries: libraryInfos,
361 compilationMoment: new DateTime.now(),
362 dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null,
363 outputUnitNumbering: outputUnitNumbering);
364 }
365
366 InfoNode visitElement(Element element) {
367 compiler.internalError(element,
368 "This element of kind ${element.kind} "
369 "does not support --dump-info");
370 return null;
371 }
372
373 InfoNode visitLibraryElement(LibraryElement element) {
374 List<InfoNode> contents = new List<InfoNode>();
375 int size = compiler.dumpInfoTask.codeSizeCounter
376 .getGeneratedSizeOf(element);
377 if (size == 0) return null;
378 stack.add(element);
379 // For some reason the patch library contains the origin libraries members,
380 // but the origin library does not contain the patch members.
381 LibraryElement contentsLibrary = element.isPatched
382 ? element.patch
383 : element;
384 contentsLibrary.forEachLocalMember((Element member) {
385 InfoNode info = member.accept(this);
386 if (info != null) {
387 contents.add(info);
388 } 244 }
389 }); 245 });
390 stack.removeLast(); 246
391 String nameString = element.getLibraryName() == "" 247 return {
392 ? "<unnamed>" 248 'name': element.name,
393 : element.getLibraryName(); 249 'size': size,
394 contents.sort((InfoNode e1, InfoNode e2) { 250 'kind': 'class',
395 return e1.name.compareTo(e2.name); 251 'modifiers': modifiers,
396 }); 252 'children': children,
397 return new ElementInfoNode( 253 'id': id
398 extra: "${element.canonicalUri}", 254 };
399 kind: "library", 255 }
400 name: nameString, 256
401 size: size, 257 Map<String, dynamic> visitFunctionElement(FunctionElement element) {
402 modifiers: "", 258 String id = mapper._function.add(element);
403 contents: contents); 259 String name = element.name;
404 } 260 String kind = "function";
405 261 List<String> children = [];
406 InfoNode visitTypedefElement(TypedefElement element) { 262 List<Map<String, dynamic>> parameters = [];
407 return element.alias == null 263 String returnType = null;
408 ? null 264 String sideEffects = null;
409 : new ElementInfoNode( 265 String code = "";
410 type: element.alias.toString(), 266
411 kind: "typedef",
412 name: element.name);
413 }
414
415 InfoNode visitFieldElement(FieldElement element) {
416 CodeBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
417 TypeMask inferredType = compiler.typesTask
418 .getGuaranteedTypeOfElement(element);
419 // If a field has an empty inferred type it is never used.
420 // Also constant fields do not get output as fields.
421 if (inferredType == null || inferredType.isEmpty || element.isConst) {
422 return null;
423 }
424 int size = 0;
425 DartType type = element.type;
426 List<InfoNode> contents = new List<InfoNode>();
427 if (emittedCode != null) {
428 contents.add(new CodeInfoNode(
429 description: "Generated initializer",
430 generatedCode: emittedCode.getText()));
431 size = emittedCode.length;
432 }
433 if (inferredType != null) {
434 contents.add(new InferredInfoNode(
435 description: "type",
436 type: inferredType.toString()));
437 stack.add(element);
438 }
439 for (Element closure in element.nestedClosures) {
440 InfoNode info = closure.accept(this);
441 if (info != null) {
442 contents.add(info);
443 size += info.size;
444 }
445 }
446 stack.removeLast();
447
448 return new ElementInfoNode(
449 kind: "field",
450 type: "$type",
451 name: element.name,
452 size: size,
453 modifiers: modifiersToString(isStatic: element.isStatic,
454 isFinal: element.isFinal,
455 isConst: element.isConst),
456 contents: contents,
457 outputUnitId: outputUnitId(element));
458 }
459
460 int outputUnitId(Element element) {
461 OutputUnit outputUnit =
462 compiler.deferredLoadTask.outputUnitForElement(element);
463 return outputUnitNumbering[outputUnit];
464 }
465
466 InfoNode visitClassElement(ClassElement element) {
467 // If the element is not emitted in the program, we omit it from the output.
468 JavaScriptBackend backend = compiler.backend;
469 if (!backend.emitter.neededClasses.contains(element)) return null;
470 String modifiersString = modifiersToString(isAbstract: element.isAbstract);
471 String supersString = element.allSupertypes == null ? "" :
472 "implements ${element.allSupertypes}";
473 List contents = [];
474 stack.add(element);
475 element.forEachLocalMember((Element member) {
476 InfoNode info = member.accept(this);
477 if (info != null) {
478 contents.add(info);
479 }
480 });
481 stack.removeLast();
482 contents.sort((InfoNode n1, InfoNode n2) {
483 return n1.name.compareTo(n2.name);
484 });
485 return new ElementInfoNode(
486 kind: "class",
487 name: element.name,
488 extra: supersString,
489 modifiers: modifiersString,
490 contents: contents,
491 outputUnitId: outputUnitId(element));
492 }
493
494 InfoNode visitFunctionElement(FunctionElement element) {
495 CodeBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); 267 CodeBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
496 int size = 0; 268 int size = 0;
497 String nameString = element.name; 269
498 String modifiersString = modifiersToString( 270 Map<String, dynamic> modifiers = {
499 isStatic: element.isStatic, 271 'static': element.isStatic,
500 isConst: element.isConst, 272 'const': element.isConst,
501 isFactory: element.isFactoryConstructor, 273 'factory': element.isFactoryConstructor,
502 isExternal: element.isPatched); 274 'external': element.isPatched
503 String kindString = "function"; 275 };
504 if (currentElement.isClass) { 276
505 kindString = "method"; 277 var enclosingElement = element.enclosingElement;
506 } else if (currentElement.isField || 278 if (enclosingElement.isField ||
507 currentElement.isFunction || 279 enclosingElement.isFunction ||
508 currentElement.isConstructor) { 280 element.isClosure ||
509 kindString = "closure"; 281 enclosingElement.isConstructor) {
510 nameString = "<unnamed>"; 282 kind = "closure";
511 } 283 name = "<unnamed>";
284 } else if (enclosingElement.isClass) {
285 kind = 'method';
286 }
287
512 if (element.isConstructor) { 288 if (element.isConstructor) {
513 nameString = element.name == "" 289 name == ""
514 ? "${element.enclosingClass.name}" 290 ? "${element.enclosingElement.name}"
515 : "${element.enclosingClass.name}.${element.name}"; 291 : "${element.enclosingElement.name}.${element.name}";
516 kindString = "constructor"; 292 kind = "constructor";
517 } 293 }
518 List contents = []; 294
519 if (emittedCode != null) { 295 if (emittedCode != null) {
520 FunctionSignature signature = element.functionSignature; 296 FunctionSignature signature = element.functionSignature;
521 signature.forEachParameter((parameter) { 297 signature.forEachParameter((parameter) {
522 contents.add(new InferredInfoNode( 298 parameters.add({
523 description: "parameter", 299 'name': parameter.name,
524 name: parameter.name, 300 'type': compiler.typesTask
525 type: compiler.typesTask 301 .getGuaranteedTypeOfElement(parameter).toString()
526 .getGuaranteedTypeOfElement(parameter).toString())); 302 });
527 }); 303 });
528 contents.add(new InferredInfoNode( 304 returnType = compiler.typesTask
529 description: "return type", 305 .getGuaranteedReturnTypeOfElement(element).toString();
530 type: compiler.typesTask 306 sideEffects = compiler.world.getSideEffectsOfElement(element).toString();
531 .getGuaranteedReturnTypeOfElement(element).toString())); 307 code = emittedCode.getText();
532 contents.add(new InferredInfoNode( 308 size += code.length;
533 description: "side effects", 309 }
534 type: compiler.world 310
535 .getSideEffectsOfElement(element).toString()));
536 contents.add(new CodeInfoNode(
537 description: "Generated code",
538 generatedCode: emittedCode.getText()));
539 size += emittedCode.length;
540 }
541 stack.add(element);
542 for (Element closure in element.nestedClosures) { 311 for (Element closure in element.nestedClosures) {
543 InfoNode info = closure.accept(this); 312 Map<String, dynamic> child = this.process(closure);
544 if (info != null) { 313 if (child != null) {
545 contents.add(info); 314 children.add(child['id']);
546 size += info.size; 315 size += child['size'];
547 } 316 }
548 } 317 }
549 stack.removeLast(); 318
550 if (size == 0) { 319 if (size == 0 && !shouldKeep(element)) {
551 return null; 320 return null;
552 } 321 }
553 322
554 return new ElementInfoNode( 323 return {
555 type: element.computeType(compiler).toString(), 324 'kind': kind,
556 kind: kindString, 325 'name': name,
557 name: nameString, 326 'id': id,
558 size: size, 327 'modifiers': modifiers,
559 modifiers: modifiersString, 328 'children': children,
560 contents: contents, 329 'size': size,
561 outputUnitId: outputUnitId(element)); 330 'returnType': returnType,
331 'parameters': parameters,
332 'sideEffects': sideEffects,
333 'code': code,
334 'type': element.computeType(compiler).toString()
335 };
562 } 336 }
563 } 337 }
564 338
339
565 class DumpInfoTask extends CompilerTask { 340 class DumpInfoTask extends CompilerTask {
566 DumpInfoTask(Compiler compiler) 341 DumpInfoTask(Compiler compiler)
567 : infoDumpVisitor = new InfoDumpVisitor(compiler), 342 : super(compiler);
568 super(compiler);
569 343
570 String name = "Dump Info"; 344 String name = "Dump Info";
571 345
572 final CodeSizeCounter codeSizeCounter = new CodeSizeCounter(); 346 final CodeSizeCounter codeSizeCounter = new CodeSizeCounter();
573 347
574 final InfoDumpVisitor infoDumpVisitor; 348 ElementToJsonVisitor infoCollector;
575 349
576 final Map<Element, jsAst.Expression>_generatedCode = 350 final Map<Element, jsAst.Expression> _generatedCode = {};
577 new Map<Element, jsAst.Expression>(); 351
578
579 /// Registers that [code] has been generated for [element] so that it can be
580 /// emitted in the info.html.
581 void registerGeneratedCode(Element element, jsAst.Expression code) { 352 void registerGeneratedCode(Element element, jsAst.Expression code) {
582 if (compiler.dumpInfo) { 353 if (compiler.dumpInfo) {
583 _generatedCode[element] = code; 354 _generatedCode[element] = code;
584 } 355 }
585 } 356 }
586 357
587 CodeBuffer codeOf(Element element) { 358
588 jsAst.Expression code = _generatedCode[element]; 359 void collectInfo() {
589 return code != null 360 infoCollector = new ElementToJsonVisitor(compiler);
590 ? jsAst.prettyPrint(code, compiler)
591 : compiler.backend.codeOf(element);
592 } 361 }
593 362
594 void dumpInfo() { 363 void dumpInfo() {
595 measure(() { 364 measure(() {
596 ProgramInfo info = infoDumpVisitor.collectDumpInfo(); 365 if (infoCollector == null) {
597 366 collectInfo();
598 StringBuffer htmlBuffer = new StringBuffer(); 367 }
599 dumpInfoHtml(info, htmlBuffer);
600 compiler.outputProvider('', 'info.html')
601 ..add(htmlBuffer.toString())
602 ..close();
603 368
604 StringBuffer jsonBuffer = new StringBuffer(); 369 StringBuffer jsonBuffer = new StringBuffer();
605 dumpInfoJson(info, jsonBuffer); 370 dumpInfoJson(jsonBuffer);
606 compiler.outputProvider('', 'info.json') 371 compiler.outputProvider('', 'info.json')
607 ..add(jsonBuffer.toString()) 372 ..add(jsonBuffer.toString())
608 ..close(); 373 ..close();
609 }); 374 });
610 } 375 }
611 376
612 void dumpInfoJson(ProgramInfo info, StringSink buffer) { 377 CodeBuffer codeOf(Element element) {
613 Map<String, dynamic> entire = <String, dynamic>{ 378 jsAst.Expression code = _generatedCode[element];
614 'program': info.toJson(), 379 return code != null
615 'libs': info.libraries.map((lib) => lib.toJson(info)).toList() 380 ? jsAst.prettyPrint(code, compiler)
616 }; 381 : compiler.backend.codeOf(element);
617 382 }
383
384 void dumpInfoJson(StringSink buffer) {
618 JsonEncoder encoder = const JsonEncoder(); 385 JsonEncoder encoder = const JsonEncoder();
386
387 // `A` uses and depends on the functions `Bs`.
388 // A Bs
389 Map<String, List<String>> holding = <String, List<String>>{};
390
391 DateTime startToJsonTime = new DateTime.now();
392
393 CompilationInformation compilationInfo =
394 infoCollector.compiler.enqueuer.codegen.compilationInfo;
395 var relations = compilationInfo.relations;
396 relations['addsToWorklist'].forEach((func, deps) {
397 if (func != null) {
398 var funcJson = infoCollector.process(func);
399 if (funcJson != null) {
400 var funcId = funcJson['id'];
401
402 List<String> heldList = <String>[];
403
404 for (var held in deps) {
405 // "process" to get the ids of the elements.
406 var heldJson = infoCollector.process(held);
407 if (heldJson != null) {
408 var heldId = heldJson['id'];
409 heldList.add(heldId);
410 }
411 }
412 holding[funcId] = heldList;
413 }
414 }
415 });
416
417 Map<String, dynamic> outJson = {};
418 outJson['elements'] = infoCollector.toJson();
419 outJson['holding'] = holding;
420 outJson['dump_version'] = 1;
421
422 Duration toJsonDuration = new DateTime.now().difference(startToJsonTime);
423
424 Map<String, dynamic> generalProgramInfo = <String, dynamic>{};
425 generalProgramInfo['size'] = infoCollector.programSize;
426 generalProgramInfo['dart2jsVersion'] = infoCollector.dart2jsVersion;
427 generalProgramInfo['compilationMoment'] = infoCollector.compilationMoment.to String();
428 generalProgramInfo['compilationDuration'] = infoCollector.compilationDuratio n.toString();
429 generalProgramInfo['toJsonDuration'] = toJsonDuration.toString();
430 generalProgramInfo['dumpInfoDuration'] = infoCollector.dumpInfoDuration.toSt ring();
431
432 outJson['program'] = generalProgramInfo;
433
619 ChunkedConversionSink<Object> sink = 434 ChunkedConversionSink<Object> sink =
620 encoder.startChunkedConversion( 435 encoder.startChunkedConversion(
621 new StringConversionSink.fromStringSink(buffer)); 436 new StringConversionSink.fromStringSink(buffer));
622 sink.add(entire); 437 sink.add(outJson);
623 }
624
625 void dumpInfoHtml(ProgramInfo info, StringSink buffer) {
626 int totalSize = info.size;
627
628 buffer.writeln("""
629 <html>
630 <head>
631 <title>Dart2JS compilation information</title>
632 <style>
633 code {margin-left: 20px; display: block; white-space: pre; }
634 div.container, div.contained, div.element, div.attr {
635 margin-top:0px;
636 margin-bottom: 0px;
637 }
638 div.container, div.element, div.attr {
639 white-space: nowrap;
640 }
641 .contents {
642 margin-left: 20px;
643 }
644 div.contained {margin-left: 20px;}
645 div {/*border: 1px solid;*/}
646 span.kind {}
647 span.modifiers {font-weight:bold;}
648 span.name {font-weight:bold; font-family: monospace;}
649 span.type {font-family: monospace; color:blue;}
650 """);
651 for (int i = 0; i < COLORS.length; i++) {
652 buffer.writeln(" .outputUnit$i "
653 "{border-left: 4px solid ${COLORS[i]}}");
654 }
655 buffer.writeln("""
656 </style>
657 </head>
658 <body>
659 <h1>Dart2js compilation information</h1>""");
660 if (info.outputUnitNumbering.length > 1) {
661 for (OutputUnit outputUnit in info.outputUnitNumbering.keys) {
662 String color = COLORS[info.outputUnitNumbering[outputUnit]
663 % COLORS.length];
664 JavaScriptBackend backend = compiler.backend;
665 int size = backend.emitter.outputBuffers[outputUnit].length;
666 buffer.writeln('<div style='
667 '"background:$color;">'
668 '${outputUnit.partFileName(compiler)} $size bytes</div>');
669 }
670 }
671 buffer.writeln(h2('Compilation took place: '
672 '${info.compilationMoment}'));
673 buffer.writeln(h2('Compilation took: '
674 '${info.compilationDuration.inSeconds} seconds'));
675 buffer.writeln(h2('Output size: ${info.size} bytes'));
676 if (info.dart2jsVersion != null) {
677 buffer.writeln(h2('Dart2js version: ${info.dart2jsVersion}'));
678 }
679
680 buffer.writeln('<a href="#" class="sort_by_size">Sort by size</a>\n');
681
682 buffer.writeln('<div class="contents">');
683 info.libraries.forEach((InfoNode node) {
684 node.emitHtml(info, buffer);
685 });
686 buffer.writeln('</div>');
687
688 // TODO (sigurdm): This script should be written in dart
689 buffer.writeln(r"""
690 <script type="text/javascript">
691 function toggler(element) {
692 return function(e) {
693 element.hidden = !element.hidden;
694 };
695 }
696 var containers = document.getElementsByClassName('container');
697 for (var i = 0; i < containers.length; i++) {
698 var container = containers[i];
699 container.querySelector('.details').addEventListener('click',
700 toggler(container.querySelector('.contents')), false);
701 container.querySelector('.contents').hidden = true;
702 }
703
704 function sortBySize() {
705 var toSort = document.querySelectorAll('.contents');
706 for (var i = 0; i < toSort.length; ++i) {
707 sortNodes(toSort[i], function(a, b) {
708 if (a[1] !== b[1]) {
709 return a[1] > b[1] ? -1 : 1;
710 }
711 return a[2] === b[2] ? 0 : a[2] > b[2] ? 1 : -1;
712 });
713 }
714 }
715
716 function findSize(node) {
717 var size = 0;
718 var details = node.querySelector('.details');
719 if (details) {
720 var sizeElement = details.querySelector('.size');
721 if (sizeElement) {
722 size = parseInt(sizeElement.textContent);
723 } else {
724 // For classes, sum up the contents for sorting purposes.
725 var kind = details.querySelector('.kind');
726 if (kind && kind.textContent === 'class') {
727 var contents = node.querySelector('.contents');
728 if (contents) {
729 var child = contents.firstElementChild;
730 while (child) {
731 size += findSize(child);
732 child = child.nextElementSibling;
733 }
734 }
735 }
736 }
737 }
738 return size;
739 }
740
741 function findName(node) {
742 var name = '';
743 var nameNode = node.querySelector('.name');
744 if (nameNode) {
745 return nameNode.textContent;
746 }
747 return node.textContent;
748 }
749 function sortNodes(node, fn) {
750 var items = [];
751 var child = node.firstElementChild;
752 while (child) {
753 items.push([child, findSize(child), findName(child)]);
754 child = child.nextElementSibling;
755 }
756 items.sort(fn);
757 for (var i = 0; i < items.length; ++i) {
758 node.appendChild(items[i][0]);
759 }
760 }
761 document.querySelector('.sort_by_size').addEventListener('click',
762 function() {
763 sortBySize();
764 }, false);
765 </script>
766 </body>
767 </html>""");
768 } 438 }
769 } 439 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698