OLD | NEW |
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, SpannableAssertionFailure; |
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 import 'compilation_info.dart' show CompilationInformation; | 25 import 'universe/universe.dart' show Selector; |
26 | 26 |
27 /// Maps elements to an id. Supports lookups in | 27 /// Maps elements to an id. Supports lookups in |
28 /// both directions. | 28 /// both directions. |
29 class ElementMapper { | 29 class ElementMapper { |
30 Map<int, Element> _idToElement = {}; | 30 Map<int, Element> _idToElement = {}; |
31 Map<Element, int> _elementToId = {}; | 31 Map<Element, int> _elementToId = {}; |
32 int _idCounter = 0; | 32 int _idCounter = 0; |
33 String name; | 33 String name; |
34 | 34 |
35 ElementMapper(this.name); | 35 ElementMapper(this.name); |
36 | 36 |
| 37 Iterable<Element> get elements => _elementToId.keys; |
| 38 |
37 String add(Element e) { | 39 String add(Element e) { |
38 if (_elementToId.containsKey(e)) { | 40 if (_elementToId.containsKey(e)) { |
39 return name + "/${_elementToId[e]}"; | 41 return name + "/${_elementToId[e]}"; |
40 } | 42 } |
41 | 43 |
42 _idToElement[_idCounter] = e; | 44 _idToElement[_idCounter] = e; |
43 _elementToId[e] = _idCounter; | 45 _elementToId[e] = _idCounter; |
44 _idCounter += 1; | 46 _idCounter += 1; |
45 return name + "/${_idCounter - 1}"; | 47 return name + "/${_idCounter - 1}"; |
46 } | 48 } |
47 } | 49 } |
48 | 50 |
49 class DividedElementMapper { | 51 class DividedElementMapper { |
50 // Mappers for specific kinds of elements. | 52 // Mappers for specific kinds of elements. |
51 ElementMapper _library = new ElementMapper('library'); | 53 ElementMapper _library = new ElementMapper('library'); |
52 ElementMapper _typedef = new ElementMapper('typedef'); | 54 ElementMapper _typedef = new ElementMapper('typedef'); |
53 ElementMapper _field = new ElementMapper('field'); | 55 ElementMapper _field = new ElementMapper('field'); |
54 ElementMapper _class = new ElementMapper('class'); | 56 ElementMapper _class = new ElementMapper('class'); |
55 ElementMapper _function = new ElementMapper('function'); | 57 ElementMapper _function = new ElementMapper('function'); |
56 | 58 |
| 59 Iterable<Element> get functions => _function.elements; |
| 60 |
57 // Convert this database of elements into JSON for rendering | 61 // Convert this database of elements into JSON for rendering |
58 Map<String, dynamic> _toJson(ElementToJsonVisitor elementToJson) { | 62 Map<String, dynamic> _toJson(ElementToJsonVisitor elementToJson) { |
59 Map<String, dynamic> json = {}; | 63 Map<String, dynamic> json = {}; |
60 var m = [_library, _typedef, _field, _class, _function]; | 64 var m = [_library, _typedef, _field, _class, _function]; |
61 for (ElementMapper mapper in m) { | 65 for (ElementMapper mapper in m) { |
62 Map<String, dynamic> innerMapper = {}; | 66 Map<String, dynamic> innerMapper = {}; |
63 mapper._idToElement.forEach((k, v) { | 67 mapper._idToElement.forEach((k, v) { |
64 // All these elements are already cached in the | 68 // All these elements are already cached in the |
65 // jsonCache, so this is just an access. | 69 // jsonCache, so this is just an access. |
66 var elementJson = elementToJson.process(v); | 70 var elementJson = elementToJson.process(v); |
67 if (elementJson != null) { | 71 if (elementJson != null) { |
68 innerMapper["$k"] = elementJson; | 72 innerMapper["$k"] = elementJson; |
69 } | 73 } |
70 }); | 74 }); |
71 json[mapper.name] = innerMapper; | 75 json[mapper.name] = innerMapper; |
72 } | 76 } |
73 return json; | 77 return json; |
74 } | 78 } |
75 } | 79 } |
76 | 80 |
77 class ElementToJsonVisitor extends ElementVisitor<Map<String, dynamic>> { | 81 class ElementToJsonVisitor extends ElementVisitor<Map<String, dynamic>> { |
78 DividedElementMapper mapper = new DividedElementMapper(); | 82 DividedElementMapper mapper = new DividedElementMapper(); |
79 Compiler compiler; | 83 Compiler compiler; |
80 | 84 |
81 CompilationInformation compilationInfo; | |
82 | |
83 Map<Element, Map<String, dynamic>> jsonCache = {}; | 85 Map<Element, Map<String, dynamic>> jsonCache = {}; |
84 Map<Element, jsAst.Expression> codeCache; | 86 Map<Element, jsAst.Expression> codeCache; |
85 | 87 |
86 int programSize; | 88 int programSize; |
87 DateTime compilationMoment; | 89 DateTime compilationMoment; |
88 String dart2jsVersion; | 90 String dart2jsVersion; |
89 Duration compilationDuration; | 91 Duration compilationDuration; |
90 Duration dumpInfoDuration; | 92 Duration dumpInfoDuration; |
91 | 93 |
92 ElementToJsonVisitor(Compiler compiler) { | 94 ElementToJsonVisitor(Compiler compiler) { |
93 this.compiler = compiler; | 95 this.compiler = compiler; |
94 this.compilationInfo = compiler.enqueuer.codegen.compilationInfo; | |
95 | 96 |
96 programSize = compiler.assembledCode.length; | 97 programSize = compiler.assembledCode.length; |
97 compilationMoment = new DateTime.now(); | 98 compilationMoment = new DateTime.now(); |
98 dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null; | 99 dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null; |
99 compilationDuration = compiler.totalCompileTime.elapsed; | 100 compilationDuration = compiler.totalCompileTime.elapsed; |
100 | 101 |
101 for (var library in compiler.libraryLoader.libraries.toList()) { | 102 for (var library in compiler.libraryLoader.libraries.toList()) { |
102 library.accept(this); | 103 library.accept(this); |
103 } | 104 } |
104 | 105 |
105 dumpInfoDuration = new DateTime.now().difference(compilationMoment); | 106 dumpInfoDuration = new DateTime.now().difference(compilationMoment); |
106 } | 107 } |
107 | 108 |
108 // If keeping the element is in question (like if a function has a size | 109 // If keeping the element is in question (like if a function has a size |
109 // of zero), only keep it if it holds dependencies to elsewhere. | 110 // of zero), only keep it if it holds dependencies to elsewhere. |
110 bool shouldKeep(Element element) { | 111 bool shouldKeep(Element element) { |
111 return compilationInfo.addsToWorkListMap.containsKey(element) || | 112 return compiler.dumpInfoTask.selectorsFromElement.containsKey(element); |
112 compilationInfo.enqueuesMap.containsKey(element); | |
113 } | 113 } |
114 | 114 |
115 Map<String, dynamic> toJson() { | 115 Map<String, dynamic> toJson() { |
116 return mapper._toJson(this); | 116 return mapper._toJson(this); |
117 } | 117 } |
118 | 118 |
119 // Memoization of the JSON creating process. | 119 // Memoization of the JSON creating process. |
120 Map<String, dynamic> process(Element element) { | 120 Map<String, dynamic> process(Element element) { |
121 return jsonCache.putIfAbsent(element, () => element.accept(this)); | 121 return jsonCache.putIfAbsent(element, () => element.accept(this)); |
122 } | 122 } |
123 | 123 |
| 124 // Returns the id of an [element] if it has already been processed. |
| 125 // If the element has not been processed, this function does not |
| 126 // process it, and simply returns null instead. |
| 127 String idOf(Element element) { |
| 128 if (jsonCache.containsKey(element) && jsonCache[element] != null) { |
| 129 return jsonCache[element]['id']; |
| 130 } else { |
| 131 return null; |
| 132 } |
| 133 } |
| 134 |
124 Map<String, dynamic> visitElement(Element element) { | 135 Map<String, dynamic> visitElement(Element element) { |
125 return null; | 136 return null; |
126 } | 137 } |
127 | 138 |
128 Map<String, dynamic> visitConstructorBodyElement(ConstructorBodyElement e) { | 139 Map<String, dynamic> visitConstructorBodyElement(ConstructorBodyElement e) { |
129 return visitFunctionElement(e.constructor); | 140 return visitFunctionElement(e.constructor); |
130 } | 141 } |
131 | 142 |
132 Map<String, dynamic> visitLibraryElement(LibraryElement element) { | 143 Map<String, dynamic> visitLibraryElement(LibraryElement element) { |
133 var id = mapper._library.add(element); | 144 var id = mapper._library.add(element); |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 final Set<jsAst.Node> _tracking = new Set<jsAst.Node>(); | 352 final Set<jsAst.Node> _tracking = new Set<jsAst.Node>(); |
342 // A mapping from Dart Elements to Javascript AST Nodes. | 353 // A mapping from Dart Elements to Javascript AST Nodes. |
343 final Map<Element, List<jsAst.Node>> _elementToNodes = | 354 final Map<Element, List<jsAst.Node>> _elementToNodes = |
344 <Element, List<jsAst.Node>>{}; | 355 <Element, List<jsAst.Node>>{}; |
345 // A mapping from Javascript AST Nodes to the size of their | 356 // A mapping from Javascript AST Nodes to the size of their |
346 // pretty-printed contents. | 357 // pretty-printed contents. |
347 final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{}; | 358 final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{}; |
348 final Map<jsAst.Node, int> _nodeBeforeSize = <jsAst.Node, int>{}; | 359 final Map<jsAst.Node, int> _nodeBeforeSize = <jsAst.Node, int>{}; |
349 final Map<Element, int> _fieldNameToSize = <Element, int>{}; | 360 final Map<Element, int> _fieldNameToSize = <Element, int>{}; |
350 | 361 |
| 362 final Map<Element, Set<Selector>> selectorsFromElement = {}; |
| 363 |
| 364 /** |
| 365 * Registers that a function uses a selector in the |
| 366 * function body |
| 367 */ |
| 368 void elementUsesSelector(Element element, Selector selector) { |
| 369 if (compiler.dumpInfo) { |
| 370 selectorsFromElement |
| 371 .putIfAbsent(element, () => new Set<Selector>()) |
| 372 .add(selector); |
| 373 } |
| 374 } |
| 375 |
| 376 /** |
| 377 * Returns an iterable of [Element]s that are used by |
| 378 * [element]. |
| 379 */ |
| 380 Iterable<Element> getRetaining(Element element) { |
| 381 if (!selectorsFromElement.containsKey(element)) { |
| 382 return const <Element>[]; |
| 383 } else { |
| 384 return selectorsFromElement[element].expand( |
| 385 (s) => compiler.world.allFunctions.filter(s)); |
| 386 } |
| 387 } |
| 388 |
351 /** | 389 /** |
352 * A callback that can be called before a jsAst [node] is | 390 * A callback that can be called before a jsAst [node] is |
353 * pretty-printed. The size of the code buffer ([aftersize]) | 391 * pretty-printed. The size of the code buffer ([aftersize]) |
354 * is also passed. | 392 * is also passed. |
355 */ | 393 */ |
356 void enteringAst(jsAst.Node node, int beforeSize) { | 394 void enteringAst(jsAst.Node node, int beforeSize) { |
357 if (isTracking(node)) { | 395 if (isTracking(node)) { |
358 _nodeBeforeSize[node] = beforeSize; | 396 _nodeBeforeSize[node] = beforeSize; |
359 } | 397 } |
360 } | 398 } |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 dumpInfoJson(jsonBuffer); | 494 dumpInfoJson(jsonBuffer); |
457 compiler.outputProvider('', 'info.json') | 495 compiler.outputProvider('', 'info.json') |
458 ..add(jsonBuffer.toString()) | 496 ..add(jsonBuffer.toString()) |
459 ..close(); | 497 ..close(); |
460 }); | 498 }); |
461 } | 499 } |
462 | 500 |
463 | 501 |
464 void dumpInfoJson(StringSink buffer) { | 502 void dumpInfoJson(StringSink buffer) { |
465 JsonEncoder encoder = const JsonEncoder(); | 503 JsonEncoder encoder = const JsonEncoder(); |
466 | |
467 // `A` uses and depends on the functions `Bs`. | |
468 // A Bs | |
469 Map<String, List<String>> holding = <String, List<String>>{}; | |
470 | |
471 DateTime startToJsonTime = new DateTime.now(); | 504 DateTime startToJsonTime = new DateTime.now(); |
472 | 505 |
473 CompilationInformation compilationInfo = | 506 Map<String, List<String>> holding = <String, List<String>>{}; |
474 infoCollector.compiler.enqueuer.codegen.compilationInfo; | 507 for (Element fn in infoCollector.mapper.functions) { |
475 compilationInfo.addsToWorkListMap.forEach((func, deps) { | 508 Iterable<Element> pulling = getRetaining(fn); |
476 if (func != null) { | 509 // Don't bother recording an empty list of dependencies. |
477 var funcJson = infoCollector.process(func); | 510 if (pulling.length > 0) { |
478 if (funcJson != null) { | 511 String fnId = infoCollector.idOf(fn); |
479 var funcId = funcJson['id']; | 512 // Some dart2js builtin functions are not |
480 | 513 // recorded. Don't register these. |
481 List<String> heldList = <String>[]; | 514 if (fnId != null) { |
482 | 515 holding[fnId] = pulling |
483 for (var held in deps) { | 516 .map((a) => infoCollector.idOf(a)) |
484 // "process" to get the ids of the elements. | 517 // Filter non-null ids for the same reason as above. |
485 var heldJson = infoCollector.process(held); | 518 .where((a) => a != null) |
486 if (heldJson != null) { | 519 .toList(); |
487 var heldId = heldJson['id']; | |
488 heldList.add(heldId); | |
489 } | |
490 } | |
491 holding[funcId] = heldList; | |
492 } | 520 } |
493 } | 521 } |
494 }); | 522 } |
495 | 523 |
496 Map<String, dynamic> outJson = { | 524 Map<String, dynamic> outJson = { |
497 'elements': infoCollector.toJson(), | 525 'elements': infoCollector.toJson(), |
498 'holding': holding, | 526 'holding': holding, |
499 'dump_version': 1, | 527 'dump_version': 1, |
500 }; | 528 }; |
501 | 529 |
502 Duration toJsonDuration = new DateTime.now().difference(startToJsonTime); | 530 Duration toJsonDuration = new DateTime.now().difference(startToJsonTime); |
503 | 531 |
504 Map<String, dynamic> generalProgramInfo = <String, dynamic> { | 532 Map<String, dynamic> generalProgramInfo = <String, dynamic> { |
505 'size': infoCollector.programSize, | 533 'size': infoCollector.programSize, |
506 'dart2jsVersion': infoCollector.dart2jsVersion, | 534 'dart2jsVersion': infoCollector.dart2jsVersion, |
507 'compilationMoment': infoCollector.compilationMoment.toString(), | 535 'compilationMoment': infoCollector.compilationMoment.toString(), |
508 'compilationDuration': infoCollector.compilationDuration.toString(), | 536 'compilationDuration': infoCollector.compilationDuration.toString(), |
509 'toJsonDuration': toJsonDuration.toString(), | 537 'toJsonDuration': toJsonDuration.toString(), |
510 'dumpInfoDuration': infoCollector.dumpInfoDuration.toString(), | 538 'dumpInfoDuration': infoCollector.dumpInfoDuration.toString(), |
511 'noSuchMethodEnabled': compiler.enabledNoSuchMethod | 539 'noSuchMethodEnabled': compiler.enabledNoSuchMethod |
512 }; | 540 }; |
513 | 541 |
514 outJson['program'] = generalProgramInfo; | 542 outJson['program'] = generalProgramInfo; |
515 | 543 |
516 ChunkedConversionSink<Object> sink = | 544 ChunkedConversionSink<Object> sink = |
517 encoder.startChunkedConversion( | 545 encoder.startChunkedConversion( |
518 new StringConversionSink.fromStringSink(buffer)); | 546 new StringConversionSink.fromStringSink(buffer)); |
519 sink.add(outJson); | 547 sink.add(outJson); |
520 } | 548 } |
521 } | 549 } |
OLD | NEW |