Add 'show' command - displays infos as text
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47bd06b..d5b20ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,8 @@
* Added backwards compatibility flag to the JSON codec, to make transition to
new tools more gradual.
+* Added a tool to dump info files in a readable text form.
+
* Consolidated all binary tools under a single command. Now you can access all
tools as follows:
```
diff --git a/README.md b/README.md
index 7152bc8..4602546 100644
--- a/README.md
+++ b/README.md
@@ -133,6 +133,8 @@
be JSON, backward-compatible JSON, binary, or protobuf schema (as defined in
`info.proto`).
+ * [`show`][show]: a tool that dumps info files in a readable text format.
+
Next we describe in detail how to use each of these tools.
### Code deps tool
@@ -514,3 +516,4 @@
[function_size]: https://github.com/dart-lang/dart2js_info/blob/master/bin/function_size_analysis.dart
[AllInfo]: http://dart-lang.github.io/dart2js_info/doc/api/dart2js_info.info/AllInfo-class.html
[convert]: https://github.com/dart-lang/dart2js_info/blob/master/bin/convert.dart
+[show]: https://github.com/dart-lang/dart2js_info/blob/master/bin/text_print.dart
diff --git a/bin/inject_text.dart b/bin/inject_text.dart
new file mode 100644
index 0000000..8dfe157
--- /dev/null
+++ b/bin/inject_text.dart
@@ -0,0 +1,40 @@
+// 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:dart2js_info/info.dart';
+
+/// Modify [info] to fill in the text of code spans.
+///
+/// By default, code spans contains the offsets but omit the text
+/// (`CodeSpan.text` is null). This function reads the output files emitted by
+/// dart2js to extract the code denoted by each span.
+void injectText(AllInfo info) {
+ // Fill the text of each code span. The binary form produced by dart2js
+ // produces code spans, but excludes the orignal text
+ info.functions.forEach((f) {
+ f.code.forEach((span) => _fillSpan(span, f.outputUnit));
+ });
+ info.fields.forEach((f) {
+ f.code.forEach((span) => _fillSpan(span, f.outputUnit));
+ });
+ info.constants.forEach((c) {
+ c.code.forEach((span) => _fillSpan(span, c.outputUnit));
+ });
+}
+
+Map<String, String> _cache = {};
+
+_getContents(OutputUnitInfo unit) => _cache.putIfAbsent(unit.filename, () {
+ var uri = Uri.base.resolve(unit.filename);
+ return new File.fromUri(uri).readAsStringSync();
+ });
+
+_fillSpan(CodeSpan span, OutputUnitInfo unit) {
+ if (span.text == null && span.start != null && span.end != 0) {
+ var contents = _getContents(unit);
+ span.text = contents.substring(span.start, span.end);
+ }
+}
diff --git a/bin/text_print.dart b/bin/text_print.dart
new file mode 100644
index 0000000..a38cd55
--- /dev/null
+++ b/bin/text_print.dart
@@ -0,0 +1,210 @@
+// 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:args/command_runner.dart';
+
+import 'package:dart2js_info/info.dart';
+import 'package:dart2js_info/src/util.dart';
+import 'package:dart2js_info/src/io.dart';
+
+import 'inject_text.dart';
+import 'usage_exception.dart';
+
+/// Shows the contents of an info file as text.
+class ShowCommand extends Command<void> with PrintUsageException {
+ final String name = "show";
+ final String description = "Show a text representation of the info file.";
+
+ ShowCommand() {
+ argParser.addOption('out',
+ abbr: 'o', help: 'Output file (defauts to stdout)');
+
+ argParser.addFlag('inject-text',
+ negatable: false,
+ help: 'Whether to inject output code snippets.\n\n'
+ 'By default dart2js produces code spans, but excludes the text. This\n'
+ 'option can be used to embed the text directly in the output.');
+ }
+
+ void run() async {
+ if (argResults.rest.length < 1) {
+ usageException('Missing argument: <input-info>');
+ }
+
+ String filename = argResults.rest[0];
+ AllInfo info = await infoFromFile(filename);
+ if (argResults['inject-text']) injectText(info);
+
+ var buffer = new StringBuffer();
+ info.accept(new TextPrinter(buffer, argResults['inject-text']));
+ var outputPath = argResults['out'];
+ if (outputPath == null) {
+ print(buffer);
+ } else {
+ new File(outputPath).writeAsStringSync('$buffer');
+ }
+ }
+}
+
+class TextPrinter implements InfoVisitor<void> {
+ final StringBuffer buffer;
+ final bool injectText;
+
+ TextPrinter(this.buffer, this.injectText);
+
+ int _indent = 0;
+ String get _textIndent => " " * _indent;
+ void _writeIndentation() {
+ buffer.write(_textIndent);
+ }
+
+ void _writeIndented(String s) {
+ _writeIndentation();
+ buffer.writeln(s.replaceAll('\n', '\n$_textIndent'));
+ }
+
+ void _writeBlock(String s, void f()) {
+ _writeIndented("$s");
+ _indent++;
+ f();
+ _indent--;
+ }
+
+ void visitAll(AllInfo info) {
+ _writeBlock("Summary data", () => visitProgram(info.program));
+ buffer.writeln();
+ _writeBlock("Libraries", () => info.libraries.forEach(visitLibrary));
+ // Note: classes, functions, typedefs, and fields are group;ed by library.
+
+ if (injectText) {
+ _writeBlock("Constants", () => info.constants.forEach(visitConstant));
+ } else {
+ int size = info.constants.fold(0, (n, c) => n + c.size);
+ _writeIndented("All constants: ${_size(size)}");
+ }
+ _writeBlock("Output units", () => info.outputUnits.forEach(visitOutput));
+ }
+
+ void visitProgram(ProgramInfo info) {
+ _writeIndented('main: ${longName(info.entrypoint, useLibraryUri: true)}');
+ _writeIndented('size: ${info.size}');
+ _writeIndented('dart2js-version: ${info.dart2jsVersion}');
+ var features = [];
+ if (info.noSuchMethodEnabled) features.add('no-such-method');
+ if (info.isRuntimeTypeUsed) features.add('runtime-type');
+ if (info.isFunctionApplyUsed) features.add('function-apply');
+ if (info.minified) features.add('minified');
+ if (features.isNotEmpty) {
+ _writeIndented('features: ${features.join(' ')}');
+ }
+ }
+
+ String _size(int size) {
+ if (size < 1024) return "$size b";
+ if (size < (1024 * 1024)) {
+ return "${(size / 1024).toStringAsFixed(2)} Kb ($size b)";
+ }
+ return "${(size / (1024 * 1024)).toStringAsFixed(2)} Mb ($size b)";
+ }
+
+ void visitLibrary(LibraryInfo info) {
+ _writeBlock('${info.uri}: ${_size(info.size)}', () {
+ if (info.topLevelFunctions.isNotEmpty) {
+ _writeBlock('Top-level functions',
+ () => info.topLevelFunctions.forEach(visitFunction));
+ buffer.writeln();
+ }
+ if (info.topLevelVariables.isNotEmpty) {
+ _writeBlock('Top-level variables',
+ () => info.topLevelVariables.forEach(visitField));
+ buffer.writeln();
+ }
+ if (info.classes.isNotEmpty) {
+ _writeBlock('Classes', () => info.classes.forEach(visitClass));
+ }
+ if (info.typedefs.isNotEmpty) {
+ _writeBlock("Typedefs", () => info.typedefs.forEach(visitTypedef));
+ buffer.writeln();
+ }
+ buffer.writeln();
+ });
+ }
+
+ void visitClass(ClassInfo info) {
+ _writeBlock(
+ '${info.name}: ${_size(info.size)} [${info.outputUnit.filename}]', () {
+ if (info.functions.isNotEmpty) {
+ _writeBlock('Methods:', () => info.functions.forEach(visitFunction));
+ }
+ if (info.fields.isNotEmpty) {
+ _writeBlock('Fields:', () => info.fields.forEach(visitField));
+ }
+ if (info.functions.isNotEmpty || info.fields.isNotEmpty) buffer.writeln();
+ });
+ }
+
+ void visitField(FieldInfo info) {
+ _writeBlock('${info.type} ${info.name}: ${_size(info.size)}', () {
+ _writeIndented('inferred type: ${info.inferredType}');
+ if (injectText) _writeBlock("code:", () => _writeCode(info.code));
+ if (info.closures.isNotEmpty) {
+ _writeBlock('Closures:', () => info.closures.forEach(visitClosure));
+ }
+ if (info.uses.isNotEmpty) {
+ _writeBlock('Dependencies:', () => info.uses.forEach(showDependency));
+ }
+ });
+ }
+
+ void visitFunction(FunctionInfo info) {
+ var outputUnitFile = '';
+ if (info.functionKind == FunctionInfo.TOP_LEVEL_FUNCTION_KIND) {
+ outputUnitFile = ' [${info.outputUnit.filename}]';
+ }
+ String params =
+ info.parameters.map((p) => "${p.declaredType} ${p.name}").join(', ');
+ _writeBlock(
+ '${info.returnType} ${info.name}($params): ${_size(info.size)}$outputUnitFile',
+ () {
+ String params = info.parameters.map((p) => "${p.type}").join(', ');
+ _writeIndented('declared type: ${info.type}');
+ _writeIndented(
+ 'inferred type: ${info.inferredReturnType} Function($params)');
+ _writeIndented('side effects: ${info.sideEffects}');
+ if (injectText) _writeBlock("code:", () => _writeCode(info.code));
+ if (info.closures.isNotEmpty) {
+ _writeBlock('Closures:', () => info.closures.forEach(visitClosure));
+ }
+ if (info.uses.isNotEmpty) {
+ _writeBlock('Dependencies:', () => info.uses.forEach(showDependency));
+ }
+ });
+ }
+
+ void showDependency(DependencyInfo info) {
+ var mask = info.mask ?? '';
+ _writeIndented('- ${longName(info.target, useLibraryUri: true)} $mask');
+ }
+
+ void visitTypedef(TypedefInfo info) {
+ _writeIndented('${info.name}: ${info.type}');
+ }
+
+ void visitClosure(ClosureInfo info) {
+ _writeBlock('${info.name}', () => visitFunction(info.function));
+ }
+
+ void visitConstant(ConstantInfo info) {
+ _writeBlock('${_size(info.size)}:', () => _writeCode(info.code));
+ }
+
+ void _writeCode(List<CodeSpan> code) {
+ _writeIndented(code.map((c) => c.text).join('\n'));
+ }
+
+ void visitOutput(OutputUnitInfo info) {
+ _writeIndented('${info.filename}: ${_size(info.size)}');
+ }
+}
diff --git a/bin/to_json.dart b/bin/to_json.dart
index ba81c55..e7b42aa 100644
--- a/bin/to_json.dart
+++ b/bin/to_json.dart
@@ -11,6 +11,7 @@
import 'package:dart2js_info/json_info_codec.dart';
import 'package:dart2js_info/src/io.dart';
+import 'inject_text.dart';
import 'usage_exception.dart';
/// Converts a dump-info file emitted by dart2js in binary format to JSON.
@@ -50,17 +51,7 @@
AllInfo info = await infoFromFile(filename);
if (isBackwardCompatible || argResults['inject-text']) {
- // Fill the text of each code span. The binary form produced by dart2js
- // produces code spans, but excludes the orignal text
- info.functions.forEach((f) {
- f.code.forEach((span) => _fillSpan(span, f.outputUnit));
- });
- info.fields.forEach((f) {
- f.code.forEach((span) => _fillSpan(span, f.outputUnit));
- });
- info.constants.forEach((c) {
- c.code.forEach((span) => _fillSpan(span, c.outputUnit));
- });
+ injectText(info);
}
var json = new AllInfoJsonCodec(isBackwardCompatible: isBackwardCompatible)
@@ -70,17 +61,3 @@
.writeAsStringSync(const JsonEncoder.withIndent(" ").convert(json));
}
}
-
-Map<String, String> _cache = {};
-
-_getContents(OutputUnitInfo unit) => _cache.putIfAbsent(unit.filename, () {
- var uri = Uri.base.resolve(unit.filename);
- return new File.fromUri(uri).readAsStringSync();
- });
-
-_fillSpan(CodeSpan span, OutputUnitInfo unit) {
- if (span.text == null && span.start != null && span.end != 0) {
- var contents = _getContents(unit);
- span.text = contents.substring(span.start, span.end);
- }
-}
diff --git a/bin/tools.dart b/bin/tools.dart
index 85f5281..32e9e33 100644
--- a/bin/tools.dart
+++ b/bin/tools.dart
@@ -16,6 +16,7 @@
import 'library_size_split.dart';
import 'live_code_size_analysis.dart';
import 'show_inferred_types.dart';
+import 'text_print.dart';
/// Entrypoint to run all dart2js_info tools.
void main(args) {
@@ -32,6 +33,7 @@
..addCommand(new FunctionSizeCommand())
..addCommand(new LibrarySizeCommand())
..addCommand(new LiveCodeAnalysisCommand())
- ..addCommand(new ShowInferredTypesCommand());
+ ..addCommand(new ShowInferredTypesCommand())
+ ..addCommand(new ShowCommand());
commandRunner.run(args);
}