Add tool to compare type masks
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1adc08d..9321bc6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
-## 0.6.0-dev.0.0
+## 0.6.1-dev
+
+* added `diff_masks` tool, mostly used by dart2js developers.
+
+## 0.6.0
This release contains several **breaking changes**:
diff --git a/bin/diff_masks.dart b/bin/diff_masks.dart
new file mode 100644
index 0000000..ed5ed72
--- /dev/null
+++ b/bin/diff_masks.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2017, 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 'package:args/command_runner.dart';
+
+import 'package:dart2js_info/src/mask_diff.dart';
+import 'package:dart2js_info/src/io.dart';
+import 'package:dart2js_info/src/util.dart';
+
+import 'usage_exception.dart';
+
+/// A command that computes the diff between type-mask from two info files.
+class DiffMaskCommand extends Command<void> with PrintUsageException {
+ final String name = "diff_masks";
+ final String description =
+ "See type mask differences between two dump-info files.";
+
+ void run() async {
+ var args = argResults.rest;
+ if (args.length < 2) {
+ usageException(
+ 'Missing arguments, expected two dump-info files to compare');
+ return;
+ }
+
+ var oldInfo = await infoFromFile(args[0]);
+ var newInfo = await infoFromFile(args[1]);
+
+ var overallSizeDiff = newInfo.program.size - oldInfo.program.size;
+ print('total_size_difference $overallSizeDiff');
+ print('');
+ var diffs = diff(oldInfo, newInfo);
+ int total = diffs.length;
+ int onlyNullTotal = 0;
+ for (var diff in diffs) {
+ bool onlyNull = _compareModuloNull(diff.oldMask, diff.newMask);
+ if (onlyNull) {
+ onlyNullTotal++;
+ } else {
+ print('\n${longName(diff.info, useLibraryUri: true)}:\n'
+ ' old: ${diff.oldMask}\n'
+ ' new: ${diff.newMask}');
+ }
+ }
+
+ print("Only null: $onlyNullTotal of $total (remaining: ${total - onlyNullTotal}");
+ }
+}
+
+bool _compareModuloNull(String a, String b) {
+ int i = 0;
+ int j = 0;
+ while (i < a.length && j < b.length) {
+ if (a[i] == b[j]) {
+ i++;
+ j++;
+ continue;
+ }
+
+ if (a[i] == "n" && a.substring(i, i+5) == "null|") {
+ i+=5;
+ continue;
+ }
+
+ return false;
+ }
+
+ return i == a.length && j == b.length;
+}
diff --git a/bin/tools.dart b/bin/tools.dart
index 32e9e33..974f060 100644
--- a/bin/tools.dart
+++ b/bin/tools.dart
@@ -8,6 +8,7 @@
import 'coverage_log_server.dart';
import 'debug_info.dart';
import 'diff.dart';
+import 'diff_masks.dart';
import 'deferred_library_check.dart';
import 'deferred_library_size.dart';
import 'deferred_library_layout.dart';
@@ -26,6 +27,7 @@
..addCommand(new CoverageLogServerCommand())
..addCommand(new DebugCommand())
..addCommand(new DiffCommand())
+ ..addCommand(new DiffMaskCommand())
..addCommand(new DeferredLibraryCheck())
..addCommand(new DeferredLibrarySize())
..addCommand(new DeferredLibraryLayout())
diff --git a/lib/src/mask_diff.dart b/lib/src/mask_diff.dart
new file mode 100644
index 0000000..9e7302a
--- /dev/null
+++ b/lib/src/mask_diff.dart
@@ -0,0 +1,133 @@
+import 'package:dart2js_info/info.dart';
+import 'package:dart2js_info/src/util.dart';
+
+class MaskDiff {
+ final BasicInfo info;
+ final String oldMask;
+ final String newMask;
+ MaskDiff(this.info, this.oldMask, this.newMask);
+}
+
+List<MaskDiff> diff(AllInfo oldInfo, AllInfo newInfo) {
+ var differ = new _InfoDiffer(oldInfo, newInfo);
+ differ.diff();
+ return differ.diffs;
+}
+
+class _InfoDiffer extends InfoVisitor<Null> {
+ final AllInfo _old;
+ final AllInfo _new;
+
+ BasicInfo _other;
+
+ List<MaskDiff> diffs = <MaskDiff>[];
+
+ _InfoDiffer(this._old, this._new);
+
+ void diff() {
+ _diffList(_old.libraries, _new.libraries);
+ }
+
+ @override
+ visitAll(AllInfo info) {
+ throw new StateError('should not diff AllInfo');
+ }
+
+ @override
+ visitProgram(ProgramInfo info) {
+ throw new StateError('should not diff ProgramInfo');
+ }
+
+ @override
+ visitOutput(OutputUnitInfo info) {
+ throw new StateError('should not diff OutputUnitInfo');
+ }
+
+ // TODO(het): diff constants
+ @override
+ visitConstant(ConstantInfo info) {
+ throw new StateError('should not diff ConstantInfo');
+ }
+
+ @override
+ visitLibrary(LibraryInfo info) {
+ var other = _other as LibraryInfo;
+ _diffList(info.topLevelVariables, other.topLevelVariables);
+ _diffList(info.topLevelFunctions, other.topLevelFunctions);
+ _diffList(info.classes, other.classes);
+ }
+
+ @override
+ visitClass(ClassInfo info) {
+ var other = _other as ClassInfo;
+ _diffList(info.fields, other.fields);
+ _diffList(info.functions, other.functions);
+ }
+
+ @override
+ visitClosure(ClosureInfo info) {
+ var other = _other as ClosureInfo;
+ _diffList([info.function], [other.function]);
+ }
+
+ @override
+ visitField(FieldInfo info) {
+ var other = _other as FieldInfo;
+ if (info.type != other.type) {
+ diffs.add(new MaskDiff(info, info.type, other.type));
+ }
+ _diffList(info.closures, other.closures);
+ }
+
+ String _signature(FunctionInfo info) {
+ var sb = new StringBuffer();
+ sb.write(info.returnType);
+ sb.write("(");
+ for (var parameter in info.parameters) {
+ sb.write(parameter.type);
+ sb.write(" ");
+ sb.write(parameter.name);
+ sb.write(",");
+ }
+ sb.write(")");
+ return '$sb';
+ }
+
+ @override
+ visitFunction(FunctionInfo info) {
+ var other = _other as FunctionInfo;
+ var infoSignature = _signature(info);
+ var otherSignature = _signature(other);
+ if (infoSignature != otherSignature) {
+ diffs.add(new MaskDiff(info, infoSignature, otherSignature));
+ }
+ _diffList(info.closures, other.closures);
+ }
+
+ @override
+ visitTypedef(TypedefInfo info) {}
+
+ void _diffList(List<BasicInfo> oldInfos, List<BasicInfo> newInfos) {
+ var oldNames = <String, BasicInfo>{};
+ var newNames = <String, BasicInfo>{};
+ for (var oldInfo in oldInfos) {
+ oldNames[longName(oldInfo, useLibraryUri: true)] = oldInfo;
+ }
+ for (var newInfo in newInfos) {
+ newNames[longName(newInfo, useLibraryUri: true)] = newInfo;
+ }
+ for (var oldName in oldNames.keys) {
+ if (newNames[oldName] == null) {
+ diffs.add(new MaskDiff(oldNames[oldName], "removed", ""));
+ } else {
+ _other = newNames[oldName];
+ oldNames[oldName].accept(this);
+ }
+ }
+ for (var newName in newNames.keys) {
+ if (oldNames[newName] == null) {
+ diffs.add(new MaskDiff(newNames[newName], "", "added"));
+ }
+ }
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 5a34a94..4076f58 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: dart2js_info
-version: 0.6.0
+version: 0.6.1-dev
description: >
Libraries and tools to process data produced when running dart2js with