Add support for --dump-info=binary

We continue to accept `--dump-info`, but now also accept `--dump-info=binary` so
we can use the new cheaper encoding.

Change-Id: I971cb9a3634ae1a333cfee14b2927c0e25000a01
Reviewed-on: https://dart-review.googlesource.com/c/93823
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Sigmund Cherem 2019-02-21 17:54:39 +00:00 committed by commit-bot@chromium.org
parent 372537f1e4
commit e3b8065625
8 changed files with 238 additions and 146 deletions

View file

@ -53,6 +53,38 @@ Upgraded the linter to `0.1.82` which adds the following improvements:
* Stopped registering "default lints".
* Fixed `hash_and_equals` to respect `hashCode` fields.
#### dart2js
* `--dump-info=binary`
A binary format was added to dump-info. The old JSON format is still
available and provided by default, but we are starting to deprecate it.
The new binary format is more compact and cheaper to generate. On some large
apps we tested, it was 4x faster to serialize and used 6x less memory.
To use it today, use `--dump-info=binary`, instead of `--dump-info`.
What to expect next?
* The [visualizer tool][visualizer] will not be updated to support this new
format, but you can find several command-line tools at
`package:dart2js_info` that provide similar features to those in the
visualizer.
* The command-line tools in `package:dart2js_info` also work with the old
JSON format, so you can start using it even before you enable the new
format.
* In a future release `--dump-info` will default to `--dump-info=binary`. At
that point, there will be an option to fallback to the JSON format, but the
visualizer tool will be deprecated.
* A release after that, the JSON format will no longer be available from
dart2js, but may be availabe from a command-line tool in
`package:dart2js_info`.
[visualizer]: https://dart-lang.github.io/dump-info-visualizer/
### Other libraries
#### `package:kernel`

2
DEPS
View file

