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

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

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
(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_info;
6
7 import 'dart:convert' show
8 HtmlEscape,
9 JsonEncoder,
10 StringConversionSink,
11 ChunkedConversionSink;
12
13 import 'elements/elements.dart';
14 import 'elements/visitor.dart';
15 import 'dart2jslib.dart' show
16 Backend,
17 CodeBuffer,
18 Compiler,
19 CompilerTask,
20 MessageKind;
21 import 'types/types.dart' show TypeMask;
22 import 'deferred_load.dart' show OutputUnit;
23 import 'js_backend/js_backend.dart' show JavaScriptBackend;
24 import 'js/js.dart' as jsAst;
25 import 'universe/universe.dart' show Selector;
26 import 'util/util.dart' show NO_LOCATION_SPANNABLE;
27
28 /// Maps objects to an id. Supports lookups in
29 /// both directions.
30 class IdMapper<T>{
31 Map<int, T> _idToElement = {};
32 Map<T, int> _elementToId = {};
33 int _idCounter = 0;
34 String name;
35
36 IdMapper(this.name);
37
38 Iterable<T> get elements => _elementToId.keys;
39
40 String add(T e) {
41 if (_elementToId.containsKey(e)) {
42 return name + "/${_elementToId[e]}";
43 }
44
45 _idToElement[_idCounter] = e;
46 _elementToId[e] = _idCounter;
47 _idCounter += 1;
48 return name + "/${_idCounter - 1}";
49 }
50 }
51
52 class GroupedIdMapper {
53 // Mappers for specific kinds of elements.
54 IdMapper<LibraryElement> _library = new IdMapper('library');
55 IdMapper<TypedefElement> _typedef = new IdMapper('typedef');
56 IdMapper<FieldElement> _field = new IdMapper('field');
57 IdMapper<ClassElement> _class = new IdMapper('class');
58 IdMapper<FunctionElement> _function = new IdMapper('function');
59 IdMapper<OutputUnit> _outputUnit = new IdMapper('outputUnit');
60
61 Iterable<Element> get functions => _function.elements;
62
63 // Convert this database of elements into JSON for rendering
64 Map<String, dynamic> _toJson(ElementToJsonVisitor elementToJson) {
65 Map<String, dynamic> json = {};
66 var m = [_library, _typedef, _field, _class, _function];
67 for (IdMapper mapper in m) {
68 Map<String, dynamic> innerMapper = {};
69 mapper._idToElement.forEach((k, v) {
70 // All these elements are already cached in the
71 // jsonCache, so this is just an access.
72 var elementJson = elementToJson.process(v);
73 if (elementJson != null) {
74 innerMapper["$k"] = elementJson;
75 }
76 });
77 json[mapper.name] = innerMapper;
78 }
79 return json;
80 }
81 }
82
83 class ElementToJsonVisitor extends ElementVisitor<Map<String, dynamic>> {
84 final GroupedIdMapper mapper = new GroupedIdMapper();
85 final Compiler compiler;
86
87 final Map<Element, Map<String, dynamic>> jsonCache = {};
88
89 int programSize;
90 String dart2jsVersion;
91
92 ElementToJsonVisitor(this.compiler);
93
94 void run() {
95 Backend backend = compiler.backend;
96 if (backend is JavaScriptBackend) {
97 // Add up the sizes of all output-buffers.
98 programSize = backend.emitter.oldEmitter.outputBuffers.values.fold(0,
99 (a, b) => a + b.length);
100 } else {
101 programSize = compiler.assembledCode.length;
102 }
103
104 dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null;
105
106 for (var library in compiler.libraryLoader.libraries.toList()) {
107 library.accept(this);
108 }
109 }
110
111 // If keeping the element is in question (like if a function has a size
112 // of zero), only keep it if it holds dependencies to elsewhere.
113 bool shouldKeep(Element element) {
114 return compiler.dumpInfoTask.selectorsFromElement.containsKey(element)
115 || compiler.dumpInfoTask.inlineCount.containsKey(element);
116 }
117
118 Map<String, dynamic> toJson() {
119 return mapper._toJson(this);
120 }
121
122 // Memoization of the JSON creating process.
123 Map<String, dynamic> process(Element element) {
124 return jsonCache.putIfAbsent(element, () => element.accept(this));
125 }
126
127 // Returns the id of an [element] if it has already been processed.
128 // If the element has not been processed, this function does not
129 // process it, and simply returns null instead.
130 String idOf(Element element) {
131 if (jsonCache.containsKey(element) && jsonCache[element] != null) {
132 return jsonCache[element]['id'];
133 } else {
134 return null;
135 }
136 }
137
138 Map<String, dynamic> visitElement(Element element) {
139 return null;
140 }
141
142 Map<String, dynamic> visitConstructorBodyElement(ConstructorBodyElement e) {
143 return visitFunctionElement(e.constructor);
144 }
145
146 Map<String, dynamic> visitLibraryElement(LibraryElement element) {
147 var id = mapper._library.add(element);
148 List<String> children = <String>[];
149
150 String libname = element.getLibraryName();
151 libname = libname == "" ? "<unnamed>" : libname;
152
153 int size = compiler.dumpInfoTask.sizeOf(element);
154
155 LibraryElement contentsOfLibrary = element.isPatched
156 ? element.patch : element;
157 contentsOfLibrary.forEachLocalMember((Element member) {
158 Map<String, dynamic> childJson = this.process(member);
159 if (childJson == null) return;
160 children.add(childJson['id']);
161 });
162
163 if (children.length == 0 && !shouldKeep(element)) {
164 return null;
165 }
166
167 return {
168 'kind': 'library',
169 'name': libname,
170 'size': size,
171 'id': id,
172 'children': children
173 };
174 }
175
176 Map<String, dynamic> visitTypedefElement(TypedefElement element) {
177 String id = mapper._typedef.add(element);
178 return element.alias == null
179 ? null
180 : {
181 'id': id,
182 'type': element.alias.toString(),
183 'kind': 'typedef',
184 'name': element.name
185 };
186 }
187
188 Map<String, dynamic> visitFieldElement(FieldElement element) {
189 String id = mapper._field.add(element);
190 List<String> children = [];
191 StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
192
193 TypeMask inferredType =
194 compiler.typesTask.getGuaranteedTypeOfElement(element);
195 // If a field has an empty inferred type it is never used.
196 if (inferredType == null || inferredType.isEmpty || element.isConst) {
197 return null;
198 }
199
200 int size = compiler.dumpInfoTask.sizeOf(element);
201 String code;
202
203 if (emittedCode != null) {
204 size += emittedCode.length;
205 code = emittedCode.toString();
206 }
207
208 for (Element closure in element.nestedClosures) {
209 var childJson = this.process(closure);
210 if (childJson != null) {
211 children.add(childJson['id']);
212 if (childJson.containsKey('size')) {
213 size += childJson['size'];
214 }
215 }
216 }
217
218 OutputUnit outputUnit =
219 compiler.deferredLoadTask.outputUnitForElement(element);
220
221 return {
222 'id': id,
223 'kind': 'field',
224 'type': element.type.toString(),
225 'inferredType': inferredType.toString(),
226 'name': element.name,
227 'children': children,
228 'size': size,
229 'code': code,
230 'outputUnit': mapper._outputUnit.add(outputUnit)
231 };
232 }
233
234 Map<String, dynamic> visitClassElement(ClassElement element) {
235 String id = mapper._class.add(element);
236 List<String> children = [];
237
238 int size = compiler.dumpInfoTask.sizeOf(element);
239 JavaScriptBackend backend = compiler.backend;
240
241 Map<String, dynamic> modifiers = { 'abstract': element.isAbstract };
242
243 element.forEachLocalMember((Element member) {
244 Map<String, dynamic> childJson = this.process(member);
245 if (childJson != null) {
246 children.add(childJson['id']);
247
248 // Closures are placed in the library namespace, but
249 // we want to attribute them to a function, and by
250 // extension, this class. Process and add the sizes
251 // here.
252 if (member is MemberElement) {
253 for (Element closure in member.nestedClosures) {
254 Map<String, dynamic> child = this.process(closure);
255
256 // Look for the parent element of this closure which should
257 // be a class. If it exists, set the display name to
258 // the name of the class + the name of the closure function.
259 Element parent = closure.enclosingElement;
260 Map<String, dynamic> processedParent = this.process(parent);
261 if (processedParent != null) {
262 child['name'] = "${processedParent['name']}.${child['name']}";
263 }
264
265 if (child != null) {
266 size += child['size'];
267 }
268 }
269 }
270 }
271 });
272
273 // Omit element if it is not needed.
274 if (!backend.emitter.neededClasses.contains(element) &&
275 children.length == 0) {
276 return null;
277 }
278
279 OutputUnit outputUnit =
280 compiler.deferredLoadTask.outputUnitForElement(element);
281
282 return {
283 'name': element.name,
284 'size': size,
285 'kind': 'class',
286 'modifiers': modifiers,
287 'children': children,
288 'id': id,
289 'outputUnit': mapper._outputUnit.add(outputUnit)
290 };
291 }
292
293 Map<String, dynamic> visitFunctionElement(FunctionElement element) {
294 String id = mapper._function.add(element);
295 String name = element.name;
296 String kind = "function";
297 List<String> children = [];
298 List<Map<String, dynamic>> parameters = [];
299 String inferredReturnType = null;
300 String returnType = null;
301 String sideEffects = null;
302 String code = "";
303
304 StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
305 int size = compiler.dumpInfoTask.sizeOf(element);
306
307 Map<String, dynamic> modifiers = {
308 'static': element.isStatic,
309 'const': element.isConst,
310 'factory': element.isFactoryConstructor,
311 'external': element.isPatched
312 };
313
314 var enclosingElement = element.enclosingElement;
315 if (enclosingElement.isField ||
316 enclosingElement.isFunction ||
317 element.isClosure ||
318 enclosingElement.isConstructor) {
319 kind = "closure";
320 name = "<unnamed>";
321 } else if (modifiers['static']) {
322 kind = 'function';
323 } else if (enclosingElement.isClass) {
324 kind = 'method';
325 }
326
327 if (element.isConstructor) {
328 name == ""
329 ? "${element.enclosingElement.name}"
330 : "${element.enclosingElement.name}.${element.name}";
331 kind = "constructor";
332 }
333
334 if (emittedCode != null) {
335 FunctionSignature signature = element.functionSignature;
336 returnType = signature.type.returnType.toString();
337 signature.forEachParameter((parameter) {
338 parameters.add({
339 'name': parameter.name,
340 'type': compiler.typesTask
341 .getGuaranteedTypeOfElement(parameter).toString(),
342 'declaredType': parameter.node.type.toString()
343 });
344 });
345 inferredReturnType = compiler.typesTask
346 .getGuaranteedReturnTypeOfElement(element).toString();
347 sideEffects = compiler.world.getSideEffectsOfElement(element).toString();
348 code = emittedCode.toString();
349 }
350
351 if (element is MemberElement) {
352 MemberElement member = element as MemberElement;
353 for (Element closure in member.nestedClosures) {
354 Map<String, dynamic> child = this.process(closure);
355 if (child != null) {
356 child['kind'] = 'closure';
357 children.add(child['id']);
358 size += child['size'];
359 }
360 }
361 }
362
363 if (size == 0 && !shouldKeep(element)) {
364 return null;
365 }
366
367 int inlinedCount = compiler.dumpInfoTask.inlineCount[element];
368 if (inlinedCount == null) {
369 inlinedCount = 0;
370 }
371
372 OutputUnit outputUnit =
373 compiler.deferredLoadTask.outputUnitForElement(element);
374
375 return {
376 'kind': kind,
377 'name': name,
378 'id': id,
379 'modifiers': modifiers,
380 'children': children,
381 'size': size,
382 'returnType': returnType,
383 'inferredReturnType': inferredReturnType,
384 'parameters': parameters,
385 'sideEffects': sideEffects,
386 'inlinedCount': inlinedCount,
387 'code': code,
388 'type': element.type.toString(),
389 'outputUnit': mapper._outputUnit.add(outputUnit)
390 };
391 }
392 }
393
394 class Selection {
395 final Element selectedElement;
396 final Selector selector;
397 Selection(this.selectedElement, this.selector);
398 }
399
400 class DumpInfoTask extends CompilerTask {
401 DumpInfoTask(Compiler compiler)
402 : super(compiler);
403
404 String name = "Dump Info";
405
406 ElementToJsonVisitor infoCollector;
407
408 // A set of javascript AST nodes that we care about the size of.
409 // This set is automatically populated when registerElementAst()
410 // is called.
411 final Set<jsAst.Node> _tracking = new Set<jsAst.Node>();
412 // A mapping from Dart Elements to Javascript AST Nodes.
413 final Map<Element, List<jsAst.Node>> _elementToNodes =
414 <Element, List<jsAst.Node>>{};
415 // A mapping from Javascript AST Nodes to the size of their
416 // pretty-printed contents.
417 final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{};
418 final Map<jsAst.Node, int> _nodeBeforeSize = <jsAst.Node, int>{};
419 final Map<Element, int> _fieldNameToSize = <Element, int>{};
420
421 final Map<Element, Set<Selector>> selectorsFromElement = {};
422 final Map<Element, int> inlineCount = <Element, int>{};
423 // A mapping from an element to a list of elements that are
424 // inlined inside of it.
425 final Map<Element, List<Element>> inlineMap = <Element, List<Element>>{};
426
427 void registerInlined(Element element, Element inlinedFrom) {
428 inlineCount.putIfAbsent(element, () => 0);
429 inlineCount[element] += 1;
430 inlineMap.putIfAbsent(inlinedFrom, () => new List<Element>());
431 inlineMap[inlinedFrom].add(element);
432 }
433
434 /**
435 * Registers that a function uses a selector in the
436 * function body
437 */
438 void elementUsesSelector(Element element, Selector selector) {
439 if (compiler.dumpInfo) {
440 selectorsFromElement
441 .putIfAbsent(element, () => new Set<Selector>())
442 .add(selector);
443 }
444 }
445
446 /**
447 * Returns an iterable of [Selection]s that are used by
448 * [element]. Each [Selection] contains an element that is
449 * used and the selector that selected the element.
450 */
451 Iterable<Selection> getRetaining(Element element) {
452 if (!selectorsFromElement.containsKey(element)) {
453 return const <Selection>[];
454 } else {
455 return selectorsFromElement[element].expand(
456 (selector) {
457 return compiler.world.allFunctions.filter(selector).map((element) {
458 return new Selection(element, selector);
459 });
460 });
461 }
462 }
463
464 /**
465 * A callback that can be called before a jsAst [node] is
466 * pretty-printed. The size of the code buffer ([aftersize])
467 * is also passed.
468 */
469 void enteringAst(jsAst.Node node, int beforeSize) {
470 if (isTracking(node)) {
471 _nodeBeforeSize[node] = beforeSize;
472 }
473 }
474
475 /**
476 * A callback that can be called after a jsAst [node] is
477 * pretty-printed. The size of the code buffer ([aftersize])
478 * is also passed.
479 */
480 void exitingAst(jsAst.Node node, int afterSize) {
481 if (isTracking(node)) {
482 int diff = afterSize - _nodeBeforeSize[node];
483 recordAstSize(node, diff);
484 }
485 }
486
487 // Returns true if we care about tracking the size of
488 // this node.
489 bool isTracking(jsAst.Node code) {
490 if (compiler.dumpInfo) {
491 return _tracking.contains(code);
492 } else {
493 return false;
494 }
495 }
496
497 // Registers that a javascript AST node `code` was produced by the
498 // dart Element `element`.
499 void registerElementAst(Element element, jsAst.Node code) {
500 if (compiler.dumpInfo) {
501 _elementToNodes
502 .putIfAbsent(element, () => new List<jsAst.Node>())
503 .add(code);
504 _tracking.add(code);
505 }
506 }
507
508 // Records the size of a dart AST node after it has been
509 // pretty-printed into the output buffer.
510 void recordAstSize(jsAst.Node code, int size) {
511 if (compiler.dumpInfo) {
512 //TODO: should I be incrementing here instead?
513 _nodeToSize[code] = size;
514 }
515 }
516
517 // Field names are treated differently by the dart compiler
518 // so they must be recorded seperately.
519 void recordFieldNameSize(Element element, int size) {
520 _fieldNameToSize[element] = size;
521 }
522
523 // Returns the size of the source code that
524 // was generated for an element. If no source
525 // code was produced, return 0.
526 int sizeOf(Element element) {
527 if (_fieldNameToSize.containsKey(element)) {
528 return _fieldNameToSize[element];
529 }
530 if (_elementToNodes.containsKey(element)) {
531 return _elementToNodes[element]
532 .map(sizeOfNode)
533 .fold(0, (a, b) => a + b);
534 } else {
535 return 0;
536 }
537 }
538
539 int sizeOfNode(jsAst.Node node) {
540 if (_nodeToSize.containsKey(node)) {
541 return _nodeToSize[node];
542 } else {
543 return 0;
544 }
545 }
546
547 StringBuffer codeOf(Element element) {
548 List<jsAst.Node> code = _elementToNodes[element];
549 if (code == null) return null;
550 // Concatenate rendered ASTs.
551 StringBuffer sb = new StringBuffer();
552 for (jsAst.Node ast in code) {
553 sb.writeln(jsAst.prettyPrint(ast, compiler).getText());
554 }
555 return sb;
556 }
557
558 void collectInfo() {
559 infoCollector = new ElementToJsonVisitor(compiler)..run();
560 }
561
562 void dumpInfo() {
563 measure(() {
564 if (infoCollector == null) {
565 collectInfo();
566 }
567
568 StringBuffer jsonBuffer = new StringBuffer();
569 dumpInfoJson(jsonBuffer);
570 compiler.outputProvider('', 'info.json')
571 ..add(jsonBuffer.toString())
572 ..close();
573 });
574 }
575
576
577 void dumpInfoJson(StringSink buffer) {
578 JsonEncoder encoder = const JsonEncoder();
579 DateTime startToJsonTime = new DateTime.now();
580
581 Map<String, List<Map<String, String>>> holding =
582 <String, List<Map<String, String>>>{};
583 for (Element fn in infoCollector.mapper.functions) {
584 Iterable<Selection> pulling = getRetaining(fn);
585 // Don't bother recording an empty list of dependencies.
586 if (pulling.length > 0) {
587 String fnId = infoCollector.idOf(fn);
588 // Some dart2js builtin functions are not
589 // recorded. Don't register these.
590 if (fnId != null) {
591 holding[fnId] = pulling
592 .map((selection) {
593 return <String, String>{
594 "id": infoCollector.idOf(selection.selectedElement),
595 "mask": selection.selector.mask.toString()
596 };
597 })
598 // Filter non-null ids for the same reason as above.
599 .where((a) => a['id'] != null)
600 .toList();
601 }
602 }
603 }
604
605 // Track dependencies that come from inlining.
606 for (Element element in inlineMap.keys) {
607 String keyId = infoCollector.idOf(element);
608 if (keyId != null) {
609 for (Element held in inlineMap[element]) {
610 String valueId = infoCollector.idOf(held);
611 if (valueId != null) {
612 holding.putIfAbsent(keyId, () => new List<Map<String, String>>())
613 .add(<String, String>{
614 "id": valueId,
615 "mask": "inlined"
616 });
617 }
618 }
619 }
620 }
621
622 List<Map<String, dynamic>> outputUnits =
623 new List<Map<String, dynamic>>();
624
625 JavaScriptBackend backend = compiler.backend;
626
627 for (OutputUnit outputUnit in
628 infoCollector.mapper._outputUnit._elementToId.keys) {
629 String id = infoCollector.mapper._outputUnit.add(outputUnit);
630 outputUnits.add(<String, dynamic> {
631 'id': id,
632 'name': outputUnit.name,
633 'size': backend.emitter.oldEmitter.outputBuffers[outputUnit].length,
634 });
635 }
636
637 Map<String, dynamic> outJson = {
638 'elements': infoCollector.toJson(),
639 'holding': holding,
640 'outputUnits': outputUnits,
641 'dump_version': 3,
642 };
643
644 Duration toJsonDuration = new DateTime.now().difference(startToJsonTime);
645
646 Map<String, dynamic> generalProgramInfo = <String, dynamic> {
647 'size': infoCollector.programSize,
648 'dart2jsVersion': infoCollector.dart2jsVersion,
649 'compilationMoment': new DateTime.now().toString(),
650 'compilationDuration': compiler.totalCompileTime.elapsed.toString(),
651 'toJsonDuration': 0,
652 'dumpInfoDuration': this.timing.toString(),
653 'noSuchMethodEnabled': compiler.enabledNoSuchMethod
654 };
655
656 outJson['program'] = generalProgramInfo;
657
658 ChunkedConversionSink<Object> sink =
659 encoder.startChunkedConversion(
660 new StringConversionSink.fromStringSink(buffer));
661 sink.add(outJson);
662 compiler.reportInfo(NO_LOCATION_SPANNABLE,
663 const MessageKind(
664 "View the dumped .info.json file at "
665 "https://dart-lang.github.io/dump-info-visualizer"));
666 }
667 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698