b782d5815f
Change-Id: Ie4a7bbe75dd8045b20361c3fa1e08784b990e951 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176142 Commit-Queue: Devon Carew <devoncarew@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com>
209 lines
6.4 KiB
Dart
209 lines
6.4 KiB
Dart
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:io';
|
|
|
|
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
import 'package:args/args.dart';
|
|
import 'package:meta/meta.dart';
|
|
import 'package:nnbd_migration/src/nullability_node.dart';
|
|
import 'package:nnbd_migration/src/postmortem_file.dart';
|
|
import 'package:nnbd_migration/src/variables.dart';
|
|
|
|
void main(List<String> args) {
|
|
ArgParser argParser = ArgParser();
|
|
ArgResults parsedArgs;
|
|
|
|
argParser.addOption('file',
|
|
abbr: 'f', help: 'The postmortem file to analyze');
|
|
|
|
argParser.addFlag('help',
|
|
abbr: 'h', negatable: false, help: 'Print usage info');
|
|
|
|
List<Subcommand> subcommands = [
|
|
Subcommand(
|
|
name: 'dot', help: 'Output graph as dot file', argParser: ArgParser()),
|
|
Subcommand(name: 'help', help: 'Print usage info', argParser: ArgParser()),
|
|
Subcommand(
|
|
name: 'steps', help: 'Print propagation steps', argParser: ArgParser()),
|
|
Subcommand(
|
|
name: 'files', help: 'Print input file paths', argParser: ArgParser()),
|
|
Subcommand(
|
|
name: 'decorations',
|
|
suffix: '<path>',
|
|
help: 'Print decorations for a file',
|
|
argParser: ArgParser()),
|
|
Subcommand(
|
|
name: 'node',
|
|
suffix: '<id>',
|
|
help: 'Print details about a node',
|
|
argParser: ArgParser(),
|
|
),
|
|
Subcommand(
|
|
name: 'trace',
|
|
suffix: '<id>',
|
|
help: 'Print a trace of why a node was made nullable/non-nullable',
|
|
argParser: ArgParser(),
|
|
),
|
|
Subcommand(
|
|
name: 'trace_lengths',
|
|
help: 'Print the lengths of all traces',
|
|
argParser: ArgParser(),
|
|
),
|
|
];
|
|
|
|
for (var subcommand in subcommands) {
|
|
argParser.addCommand(subcommand.name, subcommand.argParser);
|
|
}
|
|
|
|
try {
|
|
parsedArgs = argParser.parse(args);
|
|
} on ArgParserException {
|
|
stderr.writeln(argParser.usage);
|
|
exit(1);
|
|
}
|
|
var command = parsedArgs.command;
|
|
if (parsedArgs['help'] as bool || command == null || command.name == 'help') {
|
|
print(argParser.usage);
|
|
for (var subcommand in subcommands) {
|
|
print('');
|
|
var suffix = subcommand.suffix == null ? '' : ' ${subcommand.suffix}';
|
|
print('Subcommand ${subcommand.name}$suffix: ${subcommand.help}');
|
|
print(subcommand.argParser.usage);
|
|
}
|
|
exit(0);
|
|
}
|
|
var filePath = parsedArgs['file'] as String;
|
|
if (filePath == null) {
|
|
print('Must specify a file to analyze using -f');
|
|
exit(1);
|
|
}
|
|
var reader = PostmortemFileReader.read(
|
|
PhysicalResourceProvider.INSTANCE.getFile(filePath));
|
|
switch (command.name) {
|
|
case 'dot':
|
|
reader.graph.debugDump();
|
|
break;
|
|
case 'steps':
|
|
for (var step in reader.propagationSteps) {
|
|
print(step.toString(idMapper: reader.idMapper));
|
|
}
|
|
break;
|
|
case 'trace':
|
|
var nodes = command.rest;
|
|
if (nodes.length != 1) {
|
|
print('Must specify exactly one node id after "node"');
|
|
exit(1);
|
|
}
|
|
var id = int.parse(nodes[0]);
|
|
var node = reader.deserializer.nodeForId(id);
|
|
for (var step in reader.propagationSteps) {
|
|
if (step is DownstreamPropagationStep &&
|
|
identical(node, step.targetNode)) {
|
|
print('Trace');
|
|
int i = 0;
|
|
while (step != null) {
|
|
var codeReference = step.codeReference;
|
|
var codeReferencePrefix =
|
|
codeReference == null ? '' : '$codeReference: ';
|
|
print('#${i++}\t$codeReferencePrefix$step');
|
|
step = step.principalCause;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'trace_lengths':
|
|
for (var step in reader.propagationSteps) {
|
|
if (step is DownstreamPropagationStep) {
|
|
print('trace length ${_traceLength(step)} for node id '
|
|
'${reader.deserializer.idForNode(step.targetNode)}');
|
|
}
|
|
}
|
|
break;
|
|
case 'files':
|
|
for (var entry in reader.fileDecorations.keys) {
|
|
print(entry);
|
|
}
|
|
break;
|
|
case 'decorations':
|
|
var paths = command.rest;
|
|
if (paths.length != 1) {
|
|
print('Must specify exactly one path after "decorations"');
|
|
exit(1);
|
|
}
|
|
var path = paths[0];
|
|
var decorations = reader.fileDecorations[path];
|
|
if (decorations == null) {
|
|
print('Path not found: $path');
|
|
exit(1);
|
|
}
|
|
for (var decorationEntry in decorations.entries) {
|
|
for (var roleEntry in decorationEntry.value.entries) {
|
|
var span = Variables.spanForUniqueIdentifier(decorationEntry.key);
|
|
var nodeId = reader.idMapper.idForNode(roleEntry.value);
|
|
print('${span.offset}-${span.end}${roleEntry.key}: $nodeId');
|
|
}
|
|
}
|
|
break;
|
|
case 'node':
|
|
var nodes = command.rest;
|
|
if (nodes.length != 1) {
|
|
print('Must specify exactly one node id after "node"');
|
|
exit(1);
|
|
}
|
|
var id = int.parse(nodes[0]);
|
|
var node = reader.deserializer.nodeForId(id);
|
|
print('Node $id: $node');
|
|
print('Decorations:');
|
|
reader.findDecorationsByNode(node, (path, span, role) {
|
|
print(' $path:$span$role');
|
|
});
|
|
print('Upstream edges:');
|
|
for (var edge in node.upstreamEdges) {
|
|
var description =
|
|
(edge as NullabilityEdge).toString(idMapper: reader.idMapper);
|
|
print(' $description');
|
|
}
|
|
print('Downstream edges:');
|
|
for (var edge in node.downstreamEdges) {
|
|
var description =
|
|
(edge as NullabilityEdge).toString(idMapper: reader.idMapper);
|
|
print(' $description');
|
|
}
|
|
if (node is NullabilityNodeCompound) {
|
|
var componentsByName = node.componentsByName;
|
|
print('Components:');
|
|
for (var entry in componentsByName.entries) {
|
|
var description = entry.value.toString(idMapper: reader.idMapper);
|
|
print(' ${entry.key}: $description');
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw StateError('Unrecognized command: $command');
|
|
}
|
|
}
|
|
|
|
int _traceLength(PropagationStep step) {
|
|
int traceLength = 0;
|
|
while (step != null) {
|
|
traceLength++;
|
|
step = step.principalCause;
|
|
}
|
|
return traceLength;
|
|
}
|
|
|
|
class Subcommand {
|
|
final String name;
|
|
final String suffix;
|
|
final String help;
|
|
final ArgParser argParser;
|
|
|
|
Subcommand(
|
|
{@required this.name,
|
|
this.suffix,
|
|
@required this.help,
|
|
@required this.argParser});
|
|
}
|