mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:18:01 +00:00
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:
parent
372537f1e4
commit
e3b8065625
32
CHANGELOG.md
32
CHANGELOG.md
|
@ -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
2
DEPS
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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}'.");
|
||||
|
|
Loading…
Reference in a new issue