OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 library dart2js.inferrer.type_graph_dump; | 4 library dart2js.inferrer.type_graph_dump; |
5 | 5 |
6 import 'dart:async'; | 6 import 'dart:async'; |
7 import 'type_graph_nodes.dart'; | 7 import 'type_graph_nodes.dart'; |
8 import 'type_graph_inferrer.dart'; | 8 import 'type_graph_inferrer.dart'; |
9 import '../elements/elements.dart'; | 9 import '../elements/elements.dart'; |
10 import '../types/types.dart'; | 10 import '../types/types.dart'; |
(...skipping 11 matching lines...) Expand all Loading... |
22 /// dot -Tpng -O typegraph/main.dot | 22 /// dot -Tpng -O typegraph/main.dot |
23 /// open typegraph/main.dot.png | 23 /// open typegraph/main.dot.png |
24 /// | 24 /// |
25 /// dot -Tpng -O typegraph/dart._internal.Sort._dualPivotQuicksort.dot | 25 /// dot -Tpng -O typegraph/dart._internal.Sort._dualPivotQuicksort.dot |
26 /// open typegraph/dart._internal.Sort._dualPivotQuicksort.dot.png | 26 /// open typegraph/dart._internal.Sort._dualPivotQuicksort.dot.png |
27 /// | 27 /// |
28 class TypeGraphDump { | 28 class TypeGraphDump { |
29 static const String outputDir = 'typegraph'; | 29 static const String outputDir = 'typegraph'; |
30 | 30 |
31 final TypeGraphInferrerEngine inferrer; | 31 final TypeGraphInferrerEngine inferrer; |
32 final Map<TypeInformation, Set<TypeInformation>> assignmentsBeforeAnalysis | 32 final Map<TypeInformation, Set<TypeInformation>> assignmentsBeforeAnalysis = |
33 = <TypeInformation, Set<TypeInformation>>{}; | 33 <TypeInformation, Set<TypeInformation>>{}; |
34 final Map<TypeInformation, Set<TypeInformation>> assignmentsBeforeTracing | 34 final Map<TypeInformation, Set<TypeInformation>> assignmentsBeforeTracing = |
35 = <TypeInformation, Set<TypeInformation>>{}; | 35 <TypeInformation, Set<TypeInformation>>{}; |
36 final Set<String> usedFilenames = new Set<String>(); | 36 final Set<String> usedFilenames = new Set<String>(); |
37 | 37 |
38 TypeGraphDump(this.inferrer); | 38 TypeGraphDump(this.inferrer); |
39 | 39 |
40 /// Take a copy of the assignment set for each node, since that may change | 40 /// Take a copy of the assignment set for each node, since that may change |
41 /// during the analysis. | 41 /// during the analysis. |
42 void beforeAnalysis() { | 42 void beforeAnalysis() { |
43 for (TypeInformation node in inferrer.types.allTypes) { | 43 for (TypeInformation node in inferrer.types.allTypes) { |
44 Set<TypeInformation> copy = node.assignments.toSet(); | 44 Set<TypeInformation> copy = node.assignments.toSet(); |
45 if (!copy.isEmpty) { | 45 if (!copy.isEmpty) { |
(...skipping 12 matching lines...) Expand all Loading... |
58 } | 58 } |
59 } | 59 } |
60 | 60 |
61 /// Dumps the entire graph. | 61 /// Dumps the entire graph. |
62 void afterAnalysis() { | 62 void afterAnalysis() { |
63 // Group all the type nodes by their context member. | 63 // Group all the type nodes by their context member. |
64 Map<Element, List<TypeInformation>> nodes = | 64 Map<Element, List<TypeInformation>> nodes = |
65 <Element, List<TypeInformation>>{}; | 65 <Element, List<TypeInformation>>{}; |
66 for (TypeInformation node in inferrer.types.allTypes) { | 66 for (TypeInformation node in inferrer.types.allTypes) { |
67 if (node.contextMember != null) { | 67 if (node.contextMember != null) { |
68 nodes.putIfAbsent(node.contextMember, () => <TypeInformation>[]) | 68 nodes |
69 .add(node); | 69 .putIfAbsent(node.contextMember, () => <TypeInformation>[]) |
| 70 .add(node); |
70 } | 71 } |
71 } | 72 } |
72 // Print every group separately. | 73 // Print every group separately. |
73 for (Element element in nodes.keys) { | 74 for (Element element in nodes.keys) { |
74 EventSink<String> output; | 75 EventSink<String> output; |
75 try { | 76 try { |
76 String name = filenameFromElement(element); | 77 String name = filenameFromElement(element); |
77 output = inferrer.compiler.outputProvider('$outputDir/$name', 'dot'); | 78 output = inferrer.compiler.outputProvider('$outputDir/$name', 'dot'); |
78 _GraphGenerator visitor = new _GraphGenerator(this, element, output); | 79 _GraphGenerator visitor = new _GraphGenerator(this, element, output); |
79 for (TypeInformation node in nodes[element]) { | 80 for (TypeInformation node in nodes[element]) { |
(...skipping 13 matching lines...) Expand all Loading... |
93 /// graph for [element]. | 94 /// graph for [element]. |
94 /// | 95 /// |
95 /// Will never return the a given filename more than once, even if called with | 96 /// Will never return the a given filename more than once, even if called with |
96 /// the same element. | 97 /// the same element. |
97 String filenameFromElement(Element element) { | 98 String filenameFromElement(Element element) { |
98 // The toString method of elements include characters that are unsuitable | 99 // The toString method of elements include characters that are unsuitable |
99 // for URIs and file systems. | 100 // for URIs and file systems. |
100 List<String> parts = <String>[]; | 101 List<String> parts = <String>[]; |
101 parts.add(element.library?.libraryName); | 102 parts.add(element.library?.libraryName); |
102 parts.add(element.enclosingClass?.name); | 103 parts.add(element.enclosingClass?.name); |
103 Element namedElement = element is LocalElement | 104 Element namedElement = |
104 ? element.executableContext | 105 element is LocalElement ? element.executableContext : element; |
105 : element; | |
106 if (namedElement.isGetter) { | 106 if (namedElement.isGetter) { |
107 parts.add('get-${namedElement.name}'); | 107 parts.add('get-${namedElement.name}'); |
108 } else if (namedElement.isSetter) { | 108 } else if (namedElement.isSetter) { |
109 parts.add('set-${namedElement.name}'); | 109 parts.add('set-${namedElement.name}'); |
110 } else if (namedElement.isConstructor) { | 110 } else if (namedElement.isConstructor) { |
111 if (namedElement.name.isEmpty) { | 111 if (namedElement.name.isEmpty) { |
112 parts.add('-constructor'); | 112 parts.add('-constructor'); |
113 } else { | 113 } else { |
114 parts.add(namedElement.name); | 114 parts.add(namedElement.name); |
115 } | 115 } |
116 } else if (namedElement.isOperator) { | 116 } else if (namedElement.isOperator) { |
117 parts.add(Elements.operatorNameToIdentifier(namedElement.name) | 117 parts.add(Elements |
| 118 .operatorNameToIdentifier(namedElement.name) |
118 .replaceAll(r'$', '-')); | 119 .replaceAll(r'$', '-')); |
119 } else { | 120 } else { |
120 parts.add(namedElement.name); | 121 parts.add(namedElement.name); |
121 } | 122 } |
122 if (namedElement != element) { | 123 if (namedElement != element) { |
123 if (element.name.isEmpty) { | 124 if (element.name.isEmpty) { |
124 parts.add('anon${element.sourcePosition.begin}'); | 125 parts.add('anon${element.sourcePosition.begin}'); |
125 } else { | 126 } else { |
126 parts.add(element.name); | 127 parts.add(element.name); |
127 } | 128 } |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 | 199 |
199 /// Escapes characters in [text] so it can be used as part of a label. | 200 /// Escapes characters in [text] so it can be used as part of a label. |
200 String escapeLabel(String text) { | 201 String escapeLabel(String text) { |
201 return text.replaceAllMapped(escapeRegexp, (m) => '\\${m.group(0)}'); | 202 return text.replaceAllMapped(escapeRegexp, (m) => '\\${m.group(0)}'); |
202 } | 203 } |
203 | 204 |
204 /// Creates an edge from [src] to [dst]. | 205 /// Creates an edge from [src] to [dst]. |
205 /// | 206 /// |
206 /// If [dst] is a record type node, [port] may refer to one of the fields | 207 /// If [dst] is a record type node, [port] may refer to one of the fields |
207 /// defined in that record (e.g. `obj`, `arg0`, `arg1`, etc) | 208 /// defined in that record (e.g. `obj`, `arg0`, `arg1`, etc) |
208 void addEdge(TypeInformation src, | 209 void addEdge(TypeInformation src, TypeInformation dst, |
209 TypeInformation dst, | 210 {String port, String color: 'black'}) { |
210 {String port, | |
211 String color: 'black'}) { | |
212 if (isExternal(src) && isExternal(dst)) { | 211 if (isExternal(src) && isExternal(dst)) { |
213 return; // Do not add edges between external nodes. | 212 return; // Do not add edges between external nodes. |
214 } | 213 } |
215 String dstText = getNode(dst); | 214 String dstText = getNode(dst); |
216 if (port != null) { | 215 if (port != null) { |
217 dstText += ':$port'; | 216 dstText += ':$port'; |
218 } | 217 } |
219 if (src is ConcreteTypeInformation) { | 218 if (src is ConcreteTypeInformation) { |
220 // Concrete types can have a huge number of uses which will flood the | 219 // Concrete types can have a huge number of uses which will flood the |
221 // graph with very long hard-to-follow edges. Copy the concrete nodes | 220 // graph with very long hard-to-follow edges. Copy the concrete nodes |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 if (node.contextMember != null && node.contextMember != element) { | 262 if (node.contextMember != null && node.contextMember != element) { |
264 return '$text\n(from ${node.contextMember})'; | 263 return '$text\n(from ${node.contextMember})'; |
265 } | 264 } |
266 return text; | 265 return text; |
267 } | 266 } |
268 | 267 |
269 /// Creates a node for [node] displaying the given [text] in its box. | 268 /// Creates a node for [node] displaying the given [text] in its box. |
270 /// | 269 /// |
271 /// [inputs] specify named inputs to the node. If omitted, edges will be | 270 /// [inputs] specify named inputs to the node. If omitted, edges will be |
272 /// based on [node.assignments]. | 271 /// based on [node.assignments]. |
273 void addNode(TypeInformation node, | 272 void addNode(TypeInformation node, String text, |
274 String text, | 273 {String color: defaultNodeColor, Map<String, TypeInformation> inputs}) { |
275 {String color: defaultNodeColor, | |
276 Map<String, TypeInformation> inputs}) { | |
277 seen.add(node); | 274 seen.add(node); |
278 String style = getStyleForNode(node, color); | 275 String style = getStyleForNode(node, color); |
279 text = appendDetails(node, text); | 276 text = appendDetails(node, text); |
280 text = escapeLabel(text); | 277 text = escapeLabel(text); |
281 String id = getNode(node); | 278 String id = getNode(node); |
282 String returnType = escapeLabel(formatType(node.type)); | 279 String returnType = escapeLabel(formatType(node.type)); |
283 if (inputs != null) { | 280 if (inputs != null) { |
284 Iterable<String> keys = inputs.keys.where((key) => inputs[key] != null); | 281 Iterable<String> keys = inputs.keys.where((key) => inputs[key] != null); |
285 String header = keys.map((key) => '<a$key> $key').join('|'); | 282 String header = keys.map((key) => '<a$key> $key').join('|'); |
286 String label = '{{$header}|$text|<returnType> $returnType}'; | 283 String label = '{{$header}|$text|<returnType> $returnType}'; |
287 append('$id [shape=record,label="$label",$style]'); | 284 append('$id [shape=record,label="$label",$style]'); |
288 for (String key in keys) { | 285 for (String key in keys) { |
289 addEdge(inputs[key], node, port: 'a$key'); | 286 addEdge(inputs[key], node, port: 'a$key'); |
290 } | 287 } |
291 } else { | 288 } else { |
292 String label = '{$text|<returnType> $returnType}'; | 289 String label = '{$text|<returnType> $returnType}'; |
293 append('$id [shape=record,label="$label",$style]'); | 290 append('$id [shape=record,label="$label",$style]'); |
294 // Add assignment edges. Color the edges based on whether they were | 291 // Add assignment edges. Color the edges based on whether they were |
295 // added, removed, temporary, or unchanged. | 292 // added, removed, temporary, or unchanged. |
296 var originalSet = global.assignmentsBeforeAnalysis[node] ?? const []; | 293 var originalSet = global.assignmentsBeforeAnalysis[node] ?? const []; |
297 var tracerSet = global.assignmentsBeforeTracing[node] ?? const []; | 294 var tracerSet = global.assignmentsBeforeTracing[node] ?? const []; |
298 var currentSet = node.assignments.toSet(); | 295 var currentSet = node.assignments.toSet(); |
299 for (TypeInformation assignment in currentSet) { | 296 for (TypeInformation assignment in currentSet) { |
300 String color = originalSet.contains(assignment) | 297 String color = |
301 ? unchangedEdge | 298 originalSet.contains(assignment) ? unchangedEdge : addedEdge; |
302 : addedEdge; | |
303 addEdge(assignment, node, color: color); | 299 addEdge(assignment, node, color: color); |
304 } | 300 } |
305 for (TypeInformation assignment in originalSet) { | 301 for (TypeInformation assignment in originalSet) { |
306 if (!currentSet.contains(assignment)) { | 302 if (!currentSet.contains(assignment)) { |
307 addEdge(assignment, node, color: removedEdge); | 303 addEdge(assignment, node, color: removedEdge); |
308 } | 304 } |
309 } | 305 } |
310 for (TypeInformation assignment in tracerSet) { | 306 for (TypeInformation assignment in tracerSet) { |
311 if (!currentSet.contains(assignment) && | 307 if (!currentSet.contains(assignment) && |
312 !originalSet.contains(assignment)) { | 308 !originalSet.contains(assignment)) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
344 | 340 |
345 void visitMapTypeInformation(MapTypeInformation info) { | 341 void visitMapTypeInformation(MapTypeInformation info) { |
346 addNode(info, 'Map'); | 342 addNode(info, 'Map'); |
347 } | 343 } |
348 | 344 |
349 void visitConcreteTypeInformation(ConcreteTypeInformation info) { | 345 void visitConcreteTypeInformation(ConcreteTypeInformation info) { |
350 addNode(info, 'Concrete'); | 346 addNode(info, 'Concrete'); |
351 } | 347 } |
352 | 348 |
353 void visitStringLiteralTypeInformation(StringLiteralTypeInformation info) { | 349 void visitStringLiteralTypeInformation(StringLiteralTypeInformation info) { |
354 String text = shorten(info.value.slowToString()).replaceAll('\n','\\n'); | 350 String text = shorten(info.value.slowToString()).replaceAll('\n', '\\n'); |
355 addNode(info, 'StringLiteral\n"$text"'); | 351 addNode(info, 'StringLiteral\n"$text"'); |
356 } | 352 } |
357 | 353 |
358 void visitBoolLiteralTypeInformation(BoolLiteralTypeInformation info) { | 354 void visitBoolLiteralTypeInformation(BoolLiteralTypeInformation info) { |
359 addNode(info, 'BoolLiteral\n${info.value}'); | 355 addNode(info, 'BoolLiteral\n${info.value}'); |
360 } | 356 } |
361 | 357 |
362 void handleCall(CallSiteTypeInformation info, String text, Map inputs) { | 358 void handleCall(CallSiteTypeInformation info, String text, Map inputs) { |
363 String sourceCode = shorten('${info.call}'); | 359 String sourceCode = shorten('${info.call}'); |
364 text = '$text\n$sourceCode'; | 360 text = '$text\n$sourceCode'; |
365 if (info.arguments != null) { | 361 if (info.arguments != null) { |
366 for (int i = 0; i < info.arguments.positional.length; ++i) { | 362 for (int i = 0; i < info.arguments.positional.length; ++i) { |
367 inputs['arg$i'] = info.arguments.positional[i]; | 363 inputs['arg$i'] = info.arguments.positional[i]; |
368 } | 364 } |
369 for (String argName in info.arguments.named.keys) { | 365 for (String argName in info.arguments.named.keys) { |
370 inputs[argName] = info.arguments.named[argName]; | 366 inputs[argName] = info.arguments.named[argName]; |
371 } | 367 } |
372 } | 368 } |
373 addNode(info, text, color: callColor, inputs: inputs); | 369 addNode(info, text, color: callColor, inputs: inputs); |
374 } | 370 } |
375 | 371 |
376 void visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info)
{ | 372 void visitClosureCallSiteTypeInformation( |
| 373 ClosureCallSiteTypeInformation info) { |
377 handleCall(info, 'ClosureCallSite', {}); | 374 handleCall(info, 'ClosureCallSite', {}); |
378 } | 375 } |
379 | 376 |
380 void visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | 377 void visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
381 handleCall(info, 'StaticCallSite', {}); | 378 handleCall(info, 'StaticCallSite', {}); |
382 } | 379 } |
383 | 380 |
384 void visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info)
{ | 381 void visitDynamicCallSiteTypeInformation( |
385 handleCall(info, 'DynamicCallSite', { | 382 DynamicCallSiteTypeInformation info) { |
386 'obj': info.receiver | 383 handleCall(info, 'DynamicCallSite', {'obj': info.receiver}); |
387 }); | |
388 } | 384 } |
389 | 385 |
390 void visitMemberTypeInformation(MemberTypeInformation info) { | 386 void visitMemberTypeInformation(MemberTypeInformation info) { |
391 addNode(info, 'Member\n${info.element}'); | 387 addNode(info, 'Member\n${info.element}'); |
392 } | 388 } |
393 | 389 |
394 void visitParameterTypeInformation(ParameterTypeInformation info) { | 390 void visitParameterTypeInformation(ParameterTypeInformation info) { |
395 addNode(info, 'Parameter ${info.element?.name ?? ''}'); | 391 addNode(info, 'Parameter ${info.element?.name ?? ''}'); |
396 } | 392 } |
397 | 393 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
436 String value = formatType(type.valueType); | 432 String value = formatType(type.valueType); |
437 return '$container<$key,$value>'; | 433 return '$container<$key,$value>'; |
438 } | 434 } |
439 if (type is ValueTypeMask) { | 435 if (type is ValueTypeMask) { |
440 String baseType = formatType(type.forwardTo); | 436 String baseType = formatType(type.forwardTo); |
441 String value = type.value.toStructuredString(); | 437 String value = type.value.toStructuredString(); |
442 return '$baseType=$value'; | 438 return '$baseType=$value'; |
443 } | 439 } |
444 return '$type'; // Fall back on toString if not supported here. | 440 return '$type'; // Fall back on toString if not supported here. |
445 } | 441 } |
OLD | NEW |