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

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

Powered by Google App Engine
This is Rietveld 408576698