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

Side by Side Diff: pkg/compiler/lib/src/dump_info.dart

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

Powered by Google App Engine
This is Rietveld 408576698