| 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 |