Support modular code generation
Change-Id: Id5511296eb0b6acf812bc464d71efa4019f211d7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103523
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/backend_strategy.dart b/pkg/compiler/lib/src/backend_strategy.dart
index 44c29be..d9a48b1 100644
--- a/pkg/compiler/lib/src/backend_strategy.dart
+++ b/pkg/compiler/lib/src/backend_strategy.dart
@@ -5,6 +5,7 @@
library dart2js.backend_strategy;
import 'common.dart';
+import 'common/codegen.dart';
import 'common/tasks.dart';
import 'deferred_load.dart' show OutputUnitData;
import 'enqueue.dart';
@@ -14,6 +15,7 @@
import 'js_backend/inferred_data.dart';
import 'js_backend/interceptor_data.dart';
import 'js_backend/native_data.dart';
+import 'serialization/serialization.dart';
import 'ssa/ssa.dart';
import 'universe/codegen_world_builder.dart';
import 'universe/world_builder.dart';
@@ -40,7 +42,8 @@
OneShotInterceptorData oneShotInterceptorData);
/// Creates the [WorkItemBuilder] used by the codegen enqueuer.
- WorkItemBuilder createCodegenWorkItemBuilder(JClosedWorld closedWorld);
+ WorkItemBuilder createCodegenWorkItemBuilder(
+ JClosedWorld closedWorld, CodegenResults codegenResults);
/// Creates the [SsaBuilder] used for the element model.
SsaBuilder createSsaBuilder(
@@ -55,4 +58,15 @@
/// Creates the [TypesInferrer] used by this strategy.
TypesInferrer createTypesInferrer(
JClosedWorld closedWorld, InferredDataBuilder inferredDataBuilder);
+
+ /// Calls [f] for every member that needs to be serialized for modular code
+ /// generation and returns an [EntityWriter] for encoding these members in
+ /// the serialized data.
+ ///
+ /// The needed members include members computed on demand during non-modular
+ /// code generation, such as constructor bodies and and generator bodies.
+ EntityWriter forEachCodegenMember(void Function(MemberEntity member) f);
+
+ /// Prepare [source] to deserialize modular code generation data.
+ void prepareCodegenReader(DataSource source);
}
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 576cfc4..801a18f 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -84,6 +84,10 @@
static const String dillDependencies = '--dill-dependencies';
static const String readData = '--read-data';
static const String writeData = '--write-data';
+ static const String readCodegen = '--read-codegen';
+ static const String writeCodegen = '--write-codegen';
+ static const String codegenShard = '--codegen-shard';
+ static const String codegenShards = '--codegen-shards';
static const String cfeOnly = '--cfe-only';
static const String debugGlobalInference = '--debug-global-inference';
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index 153c54b..928e70d 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -6,18 +6,22 @@
import 'package:js_ast/src/precedence.dart' as js show PRIMARY;
+import '../common.dart';
import '../common_elements.dart';
import '../constants/values.dart';
import '../deferred_load.dart';
import '../elements/entities.dart';
import '../elements/types.dart' show DartType, InterfaceType;
import '../inferrer/abstract_value_domain.dart';
+import '../inferrer/types.dart';
import '../io/source_information.dart';
import '../js/js.dart' as js;
+import '../js_backend/backend.dart';
import '../js_backend/namer.dart';
import '../js_emitter/code_emitter_task.dart' show Emitter;
import '../native/behavior.dart';
import '../serialization/serialization.dart';
+import '../ssa/ssa.dart';
import '../universe/feature.dart';
import '../universe/selector.dart';
import '../universe/use.dart' show ConstantUse, DynamicUse, StaticUse, TypeUse;
@@ -414,6 +418,58 @@
}
}
+/// Interface for reading the code generation results for all [MemberEntity]s.
+abstract class CodegenResults {
+ GlobalTypeInferenceResults get globalTypeInferenceResults;
+ CodegenInputs get codegenInputs;
+ CodegenResult getCodegenResults(MemberEntity member);
+}
+
+/// Code generation results computed on-demand.
+///
+/// This is used in the non-modular codegen enqueuer driving code generation.
+class OnDemandCodegenResults extends CodegenResults {
+ @override
+ final GlobalTypeInferenceResults globalTypeInferenceResults;
+ @override
+ final CodegenInputs codegenInputs;
+ final SsaFunctionCompiler _functionCompiler;
+
+ OnDemandCodegenResults(this.globalTypeInferenceResults, this.codegenInputs,
+ this._functionCompiler);
+
+ @override
+ CodegenResult getCodegenResults(MemberEntity member) {
+ return _functionCompiler.compile(member);
+ }
+}
+
+/// Deserialized code generation results.
+///
+/// This is used for modular code generation.
+class DeserializedCodegenResults extends CodegenResults {
+ @override
+ final GlobalTypeInferenceResults globalTypeInferenceResults;
+ @override
+ final CodegenInputs codegenInputs;
+
+ final Map<MemberEntity, CodegenResult> _map;
+
+ DeserializedCodegenResults(
+ this.globalTypeInferenceResults, this.codegenInputs, this._map);
+
+ @override
+ CodegenResult getCodegenResults(MemberEntity member) {
+ CodegenResult result = _map[member];
+ if (result == null) {
+ failedAt(member,
+ "No codegen results from $member (${identityHashCode(member)}).");
+ }
+ return result;
+ }
+}
+
+/// The code generation result for a single [MemberEntity].
class CodegenResult {
static const String tag = 'codegen-result';
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 2988fbb..85057ab 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -11,6 +11,7 @@
import '../compiler_new.dart' as api;
import 'backend_strategy.dart';
+import 'common/codegen.dart';
import 'common/names.dart' show Selectors, Uris;
import 'common/tasks.dart' show CompilerTask, GenericTask, Measurer;
import 'common/work.dart' show WorkItem;
@@ -171,7 +172,8 @@
enqueuer,
dumpInfoTask = new DumpInfoTask(this),
selfTask,
- serializationTask = new SerializationTask(this, measurer),
+ serializationTask = new SerializationTask(
+ options, reporter, provider, outputProvider, measurer),
];
tasks.addAll(backend.tasks);
@@ -227,13 +229,14 @@
reporter.log('Compiling $uri (${options.buildId})');
if (options.readDataUri != null) {
- GlobalTypeInferenceResults results =
- await serializationTask.deserialize();
+ GlobalTypeInferenceResults globalTypeInferenceResults =
+ await serializationTask.deserializeGlobalTypeInference(
+ environment, abstractValueStrategy);
if (options.debugGlobalInference) {
- performGlobalTypeInference(results.closedWorld);
+ performGlobalTypeInference(globalTypeInferenceResults.closedWorld);
return;
}
- emitJavaScriptCode(results);
+ await generateJavaScriptCode(globalTypeInferenceResults);
} else {
KernelResult result = await kernelLoader.load(uri);
reporter.log("Kernel load complete");
@@ -263,6 +266,31 @@
}
}
+ void generateJavaScriptCode(
+ GlobalTypeInferenceResults globalTypeInferenceResults) async {
+ JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
+ backendStrategy.registerJClosedWorld(closedWorld);
+ if (options.showInternalProgress) reporter.log('Compiling...');
+ phase = PHASE_COMPILING;
+ CodegenInputs codegenInputs =
+ backend.onCodegenStart(globalTypeInferenceResults);
+
+ if (options.readCodegenUri != null) {
+ CodegenResults codegenResults =
+ await serializationTask.deserializeCodegen(
+ backendStrategy, globalTypeInferenceResults, codegenInputs);
+ runCodegenEnqueuer(codegenResults);
+ } else {
+ CodegenResults codegenResults = new OnDemandCodegenResults(
+ globalTypeInferenceResults, codegenInputs, backend.functionCompiler);
+ if (options.writeCodegenUri != null) {
+ serializationTask.serializeCodegen(backendStrategy, codegenResults);
+ } else {
+ runCodegenEnqueuer(codegenResults);
+ }
+ }
+ }
+
/// Clear the internal compiler state to prevent memory leaks when invoking
/// the compiler multiple times (e.g. in batch mode).
// TODO(ahe): implement a better mechanism where we can store
@@ -355,15 +383,13 @@
mainFunction, closedWorld, inferredDataBuilder);
}
- void emitJavaScriptCode(GlobalTypeInferenceResults globalInferenceResults) {
+ void runCodegenEnqueuer(CodegenResults codegenResults) {
+ GlobalTypeInferenceResults globalInferenceResults =
+ codegenResults.globalTypeInferenceResults;
JClosedWorld closedWorld = globalInferenceResults.closedWorld;
- backendStrategy.registerJClosedWorld(closedWorld);
- if (options.showInternalProgress) reporter.log('Compiling...');
- phase = PHASE_COMPILING;
-
- CodegenInputs codegen = backend.onCodegenStart(globalInferenceResults);
+ CodegenInputs codegenInputs = codegenResults.codegenInputs;
Enqueuer codegenEnqueuer = enqueuer.createCodegenEnqueuer(
- closedWorld, globalInferenceResults, codegen);
+ closedWorld, globalInferenceResults, codegenInputs, codegenResults);
_codegenWorldBuilder = codegenEnqueuer.worldBuilder;
FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction;
@@ -375,14 +401,14 @@
codegenWorldForTesting = codegenWorld;
}
int programSize = backend.assembleProgram(closedWorld,
- globalInferenceResults.inferredData, codegen, codegenWorld);
+ globalInferenceResults.inferredData, codegenInputs, codegenWorld);
if (options.dumpInfo) {
dumpInfoTask.reportSize(programSize);
dumpInfoTask.dumpInfo(closedWorld, globalInferenceResults);
}
- backend.onCodegenEnd(codegen);
+ backend.onCodegenEnd(codegenInputs);
checkQueue(codegenEnqueuer);
}
@@ -397,7 +423,8 @@
GlobalTypeInferenceResults globalInferenceResults =
performGlobalTypeInference(closedWorld);
if (options.writeDataUri != null) {
- serializationTask.serialize(globalInferenceResults);
+ serializationTask
+ .serializeGlobalTypeInference(globalInferenceResults);
return;
}
if (options.testMode) {
@@ -415,7 +442,7 @@
worldData);
}
if (stopAfterTypeInference) return;
- emitJavaScriptCode(globalInferenceResults);
+ generateJavaScriptCode(globalInferenceResults);
}
});
}
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index ebdc6a0..7348d20 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -110,6 +110,10 @@
Uri sourceMapOut;
Uri readDataUri;
Uri writeDataUri;
+ Uri readCodegenUri;
+ Uri writeCodegenUri;
+ int codegenShard;
+ int codegenShards;
List<String> bazelPaths;
Uri packageConfig = null;
Uri packageRoot = null;
@@ -129,7 +133,8 @@
int optimizationLevel = null;
Uri platformBinaries;
Map<String, String> environment = new Map<String, String>();
- CompilationStrategy compilationStrategy = CompilationStrategy.direct;
+ ReadStrategy readStrategy = ReadStrategy.fromDart;
+ WriteStrategy writeStrategy = WriteStrategy.toJs;
void passThrough(String argument) => options.add(argument);
void ignoreOption(String argument) {}
@@ -246,13 +251,12 @@
}
void setReadData(String argument) {
- if (compilationStrategy == CompilationStrategy.toData) {
- fail("Cannot read and write serialized simultaneously.");
- }
if (argument != Flags.readData) {
readDataUri = nativeToUri(extractPath(argument, isDirectory: false));
}
- compilationStrategy = CompilationStrategy.fromData;
+ if (readStrategy != ReadStrategy.fromCodegen) {
+ readStrategy = ReadStrategy.fromData;
+ }
}
void setDillDependencies(String argument) {
@@ -263,17 +267,58 @@
}
void setCfeOnly(String argument) {
- compilationStrategy = CompilationStrategy.toKernel;
+ if (writeStrategy == WriteStrategy.toData) {
+ fail("Cannot use ${Flags.cfeOnly} "
+ "and write serialized data simultaneously.");
+ }
+ if (writeStrategy == WriteStrategy.toCodegen) {
+ fail("Cannot use ${Flags.cfeOnly} "
+ "and write serialized codegen simultaneously.");
+ }
+ writeStrategy = WriteStrategy.toKernel;
+ }
+
+ void setReadCodegen(String argument) {
+ if (argument != Flags.readCodegen) {
+ readCodegenUri = nativeToUri(extractPath(argument, isDirectory: false));
+ }
+ readStrategy = ReadStrategy.fromCodegen;
}
void setWriteData(String argument) {
- if (compilationStrategy == CompilationStrategy.fromData) {
- fail("Cannot read and write serialized simultaneously.");
+ if (writeStrategy == WriteStrategy.toKernel) {
+ fail("Cannot use ${Flags.cfeOnly} "
+ "and write serialized data simultaneously.");
+ }
+ if (writeStrategy == WriteStrategy.toCodegen) {
+ fail("Cannot write serialized data and codegen simultaneously.");
}
if (argument != Flags.writeData) {
writeDataUri = nativeToUri(extractPath(argument, isDirectory: false));
}
- compilationStrategy = CompilationStrategy.toData;
+ writeStrategy = WriteStrategy.toData;
+ }
+
+ void setWriteCodegen(String argument) {
+ if (writeStrategy == WriteStrategy.toKernel) {
+ fail("Cannot use ${Flags.cfeOnly} "
+ "and write serialized codegen simultaneously.");
+ }
+ if (writeStrategy == WriteStrategy.toData) {
+ fail("Cannot write serialized data and codegen data simultaneously.");
+ }
+ if (argument != Flags.writeCodegen) {
+ writeCodegenUri = nativeToUri(extractPath(argument, isDirectory: false));
+ }
+ writeStrategy = WriteStrategy.toCodegen;
+ }
+
+ void setCodegenShard(String argument) {
+ codegenShard = int.parse(extractParameter(argument));
+ }
+
+ void setCodegenShards(String argument) {
+ codegenShards = int.parse(extractParameter(argument));
}
void setDumpInfo(String argument) {
@@ -349,6 +394,12 @@
new OptionHandler('${Flags.dillDependencies}=.+', setDillDependencies),
new OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
new OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
+ new OptionHandler(
+ '${Flags.readCodegen}|${Flags.readCodegen}=.+', setReadCodegen),
+ new OptionHandler(
+ '${Flags.writeCodegen}|${Flags.writeCodegen}=.+', setWriteCodegen),
+ new OptionHandler('${Flags.codegenShard}=.+', setCodegenShard),
+ new OptionHandler('${Flags.codegenShards}=.+', setCodegenShards),
new OptionHandler(Flags.cfeOnly, setCfeOnly),
new OptionHandler(Flags.debugGlobalInference, passThrough),
new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
@@ -511,28 +562,86 @@
String scriptName = arguments[0];
- switch (compilationStrategy) {
- case CompilationStrategy.direct:
+ switch (writeStrategy) {
+ case WriteStrategy.toJs:
out ??= currentDirectory.resolve('out.js');
break;
- case CompilationStrategy.toKernel:
+ case WriteStrategy.toKernel:
out ??= currentDirectory.resolve('out.dill');
options.add(Flags.cfeOnly);
+ if (readStrategy == ReadStrategy.fromData) {
+ fail("Cannot use ${Flags.cfeOnly} "
+ "and read serialized data simultaneously.");
+ } else if (readStrategy == ReadStrategy.fromCodegen) {
+ fail("Cannot use ${Flags.cfeOnly} "
+ "and read serialized codegen simultaneously.");
+ }
break;
- case CompilationStrategy.toData:
+ case WriteStrategy.toData:
out ??= currentDirectory.resolve('out.dill');
writeDataUri ??= currentDirectory.resolve('$out.data');
options.add('${Flags.writeData}=${writeDataUri}');
+ if (readStrategy == ReadStrategy.fromData) {
+ fail("Cannot read and write serialized data simultaneously.");
+ } else if (readStrategy == ReadStrategy.fromCodegen) {
+ fail("Cannot read serialized codegen and "
+ "write serialized data simultaneously.");
+ }
break;
- case CompilationStrategy.fromData:
- out ??= currentDirectory.resolve('out.js');
+ case WriteStrategy.toCodegen:
+ // TODO(johnniwinther): Avoid the need for an [out] value in this case or
+ // use [out] to pass [writeCodegenUri].
+ out ??= currentDirectory.resolve('out');
+ writeCodegenUri ??= currentDirectory.resolve('$out.code');
+ options.add('${Flags.writeCodegen}=${writeCodegenUri}');
+ if (readStrategy == ReadStrategy.fromCodegen) {
+ fail("Cannot read and write serialized codegen simultaneously.");
+ }
+ if (readStrategy != ReadStrategy.fromData) {
+ fail("Can only write serialized codegen from serialized data.");
+ }
+ if (codegenShards == null) {
+ fail("Cannot write serialized codegen without setting "
+ "${Flags.codegenShards}.");
+ } else if (codegenShards <= 0) {
+ fail("${Flags.codegenShards} must be a positive integer.");
+ }
+ if (codegenShard == null) {
+ fail("Cannot write serialized codegen without setting "
+ "${Flags.codegenShard}.");
+ } else if (codegenShard < 0 || codegenShard >= codegenShards) {
+ fail("${Flags.codegenShard} must be between 0 and "
+ "${Flags.codegenShards}.");
+ }
+ options.add('${Flags.codegenShard}=$codegenShard');
+ options.add('${Flags.codegenShards}=$codegenShards');
+ break;
+ }
+ switch (readStrategy) {
+ case ReadStrategy.fromDart:
+ break;
+ case ReadStrategy.fromData:
readDataUri ??= currentDirectory.resolve('$scriptName.data');
options.add('${Flags.readData}=${readDataUri}');
break;
+ case ReadStrategy.fromCodegen:
+ readDataUri ??= currentDirectory.resolve('$scriptName.data');
+ options.add('${Flags.readData}=${readDataUri}');
+ readCodegenUri ??= currentDirectory.resolve('$scriptName.code');
+ options.add('${Flags.readCodegen}=${readCodegenUri}');
+ if (codegenShards == null) {
+ fail("Cannot write serialized codegen without setting "
+ "${Flags.codegenShards}.");
+ } else if (codegenShards <= 0) {
+ fail("${Flags.codegenShards} must be a positive integer.");
+ }
+ options.add('${Flags.codegenShards}=$codegenShards');
}
options.add('--out=$out');
- sourceMapOut = Uri.parse('$out.map');
- options.add('--source-map=${sourceMapOut}');
+ if (writeStrategy == WriteStrategy.toJs) {
+ sourceMapOut = Uri.parse('$out.map');
+ options.add('--source-map=${sourceMapOut}');
+ }
RandomAccessFileOutputProvider outputProvider =
new RandomAccessFileOutputProvider(out, sourceMapOut,
@@ -544,86 +653,98 @@
}
writeString(
Uri.parse('$out.deps'), getDepsOutput(inputProvider.getSourceUris()));
- switch (compilationStrategy) {
- case CompilationStrategy.direct:
- int dartCharactersRead = inputProvider.dartCharactersRead;
- int jsCharactersWritten =
- outputProvider.totalCharactersWrittenJavaScript;
- int jsCharactersPrimary = outputProvider.totalCharactersWrittenPrimary;
- print('Compiled '
- '${_formatCharacterCount(dartCharactersRead)} characters Dart to '
- '${_formatCharacterCount(jsCharactersWritten)} characters '
- 'JavaScript in '
- '${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
- diagnosticHandler
- .info('${_formatCharacterCount(jsCharactersPrimary)} characters '
- 'JavaScript in '
- '${relativize(currentDirectory, out, Platform.isWindows)}');
- if (outputSpecified || diagnosticHandler.verbose) {
- String input = uriPathToNative(scriptName);
- String output = relativize(currentDirectory, out, Platform.isWindows);
- print('Dart file ($input) compiled to JavaScript: $output');
- if (diagnosticHandler.verbose) {
- var files = outputProvider.allOutputFiles;
- int jsCount = files.where((f) => f.endsWith('.js')).length;
- print('Emitted file $jsCount JavaScript files.');
- }
- }
+ String input = uriPathToNative(scriptName);
+ int inputSize;
+ String processName;
+ String inputName;
+
+ int outputSize;
+ int primaryOutputSize;
+ String outputName;
+
+ String summary;
+ switch (readStrategy) {
+ case ReadStrategy.fromDart:
+ inputName = 'characters Dart';
+ inputSize = inputProvider.dartCharactersRead;
+ summary = 'Dart file $input ';
break;
- case CompilationStrategy.toKernel:
- int dartCharactersRead = inputProvider.dartCharactersRead;
- int dataBytesWritten = outputProvider.totalDataWritten;
- print('Compiled '
- '${_formatCharacterCount(dartCharactersRead)} characters Dart to '
- '${_formatCharacterCount(dataBytesWritten)} kernel bytes in '
- '${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
- String input = uriPathToNative(scriptName);
- String dillOutput =
- relativize(currentDirectory, out, Platform.isWindows);
- print('Dart file ($input) compiled to ${dillOutput}.');
+ case ReadStrategy.fromData:
+ inputName = 'bytes data';
+ inputSize = inputProvider.dartCharactersRead;
+ String dataInput =
+ relativize(currentDirectory, readDataUri, Platform.isWindows);
+ summary = 'Data files $input and $dataInput ';
break;
- case CompilationStrategy.toData:
- int dartCharactersRead = inputProvider.dartCharactersRead;
- int dataBytesWritten = outputProvider.totalDataWritten;
- print('Serialized '
- '${_formatCharacterCount(dartCharactersRead)} characters Dart to '
- '${_formatCharacterCount(dataBytesWritten)} bytes data in '
- '${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
- String input = uriPathToNative(scriptName);
- String dillOutput =
- relativize(currentDirectory, out, Platform.isWindows);
+ case ReadStrategy.fromCodegen:
+ inputName = 'bytes data';
+ inputSize = inputProvider.dartCharactersRead;
+ String dataInput =
+ relativize(currentDirectory, readDataUri, Platform.isWindows);
+ String codeInput =
+ relativize(currentDirectory, readCodegenUri, Platform.isWindows);
+ summary = 'Data files $input, $dataInput and '
+ '${codeInput}[0-${codegenShards - 1}] ';
+ break;
+ }
+
+ switch (writeStrategy) {
+ case WriteStrategy.toJs:
+ processName = 'Compiled';
+ outputName = 'characters JavaScript';
+ outputSize = outputProvider.totalCharactersWrittenJavaScript;
+ primaryOutputSize = outputProvider.totalCharactersWrittenPrimary;
+ String output = relativize(currentDirectory, out, Platform.isWindows);
+ summary += 'compiled to JavaScript: ${output}';
+ break;
+ case WriteStrategy.toKernel:
+ processName = 'Compiled';
+ outputName = 'kernel bytes';
+ outputSize = outputProvider.totalDataWritten;
+ String output = relativize(currentDirectory, out, Platform.isWindows);
+ summary += 'compiled to dill: ${output}.';
+ break;
+ case WriteStrategy.toData:
+ processName = 'Serialized';
+ outputName = 'bytes data';
+ outputSize = outputProvider.totalDataWritten;
+ String output = relativize(currentDirectory, out, Platform.isWindows);
String dataOutput =
relativize(currentDirectory, writeDataUri, Platform.isWindows);
- print('Dart file ($input) serialized to '
- '${dillOutput} and ${dataOutput}.');
+ summary += 'serialized to dill and data: ${output} and ${dataOutput}.';
break;
- case CompilationStrategy.fromData:
- int dataCharactersRead = inputProvider.dartCharactersRead;
- int jsCharactersWritten =
- outputProvider.totalCharactersWrittenJavaScript;
- int jsCharactersPrimary = outputProvider.totalCharactersWrittenPrimary;
- print('Compiled '
- '${_formatCharacterCount(dataCharactersRead)} bytes data to '
- '${_formatCharacterCount(jsCharactersWritten)} characters '
- 'JavaScript in '
- '${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
+ case WriteStrategy.toCodegen:
+ processName = 'Serialized';
+ outputName = 'bytes data';
+ outputSize = outputProvider.totalDataWritten;
+ String codeOutput =
+ relativize(currentDirectory, writeCodegenUri, Platform.isWindows);
+ summary += 'serialized to codegen data: '
+ '${codeOutput}${codegenShard}.';
+ break;
+ }
- diagnosticHandler
- .info('${_formatCharacterCount(jsCharactersPrimary)} characters '
- 'JavaScript in '
- '${relativize(currentDirectory, out, Platform.isWindows)}');
- if (outputSpecified || diagnosticHandler.verbose) {
- String input = uriPathToNative(scriptName);
- String output = relativize(currentDirectory, out, Platform.isWindows);
- print('Dart file ($input) compiled to JavaScript: $output');
- if (diagnosticHandler.verbose) {
- var files = outputProvider.allOutputFiles;
- int jsCount = files.where((f) => f.endsWith('.js')).length;
- print('Emitted file $jsCount JavaScript files.');
- }
+ print('$processName '
+ '${_formatCharacterCount(inputSize)} $inputName to '
+ '${_formatCharacterCount(outputSize)} $outputName in '
+ '${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
+ if (primaryOutputSize != null) {
+ diagnosticHandler
+ .info('${_formatCharacterCount(primaryOutputSize)} $outputName '
+ 'in ${relativize(currentDirectory, out, Platform.isWindows)}');
+ }
+ if (writeStrategy == WriteStrategy.toJs) {
+ if (outputSpecified || diagnosticHandler.verbose) {
+ print(summary);
+ if (diagnosticHandler.verbose) {
+ var files = outputProvider.allOutputFiles;
+ int jsCount = files.where((f) => f.endsWith('.js')).length;
+ print('Emitted file $jsCount JavaScript files.');
}
- break;
+ }
+ } else {
+ print(summary);
}
return result;
@@ -1043,4 +1164,5 @@
});
}
-enum CompilationStrategy { direct, toKernel, toData, fromData }
+enum ReadStrategy { fromDart, fromData, fromCodegen }
+enum WriteStrategy { toKernel, toData, toCodegen, toJs }
diff --git a/pkg/compiler/lib/src/elements/indexed.dart b/pkg/compiler/lib/src/elements/indexed.dart
index e83b8a0..6a6afc4 100644
--- a/pkg/compiler/lib/src/elements/indexed.dart
+++ b/pkg/compiler/lib/src/elements/indexed.dart
@@ -56,6 +56,8 @@
/// Base implementation for an index based map of entities of type [E].
abstract class EntityMapBase<E extends _Indexed> {
+ bool _closed = false;
+
int _size = 0;
List<E> _list = <E>[];
@@ -67,6 +69,14 @@
/// Returns the number (null and non-null) entities in the map.
int get length => _list.length;
+
+ /// Closes the entity map, prohibiting further registration.
+ ///
+ /// This is used to ensure that no new entities are added while serializing
+ /// modular code generation data.
+ void close() {
+ _closed = true;
+ }
}
/// Index based map of entities of type [E].
@@ -76,6 +86,8 @@
/// The index of [entity] is set to match its index in the entity list in this
/// map.
E0 register<E0 extends E>(E0 entity) {
+ assert(
+ !_closed, "Trying to register $entity @ ${_list.length} when closed.");
assert(entity != null);
assert(entity._index == null);
entity._index = _list.length;
@@ -134,6 +146,8 @@
/// The index of [entity] is set to match its index in the entity and data
/// lists in this map.
E0 register<E0 extends E, D0 extends D>(E0 entity, D0 data) {
+ assert(
+ !_closed, "Trying to register $entity @ ${_list.length} when closed.");
assert(entity != null);
assert(entity._index == null);
assert(
@@ -199,6 +213,8 @@
/// environment lists in this map.
E0 register<E0 extends E, D0 extends D, V0 extends V>(
E0 entity, D0 data, V0 env) {
+ assert(
+ !_closed, "Trying to register $entity @ ${_list.length} when closed.");
assert(entity != null);
assert(entity._index == null);
assert(
diff --git a/pkg/compiler/lib/src/enqueue.dart b/pkg/compiler/lib/src/enqueue.dart
index 5439e6e..81ca07d 100644
--- a/pkg/compiler/lib/src/enqueue.dart
+++ b/pkg/compiler/lib/src/enqueue.dart
@@ -6,6 +6,7 @@
import 'dart:collection' show Queue;
+import 'common/codegen.dart';
import 'common/tasks.dart' show CompilerTask;
import 'common/work.dart' show WorkItem;
import 'common.dart';
@@ -68,9 +69,10 @@
Enqueuer createCodegenEnqueuer(
JClosedWorld closedWorld,
GlobalTypeInferenceResults globalInferenceResults,
- CodegenInputs codegen) {
- Enqueuer enqueuer = compiler.backend.createCodegenEnqueuer(
- this, compiler, closedWorld, globalInferenceResults, codegen)
+ CodegenInputs codegenInputs,
+ CodegenResults codegenResults) {
+ Enqueuer enqueuer = compiler.backend.createCodegenEnqueuer(this, compiler,
+ closedWorld, globalInferenceResults, codegenInputs, codegenResults)
..onEmptyForTesting = compiler.onCodegenQueueEmptyForTesting;
if (retainDataForTesting) {
codegenEnqueuerForTesting = enqueuer;
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index ea610ea..3ced9a0 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -535,7 +535,8 @@
Compiler compiler,
JClosedWorld closedWorld,
GlobalTypeInferenceResults globalInferenceResults,
- CodegenInputs codegen) {
+ CodegenInputs codegen,
+ CodegenResults codegenResults) {
OneShotInterceptorData oneShotInterceptorData = new OneShotInterceptorData(
closedWorld.interceptorData,
closedWorld.commonElements,
@@ -555,7 +556,8 @@
closedWorld,
compiler.abstractValueStrategy.createSelectorStrategy(),
oneShotInterceptorData),
- compiler.backendStrategy.createCodegenWorkItemBuilder(closedWorld),
+ compiler.backendStrategy
+ .createCodegenWorkItemBuilder(closedWorld, codegenResults),
new CodegenEnqueuerListener(
elementEnvironment,
commonElements,
@@ -568,10 +570,14 @@
Map<MemberEntity, WorldImpact> codegenImpactsForTesting;
- WorldImpact generateCode(WorkItem work, JClosedWorld closedWorld,
- EntityLookup entityLookup, ComponentLookup componentLookup) {
+ WorldImpact generateCode(
+ WorkItem work,
+ JClosedWorld closedWorld,
+ CodegenResults codegenResults,
+ EntityLookup entityLookup,
+ ComponentLookup componentLookup) {
MemberEntity member = work.element;
- CodegenResult result = functionCompiler.compile(member);
+ CodegenResult result = codegenResults.getCodegenResults(member);
if (compiler.options.testMode) {
bool useDataKinds = true;
List<Object> data = [];
@@ -617,9 +623,9 @@
/// Generates the output and returns the total size of the generated code.
int assembleProgram(JClosedWorld closedWorld, InferredData inferredData,
- CodegenInputs codegen, CodegenWorld codegenWorld) {
+ CodegenInputs codegenInputs, CodegenWorld codegenWorld) {
int programSize = emitterTask.assembleProgram(
- _namer, closedWorld, inferredData, codegen, codegenWorld);
+ _namer, closedWorld, inferredData, codegenInputs, codegenWorld);
closedWorld.noSuchMethodData.emitDiagnostic(reporter);
return programSize;
}
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index e5c066f..5393c9b 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -107,10 +107,10 @@
Namer namer,
JClosedWorld closedWorld,
InferredData inferredData,
- CodegenInputs codegen,
+ CodegenInputs codegenInputs,
CodegenWorld codegenWorld) {
return measure(() {
- _finalizeRti(codegen, codegenWorld);
+ _finalizeRti(codegenInputs, codegenWorld);
ProgramBuilder programBuilder = new ProgramBuilder(
_compiler.options,
_compiler.reporter,
@@ -124,7 +124,7 @@
closedWorld.rtiNeed,
closedWorld.interceptorData,
typeTestRegistry.rtiChecks,
- codegen.rtiEncoder,
+ codegenInputs.rtiEncoder,
codegenWorld.oneShotInterceptorData,
_backend.customElementsCodegenAnalysis,
_backend.generatedCode,
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 5356e84..5c46220 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -441,6 +441,31 @@
source.end(tag);
}
+ /// Prepares the entity maps for codegen serialization and returns the member
+ /// index limit for early members.
+ ///
+ /// This method creates all late members, such as constructor bodies and
+ /// generator bodies, and closes the entity maps for further registration.
+ int prepareForCodegenSerialization() {
+ int length = members.length;
+ for (int memberIndex = 0; memberIndex < length; memberIndex++) {
+ MemberEntity member = members.getEntity(memberIndex);
+ if (member == null) continue;
+ if (member is JGenerativeConstructor) {
+ getConstructorBody(members.getData(member).definition.node);
+ }
+ if (member is IndexedFunction && member.asyncMarker != AsyncMarker.SYNC) {
+ getGeneratorBody(member);
+ }
+ }
+ libraries.close();
+ classes.close();
+ members.close();
+ typedefs.close();
+ typeVariables.close();
+ return length;
+ }
+
/// Serializes this [JsToElementMap] to [sink].
void writeToDataSink(DataSink sink) {
sink.begin(tag);
@@ -1604,27 +1629,27 @@
@override
FunctionEntity getConstructorBody(ir.Constructor node) {
ConstructorEntity constructor = getConstructor(node);
- return _getConstructorBody(node, constructor);
+ return _getConstructorBody(constructor);
}
- FunctionEntity _getConstructorBody(
- ir.Constructor node, covariant IndexedConstructor constructor) {
+ JConstructorBody _getConstructorBody(IndexedConstructor constructor) {
JConstructorDataImpl data = members.getData(constructor);
if (data.constructorBody == null) {
/// The constructor calls the constructor body with all parameters.
// TODO(johnniwinther): Remove parameters that are not used in the
// constructor body.
ParameterStructure parameterStructure =
- _getParameterStructureFromFunctionNode(node.function);
+ _getParameterStructureFromFunctionNode(data.node.function);
JConstructorBody constructorBody =
createConstructorBody(constructor, parameterStructure);
members.register<IndexedFunction, FunctionData>(
constructorBody,
new ConstructorBodyDataImpl(
- node,
- node.function,
- new SpecialMemberDefinition(node, MemberKind.constructorBody),
+ data.node,
+ data.node.function,
+ new SpecialMemberDefinition(
+ data.node, MemberKind.constructorBody),
data.staticTypes));
IndexedClass cls = constructor.enclosingClass;
JClassEnvImpl classEnv = classes.getEnv(cls);
@@ -2079,6 +2104,7 @@
(_injectedClassMembers[function.enclosingClass] ??= <IndexedMember>[])
.add(generatorBody);
}
+ _generatorBodies[function] = generatorBody;
}
return generatorBody;
}
@@ -2616,3 +2642,81 @@
return _elementMap.libraries.getEntity(index);
}
}
+
+/// Enum used for serialization of 'late members', that is, members normally
+/// created on demand, like constructor bodies and generator bodies.
+enum LateMemberKind {
+ constructorBody,
+ generatorBody,
+}
+
+/// Entity reader that supports member entities normally created on-demand, like
+/// constructor bodies and generator bodies.
+///
+/// The support encoding corresponds to the encoding generated by the
+/// [ClosedEntityWriter].
+class ClosedEntityReader extends EntityReader {
+ final JsKernelToElementMap _elementMap;
+
+ ClosedEntityReader(this._elementMap);
+
+ @override
+ IndexedMember readMemberFromDataSource(
+ DataSource source, EntityLookup entityLookup) {
+ int index = source.readInt();
+ if (index == 0) {
+ return _readLateMemberFromDataSource(source, entityLookup);
+ } else {
+ return entityLookup.getMemberByIndex(index - 1);
+ }
+ }
+
+ IndexedMember _readLateMemberFromDataSource(
+ DataSource source, EntityLookup entityLookup) {
+ LateMemberKind kind = source.readEnum(LateMemberKind.values);
+ switch (kind) {
+ case LateMemberKind.constructorBody:
+ IndexedConstructor constructor = source.readMember();
+ return _elementMap._getConstructorBody(constructor);
+ case LateMemberKind.generatorBody:
+ IndexedFunction function = source.readMember();
+ return _elementMap.getGeneratorBody(function);
+ }
+ throw new UnsupportedError("Unexpected late member kind: $kind.");
+ }
+}
+
+/// Entity writer that supports member entities normally created on-demand, like
+/// constructor bodies and generator bodies.
+///
+/// The generated encoding corresponds to the encoding read by the
+/// [ClosedEntityReader].
+class ClosedEntityWriter extends EntityWriter {
+ final int _earlyMemberIndexLimit;
+
+ final JsKernelToElementMap _elementMap;
+
+ ClosedEntityWriter(this._elementMap, this._earlyMemberIndexLimit);
+
+ @override
+ void writeMemberToDataSink(DataSink sink, IndexedMember value) {
+ if (value.memberIndex >= _earlyMemberIndexLimit) {
+ sink.writeInt(0);
+ _writeLateMemberToDataSink(sink, value);
+ } else {
+ sink.writeInt(value.memberIndex + 1);
+ }
+ }
+
+ void _writeLateMemberToDataSink(DataSink sink, IndexedMember value) {
+ if (value is JConstructorBody) {
+ sink.writeEnum(LateMemberKind.constructorBody);
+ sink.writeMember(value.constructor);
+ } else if (value is JGeneratorBody) {
+ sink.writeEnum(LateMemberKind.generatorBody);
+ sink.writeMember(value.function);
+ } else {
+ throw new UnsupportedError("Unexpected late member $value.");
+ }
+ }
+}
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index a6126a1..d24290f 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -8,7 +8,7 @@
import '../backend_strategy.dart';
import '../common.dart';
-import '../common/codegen.dart' show CodegenRegistry;
+import '../common/codegen.dart' show CodegenRegistry, CodegenResults;
import '../common/tasks.dart';
import '../common/work.dart';
import '../compiler.dart';
@@ -112,12 +112,14 @@
}
@override
- WorkItemBuilder createCodegenWorkItemBuilder(JClosedWorld closedWorld) {
+ WorkItemBuilder createCodegenWorkItemBuilder(
+ JClosedWorld closedWorld, CodegenResults codegenResults) {
assert(_elementMap != null,
"JsBackendStrategy.elementMap has not been created yet.");
return new KernelCodegenWorkItemBuilder(
_compiler.backend,
closedWorld,
+ codegenResults,
new ClosedEntityLookup(_elementMap),
// TODO(johnniwinther): Avoid the need for a [ComponentLookup]. This
// is caused by some type masks holding a kernel node for using in
@@ -145,40 +147,65 @@
JClosedWorld closedWorld, InferredDataBuilder inferredDataBuilder) {
return new TypeGraphInferrer(_compiler, closedWorld, inferredDataBuilder);
}
+
+ @override
+ void prepareCodegenReader(DataSource source) {
+ source.registerEntityReader(new ClosedEntityReader(_elementMap));
+ source.registerEntityLookup(new ClosedEntityLookup(_elementMap));
+ source.registerComponentLookup(
+ new ComponentLookup(_elementMap.programEnv.mainComponent));
+ }
+
+ @override
+ EntityWriter forEachCodegenMember(void Function(MemberEntity member) f) {
+ int earlyMemberIndexLimit = _elementMap.prepareForCodegenSerialization();
+ ClosedEntityWriter entityWriter =
+ new ClosedEntityWriter(_elementMap, earlyMemberIndexLimit);
+ for (int memberIndex = 0;
+ memberIndex < _elementMap.members.length;
+ memberIndex++) {
+ MemberEntity member = _elementMap.members.getEntity(memberIndex);
+ if (member == null || member.isAbstract) continue;
+ f(member);
+ }
+ return entityWriter;
+ }
}
class KernelCodegenWorkItemBuilder implements WorkItemBuilder {
final JavaScriptBackend _backend;
final JClosedWorld _closedWorld;
+ final CodegenResults _codegenResults;
final EntityLookup _entityLookup;
final ComponentLookup _componentLookup;
KernelCodegenWorkItemBuilder(this._backend, this._closedWorld,
- this._entityLookup, this._componentLookup);
+ this._codegenResults, this._entityLookup, this._componentLookup);
@override
WorkItem createWorkItem(MemberEntity entity) {
if (entity.isAbstract) return null;
- return new KernelCodegenWorkItem(
- _backend, _closedWorld, _entityLookup, _componentLookup, entity);
+ return new KernelCodegenWorkItem(_backend, _closedWorld, _codegenResults,
+ _entityLookup, _componentLookup, entity);
}
}
class KernelCodegenWorkItem extends WorkItem {
final JavaScriptBackend _backend;
final JClosedWorld _closedWorld;
+ final CodegenResults _codegenResults;
final EntityLookup _entityLookup;
final ComponentLookup _componentLookup;
@override
final MemberEntity element;
- KernelCodegenWorkItem(this._backend, this._closedWorld, this._entityLookup,
- this._componentLookup, this.element);
+ KernelCodegenWorkItem(this._backend, this._closedWorld, this._codegenResults,
+ this._entityLookup, this._componentLookup, this.element);
@override
WorldImpact run() {
return _backend.generateCode(
- this, _closedWorld, _entityLookup, _componentLookup);
+ this, _closedWorld, _codegenResults, _entityLookup, _componentLookup);
}
}
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 2e4ac4b..acce37c 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -76,6 +76,16 @@
/// If this is set, the compilation stops after type inference.
Uri writeDataUri;
+ /// Location from which codegen data is read.
+ ///
+ /// If this is set, the compilation starts at codegen enqueueing.
+ Uri readCodegenUri;
+
+ /// Location to which codegen data is serialized.
+ ///
+ /// If this is set, the compilation stops after code generation.
+ Uri writeCodegenUri;
+
/// Whether to run only the CFE and emit the generated kernel file in
/// [outputUri].
bool cfeOnly = false;
@@ -325,6 +335,13 @@
/// If specified, a bundle of optimizations to enable (or disable).
int optimizationLevel = null;
+ /// The shard to serialize when using [writeCodegenUri].
+ int codegenShard;
+
+ /// The number of shards to serialize when using [writeCodegenUri] or to
+ /// deserialize when using [readCodegenUri].
+ int codegenShards;
+
// -------------------------------------------------
// Options for deprecated features
// -------------------------------------------------
@@ -407,6 +424,10 @@
_extractUriListOption(options, '${Flags.dillDependencies}')
..readDataUri = _extractUriOption(options, '${Flags.readData}=')
..writeDataUri = _extractUriOption(options, '${Flags.writeData}=')
+ ..readCodegenUri = _extractUriOption(options, '${Flags.readCodegen}=')
+ ..writeCodegenUri = _extractUriOption(options, '${Flags.writeCodegen}=')
+ ..codegenShard = _extractIntOption(options, '${Flags.codegenShard}=')
+ ..codegenShards = _extractIntOption(options, '${Flags.codegenShards}=')
..cfeOnly = _hasOption(options, Flags.cfeOnly)
..debugGlobalInference = _hasOption(options, Flags.debugGlobalInference);
}
@@ -533,10 +554,15 @@
}
Uri _extractUriOption(List<String> options, String prefix) {
- var option = _extractStringOption(options, prefix, null);
+ String option = _extractStringOption(options, prefix, null);
return (option == null) ? null : Uri.parse(option);
}
+int _extractIntOption(List<String> options, String prefix) {
+ String option = _extractStringOption(options, prefix, null);
+ return (option == null) ? null : int.parse(option);
+}
+
bool _hasOption(List<String> options, String option) {
return options.indexOf(option) >= 0;
}
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index 66909c3..2015a3a 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -34,6 +34,7 @@
Map<Type, IndexedSink> _generalCaches = {};
+ EntityWriter _entityWriter = const EntityWriter();
CodegenWriter _codegenWriter;
AbstractDataSink({this.useDataKinds: false}) {
@@ -307,22 +308,26 @@
@override
void writeLibrary(IndexedLibrary value) {
- writeInt(value.libraryIndex);
+ _entityWriter.writeLibraryToDataSink(this, value);
}
@override
void writeClass(IndexedClass value) {
- writeInt(value.classIndex);
+ _entityWriter.writeClassToDataSink(this, value);
}
@override
void writeTypedef(IndexedTypedef value) {
- writeInt(value.typedefIndex);
+ _entityWriter.writeTypedefToDataSink(this, value);
}
@override
void writeMember(IndexedMember value) {
- writeInt(value.memberIndex);
+ _entityWriter.writeMemberToDataSink(this, value);
+ }
+
+ void writeTypeVariable(IndexedTypeVariable value) {
+ _entityWriter.writeTypeVariableToDataSink(this, value);
}
@override
@@ -490,6 +495,12 @@
}
@override
+ void registerEntityWriter(EntityWriter writer) {
+ assert(writer != null);
+ _entityWriter = writer;
+ }
+
+ @override
void registerCodegenWriter(CodegenWriter writer) {
assert(writer != null);
assert(_codegenWriter == null);
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index f10276c..a2fa583 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -9,6 +9,7 @@
abstract class AbstractDataSource extends DataSourceMixin
implements DataSource {
final bool useDataKinds;
+ EntityReader _entityReader = const EntityReader();
ComponentLookup _componentLookup;
EntityLookup _entityLookup;
LocalLookup _localLookup;
@@ -66,6 +67,12 @@
_localLookup = localLookup;
}
+ @override
+ void registerEntityReader(EntityReader reader) {
+ assert(reader != null);
+ _entityReader = reader;
+ }
+
LocalLookup get localLookup {
assert(_localLookup != null);
return _localLookup;
@@ -79,6 +86,12 @@
}
@override
+ void deregisterCodegenReader(CodegenReader reader) {
+ assert(_codegenReader == reader);
+ _codegenReader = null;
+ }
+
+ @override
E readCached<E>(E f()) {
IndexedSource source = _generalCaches[E] ??= new IndexedSource<E>(this);
return source.read(f);
@@ -86,38 +99,27 @@
@override
IndexedLibrary readLibrary() {
- return getIndexedLibrary(readInt());
+ return _entityReader.readLibraryFromDataSource(this, entityLookup);
}
@override
IndexedClass readClass() {
- return getIndexedClass(readInt());
+ return _entityReader.readClassFromDataSource(this, entityLookup);
}
@override
IndexedTypedef readTypedef() {
- return getIndexedTypedef(readInt());
+ return _entityReader.readTypedefFromDataSource(this, entityLookup);
}
@override
IndexedMember readMember() {
- return getIndexedMember(readInt());
+ return _entityReader.readMemberFromDataSource(this, entityLookup);
}
- IndexedLibrary getIndexedLibrary(int libraryIndex) =>
- entityLookup.getLibraryByIndex(libraryIndex);
-
- IndexedClass getIndexedClass(int classIndex) =>
- entityLookup.getClassByIndex(classIndex);
-
- IndexedTypedef getIndexedTypedef(int typedefIndex) =>
- entityLookup.getTypedefByIndex(typedefIndex);
-
- IndexedMember getIndexedMember(int memberIndex) =>
- entityLookup.getMemberByIndex(memberIndex);
-
- IndexedTypeVariable getIndexedTypeVariable(int typeVariableIndex) =>
- entityLookup.getTypeVariableByIndex(typeVariableIndex);
+ IndexedTypeVariable readTypeVariable() {
+ return _entityReader.readTypeVariableFromDataSource(this, entityLookup);
+ }
List<DartType> _readDartTypes(
List<FunctionTypeVariable> functionTypeVariables) {
@@ -164,7 +166,7 @@
case DartTypeKind.voidType:
return const VoidType();
case DartTypeKind.typeVariable:
- return new TypeVariableType(getIndexedTypeVariable(readInt()));
+ return new TypeVariableType(readTypeVariable());
case DartTypeKind.functionTypeVariable:
int index = readInt();
assert(0 <= index && index < functionTypeVariables.length);
@@ -200,11 +202,11 @@
typeVariables);
case DartTypeKind.interfaceType:
- IndexedClass cls = getIndexedClass(readInt());
+ IndexedClass cls = readClass();
List<DartType> typeArguments = _readDartTypes(functionTypeVariables);
return new InterfaceType(cls, typeArguments);
case DartTypeKind.typedef:
- IndexedTypedef typedef = getIndexedTypedef(readInt());
+ IndexedTypedef typedef = readTypedef();
List<DartType> typeArguments = _readDartTypes(functionTypeVariables);
DartType unaliased = _readDartType(functionTypeVariables);
return new TypedefType(typedef, typeArguments, unaliased);
diff --git a/pkg/compiler/lib/src/serialization/helpers.dart b/pkg/compiler/lib/src/serialization/helpers.dart
index 40cc83d..c0c8c29 100644
--- a/pkg/compiler/lib/src/serialization/helpers.dart
+++ b/pkg/compiler/lib/src/serialization/helpers.dart
@@ -130,7 +130,7 @@
List<FunctionTypeVariable> functionTypeVariables) {
_sink.writeEnum(DartTypeKind.typeVariable);
IndexedTypeVariable typeVariable = type.element;
- _sink.writeInt(typeVariable.typeVariableIndex);
+ _sink.writeTypeVariable(typeVariable);
}
@override
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
index d4f574c..92e6ad0 100644
--- a/pkg/compiler/lib/src/serialization/serialization.dart
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -399,6 +399,10 @@
/// This feature is only available a [CodegenWriter] has been registered.
void writeJsNodeOrNull(js.Node value);
+ /// Register an [EntityWriter] with this data sink for non-default encoding
+ /// of entity references.
+ void registerEntityWriter(EntityWriter writer);
+
/// Register a [CodegenWriter] with this data sink to support serialization
/// of codegen only data.
void registerCodegenWriter(CodegenWriter writer);
@@ -426,13 +430,21 @@
/// deserialization of references to indexed entities.
void registerEntityLookup(EntityLookup entityLookup);
+ /// Registers an [EntityReader] with this data source for non-default encoding
+ /// of entity references.
+ void registerEntityReader(EntityReader reader);
+
/// Registers a [LocalLookup] object with this data source to support
/// deserialization of references to locals.
void registerLocalLookup(LocalLookup localLookup);
/// Registers a [CodegenReader] with this data source to support
/// deserialization of codegen only data.
- void registerCodegenReader(CodegenReader read);
+ void registerCodegenReader(CodegenReader reader);
+
+ /// Unregisters the [CodegenReader] from this data source to remove support
+ /// for deserialization of codegen only data.
+ void deregisterCodegenReader(CodegenReader reader);
/// Reads a reference to an [E] value from this data source. If the value has
/// not yet been deserialized, [f] is called to deserialize the value itself.
@@ -771,6 +783,61 @@
IndexedTypeVariable getTypeVariableByIndex(int index);
}
+/// Decoding strategy for entity references.
+class EntityReader {
+ const EntityReader();
+
+ IndexedLibrary readLibraryFromDataSource(
+ DataSource source, EntityLookup entityLookup) {
+ return entityLookup.getLibraryByIndex(source.readInt());
+ }
+
+ IndexedClass readClassFromDataSource(
+ DataSource source, EntityLookup entityLookup) {
+ return entityLookup.getClassByIndex(source.readInt());
+ }
+
+ IndexedTypedef readTypedefFromDataSource(
+ DataSource source, EntityLookup entityLookup) {
+ return entityLookup.getTypedefByIndex(source.readInt());
+ }
+
+ IndexedMember readMemberFromDataSource(
+ DataSource source, EntityLookup entityLookup) {
+ return entityLookup.getMemberByIndex(source.readInt());
+ }
+
+ IndexedTypeVariable readTypeVariableFromDataSource(
+ DataSource source, EntityLookup entityLookup) {
+ return entityLookup.getTypeVariableByIndex(source.readInt());
+ }
+}
+
+/// Encoding strategy for entity references.
+class EntityWriter {
+ const EntityWriter();
+
+ void writeLibraryToDataSink(DataSink sink, IndexedLibrary value) {
+ sink.writeInt(value.libraryIndex);
+ }
+
+ void writeClassToDataSink(DataSink sink, IndexedClass value) {
+ sink.writeInt(value.classIndex);
+ }
+
+ void writeTypedefToDataSink(DataSink sink, IndexedTypedef value) {
+ sink.writeInt(value.typedefIndex);
+ }
+
+ void writeMemberToDataSink(DataSink sink, IndexedMember value) {
+ sink.writeInt(value.memberIndex);
+ }
+
+ void writeTypeVariableToDataSink(DataSink sink, IndexedTypeVariable value) {
+ sink.writeInt(value.typeVariableIndex);
+ }
+}
+
/// Interface used for looking up locals by index during deserialization.
abstract class LocalLookup {
Local getLocalByIndex(MemberEntity memberContext, int index);
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 56ab4bd..61771d5 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -7,16 +7,20 @@
import 'package:kernel/binary/ast_from_binary.dart' as ir;
import 'package:kernel/binary/ast_to_binary.dart' as ir;
import '../../compiler_new.dart' as api;
+import '../backend_strategy.dart';
+import '../common/codegen.dart';
import '../common/tasks.dart';
-import '../compiler.dart';
import '../diagnostics/diagnostic_listener.dart';
+import '../elements/entities.dart';
import '../environment.dart';
import '../inferrer/abstract_value_domain.dart';
import '../inferrer/types.dart';
+import '../js_backend/backend.dart';
import '../js_backend/inferred_data.dart';
import '../js_model/js_world.dart';
import '../options.dart';
import '../util/sink_adapter.dart';
+import '../world.dart';
import 'serialization.dart';
void serializeGlobalTypeInferenceResults(
@@ -45,21 +49,26 @@
}
class SerializationTask extends CompilerTask {
- final Compiler compiler;
+ final CompilerOptions _options;
+ final DiagnosticReporter _reporter;
+ final api.CompilerInput _provider;
+ final api.CompilerOutput _outputProvider;
- SerializationTask(this.compiler, Measurer measurer) : super(measurer);
+ SerializationTask(this._options, this._reporter, this._provider,
+ this._outputProvider, Measurer measurer)
+ : super(measurer);
@override
String get name => 'Serialization';
- void serialize(GlobalTypeInferenceResults results) {
+ void serializeGlobalTypeInference(GlobalTypeInferenceResults results) {
measureSubtask('serialize dill', () {
// TODO(sigmund): remove entirely: we will do this immediately as soon as
// we get the component in the kernel/loader.dart task once we refactor
// how we apply our modular kernel transformation for super mixin calls.
- compiler.reporter.log('Writing dill to ${compiler.options.outputUri}');
+ _reporter.log('Writing dill to ${_options.outputUri}');
api.BinaryOutputSink dillOutput =
- compiler.outputProvider.createBinarySink(compiler.options.outputUri);
+ _outputProvider.createBinarySink(_options.outputUri);
JsClosedWorld closedWorld = results.closedWorld;
ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
BinaryOutputSinkAdapter irSink = new BinaryOutputSinkAdapter(dillOutput);
@@ -69,40 +78,99 @@
});
measureSubtask('serialize data', () {
- compiler.reporter.log('Writing data to ${compiler.options.writeDataUri}');
- api.BinaryOutputSink dataOutput = compiler.outputProvider
- .createBinarySink(compiler.options.writeDataUri);
+ _reporter.log('Writing data to ${_options.writeDataUri}');
+ api.BinaryOutputSink dataOutput =
+ _outputProvider.createBinarySink(_options.writeDataUri);
DataSink sink = new BinarySink(new BinaryOutputSinkAdapter(dataOutput));
serializeGlobalTypeInferenceResults(results, sink);
});
}
- Future<GlobalTypeInferenceResults> deserialize() async {
+ Future<GlobalTypeInferenceResults> deserializeGlobalTypeInference(
+ Environment environment,
+ AbstractValueStrategy abstractValueStrategy) async {
ir.Component component =
await measureIoSubtask('deserialize dill', () async {
- compiler.reporter.log('Reading dill from ${compiler.options.entryPoint}');
- api.Input<List<int>> dillInput = await compiler.provider.readFromUri(
- compiler.options.entryPoint,
- inputKind: api.InputKind.binary);
+ _reporter.log('Reading dill from ${_options.entryPoint}');
+ api.Input<List<int>> dillInput = await _provider
+ .readFromUri(_options.entryPoint, inputKind: api.InputKind.binary);
ir.Component component = new ir.Component();
new ir.BinaryBuilder(dillInput.data).readComponent(component);
return component;
});
return await measureIoSubtask('deserialize data', () async {
- compiler.reporter
- .log('Reading data from ${compiler.options.readDataUri}');
- api.Input<List<int>> dataInput = await compiler.provider.readFromUri(
- compiler.options.readDataUri,
- inputKind: api.InputKind.binary);
+ _reporter.log('Reading data from ${_options.readDataUri}');
+ api.Input<List<int>> dataInput = await _provider
+ .readFromUri(_options.readDataUri, inputKind: api.InputKind.binary);
DataSource source = new BinarySourceImpl(dataInput.data);
- return deserializeGlobalTypeInferenceResults(
- compiler.options,
- compiler.reporter,
- compiler.environment,
- compiler.abstractValueStrategy,
- component,
- source);
+ return deserializeGlobalTypeInferenceResults(_options, _reporter,
+ environment, abstractValueStrategy, component, source);
});
}
+
+ void serializeCodegen(
+ BackendStrategy backendStrategy, CodegenResults codegenResults) {
+ GlobalTypeInferenceResults globalTypeInferenceResults =
+ codegenResults.globalTypeInferenceResults;
+ JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
+ int shard = _options.codegenShard;
+ int shards = _options.codegenShards;
+ Map<MemberEntity, CodegenResult> results = {};
+ int index = 0;
+ EntityWriter entityWriter =
+ backendStrategy.forEachCodegenMember((MemberEntity member) {
+ if (index % shards == shard) {
+ CodegenResult codegenResult = codegenResults.getCodegenResults(member);
+ results[member] = codegenResult;
+ }
+ index++;
+ });
+ measureSubtask('serialize codegen', () {
+ Uri uri = Uri.parse('${_options.writeCodegenUri}$shard');
+ api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(uri);
+ DataSink sink = new BinarySink(new BinaryOutputSinkAdapter(dataOutput));
+ _reporter.log('Writing data to ${uri}');
+ sink.registerEntityWriter(entityWriter);
+ sink.registerCodegenWriter(new CodegenWriterImpl(closedWorld));
+ sink.writeMemberMap(
+ results, (CodegenResult result) => result.writeToDataSink(sink));
+ sink.close();
+ });
+ }
+
+ Future<CodegenResults> deserializeCodegen(
+ BackendStrategy backendStrategy,
+ GlobalTypeInferenceResults globalTypeInferenceResults,
+ CodegenInputs codegenInputs) async {
+ int shards = _options.codegenShards;
+ JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
+ Map<MemberEntity, CodegenResult> results = {};
+ for (int shard = 0; shard < shards; shard++) {
+ Uri uri = Uri.parse('${_options.readCodegenUri}$shard');
+ await measureIoSubtask('deserialize codegen', () async {
+ _reporter.log('Reading data from ${uri}');
+ api.Input<List<int>> dataInput =
+ await _provider.readFromUri(uri, inputKind: api.InputKind.binary);
+ DataSource source = new BinarySourceImpl(dataInput.data);
+ backendStrategy.prepareCodegenReader(source);
+ Map<MemberEntity, CodegenResult> codegenResults =
+ source.readMemberMap(() {
+ List<ModularName> modularNames = [];
+ List<ModularExpression> modularExpressions = [];
+ CodegenReader reader = new CodegenReaderImpl(
+ closedWorld, modularNames, modularExpressions);
+ source.registerCodegenReader(reader);
+ CodegenResult result = CodegenResult.readFromDataSource(
+ source, modularNames, modularExpressions);
+ source.deregisterCodegenReader(reader);
+ return result;
+ });
+ _reporter.log('Read ${codegenResults.length} members from ${uri}');
+ results.addAll(codegenResults);
+ });
+ }
+ return new DeserializedCodegenResults(
+ globalTypeInferenceResults, codegenInputs, results);
+ }
}
diff --git a/pkg/compiler/tool/modular_dart2js.dart b/pkg/compiler/tool/modular_dart2js.dart
new file mode 100644
index 0000000..e313a9d
--- /dev/null
+++ b/pkg/compiler/tool/modular_dart2js.dart
@@ -0,0 +1,168 @@
+// 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:convert';
+import 'dart:io';
+import 'package:compiler/src/commandline_options.dart';
+
+main(List<String> args) async {
+ Stopwatch stopwatch = new Stopwatch();
+ String input;
+ String output = 'out.js';
+ List<String> arguments = [];
+ int start = 0;
+ int shards;
+ bool enableAssertions = false;
+ for (String arg in args) {
+ if (arg.startsWith('-')) {
+ if (arg.startsWith('--start=')) {
+ start = int.parse(arg.substring('--start='.length));
+ } else if (arg.startsWith('--shards=')) {
+ shards = int.parse(arg.substring('--shards='.length));
+ } else if (arg == '-ea' || arg == '--enable_asserts') {
+ enableAssertions = true;
+ } else if (arg.startsWith('-o')) {
+ output = arg.substring('-o'.length);
+ } else if (arg.startsWith('--out=')) {
+ output = arg.substring('--out='.length);
+ } else {
+ arguments.add(arg);
+ }
+ } else {
+ if (input != null) {
+ print("Multiple entrypoints provided: '${input}' and '${arg}'.");
+ exit(-1);
+ }
+ input = arg;
+ }
+ }
+
+ if (input == null) {
+ print("No entrypoint provided.");
+ exit(-1);
+ }
+
+ String outputPrefix = output;
+ if (output.endsWith('.js')) {
+ outputPrefix = output.substring(0, output.length - '.js'.length);
+ }
+
+ List<String> baseOptions = ['--packages=${Platform.packageConfig}'];
+ if (enableAssertions) {
+ baseOptions.add('--enable_asserts');
+ }
+ baseOptions.add('package:compiler/src/dart2js.dart');
+ baseOptions.addAll(arguments);
+
+ String cfeOutput = '${outputPrefix}0.dill';
+ String dillOutput = '${outputPrefix}.dill';
+ String dataOutput = '${outputPrefix}.dill.data';
+ String codeOutput = '${outputPrefix}.code';
+ shards ??= 2;
+
+ stopwatch.start();
+ if (start <= 0) {
+ await subProcess(
+ baseOptions, [input, Flags.cfeOnly, '--out=$cfeOutput'], '0:\t');
+ }
+ if (start <= 1) {
+ await subProcess(
+ baseOptions,
+ [cfeOutput, '--out=$dillOutput', '${Flags.writeData}=${dataOutput}'],
+ '1:\t');
+ }
+ if (shards <= 1) {
+ await subProcess(
+ baseOptions,
+ [dillOutput, '${Flags.readData}=${dataOutput}', '--out=${output}'],
+ '3:\t');
+ } else {
+ if (start <= 2) {
+ List<List<String>> additionalArguments = [];
+ List<String> outputPrefixes = [];
+ for (int shard = 0; shard < shards; shard++) {
+ additionalArguments.add([
+ dillOutput,
+ '${Flags.readData}=${dataOutput}',
+ '${Flags.codegenShard}=$shard',
+ '${Flags.codegenShards}=$shards',
+ '${Flags.writeCodegen}=${codeOutput}'
+ ]);
+ outputPrefixes.add('2:${shard + 1}/$shards\t');
+ }
+
+ Stopwatch subwatch = new Stopwatch();
+ subwatch.start();
+ await Future.wait(new List<Future>.generate(shards, (int shard) {
+ return subProcess(
+ baseOptions, additionalArguments[shard], outputPrefixes[shard]);
+ }));
+ subwatch.stop();
+ print('2:\tTotal time: ${_formatMs(subwatch.elapsedMilliseconds)}');
+ }
+ if (start <= 3) {
+ await subProcess(
+ baseOptions,
+ [
+ dillOutput,
+ '${Flags.readData}=${dataOutput}',
+ '${Flags.readCodegen}=${codeOutput}',
+ '${Flags.codegenShards}=$shards',
+ '--out=${output}'
+ ],
+ '3:\t');
+ }
+ }
+ stopwatch.stop();
+ print('Total time: ${_formatMs(stopwatch.elapsedMilliseconds)}');
+}
+
+Future subProcess(List<String> baseOptions, List<String> additionalOptions,
+ String outputPrefix) async {
+ List<String> options = []..addAll(baseOptions)..addAll(additionalOptions);
+ print(
+ '${outputPrefix}Command: ${Platform.resolvedExecutable} ${options.join(' ')}');
+ Process process = await Process.start(Platform.resolvedExecutable, options,
+ runInShell: true);
+ _Prefixer stdoutPrefixer = new _Prefixer(outputPrefix, stdout);
+ _Prefixer stderrOutputter = new _Prefixer(outputPrefix, stderr);
+ process.stdout.transform(utf8.decoder).listen(stdoutPrefixer);
+ process.stderr.transform(utf8.decoder).listen(stderrOutputter);
+
+ int exitCode = await process.exitCode;
+ if (exitCode != 0) {
+ exit(exitCode);
+ }
+}
+
+class _Prefixer {
+ final String _prefix;
+ final Stdout _stdout;
+ bool _atNewLine = true;
+
+ _Prefixer(this._prefix, this._stdout);
+
+ void call(String text) {
+ int index = 0;
+ while (index < text.length) {
+ if (_atNewLine) {
+ _stdout.write(_prefix);
+ _atNewLine = false;
+ }
+ int pos = text.indexOf('\n', index);
+ if (pos != -1) {
+ _stdout.write(text.substring(index, pos + 1));
+ _atNewLine = true;
+ index = pos + 1;
+ } else {
+ _stdout.write(text.substring(index));
+ index = text.length;
+ }
+ }
+ }
+}
+
+String _formatMs(int ms) {
+ return (ms / 1000).toStringAsFixed(3) + 's';
+}
diff --git a/tests/compiler/dart2js/end_to_end/command_line_test.dart b/tests/compiler/dart2js/end_to_end/command_line_test.dart
index 88fb3e8..076b918 100644
--- a/tests/compiler/dart2js/end_to_end/command_line_test.dart
+++ b/tests/compiler/dart2js/end_to_end/command_line_test.dart
@@ -10,6 +10,7 @@
import 'package:expect/expect.dart';
import 'package:compiler/compiler_new.dart' as api;
+import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/dart2js.dart' as entry;
import 'package:compiler/src/options.dart' show CompilerOptions;
@@ -17,11 +18,191 @@
entry.enableWriteString = false;
asyncTest(() async {
await test([], exitCode: 1);
- await test(['foo.dart']);
+ await test(['foo.dart'], out: 'out.js');
+ await test(['foo.dart', '-ofoo.js'], out: 'foo.js');
+ await test(['foo.dart', '--out=foo.js'], out: 'foo.js');
+
+ await test([Flags.cfeOnly], exitCode: 1);
+ await test([Flags.cfeOnly, 'foo.dart'], out: 'out.dill');
+ await test([Flags.cfeOnly, 'foo.dart', '--out=out.dill'], out: 'out.dill');
+ await test([Flags.cfeOnly, 'foo.dart', Flags.readData], exitCode: 1);
+ await test(['foo.dart', Flags.readData, Flags.cfeOnly], exitCode: 1);
+ await test([Flags.cfeOnly, 'foo.dart', Flags.readCodegen], exitCode: 1);
+ await test(['foo.dart', Flags.readCodegen, Flags.cfeOnly], exitCode: 1);
+ await test([Flags.cfeOnly, 'foo.dart', Flags.writeData], exitCode: 1);
+ await test(['foo.dart', Flags.writeData, Flags.cfeOnly], exitCode: 1);
+ await test([Flags.cfeOnly, 'foo.dart', Flags.writeCodegen], exitCode: 1);
+ await test(['foo.dart', Flags.writeCodegen, Flags.cfeOnly], exitCode: 1);
+
+ await test([Flags.writeData, 'foo.dart'],
+ out: 'out.dill', writeData: 'out.dill.data');
+ await test(['${Flags.writeData}=foo.data', 'foo.dart', '--out=foo.dill'],
+ out: 'foo.dill', writeData: 'foo.data');
+ await test([Flags.readData, Flags.writeData, 'foo.dart'], exitCode: 1);
+ await test([Flags.writeData, Flags.readData, 'foo.dart'], exitCode: 1);
+ await test([Flags.readCodegen, Flags.writeData, 'foo.dart'], exitCode: 1);
+ await test([Flags.writeData, Flags.readCodegen, 'foo.dart'], exitCode: 1);
+
+ await test([Flags.readData, 'foo.dill'],
+ out: 'out.js', readData: 'foo.dill.data');
+ await test([Flags.readData, 'foo.dill', '--out=foo.js'],
+ out: 'foo.js', readData: 'foo.dill.data');
+ await test(['${Flags.readData}=out.data', 'foo.dill'],
+ out: 'out.js', readData: 'out.data');
+ await test(['${Flags.readData}=out.data', 'foo.dill', '--out=foo.js'],
+ out: 'foo.js', readData: 'out.data');
+
+ await test([
+ Flags.writeCodegen,
+ 'foo.dill',
+ '${Flags.codegenShard}=0',
+ '${Flags.codegenShards}=2'
+ ], exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.writeCodegen,
+ 'foo.dill',
+ '${Flags.codegenShard}=0',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out',
+ readData: 'foo.dill.data',
+ writeCodegen: 'out.code',
+ codegenShard: 0,
+ codegenShards: 2);
+ await test([
+ Flags.writeCodegen,
+ Flags.readData,
+ 'foo.dill',
+ '${Flags.codegenShard}=1',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out',
+ readData: 'foo.dill.data',
+ writeCodegen: 'out.code',
+ codegenShard: 1,
+ codegenShards: 2);
+ await test([
+ '${Flags.readData}=foo.data',
+ '${Flags.writeCodegen}=foo.code',
+ 'foo.dill',
+ '${Flags.codegenShard}=0',
+ '${Flags.codegenShards}=3'
+ ],
+ out: 'out',
+ readData: 'foo.data',
+ writeCodegen: 'foo.code',
+ codegenShard: 0,
+ codegenShards: 3);
+ await test([
+ '${Flags.readData}=foo.data',
+ '${Flags.writeCodegen}',
+ 'foo.dill',
+ '--out=foo.js',
+ '${Flags.codegenShard}=0',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'foo.js',
+ readData: 'foo.data',
+ writeCodegen: 'foo.js.code',
+ codegenShard: 0,
+ codegenShards: 2);
+ await test([Flags.writeCodegen, 'foo.dill', Flags.readCodegen],
+ exitCode: 1);
+ await test([Flags.readCodegen, Flags.writeCodegen, 'foo.dill'],
+ exitCode: 1);
+ await test(
+ [Flags.readData, Flags.writeCodegen, 'foo.dill', Flags.readCodegen],
+ exitCode: 1);
+ await test(
+ [Flags.readCodegen, Flags.readData, Flags.writeCodegen, 'foo.dill'],
+ exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.writeCodegen,
+ 'foo.dill',
+ ], exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.writeCodegen,
+ 'foo.dill',
+ '${Flags.codegenShard}=0'
+ ], exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.writeCodegen,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ], exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.writeCodegen,
+ 'foo.dill',
+ '${Flags.codegenShards}=0'
+ ], exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.writeCodegen,
+ 'foo.dill',
+ '${Flags.codegenShard}=-1',
+ '${Flags.codegenShards}=2'
+ ], exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.writeCodegen,
+ 'foo.dill',
+ '${Flags.codegenShard}=2',
+ '${Flags.codegenShards}=2'
+ ], exitCode: 1);
+
+ await test([Flags.readCodegen, 'foo.dill', '${Flags.codegenShards}=2'],
+ out: 'out.js',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ '${Flags.readCodegen}=foo.code',
+ 'foo.dill',
+ '${Flags.codegenShards}=3'
+ ],
+ out: 'out.js',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.code',
+ codegenShards: 3);
+
+ await test([
+ Flags.readData,
+ Flags.readCodegen,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ '${Flags.readData}=foo.data',
+ '${Flags.readCodegen}=foo.code',
+ 'foo.dill',
+ '${Flags.codegenShards}=3',
+ '-v'
+ ],
+ out: 'out.js',
+ readData: 'foo.data',
+ readCodegen: 'foo.code',
+ codegenShards: 3);
});
}
-Future test(List<String> arguments, {int exitCode}) async {
+Future test(List<String> arguments,
+ {int exitCode,
+ String out,
+ String readData,
+ String writeData,
+ String readCodegen,
+ String writeCodegen,
+ int codegenShard,
+ int codegenShards}) async {
print('--------------------------------------------------------------------');
print('dart2js ${arguments.join(' ')}');
print('--------------------------------------------------------------------');
@@ -47,8 +228,23 @@
Expect.equals(exitCode, actualExitCode, "Unexpected exit code");
if (actualExitCode == null) {
Expect.isNotNull(options, "Missing options object");
+ Expect.equals(toUri(out), options.outputUri, "Unexpected output uri.");
+ Expect.equals(
+ toUri(readData), options.readDataUri, "Unexpected readData uri");
+ Expect.equals(
+ toUri(writeData), options.writeDataUri, "Unexpected writeData uri");
+ Expect.equals(toUri(readCodegen), options.readCodegenUri,
+ "Unexpected readCodegen uri");
+ Expect.equals(toUri(writeCodegen), options.writeCodegenUri,
+ "Unexpected writeCodegen uri");
+ Expect.equals(
+ codegenShard, options.codegenShard, "Unexpected codegenShard uri");
+ Expect.equals(
+ codegenShards, options.codegenShards, "Unexpected codegenShards uri");
}
entry.compileFunc = oldCompileFunc;
entry.exitFunc = oldExitFunc;
}
+
+Uri toUri(String path) => path != null ? Uri.base.resolve(path) : null;
diff --git a/tests/compiler/dart2js/end_to_end/exit_code_test.dart b/tests/compiler/dart2js/end_to_end/exit_code_test.dart
index e424fad..3397753 100644
--- a/tests/compiler/dart2js/end_to_end/exit_code_test.dart
+++ b/tests/compiler/dart2js/end_to_end/exit_code_test.dart
@@ -11,6 +11,7 @@
import 'package:compiler/compiler_new.dart' as api;
import 'package:compiler/src/commandline_options.dart';
+import 'package:compiler/src/common/codegen.dart';
import 'package:compiler/src/common/work.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/dart2js.dart' as entry;
@@ -110,10 +111,15 @@
useNewSourceInfo: compiler.options.useNewSourceInfo);
@override
- WorldImpact generateCode(WorkItem work, JClosedWorld closedWorld,
- EntityLookup entityLookup, ComponentLookup componentLookup) {
+ WorldImpact generateCode(
+ WorkItem work,
+ JClosedWorld closedWorld,
+ CodegenResults codegenResults,
+ EntityLookup entityLookup,
+ ComponentLookup componentLookup) {
compiler.test('Compiler.codegen');
- return super.generateCode(work, closedWorld, entityLookup, componentLookup);
+ return super.generateCode(
+ work, closedWorld, codegenResults, entityLookup, componentLookup);
}
}
diff --git a/tests/compiler/dart2js/serialization/serialization_test_helper.dart b/tests/compiler/dart2js/serialization/serialization_test_helper.dart
index 43ee996..9f48d01 100644
--- a/tests/compiler/dart2js/serialization/serialization_test_helper.dart
+++ b/tests/compiler/dart2js/serialization/serialization_test_helper.dart
@@ -60,7 +60,7 @@
Map<OutputType, Map<String, String>> output = collector1.clear();
- compiler.emitJavaScriptCode(newGlobalInferenceResults);
+ compiler.generateJavaScriptCode(newGlobalInferenceResults);
Map<OutputType, Map<String, String>> newOutput = collector2.clear();
Expect.setEquals(output.keys, newOutput.keys, "Output type mismatch.");