@ -65,7 +65,7 @@ vars = {
"convert_tag": "2.0.2",
"crypto_tag" : "2.0.6",
"csslib_tag" : "0.14.4+1",
"dart2js_info_tag" : "0.5.15",
"dart2js_info_tag" : "0.6.0",
# Note: updates to dart_style have to be coordinated carefully with
# the infrastructure-team so that the internal formatter in

View file

@ -271,6 +271,19 @@ Future<api.CompilationResult> compile(List<String> argv,
compilationStrategy = CompilationStrategy.toData;
}
void setDumpInfo(String argument) {
passThrough(Flags.dumpInfo);
if (argument == Flags.dumpInfo || argument == "${Flags.dumpInfo}=json") {
return;
}
if (argument == "${Flags.dumpInfo}=binary") {
passThrough(argument);
return;
}
helpAndFail("Error: Unsupported dump-info format '$argument', "
"supported formats are: json or binary");
}
void handleThrowOnError(String argument) {
throwOnError = true;
String parameter = extractParameter(argument, isOptionalArgument: true);
@ -372,7 +385,7 @@ Future<api.CompilationResult> compile(List<String> argv,
new OptionHandler('--deferred-map=.+', passThrough),
new OptionHandler(Flags.newDeferredSplit, passThrough),
new OptionHandler(Flags.reportInvalidInferredDeferredTypes, passThrough),
new OptionHandler(Flags.dumpInfo, passThrough),
new OptionHandler('${Flags.dumpInfo}|${Flags.dumpInfo}=.+', setDumpInfo),
new OptionHandler('--disallow-unsafe-eval', ignoreOption),
new OptionHandler(Option.showPackageWarnings, passThrough),
new OptionHandler(Option.enableLanguageExperiments, passThrough),
@ -874,10 +887,10 @@ be removed in a future version:
Generates a json file with a mapping from each deferred import to a list of
the part.js files that will be loaded.
--dump-info
Generates an out.info.json file with information about the generated code.
You can inspect the generated file with the viewer at:
https://dart-lang.github.io/dump-info-visualizer/
--dump-info[=<format>]
Generates information about the generated code. 'format' can be either
'json' or 'binary'.
You can inspect the generated data using tools from 'package:dart2js_info'.
--generate-code-with-compile-time-errors
Generates output even if the program contains compile-time errors. Use the

View file

@ -8,6 +8,8 @@ import 'dart:convert'
show ChunkedConversionSink, JsonEncoder, StringConversionSink;
import 'package:dart2js_info/info.dart';
import 'package:dart2js_info/json_info_codec.dart';
import 'package:dart2js_info/binary_serialization.dart' as dump_info;
import '../compiler_new.dart';
import 'common/names.dart';
@ -16,7 +18,7 @@ import 'common.dart';
import 'common_elements.dart' show JElementEnvironment;
import 'compiler.dart' show Compiler;
import 'constants/values.dart' show ConstantValue, InterceptorConstantValue;
import 'deferred_load.dart' show OutputUnit;
import 'deferred_load.dart' show OutputUnit, deferredPartFileName;
import 'elements/entities.dart';
import 'inferrer/abstract_value_domain.dart';
import 'inferrer/types.dart'
@ -26,6 +28,7 @@ import 'js_backend/js_backend.dart' show JavaScriptBackend;
import 'universe/codegen_world_builder.dart';
import 'universe/world_impact.dart'
show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl;
import 'util/sink_adapter.dart';
import 'world.dart' show JClosedWorld;
class ElementInfoCollector {
@ -48,11 +51,11 @@ class ElementInfoCollector {
void run() {
dumpInfoTask._constantToNode.forEach((constant, node) {
// TODO(sigmund): add dependencies on other constants
var size = dumpInfoTask._nodeData[node].length;
var code = jsAst.prettyPrint(node,
enableMinification: compiler.options.enableMinification);
var span = dumpInfoTask._nodeData[node];
var info = new ConstantInfo(
size: size, code: code, outputUnit: _unitInfoForConstant(constant));
size: span.end - span.start,
code: [span],
outputUnit: _unitInfoForConstant(constant));
_constantToInfo[constant] = info;
result.constants.add(info);
});
@ -124,7 +127,7 @@ class ElementInfoCollector {
}
int size = dumpInfoTask.sizeOf(field);
String code = dumpInfoTask.codeOf(field);
List<CodeSpan> code = dumpInfoTask.codeOf(field);
// TODO(het): Why doesn't `size` account for the code size already?
if (code != null) size += code.length;
@ -261,7 +264,7 @@ class ElementInfoCollector {
: false,
isExternal: function.isExternal,
);
String code = dumpInfoTask.codeOf(function);
List<CodeSpan> code = dumpInfoTask.codeOf(function);
List<ParameterInfo> parameters = <ParameterInfo>[];
List<String> inferredParameterTypes = <String>[];
@ -340,8 +343,11 @@ class ElementInfoCollector {
// emitter is used it will fail here.
JavaScriptBackend backend = compiler.backend;
assert(outputUnit.name != null || outputUnit.isMainOutput);
OutputUnitInfo info = new OutputUnitInfo(
outputUnit.name, backend.emitter.emitter.generatedSize(outputUnit));
var filename = outputUnit.isMainOutput
? compiler.options.outputUri.pathSegments.last
: deferredPartFileName(compiler.options, outputUnit.name);
OutputUnitInfo info = new OutputUnitInfo(filename, outputUnit.name,
backend.emitter.emitter.generatedSize(outputUnit));
info.imports
.addAll(closedWorld.outputUnitData.getImportNames(outputUnit));
result.outputUnits.add(info);
@ -389,9 +395,10 @@ abstract class InfoReporter {
class DumpInfoTask extends CompilerTask implements InfoReporter {
static const ImpactUseCase IMPACT_USE = const ImpactUseCase('Dump info');
final Compiler compiler;
final bool useBinaryFormat;
DumpInfoTask(Compiler compiler)
: compiler = compiler,
DumpInfoTask(this.compiler)
: useBinaryFormat = compiler.options.useDumpInfoBinaryFormat,
super(compiler.measurer);
String get name => "Dump Info";
@ -404,7 +411,7 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
/// Data associated with javascript AST nodes. The map only contains keys for
/// nodes that we care about. Keys are automatically added when
/// [registerEntityAst] is called.
final Map<jsAst.Node, _CodeData> _nodeData = <jsAst.Node, _CodeData>{};
final Map<jsAst.Node, CodeSpan> _nodeData = <jsAst.Node, CodeSpan>{};
// A mapping from Dart Entities to Javascript AST Nodes.
final Map<Entity, List<jsAst.Node>> _entityToNodes =
@ -475,7 +482,7 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
_entityToNodes
.putIfAbsent(entity, () => new List<jsAst.Node>())
.add(code);
_nodeData[code] ??= _CodeData();
_nodeData[code] ??= useBinaryFormat ? new CodeSpan() : new _CodeData();
}
}
@ -484,34 +491,39 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
assert(_constantToNode[constant] == null ||
_constantToNode[constant] == code);
_constantToNode[constant] = code;
_nodeData[code] ??= _CodeData();
_nodeData[code] ??= useBinaryFormat ? new CodeSpan() : new _CodeData();
}
}
bool get shouldEmitText => !useBinaryFormat;
// TODO(sigmund): delete the stack once we stop emitting the source text.
List<_CodeData> _stack = [];
void enterNode(jsAst.Node node, int start) {
var data = _nodeData[node];
if (data != null) {
data?.start = start;
if (shouldEmitText && data != null) {
_stack.add(data);
data.start = start;
}
}
void emit(String string) {
// Note: historically we emitted the full body of classes and methods, so
// instance methods ended up emitted twice. Once we use a different
// encoding of dump info, we also plan to remove this duplication.
_stack.forEach((f) => f.text.write(string));
if (shouldEmitText) {
// Note: historically we emitted the full body of classes and methods, so
// instance methods ended up emitted twice. Once we use a different
// encoding of dump info, we also plan to remove this duplication.
_stack.forEach((f) => f._text.write(string));
}
}
void exitNode(jsAst.Node node, int start, int end, int closing) {
var data = _nodeData[node];
if (data == null) return;
var last = _stack.removeLast();
assert(data == last);
assert(data.start == start);
data.end = end;
data?.end = end;
if (shouldEmitText && data != null) {
var last = _stack.removeLast();
assert(data == last);
assert(data.start == start);
}
}
/// Returns the size of the source code that was generated for an entity.
@ -524,17 +536,16 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
}
}
int sizeOfNode(jsAst.Node node) => _nodeData[node].length ?? 0;
int sizeOfNode(jsAst.Node node) {
CodeSpan span = _nodeData[node];
if (span == null) return 0;
return span.end - span.start;
}
String codeOf(Entity entity) {
List<CodeSpan> codeOf(MemberEntity entity) {
List<jsAst.Node> code = _entityToNodes[entity];
if (code == null) return null;
// Concatenate rendered ASTs.
StringBuffer sb = new StringBuffer();
for (jsAst.Node ast in code) {
sb.writeln(_nodeData[ast].text);
}
return sb.toString();
if (code == null) return const [];
return code.map((ast) => _nodeData[ast]).toList();
}
void dumpInfo(JClosedWorld closedWorld,
@ -543,20 +554,46 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
infoCollector = new ElementInfoCollector(
compiler, this, closedWorld, globalInferenceResults)
..run();
StringBuffer jsonBuffer = new StringBuffer();
dumpInfoJson(jsonBuffer, closedWorld);
compiler.outputProvider.createOutputSink(
compiler.options.outputUri.pathSegments.last,
'info.json',
OutputType.dumpInfo)
..add(jsonBuffer.toString())
..close();
BasicInfo.resetIds();
var allInfo = buildDumpInfoData(closedWorld);
if (useBinaryFormat) {
dumpInfoBinary(allInfo);
} else {
dumpInfoJson(allInfo);
}
});
}
void dumpInfoJson(StringSink buffer, JClosedWorld closedWorld) {
void dumpInfoJson(AllInfo data) {
StringBuffer jsonBuffer = new StringBuffer();
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
ChunkedConversionSink<Object> sink = encoder.startChunkedConversion(
new StringConversionSink.fromStringSink(jsonBuffer));
sink.add(new AllInfoJsonCodec(isBackwardCompatible: true).encode(data));
compiler.outputProvider.createOutputSink(
compiler.options.outputUri.pathSegments.last,
'info.json',
OutputType.dumpInfo)
..add(jsonBuffer.toString())
..close();
compiler.reporter.reportInfo(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
'text': "View the dumped .info.json file at "
"https://dart-lang.github.io/dump-info-visualizer"
});
}
void dumpInfoBinary(AllInfo data) {
var name = compiler.options.outputUri.pathSegments.last + ".info.data";
Sink<List<int>> sink = new BinaryOutputSinkAdapter(compiler.outputProvider
.createBinarySink(compiler.options.outputUri.resolve(name)));
dump_info.encode(data, sink);
compiler.reporter.reportInfo(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
'text': "Use `package:dart2js_info` to parse and process the dumped "
".info.data file."
});
}
AllInfo buildDumpInfoData(JClosedWorld closedWorld) {
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
@ -573,8 +610,8 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
// Don't register dart2js builtin functions that are not recorded.
Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
if (useInfo == null) continue;
info.uses.add(
new DependencyInfo(useInfo, '${selection.receiverConstraint}'));
info.uses.add(new DependencyInfo(
useInfo, selection.receiverConstraint?.toString()));
}
}
@ -588,8 +625,8 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
for (Selection selection in uses) {
Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
if (useInfo == null) continue;
info.uses.add(
new DependencyInfo(useInfo, '${selection.receiverConstraint}'));
info.uses.add(new DependencyInfo(
useInfo, selection.receiverConstraint?.toString()));
}
}
@ -629,24 +666,14 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
isMirrorsUsed: closedWorld.backendUsage.isMirrorsUsed,
minified: compiler.options.enableMinification);
ChunkedConversionSink<Object> sink = encoder.startChunkedConversion(
new StringConversionSink.fromStringSink(buffer));
sink.add(new AllInfoJsonCodec().encode(result));
compiler.reporter.reportInfo(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
'text': "View the dumped .info.json file at "
"https://dart-lang.github.io/dump-info-visualizer"
});
return result;
}
}
/// Helper class to store what dump-info will show for a piece of code.
///
/// Currently we print out the actual text, in the future, we will only emit
/// start and end offsets.
class _CodeData {
int start;
int end;
StringBuffer text = new StringBuffer();
// TODO(sigmund): delete once we no longer emit text by default.
class _CodeData extends CodeSpan {
StringBuffer _text = new StringBuffer();
String get text => '$_text';
int get length => end - start;
}

View file

@ -152,12 +152,15 @@ class CompilerOptions implements DiagnosticOptions {
/// Whether to disable optimization for need runtime type information.
bool disableRtiOptimization = false;
/// Whether to emit a .json file with a summary of the information used by the
/// compiler during optimization. This includes resolution details,
/// dependencies between elements, results of type inference, and the output
/// code for each function.
/// Whether to emit a summary of the information used by the compiler during
/// optimization. This includes resolution details, dependencies between
/// elements, results of type inference, and data about generated code.
bool dumpInfo = false;
/// Whether to use the new dump-info binary format. This will be the default
/// after a transitional period.
bool useDumpInfoBinaryFormat = false;
/// Whether we allow passing an extra argument to `assert`, containing a
/// reason for why an assertion fails. (experimental)
///
@ -191,7 +194,7 @@ class CompilerOptions implements DiagnosticOptions {
/// Whether to generate a source-map file together with the output program.
bool generateSourceMap = true;
/// URI of the main output if the compiler is generating source maps.
/// URI of the main output of the compiler.
Uri outputUri;
/// Location of the libraries specification file.
@ -322,6 +325,8 @@ class CompilerOptions implements DiagnosticOptions {
..disableRtiOptimization =
_hasOption(options, Flags.disableRtiOptimization)
..dumpInfo = _hasOption(options, Flags.dumpInfo)
..useDumpInfoBinaryFormat =
_hasOption(options, "${Flags.dumpInfo}=binary")
..enableExperimentalMirrors =
_hasOption(options, Flags.enableExperimentalMirrors)
..enableMinification = _hasOption(options, Flags.minify)

View file

@ -153,10 +153,13 @@
"Dynamic access of 'index'.": 1
},
"third_party/pkg/dart2js_info/lib/json_info_codec.dart": {
"Dynamic invocation of '[]'.": 13,
"Dynamic invocation of 'forEach'.": 3,
"Dynamic invocation of '[]'.": 11,
"Dynamic invocation of 'forEach'.": 2,
"Dynamic invocation of 'map'.": 2,
"Dynamic access of 'length'.": 1
"Dynamic invocation of 'compareTo'.": 1
},
"third_party/pkg/dart2js_info/lib/binary_serialization.dart": {
"Dynamic invocation of 'cast'.": 1
},
"pkg/compiler/lib/src/util/enumset.dart": {
"Dynamic access of 'index'.": 4
@ -216,14 +219,13 @@
"Dynamic invocation of '>='.": 1,
"Dynamic invocation of 'codeUnitAt'.": 1
},
"third_party/pkg/dart2js_info/lib/src/measurements.dart": {
"Dynamic access of 'name'.": 1,
"Dynamic invocation of 'call'.": 1
},
"third_party/pkg/dart2js_info/lib/src/util.dart": {
"Dynamic access of 'name'.": 1,
"Dynamic invocation of '-'.": 1
},
"third_party/pkg/dart2js_info/lib/src/binary/sink.dart": {
"Dynamic access of 'index'.": 1
},
"pkg/js_ast/lib/src/builder.dart": {
"Dynamic invocation of 'call'.": 2
},

View file

@ -2,9 +2,12 @@
// 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.
// Test that parameters keep their names in the output.
import 'dart:convert';
import 'package:compiler/compiler_new.dart';
import 'package:dart2js_info/info.dart';
import 'package:dart2js_info/json_info_codec.dart';
import 'package:dart2js_info/binary_serialization.dart' as binary;
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:expect/expect.dart';
@ -96,92 +99,82 @@ const String TEST_INLINED_2 = r"""
main() => funcA();
""";
typedef void JsonTaking(Map<String, dynamic> json);
typedef InfoCheck = void Function(AllInfo);
jsonTest(String program, JsonTaking testFn) async {
infoTest(String program, bool useBinary, InfoCheck check) async {
var options = ['--out=out.js', Flags.dumpInfo];
// Note: we always pass '--dump-info' because the memory-compiler does not
// have the logic in dart2js.dart to imply dump-info when --dump-info=binary
// is provided.
if (useBinary) options.add("${Flags.dumpInfo}=binary");
var collector = new OutputCollector();
var result = await runCompiler(
memorySourceFiles: {'main.dart': program}, options: options);
memorySourceFiles: {'main.dart': program},
options: options,
outputProvider: collector);
var compiler = result.compiler;
Expect.isFalse(compiler.compilationFailed);
var dumpTask = compiler.dumpInfoTask;
StringBuffer sb = new StringBuffer();
dumpTask.dumpInfoJson(sb, compiler.backendClosedWorldForTesting);
String jsonString = sb.toString();
Map<String, dynamic> map = json.decode(jsonString);
testFn(map);
AllInfo info;
if (useBinary) {
var sink = collector.binaryOutputMap[Uri.parse('out.js.info.data')];
info = binary.decode(sink.list);
} else {
info = new AllInfoJsonCodec().decode(
json.decode(collector.getOutput("out.js", OutputType.dumpInfo)));
}
check(info);
}
main() {
asyncTest(() async {
print('--test from kernel------------------------------------------------');
await runTests();
await runTests(useBinary: false);
await runTests(useBinary: true);
});
}
runTests() async {
await jsonTest(TEST_BASIC, (map) {
Expect.isTrue(map['elements'].isNotEmpty);
Expect.isTrue(map['elements']['function'].isNotEmpty);
Expect.isTrue(map['elements']['library'].isNotEmpty);
Expect.isTrue(map['elements']['library'].values.any((lib) {
return lib['name'] == "main";
runTests({bool useBinary: false}) async {
await infoTest(TEST_BASIC, useBinary, (info) {
Expect.isTrue(info.functions.isNotEmpty);
Expect.isTrue(info.libraries.isNotEmpty);
Expect.isTrue(info.libraries.any((lib) => lib.name == "main"));
Expect.isTrue(info.classes.any((c) => c.name == 'c'));
Expect.isTrue(info.functions.any((f) => f.name == 'f'));
});
await infoTest(TEST_CLOSURES, useBinary, (info) {
Expect.isTrue(info.functions.any((fn) {
return fn.name == 'bar' && fn.closures.length == 11;
}));
Expect.isTrue(map['elements']['class'].values.any((clazz) {
return clazz['name'] == "c";
}));
Expect.isTrue(map['elements']['function'].values.any((fun) {
return fun['name'] == 'f';
Expect.isTrue(info.functions.any((fn) {
return fn.name == 'foo' && fn.closures.length == 10;
}));
});
await jsonTest(TEST_CLOSURES, (map) {
var functions = map['elements']['function'].values;
Expect.isTrue(functions.any((fn) {
return fn['name'] == 'bar' && fn['children'].length == 11;
}));
Expect.isTrue(functions.any((fn) {
return fn['name'] == 'foo' && fn['children'].length == 10;
await infoTest(TEST_STATICS, useBinary, (info) {
Expect.isTrue(info.functions.any((fn) => fn.name == 'does_something'));
Expect.isTrue(info.classes.any((cls) {
return cls.name == 'ContainsStatics' && cls.functions.length >= 1;
}));
});
await jsonTest(TEST_STATICS, (map) {
var functions = map['elements']['function'].values;
var classes = map['elements']['class'].values;
Expect.isTrue(functions.any((fn) {
return fn['name'] == 'does_something';
await infoTest(TEST_INLINED_1, useBinary, (info) {
Expect.isTrue(info.functions.any((fn) {
return fn.name == 'double' && fn.inlinedCount == 1;
}));
Expect.isTrue(classes.any((cls) {
return cls['name'] == 'ContainsStatics' && cls['children'].length >= 1;
Expect.isTrue(info.classes.any((cls) {
return cls.name == 'Doubler' && cls.functions.length >= 1;
}));
});
await jsonTest(TEST_INLINED_1, (map) {
var functions = map['elements']['function'].values;
var classes = map['elements']['class'].values;
Expect.isTrue(functions.any((fn) {
return fn['name'] == 'double' && fn['inlinedCount'] == 1;
}));
Expect.isTrue(classes.any((cls) {
return cls['name'] == 'Doubler' && cls['children'].length >= 1;
}));
});
await jsonTest(TEST_INLINED_2, (map) {
var functions = map['elements']['function'].values;
var deps = map['holding'];
var main_ = functions.firstWhere((v) => v['name'] == 'main');
var fn1 = functions.firstWhere((v) => v['name'] == 'funcA');
var fn2 = functions.firstWhere((v) => v['name'] == 'funcB');
await infoTest(TEST_INLINED_2, useBinary, (info) {
var main_ = info.functions.firstWhere((v) => v.name == 'main');
var fn1 = info.functions.firstWhere((v) => v.name == 'funcA');
var fn2 = info.functions.firstWhere((v) => v.name == 'funcB');
Expect.isTrue(main_ != null);
Expect.isTrue(fn1 != null);
Expect.isTrue(fn2 != null);
Expect.isTrue(deps.containsKey(main_['id']));
Expect.isTrue(deps.containsKey(fn1['id']));
Expect.isTrue(deps.containsKey(fn2['id']));
Expect.isTrue(deps[main_['id']].any((dep) => dep['id'] == fn1['id']));
Expect.isTrue(deps[fn1['id']].any((dep) => dep['id'] == fn2['id']));
Expect.isTrue(main_.uses.any((dep) => dep.target == fn1));
Expect.isTrue(fn1.uses.any((dep) => dep.target == fn2));
});
}

View file

@ -30,8 +30,10 @@ void main() {
Directory tmpDir = Directory.systemTemp.createTempSync('dump_info_test_');
Directory out1 = new Directory.fromUri(tmpDir.uri.resolve('without'));
out1.createSync();
Directory out2 = new Directory.fromUri(tmpDir.uri.resolve('with'));
Directory out2 = new Directory.fromUri(tmpDir.uri.resolve('json'));
out2.createSync();
Directory out3 = new Directory.fromUri(tmpDir.uri.resolve('binary'));
out3.createSync();
Directory appDir =
new Directory.fromUri(Uri.base.resolve('samples-dev/swarm'));
@ -52,7 +54,7 @@ void main() {
.readAsStringSync();
command =
dart2JsCommand(['--out=with/out.js', 'swarm.dart', '--dump-info']);
dart2JsCommand(['--out=json/out.js', 'swarm.dart', '--dump-info']);
print('Run $command');
result = Process.runSync(Platform.resolvedExecutable, command,
workingDirectory: tmpDir.path);
@ -63,10 +65,28 @@ void main() {
print(result.stderr);
Expect.equals(0, result.exitCode);
String output2 =
new File.fromUri(tmpDir.uri.resolve('with/out.js')).readAsStringSync();
new File.fromUri(tmpDir.uri.resolve('json/out.js')).readAsStringSync();
print('Compare outputs...');
Expect.equals(output1, output2);
command = dart2JsCommand(
['--out=binary/out.js', 'swarm.dart', '--dump-info=binary']);
print('Run $command');
result = Process.runSync(Platform.resolvedExecutable, command,
workingDirectory: tmpDir.path);
print('exit code: ${result.exitCode}');
print('stdout:');
print(result.stdout);
print('stderr:');
print(result.stderr);
Expect.equals(0, result.exitCode);
String output3 = new File.fromUri(tmpDir.uri.resolve('binary/out.js'))
.readAsStringSync();
print('Compare outputs...');
Expect.equals(output1, output3);
print('Done');
} finally {
print("Deleting '${tmpDir.path}'.");