1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-05 09:20:04 +00:00

[dart2js] Delete modular compilation step.

The Dart2JS team has agreed that this mode of compilation is no longer worth investing in at this time and its existence adds some overhead to other feature work so it is worth fully removing. In the future we may revisit this mode of compilation. Below is some more context for any future exploration in this space.

What didn't work with modular analysis:
- current modular analysis was computing impacts, which were dense (50% of the size of kernel)
- using it moved work to a modular phase, and cut Phase1 in half however end-to-end time was not better
- data overhead was very high
- it made it much harder to maintain invariants throughout the pipeline: the data is tightly coupled with the kernel AST, making it hard to make late modifications to the AST.

How to potentially make it better:
- make the data much more sparse
- make the data more independent from the kernel AST so that transformations are not breaking
- reduce the critical path in a more substantial way.

Note: We retain and ignore the commandline flags used for modular analysis in order to avoid breaking build pipelines that were passing them. We may remove these at a later date.

Change-Id: If574ce2358280ab5fedd89c62665328601e72e22
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/333360
Reviewed-by: Mayank Patke <fishythefish@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
Nate Biggs 2023-11-03 00:11:50 +00:00 committed by Commit Queue
parent c753f25160
commit ad2708d071
16 changed files with 153 additions and 993 deletions

View File

@ -28,19 +28,7 @@ The current compiler phases are:
The result of this phase is a kernel AST which is serialized as a `.dill`
file.
2. **modular analysis**: Using kernel as input, compute data recording
properties about each method in the program, especially around dependencies
and features they may need. We call this "impact data" (i1).
When the compiler runs as a single process, this is done lazily/on-demand
during the tree-shaking phase (below). However, this data can also be
computed independently for individual methods, files, or packages in the
application. That makes it possible to run this modularly and in parallel.
The result of this phase can be emitted as files containing impact data in
a serialized format.
3. **tree-shake and create world**: Create a model to understand what parts of
2. **tree-shake and create world**: Create a model to understand what parts of
the code are used by an application. This consists of:
* creating an intermediate representation called the "K model" that
wraps our kernel representation
@ -56,7 +44,7 @@ The current compiler phases are:
in any subtype of some interface? The answers to these questions can help
the compiler generate higher quality JavaScript.
4. **global analysis**: Run a global analysis that assumes closed world
3. **global analysis**: Run a global analysis that assumes closed world
semantics (from w1) and propagates information across method boundaries to
further understand what values flow through the program. This phase is
very valuable in narrowing down possibilities that are ambiguous based
@ -66,13 +54,13 @@ The current compiler phases are:
The result of this phase is a "global result" (g).
5. **codegen model**: Create a JS or backend model of the program. This is an
4. **codegen model**: Create a JS or backend model of the program. This is an
intermediate representation of the entities in the program we referred to
as the "J model". It is very similar to the "K model", but it is tailored
to model JavaScript specific concepts (like the split of constructor bodies
as separate elements) and provide a mapping to the Dart model.
6. **codegen**: Generate code for each method that is deemed necessary. This
5. **codegen**: Generate code for each method that is deemed necessary. This
includes:
* build an SSA graph from kernel ASTs and global results (g)
* optimize the SSA representation
@ -80,7 +68,7 @@ The current compiler phases are:
* emit JS ASTs for the code
7. **link tree-shake**: Using the results of codegen, we perform a second
6. **link tree-shake**: Using the results of codegen, we perform a second
round of tree-shaking. This is important because code that was deemed
reachable in (w1) may be found unreachable after optimizations. The process
is very similar to the earlier phase: we combine incrementally the codegen
@ -90,7 +78,7 @@ The current compiler phases are:
When dart2js runs as a single process the codegen phase is done lazily and
on-demand, together with the tree-shaking phase.
8. **emit JavaScript files**: The final step is to assemble and minify the
7. **emit JavaScript files**: The final step is to assemble and minify the
final program. This includes:
* Build a JavaScript program structure from the compiled pieces (w2)
* Use frequency namer to minify names.

View File

@ -47,7 +47,6 @@ import 'inferrer/types.dart'
import 'inferrer/wrapped.dart' show WrappedAbstractValueStrategy;
import 'io/source_information.dart';
import 'ir/annotations.dart';
import 'ir/modular.dart' hide reportLocatedMessage;
import 'js_backend/codegen_inputs.dart' show CodegenInputs;
import 'js_backend/enqueuer.dart';
import 'js_backend/inferred_data.dart';
@ -62,7 +61,6 @@ import 'kernel/kernel_world.dart';
import 'null_compiler_output.dart' show NullCompilerOutput;
import 'options.dart' show CompilerOptions, Dart2JSStage;
import 'phase/load_kernel.dart' as load_kernel;
import 'phase/modular_analysis.dart' as modular_analysis;
import 'resolution/enqueuer.dart';
import 'serialization/serialization.dart';
import 'serialization/task.dart';
@ -337,10 +335,9 @@ class Compiler {
}
}
JClosedWorld? computeClosedWorld(ir.Component component,
ModuleData? moduleData, Uri rootLibraryUri, List<Uri> libraries) {
JClosedWorld? computeClosedWorld(
ir.Component component, Uri rootLibraryUri, List<Uri> libraries) {
frontendStrategy.registerLoadedLibraries(component, libraries);
frontendStrategy.registerModuleData(moduleData);
ResolutionEnqueuer resolutionEnqueuer = frontendStrategy
.createResolutionEnqueuer(enqueueTask, this)
..onEmptyForTesting = onResolutionQueueEmptyForTesting;
@ -410,13 +407,6 @@ class Compiler {
untrimmedComponentForDumpInfo = component;
}
if (stage.shouldOnlyComputeDill) {
// [ModuleData] must be deserialized with the full component, i.e.
// before trimming.
ModuleData? moduleData;
if (options.modularAnalysisInputs != null) {
moduleData = await serializationTask.deserializeModuleData(component);
}
Set<Uri> includedLibraries = output.libraries!.toSet();
if (stage.shouldLoadFromDill) {
if (options.dumpUnusedLibraries) {
@ -426,15 +416,7 @@ class Compiler {
component = trimComponent(component, includedLibraries);
}
}
if (moduleData == null) {
serializationTask.serializeComponent(component);
} else {
// Trim [moduleData] down to only the included libraries.
moduleData.impactData
.removeWhere((uri, _) => !includedLibraries.contains(uri));
serializationTask.serializeModuleData(
moduleData, component, includedLibraries);
}
serializationTask.serializeComponent(component);
}
return output.withNewComponent(component);
} else {
@ -443,7 +425,7 @@ class Compiler {
if (retainDataForTesting) {
componentForTesting = component;
}
return load_kernel.Output(component, null, null, null, null);
return load_kernel.Output(component, null, null, null);
}
}
@ -478,38 +460,6 @@ class Compiler {
.run();
}
bool get usingModularAnalysis =>
stage.shouldComputeModularAnalysis || options.hasModularAnalysisInputs;
Future<ModuleData> runModularAnalysis(
load_kernel.Output output, Set<Uri> moduleLibraries) async {
ir.Component component = output.component;
List<Uri> libraries = output.libraries!;
final input = modular_analysis.Input(
options, reporter, environment, component, libraries, moduleLibraries);
return await selfTask.measureSubtask(
'runModularAnalysis', () async => modular_analysis.run(input));
}
Future<ModuleData> produceModuleData(load_kernel.Output output) async {
ir.Component component = output.component;
if (stage.shouldComputeModularAnalysis) {
Set<Uri> moduleLibraries = output.moduleLibraries!.toSet();
ModuleData moduleData = await runModularAnalysis(output, moduleLibraries);
if (!compilationFailed) {
serializationTask.testModuleSerialization(moduleData, component);
serializationTask.serializeModuleData(
moduleData, component, moduleLibraries);
}
return moduleData;
} else {
return await serializationTask.deserializeModuleData(component);
}
}
bool get shouldStopAfterModularAnalysis =>
compilationFailed || stage.shouldComputeModularAnalysis;
GlobalTypeInferenceResults performGlobalTypeInference(
JClosedWorld closedWorld) {
FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction!;
@ -591,30 +541,19 @@ class Compiler {
indices);
}
Future<JClosedWorld?> produceClosedWorld(load_kernel.Output output,
ModuleData? moduleData, SerializationIndices indices) async {
Future<JClosedWorld?> produceClosedWorld(
load_kernel.Output output, SerializationIndices indices) async {
ir.Component component = output.component;
JClosedWorld? closedWorld;
if (!stage.shouldReadClosedWorld) {
if (!usingModularAnalysis) {
// If we're deserializing the closed world, the input .dill already
// contains the modified AST, so the transformer only needs to run if
// the closed world is being computed from scratch.
//
// However, the transformer is not currently compatible with modular
// analysis. When modular analysis is enabled in Blaze, some aspects run
// before this phase of the compiler. This can cause dart2js to crash if
// the kernel AST is mutated, since we will attempt to serialize and
// deserialize against different ASTs.
//
// TODO(fishythefish): Make this compatible with modular analysis.
simplifyConstConditionals(component);
}
// If we're deserializing the closed world, the input .dill already
// contains the modified AST, so the transformer only needs to run if
// the closed world is being computed from scratch.
simplifyConstConditionals(component);
Uri rootLibraryUri = output.rootLibraryUri!;
List<Uri> libraries = output.libraries!;
closedWorld =
computeClosedWorld(component, moduleData, rootLibraryUri, libraries);
closedWorld = computeClosedWorld(component, rootLibraryUri, libraries);
if (stage == Dart2JSStage.closedWorld && closedWorld != null) {
serializationTask.serializeComponent(
closedWorld.elementMap.programEnv.mainComponent,
@ -719,18 +658,10 @@ class Compiler {
final output = await produceKernel();
if (shouldStopAfterLoadKernel(output)) return;
// Run modular analysis. This may be null if modular analysis was not
// requested for this pipeline.
ModuleData? moduleData;
if (usingModularAnalysis) {
moduleData = await produceModuleData(output!);
}
if (shouldStopAfterModularAnalysis) return;
final indices = SerializationIndices();
// Compute closed world.
JClosedWorld? closedWorld =
await produceClosedWorld(output!, moduleData, indices);
JClosedWorld? closedWorld = await produceClosedWorld(output!, indices);
if (shouldStopAfterClosedWorld(closedWorld)) return;
// Run global analysis.

View File

@ -156,7 +156,6 @@ Future<api.CompilationResult> compile(List<String> argv,
bool? showWarnings;
bool? showHints;
bool? enableColors;
List<Uri>? sources;
int? optimizationLevel;
Uri? platformBinaries;
Map<String, String> environment = Map<String, String>();
@ -326,18 +325,10 @@ Future<api.CompilationResult> compile(List<String> argv,
return uris;
}
void setModularAnalysisInputs(String argument) {
setUriList(Flags.readModularAnalysis, argument);
}
void setDillDependencies(String argument) {
setUriList(Flags.dillDependencies, argument);
}
void setSources(String argument) {
sources = setUriList(Flags.sources, argument);
}
void setDumpInfo(String argument) {
passThrough(Flags.dumpInfo);
if (argument == Flags.dumpInfo || argument == "${Flags.dumpInfo}=json") {
@ -425,10 +416,9 @@ Future<api.CompilationResult> compile(List<String> argv,
_OneOption('--library-root=.+', ignoreOption),
_OneOption('--libraries-spec=.+', setLibrarySpecificationUri),
_OneOption('${Flags.dillDependencies}=.+', setDillDependencies),
_OneOption('${Flags.sources}=.+', setSources),
_OneOption('${Flags.readModularAnalysis}=.+', setModularAnalysisInputs),
_OneOption('${Flags.writeModularAnalysis}=.+',
setDataUri(Flags.writeModularAnalysis)),
_OneOption('${Flags.sources}=.+', ignoreOption),
_OneOption('${Flags.readModularAnalysis}=.+', ignoreOption),
_OneOption('${Flags.writeModularAnalysis}=.+', ignoreOption),
_OneOption('${Flags.readData}=.+', setDataUri(Flags.readData)),
_OneOption('${Flags.writeData}=.+', setDataUri(Flags.writeData)),
_OneOption(
@ -651,10 +641,7 @@ Future<api.CompilationResult> compile(List<String> argv,
print("Compiler invoked from: '$invoker'");
}
if (arguments.isEmpty &&
entryUri == null &&
inputDillUri == null &&
sources == null) {
if (arguments.isEmpty && entryUri == null && inputDillUri == null) {
_helpAndFail('No Dart file specified.');
}
@ -682,12 +669,8 @@ Future<api.CompilationResult> compile(List<String> argv,
}
// Make [scriptName] a relative path.
String scriptName = sources == null
? fe.relativizeUri(
Uri.base, inputDillUri ?? entryUri!, Platform.isWindows)
: sources!
.map((uri) => fe.relativizeUri(Uri.base, uri, Platform.isWindows))
.join(',');
String scriptName =
fe.relativizeUri(Uri.base, inputDillUri ?? entryUri!, Platform.isWindows);
CompilerOptions compilerOptions = CompilerOptions.parse(options,
featureOptions: features,
@ -768,8 +751,6 @@ Future<api.CompilationResult> compile(List<String> argv,
case Dart2JSStage.cfe:
case Dart2JSStage.allFromDill:
case Dart2JSStage.cfeFromDill:
case Dart2JSStage.modularAnalysis:
case Dart2JSStage.modularAnalysisFromDill:
case Dart2JSStage.closedWorld:
final sourceCharCount =
_formatCharacterCount(inputProvider.sourceBytesFromDill);
@ -848,18 +829,6 @@ Future<api.CompilationResult> compile(List<String> argv,
String output = fe.relativizeUri(Uri.base, out!, Platform.isWindows);
summary += 'compiled to dill: ${output}.';
break;
case Dart2JSStage.modularAnalysis:
case Dart2JSStage.modularAnalysisFromDill:
processName = 'Serialized';
outputName = 'bytes data';
outputSize = outputProvider.totalDataWritten;
String output = fe.relativizeUri(Uri.base, out!, Platform.isWindows);
String dataOutput = fe.relativizeUri(
Uri.base,
compilerOptions.dataOutputUriForStage(compilerOptions.stage),
Platform.isWindows);
summary += 'serialized to dill and data: ${output} and ${dataOutput}.';
break;
case Dart2JSStage.closedWorld:
processName = 'Serialized';
outputName = 'bytes data';
@ -1400,27 +1369,3 @@ void batchMain(List<String> batchArguments) {
});
});
}
// TODO(joshualitt): Clean up the combinatorial explosion of read strategies.
// Right now only fromClosedWorld, fromDataAndClosedWorld, and
// fromCodegenAndClosedWorldAndData are valid.
enum ReadStrategy {
fromDart,
fromClosedWorld,
fromData,
fromDataAndClosedWorld,
fromCodegen,
fromCodegenAndClosedWorld,
fromCodegenAndData,
fromCodegenAndClosedWorldAndData,
}
enum WriteStrategy {
toKernel,
toKernelWithModularAnalysis,
toModularAnalysis,
toClosedWorld,
toData,
toCodegen,
toJs
}

View File

@ -30,15 +30,8 @@ enum Dart2JSStage {
fromDillFlag: Dart2JSStage.cfeFromDill,
emitsKernel: true,
emitsJs: false),
modularAnalysis('modular-analysis',
fromDillFlag: Dart2JSStage.modularAnalysisFromDill,
dataOutputName: 'modular.data',
emitsKernel: true,
emitsJs: false),
allFromDill(null, emitsKernel: false, emitsJs: true),
cfeFromDill('cfe', emitsKernel: true, emitsJs: false),
modularAnalysisFromDill('modular-analysis',
dataOutputName: 'modular.data', emitsKernel: true, emitsJs: false),
deferredLoadIds('deferred-load-ids',
dataOutputName: 'deferred_load_ids.data',
emitsKernel: false,
@ -74,27 +67,14 @@ enum Dart2JSStage {
this == Dart2JSStage.all ||
this == Dart2JSStage.allFromDill;
bool get shouldLoadFromDill => this.index >= Dart2JSStage.allFromDill.index;
bool get shouldComputeModularAnalysis =>
this == Dart2JSStage.modularAnalysis ||
this == Dart2JSStage.modularAnalysisFromDill;
/// Global kernel transformations should be run in phase 0b, i.e. after
/// concatenating dills, but before serializing the output of phase 0.
/// We also need to include modular analysis, or else the modular test suite
/// breaks when it deserializes module data because it reads transformed AST
/// nodes when it's expecting untransformed ones.
// TODO(fishythefish, natebiggs): Address the modular analysis inconsistency.
// Ideally, modular analysis shouldn't require global transformations to be
// run again, so we need to either delete modular analysis or make the data
// less brittle in the presence of AST modifications.
// TODO(fishythefish): Add AST metadata to ensure transformations aren't rerun
// unnecessarily.
bool get shouldRunGlobalTransforms =>
this.index <= Dart2JSStage.modularAnalysisFromDill.index;
this.index <= Dart2JSStage.cfeFromDill.index;
bool get canUseModularAnalysis =>
this == Dart2JSStage.modularAnalysis ||
this.index >= Dart2JSStage.modularAnalysisFromDill.index;
bool get shouldReadClosedWorld => this.index > Dart2JSStage.closedWorld.index;
bool get shouldReadGlobalInference =>
this.index > Dart2JSStage.globalInference.index;
@ -125,11 +105,6 @@ enum Dart2JSStage {
if (options._cfeOnly) {
return options._fromDill ? Dart2JSStage.cfeFromDill : Dart2JSStage.cfe;
}
if (options._writeModularAnalysisUri != null) {
return options._fromDill
? Dart2JSStage.modularAnalysisFromDill
: Dart2JSStage.modularAnalysis;
}
if (options._deferredLoadIdMapUri != null) {
return Dart2JSStage.deferredLoadIds;
}
@ -323,7 +298,6 @@ class CompilerOptions implements DiagnosticOptions {
_inputDillUri ?? entryUri ?? _defaultInputDillUri;
bool get _fromDill {
if (sources != null) return false;
var targetPath = (_inputDillUri ?? entryUri)?.path;
return targetPath == null || targetPath.endsWith('.dill');
}
@ -333,26 +307,10 @@ class CompilerOptions implements DiagnosticOptions {
/// List of kernel files to load.
///
/// When compiling modularly, this contains kernel files that are needed
/// to compile a single module.
///
/// When linking, this contains all kernel files that form part of the final
/// program.
///
/// At this time, this list points to full kernel files. In the future, we may
/// use a list of outline files for modular compiles, and only use full kernel
/// files for linking.
/// This contains all kernel files that form part of the final program. The
/// dills passed here should contain full kernel ASTs, not just outlines.
List<Uri>? dillDependencies;
/// A list of sources to compile, only used for modular analysis.
List<Uri>? sources;
Uri? _writeModularAnalysisUri;
List<Uri>? modularAnalysisInputs;
bool get hasModularAnalysisInputs => modularAnalysisInputs != null;
/// Uses a memory mapped view of files for I/O.
bool memoryMappedFiles = false;
@ -825,8 +783,6 @@ class CompilerOptions implements DiagnosticOptions {
case Dart2JSStage.cfeFromDill:
case Dart2JSStage.jsEmitter:
case Dart2JSStage.codegenAndJsEmitter:
case Dart2JSStage.modularAnalysis:
case Dart2JSStage.modularAnalysisFromDill:
case Dart2JSStage.deferredLoadIds:
return null;
case Dart2JSStage.closedWorld:
@ -860,9 +816,6 @@ class CompilerOptions implements DiagnosticOptions {
return _deferredLoadIdMapUri;
case Dart2JSStage.cfe:
case Dart2JSStage.cfeFromDill:
case Dart2JSStage.modularAnalysis:
case Dart2JSStage.modularAnalysisFromDill:
return _writeModularAnalysisUri;
case Dart2JSStage.closedWorld:
return _writeClosedWorldUri;
case Dart2JSStage.globalInference:
@ -995,13 +948,8 @@ class CompilerOptions implements DiagnosticOptions {
..showInternalProgress = _hasOption(options, Flags.progress)
..dillDependencies =
_extractUriListOption(options, '${Flags.dillDependencies}')
..sources = _extractUriListOption(options, '${Flags.sources}')
..readProgramSplit =
_extractUriOption(options, '${Flags.readProgramSplit}=')
.._writeModularAnalysisUri =
_extractUriOption(options, '${Flags.writeModularAnalysis}=')
..modularAnalysisInputs =
_extractUriListOption(options, '${Flags.readModularAnalysis}')
.._readDataUri = _extractUriOption(options, '${Flags.readData}=')
.._writeDataUri = _extractUriOption(options, '${Flags.writeData}=')
..memoryMappedFiles = _hasOption(options, Flags.memoryMappedFiles)
@ -1038,8 +986,6 @@ class CompilerOptions implements DiagnosticOptions {
bool expectSourcesIn = false;
bool expectKernelIn = false;
bool expectKernelOut = false;
bool expectModularIn = false;
bool expectModularOut = false;
bool expectDeferredLoadIdsOut = false;
bool expectClosedWorldIn = false;
bool expectClosedWorldOut = false;
@ -1057,22 +1003,10 @@ class CompilerOptions implements DiagnosticOptions {
case Dart2JSStage.cfe:
expectSourcesIn = true;
expectKernelOut = true;
expectModularIn = true;
expectModularOut = true;
break;
case Dart2JSStage.cfeFromDill:
expectKernelIn = true;
expectKernelOut = true;
expectModularIn = true;
expectModularOut = true;
break;
case Dart2JSStage.modularAnalysis:
expectKernelOut = true;
expectModularOut = true;
break;
case Dart2JSStage.modularAnalysisFromDill:
expectKernelOut = true;
expectModularOut = true;
break;
case Dart2JSStage.deferredLoadIds:
expectKernelIn = true;
@ -1081,30 +1015,25 @@ class CompilerOptions implements DiagnosticOptions {
case Dart2JSStage.closedWorld:
expectClosedWorldOut = true;
expectKernelIn = true;
expectModularIn = true;
break;
case Dart2JSStage.globalInference:
expectGlobalOut = true;
expectKernelIn = true;
expectModularIn = true;
expectClosedWorldIn = true;
break;
case Dart2JSStage.codegenSharded:
expectCodegenOut = true;
expectKernelIn = true;
expectModularIn = true;
expectClosedWorldIn = true;
expectGlobalIn = true;
break;
case Dart2JSStage.codegenAndJsEmitter:
expectKernelIn = true;
expectModularIn = true;
expectClosedWorldIn = true;
expectGlobalIn = true;
break;
case Dart2JSStage.jsEmitter:
expectKernelIn = true;
expectModularIn = true;
expectClosedWorldIn = true;
expectGlobalIn = true;
expectCodegenIn = true;
@ -1124,15 +1053,6 @@ class CompilerOptions implements DiagnosticOptions {
return 'Cannot write serialized data during ${stage.name} stage.';
}
// Check modular analysis flags.
if (_writeModularAnalysisUri != null && !expectModularOut) {
return 'Cannot write modular data during ${stage.name} stage.';
}
if (modularAnalysisInputs != null && !expectModularIn) {
return 'Cannot read modular analysis inputs in '
'stage ${stage.name}.';
}
if (_deferredLoadIdMapUri != null && !expectDeferredLoadIdsOut) {
return 'Cannot write deferred load ID map during ${stage.name} stage.';
}

View File

@ -59,21 +59,13 @@ class Output {
/// Note that [component] may contain some libraries that are excluded here.
final List<Uri>? libraries;
/// When running only dart2js modular analysis, returns the [Uri]s for
/// libraries loaded in the input module.
///
/// This excludes other libraries reachable from them that were loaded as
/// dependencies. The result of [moduleLibraries] is always a subset of
/// [libraries].
final List<Uri>? moduleLibraries;
final fe.InitializedCompilerState? initializedCompilerState;
Output withNewComponent(ir.Component component) => Output(component,
rootLibraryUri, libraries, moduleLibraries, initializedCompilerState);
Output withNewComponent(ir.Component component) =>
Output(component, rootLibraryUri, libraries, initializedCompilerState);
Output(this.component, this.rootLibraryUri, this.libraries,
this.moduleLibraries, this.initializedCompilerState);
this.initializedCompilerState);
}
Library _findEntryLibrary(Component component, Uri entryUri) {
@ -114,10 +106,8 @@ String _getPlatformFilename(CompilerOptions options, String targetName) {
class _LoadFromKernelResult {
final ir.Component? component;
final Library? entryLibrary;
final List<Uri> moduleLibraries;
_LoadFromKernelResult(
this.component, this.entryLibrary, this.moduleLibraries);
_LoadFromKernelResult(this.component, this.entryLibrary);
}
// Perform any backend-specific transforms here that can be done on both
@ -142,7 +132,6 @@ Future<_LoadFromKernelResult> _loadFromKernel(CompilerOptions options,
Library? entryLibrary;
var resolvedUri = options.compilationTarget;
ir.Component component = ir.Component();
List<Uri> moduleLibraries = [];
Future<void> read(Uri uri) async {
api.Input input =
@ -152,10 +141,6 @@ Future<_LoadFromKernelResult> _loadFromKernel(CompilerOptions options,
await read(resolvedUri);
if (options.stage.shouldComputeModularAnalysis) {
moduleLibraries = component.libraries.map((lib) => lib.importUri).toList();
}
var isStrongDill =
component.mode == ir.NonNullableByDefaultCompiledMode.Strong;
var incompatibleNullSafetyMode =
@ -167,18 +152,10 @@ Future<_LoadFromKernelResult> _loadFromKernel(CompilerOptions options,
"safety and is incompatible with the '$option' option");
}
// When compiling modularly, a dill for the SDK will be provided. In those
// cases we ignore the implicit platform binary.
bool platformBinariesIncluded = options.stage.shouldComputeModularAnalysis ||
options.hasModularAnalysisInputs;
if (options.platformBinaries != null &&
options.stage.shouldReadPlatformBinaries &&
!platformBinariesIncluded) {
options.stage.shouldReadPlatformBinaries) {
var platformUri = options.platformBinaries
?.resolve(_getPlatformFilename(options, targetName));
// Modular analysis can be run on the sdk by providing directly the
// path to the platform.dill file. In that case, we do not load the
// platform file implicitly.
// TODO(joshualitt): Change how we detect this case so it is less
// brittle.
if (platformUri != resolvedUri) await read(platformUri!);
@ -199,16 +176,14 @@ Future<_LoadFromKernelResult> _loadFromKernel(CompilerOptions options,
_doTransformsOnKernelLoad(component, options);
registerSources(component, compilerInput);
return _LoadFromKernelResult(component, entryLibrary, moduleLibraries);
return _LoadFromKernelResult(component, entryLibrary);
}
class _LoadFromSourceResult {
final ir.Component? component;
final fe.InitializedCompilerState initializedCompilerState;
final List<Uri> moduleLibraries;
_LoadFromSourceResult(
this.component, this.initializedCompilerState, this.moduleLibraries);
_LoadFromSourceResult(this.component, this.initializedCompilerState);
}
Future<_LoadFromSourceResult> _loadFromSource(
@ -234,53 +209,41 @@ Future<_LoadFromSourceResult> _loadFromSource(
}
};
// If we are passed a list of sources, then we are performing a modular
// compile. In this case, we cannot infer null safety from the source files
// and must instead rely on the options passed in on the command line.
bool isModularCompile = false;
List<Uri> sources = [];
if (options.sources != null) {
isModularCompile = true;
sources.addAll(options.sources!);
} else {
fe.CompilerOptions feOptions = fe.CompilerOptions()
..target = target
..librariesSpecificationUri = options.librariesSpecificationUri
..packagesFileUri = options.packageConfig
..explicitExperimentalFlags = options.explicitExperimentalFlags
..environmentDefines = environment
..verbose = verbose
..fileSystem = fileSystem
..onDiagnostic = onDiagnostic
..verbosity = verbosity;
Uri resolvedUri = options.compilationTarget;
bool isLegacy =
await fe.uriUsesLegacyLanguageVersion(resolvedUri, feOptions);
if (isLegacy && options.experimentNullSafetyChecks) {
reporter.reportErrorMessage(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
'text': 'The ${Flags.experimentNullSafetyChecks} option may be used '
'only after all libraries have been migrated to null safety. Some '
'libraries reached from $resolvedUri are still opted out of null '
'safety. Please migrate these libraries before passing '
'${Flags.experimentNullSafetyChecks}.',
});
}
if (isLegacy && options.nullSafetyMode == NullSafetyMode.sound) {
reporter.reportErrorMessage(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
'text': "Starting with Dart 3.0, `dart compile js` expects programs to "
"be null-safe by default. Some libraries reached from $resolvedUri "
"are opted out of null safety. You can temporarily compile this "
"application using the deprecated '${Flags.noSoundNullSafety}' "
"option."
});
}
sources.add(options.compilationTarget);
List<Uri> sources = [options.compilationTarget];
fe.CompilerOptions feOptions = fe.CompilerOptions()
..target = target
..librariesSpecificationUri = options.librariesSpecificationUri
..packagesFileUri = options.packageConfig
..explicitExperimentalFlags = options.explicitExperimentalFlags
..environmentDefines = environment
..verbose = verbose
..fileSystem = fileSystem
..onDiagnostic = onDiagnostic
..verbosity = verbosity;
Uri resolvedUri = options.compilationTarget;
bool isLegacy = await fe.uriUsesLegacyLanguageVersion(resolvedUri, feOptions);
if (isLegacy && options.experimentNullSafetyChecks) {
reporter.reportErrorMessage(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
'text': 'The ${Flags.experimentNullSafetyChecks} option may be used '
'only after all libraries have been migrated to null safety. Some '
'libraries reached from $resolvedUri are still opted out of null '
'safety. Please migrate these libraries before passing '
'${Flags.experimentNullSafetyChecks}.',
});
}
if (isLegacy && options.nullSafetyMode == NullSafetyMode.sound) {
reporter.reportErrorMessage(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
'text': "Starting with Dart 3.0, `dart compile js` expects programs to "
"be null-safe by default. Some libraries reached from $resolvedUri "
"are opted out of null safety. You can temporarily compile this "
"application using the deprecated '${Flags.noSoundNullSafety}' "
"option."
});
}
// If we are performing a modular compile, we expect the platform binary to be
// supplied along with other dill dependencies.
List<Uri> dependencies = [];
if (options.platformBinaries != null && !isModularCompile) {
if (options.platformBinaries != null) {
dependencies.add(options.platformBinaries!
.resolve(_getPlatformFilename(options, targetName)));
}
@ -300,8 +263,8 @@ Future<_LoadFromSourceResult> _loadFromSource(
options.useLegacySubtyping ? fe.NnbdMode.Weak : fe.NnbdMode.Strong,
invocationModes: options.cfeInvocationModes,
verbosity: verbosity);
ir.Component? component = await fe.compile(initializedCompilerState, verbose,
fileSystem, onDiagnostic, sources, isModularCompile);
ir.Component? component = await fe.compile(
initializedCompilerState, verbose, fileSystem, onDiagnostic, sources);
if (component != null) {
assert(() {
@ -312,17 +275,10 @@ Future<_LoadFromSourceResult> _loadFromSource(
_doTransformsOnKernelLoad(component, options);
// We have to compute canonical names on the component here to avoid missing
// canonical names downstream.
if (isModularCompile) {
component.computeCanonicalNames();
}
registerSources(component, compilerInput);
}
return _LoadFromSourceResult(
component, initializedCompilerState, isModularCompile ? sources : []);
return _LoadFromSourceResult(component, initializedCompilerState);
}
Output _createOutput(
@ -330,66 +286,57 @@ Output _createOutput(
DiagnosticReporter reporter,
Library? entryLibrary,
ir.Component component,
List<Uri> moduleLibraries,
fe.InitializedCompilerState? initializedCompilerState) {
Uri? rootLibraryUri = null;
Iterable<ir.Library> libraries = component.libraries;
if (!options.stage.shouldComputeModularAnalysis) {
// For non-modular builds we should always have a [mainMethod] at this
// point.
if (component.mainMethod == null) {
// TODO(sigmund): move this so that we use the same error template
// from the CFE.
reporter.reportError(reporter.createMessage(NO_LOCATION_SPANNABLE,
MessageKind.GENERIC, {'text': "No 'main' method found."}));
}
// If we are building from dill and are passed an [entryUri], then we use
// that to find the appropriate [entryLibrary]. Otherwise, we fallback to
// the [enclosingLibrary] of the [mainMethod].
// NOTE: Under some circumstances, the [entryLibrary] exports the
// [mainMethod] from another library, and thus the [enclosingLibrary] of
// the [mainMethod] may not be the same as the [entryLibrary].
var root = entryLibrary ?? component.mainMethod!.enclosingLibrary;
rootLibraryUri = root.importUri;
// Filter unreachable libraries: [Component] was built by linking in the
// entire SDK libraries, not all of them are used. We include anything
// that is reachable from `main`. Note that all internal libraries that
// the compiler relies on are reachable from `dart:core`.
var seen = Set<Library>();
search(ir.Library current) {
if (!seen.add(current)) return;
for (ir.LibraryDependency dep in current.dependencies) {
search(dep.targetLibrary);
}
}
search(root);
// Libraries dependencies do not show implicit imports to certain internal
// libraries.
const Set<String> alwaysInclude = {
'dart:_internal',
'dart:core',
'dart:async',
...implicitlyUsedLibraries,
};
for (String uri in alwaysInclude) {
Library library = component.libraries.firstWhere((lib) {
return '${lib.importUri}' == uri;
});
search(library);
}
libraries = libraries.where(seen.contains);
if (component.mainMethod == null) {
// TODO(sigmund): move this so that we use the same error template
// from the CFE.
reporter.reportError(reporter.createMessage(NO_LOCATION_SPANNABLE,
MessageKind.GENERIC, {'text': "No 'main' method found."}));
}
return Output(
component,
rootLibraryUri,
libraries.map((lib) => lib.importUri).toList(),
moduleLibraries,
initializedCompilerState);
// If we are building from dill and are passed an [entryUri], then we use
// that to find the appropriate [entryLibrary]. Otherwise, we fallback to
// the [enclosingLibrary] of the [mainMethod].
// NOTE: Under some circumstances, the [entryLibrary] exports the
// [mainMethod] from another library, and thus the [enclosingLibrary] of
// the [mainMethod] may not be the same as the [entryLibrary].
var root = entryLibrary ?? component.mainMethod!.enclosingLibrary;
rootLibraryUri = root.importUri;
// Filter unreachable libraries: [Component] was built by linking in the
// entire SDK libraries, not all of them are used. We include anything
// that is reachable from `main`. Note that all internal libraries that
// the compiler relies on are reachable from `dart:core`.
var seen = Set<Library>();
search(ir.Library current) {
if (!seen.add(current)) return;
for (ir.LibraryDependency dep in current.dependencies) {
search(dep.targetLibrary);
}
}
search(root);
// Libraries dependencies do not show implicit imports to certain internal
// libraries.
const Set<String> alwaysInclude = {
'dart:_internal',
'dart:core',
'dart:async',
...implicitlyUsedLibraries,
};
for (String uri in alwaysInclude) {
Library library = component.libraries.firstWhere((lib) {
return '${lib.importUri}' == uri;
});
search(library);
}
libraries = libraries.where(seen.contains);
return Output(component, rootLibraryUri,
libraries.map((lib) => lib.importUri).toList(), initializedCompilerState);
}
/// Loads an entire Kernel [Component] from a file on disk.
@ -402,7 +349,6 @@ Future<Output?> run(Input input) async {
Library? entryLibrary;
ir.Component? component;
List<Uri> moduleLibraries = const [];
fe.InitializedCompilerState? initializedCompilerState =
input.initializedCompilerState;
if (options.stage.shouldLoadFromDill) {
@ -410,13 +356,11 @@ Future<Output?> run(Input input) async {
await _loadFromKernel(options, compilerInput, targetName);
component = result.component;
entryLibrary = result.entryLibrary;
moduleLibraries = result.moduleLibraries;
} else {
_LoadFromSourceResult result = await _loadFromSource(options, compilerInput,
reporter, input.initializedCompilerState, targetName);
component = result.component;
initializedCompilerState = result.initializedCompilerState;
moduleLibraries = result.moduleLibraries;
}
if (component == null) return null;
if (input.forceSerialization) {
@ -427,14 +371,14 @@ Future<Output?> run(Input input) async {
// Ensure we use the new deserialized entry point library.
entryLibrary = _findEntryLibrary(component, options.entryUri!);
}
return _createOutput(options, reporter, entryLibrary, component,
moduleLibraries, initializedCompilerState);
return _createOutput(
options, reporter, entryLibrary, component, initializedCompilerState);
}
/// Registers with the dart2js compiler all sources embedded in a kernel
/// component. This may include sources that were read from disk directly as
/// files, but also sources that were embedded in binary `.dill` files (like the
/// platform kernel file and kernel files from modular compilation pipelines).
/// platform kernel file).
///
/// This registration improves how locations are presented when errors
/// or crashes are reported by the dart2js compiler.

View File

@ -1,88 +0,0 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart' as ir;
import '../common.dart';
import '../elements/entities.dart';
import '../environment.dart';
import '../ir/annotations.dart';
import '../ir/impact.dart';
import '../ir/modular.dart';
import '../ir/scope.dart';
import '../kernel/dart2js_target.dart';
import '../kernel/native_basic_data.dart';
import '../js_backend/annotations.dart';
import '../kernel/element_map.dart';
import '../options.dart';
class Input {
final CompilerOptions options;
final DiagnosticReporter reporter;
final Environment environment;
final ir.Component component;
final List<Uri> libraries;
final Set<Uri> moduleLibraries;
Input(this.options, this.reporter, this.environment, this.component,
this.libraries, this.moduleLibraries);
}
KernelToElementMap _createElementMap(
CompilerOptions options,
DiagnosticReporter reporter,
Environment environment,
ir.Component component,
List<Uri> libraries) {
final elementMap = KernelToElementMap(reporter, environment, options);
elementMap.addComponent(component);
IrAnnotationData irAnnotationData =
processAnnotations(ModularCore(component, elementMap.constantEvaluator));
final annotationProcessor = KernelAnnotationProcessor(
elementMap, elementMap.nativeBasicDataBuilder, irAnnotationData);
for (final uri in libraries) {
LibraryEntity library = elementMap.elementEnvironment.lookupLibrary(uri)!;
if (maybeEnableNative(library.canonicalUri)) {
annotationProcessor.extractNativeAnnotations(library);
}
annotationProcessor.extractJsInteropAnnotations(library);
}
return elementMap;
}
Map<ir.Member, ImpactBuilderData> _computeForLibrary(
CompilerOptions options,
DiagnosticReporter reporter,
KernelToElementMap elementMap,
ir.Library library) {
Map<ir.Member, ImpactBuilderData> result = {};
void computeForMember(ir.Member member) {
final scopeModel = ScopeModel.from(member, elementMap.constantEvaluator);
final annotations = processMemberAnnotations(
options, reporter, member, computePragmaAnnotationDataFromIr(member));
result[member] =
computeModularMemberData(elementMap, member, scopeModel, annotations)
.impactBuilderData;
}
library.members.forEach(computeForMember);
for (final cls in library.classes) {
cls.members.forEach(computeForMember);
}
return result;
}
ModuleData run(Input input) {
final options = input.options;
final reporter = input.reporter;
final elementMap = _createElementMap(
options, reporter, input.environment, input.component, input.libraries);
Map<Uri, Map<ir.Member, ImpactBuilderData>> result = {};
for (final library in input.component.libraries) {
if (!input.moduleLibraries.contains(library.importUri)) continue;
result[library.importUri] =
_computeForLibrary(options, reporter, elementMap, library);
}
return ModuleData.fromImpactData(result);
}

View File

@ -6,7 +6,6 @@ import 'dart:async';
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/binary/ast_from_binary.dart' as ir;
import 'package:kernel/binary/ast_to_binary.dart' as ir;
import 'package:front_end/src/fasta/util/bytes_sink.dart';
import '../../compiler_api.dart' as api;
import '../commandline_options.dart' show Flags;
import '../common/codegen.dart';
@ -20,7 +19,6 @@ import '../inferrer/abstract_value_domain.dart';
import '../inferrer/abstract_value_strategy.dart';
import '../inferrer/types.dart';
import '../io/source_information.dart';
import '../ir/modular.dart';
import '../js_backend/codegen_inputs.dart';
import '../js_backend/inferred_data.dart';
import '../js_model/js_world.dart';
@ -59,9 +57,6 @@ class SerializationTask extends CompilerTask {
void serializeComponent(ir.Component component,
{bool includeSourceBytes = true}) {
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.
_reporter.log('Writing dill to ${_options.outputUri}');
api.BinaryOutputSink dillOutput =
_outputProvider.createBinarySink(_options.outputUri!);
@ -112,80 +107,6 @@ class SerializationTask extends CompilerTask {
return component;
}
void serializeModuleData(
ModuleData data, ir.Component component, Set<Uri> includedLibraries) {
measureSubtask('serialize transformed dill', () {
_reporter.log('Writing dill to ${_options.outputUri}');
var dillOutput = _outputProvider.createBinarySink(_options.outputUri!);
ir.BinaryPrinter printer = ir.BinaryPrinter(dillOutput,
libraryFilter: (ir.Library l) =>
includedLibraries.contains(l.importUri));
printer.writeComponentFile(component);
dillOutput.close();
});
measureSubtask('serialize module data', () {
final outputUri =
_options.dataOutputUriForStage(Dart2JSStage.modularAnalysis);
_reporter.log('Writing data to $outputUri');
api.BinaryOutputSink dataOutput =
_outputProvider.createBinarySink(outputUri);
// Use empty indices since module data is ephemeral, later phases should
// not depend on data indexed in this file.
DataSinkWriter sink = DataSinkWriter(
BinaryDataSink(dataOutput), _options, SerializationIndices());
data.toDataSink(sink);
sink.close();
});
}
void testModuleSerialization(ModuleData data, ir.Component component) {
if (_options.testMode) {
// TODO(joshualitt):
// Consider using a strategy like we do for the global data, so we can also
// test it with the objectSink/objectSource:
// List<Object> encoding = [];
// DataSink sink = ObjectSink(encoding, useDataKinds: true);
// data.toDataSink(sink);
// DataSource source = ObjectSource(encoding, useDataKinds: true);
// source.registerComponentLookup(new ComponentLookup(component));
// ModuleData.fromDataSource(source);
// Use empty indices since module data is ephemeral, later phases should
// not depend on data indexed in this file.
BytesSink bytes = BytesSink();
DataSinkWriter binarySink = DataSinkWriter(
BinaryDataSink(bytes), _options, SerializationIndices(),
useDataKinds: true);
data.toDataSink(binarySink);
binarySink.close();
var source = DataSourceReader(BinaryDataSource(bytes.builder.toBytes()),
_options, SerializationIndices(),
useDataKinds: true, interner: _valueInterner);
source.registerComponentLookup(ComponentLookup(component));
ModuleData.fromDataSource(source);
}
}
Future<ModuleData> deserializeModuleData(ir.Component component) async {
return await measureIoSubtask('deserialize module data', () async {
_reporter.log('Reading data from ${_options.modularAnalysisInputs}');
final results = ModuleData();
for (Uri uri in _options.modularAnalysisInputs!) {
final dataInput =
await _provider.readFromUri(uri, inputKind: api.InputKind.binary);
// Use empty indices since module data is ephemeral, later phases should
// not depend on data indexed in this file.
DataSourceReader source = DataSourceReader(
BinaryDataSource(dataInput.data), _options, SerializationIndices(),
interner: _valueInterner);
source.registerComponentLookup(ComponentLookup(component));
results.readMoreFromDataSource(source);
}
return results;
});
}
void serializeClosedWorld(
JClosedWorld closedWorld, SerializationIndices indices) {
measureSubtask('serialize closed world', () {

View File

@ -48,17 +48,6 @@ main() {
await test([Flags.cfeOnly, 'foo.dart', '--out=prefix-'], out: 'prefix-');
await test([Flags.cfeOnly, 'foo.dart', '--out=/some/path/prefix-'],
out: '/some/path/prefix-');
await test(
[
Flags.cfeOnly,
'foo.dart',
'${Flags.readModularAnalysis}=modular1.data,modular2.data',
'${Flags.writeModularAnalysis}=modularcfe.data',
],
cfeModularAnalysis: true,
readModularAnalysis: ['modular1.data', 'modular2.data'],
writeModularAnalysis: 'modularcfe.data',
out: 'out.dill');
await test(['foo.dart', '${Flags.stage}=cfe', '--out=/some/path/'],
out: '/some/path/out.dill');
await test(['foo.dart', '${Flags.stage}=cfe', '--out=prefix-'],
@ -83,18 +72,6 @@ main() {
cfeFromDill: true, out: 'prefix-');
await test([Flags.cfeOnly, 'foo.dill', '--out=/some/path/prefix-'],
cfeFromDill: true, out: '/some/path/prefix-');
await test(
[
Flags.cfeOnly,
'foo.dill',
'${Flags.readModularAnalysis}=modular1.data,modular2.data',
'${Flags.writeModularAnalysis}=modularcfe.data',
],
cfeFromDill: true,
cfeModularAnalysis: true,
readModularAnalysis: ['modular1.data', 'modular2.data'],
writeModularAnalysis: 'modularcfe.data',
out: 'out.dill');
await test(['foo.dill', '${Flags.stage}=cfe', '--out=/some/path/'],
cfeFromDill: true, out: '/some/path/out.dill');
await test(['foo.dill', '${Flags.stage}=cfe', '--out=prefix-'],
@ -105,43 +82,6 @@ main() {
'--out=/some/path/prefix-',
], cfeFromDill: true, out: '/some/path/prefix-out.dill');
// Run modular analysis only
await test(['${Flags.stage}=modular-analysis', 'foo.dart'],
writeModularAnalysis: 'modular.data', out: 'out.dill');
await test(
['${Flags.stage}=modular-analysis', '--out=out1.dill', 'foo.dart'],
writeModularAnalysis: 'modular.data', out: 'out1.dill');
await test([
'${Flags.stage}=modular-analysis',
'${Flags.writeModularAnalysis}=modular1.data',
'foo.dart'
], writeModularAnalysis: 'modular1.data', out: 'out.dill');
await test(['${Flags.writeModularAnalysis}=modular1.data', 'foo.dart'],
out: 'out.dill', writeModularAnalysis: 'modular1.data');
await test([
'${Flags.writeModularAnalysis}=modular1.data',
'foo.dart',
'--out=out1.dill'
], out: 'out1.dill', writeModularAnalysis: 'modular1.data');
await test([
'${Flags.writeModularAnalysis}=modular1.data',
'foo.dart',
'-oout1.dill'
], out: 'out1.dill', writeModularAnalysis: 'modular1.data');
await test(
['foo.dart', '${Flags.stage}=modular-analysis', '--out=/some/path/'],
writeModularAnalysis: '/some/path/modular.data',
out: '/some/path/out.dill');
await test(['foo.dart', '${Flags.stage}=modular-analysis', '--out=prefix-'],
writeModularAnalysis: 'prefix-modular.data', out: 'prefix-out.dill');
await test([
'foo.dart',
'${Flags.stage}=modular-analysis',
'--out=/some/path/prefix-'
],
writeModularAnalysis: '/some/path/prefix-modular.data',
out: '/some/path/prefix-out.dill');
// Run deferred load ids only
await test([
'${Flags.stage}=deferred-load-ids',
@ -183,15 +123,6 @@ main() {
'foo.dill',
'--out=/some/path/prefix-'
], out: '/some/path/prefix-', writeClosedWorld: 'world1.data');
await test(
[
'${Flags.readModularAnalysis}=modular1.data,modular2.data',
'${Flags.writeClosedWorld}=world1.data',
'foo.dill'
],
out: 'out.dill',
readModularAnalysis: ['modular1.data', 'modular2.data'],
writeClosedWorld: 'world1.data');
await test(['foo.dill', '${Flags.stage}=closed-world', '--out=/some/path/'],
writeClosedWorld: '/some/path/world.data', out: '/some/path/out.dill');
await test(['foo.dill', '${Flags.stage}=closed-world', '--out=prefix-'],
@ -234,15 +165,6 @@ main() {
'foo.dill',
'--out=/some/path/prefix-'
], readClosedWorld: 'world1.data', writeData: 'global1.data');
await test([
'${Flags.readModularAnalysis}=modular1.data,modular2.data',
'${Flags.readClosedWorld}=world1.data',
'${Flags.writeData}=global1.data',
'foo.dill'
], readModularAnalysis: [
'modular1.data',
'modular2.data'
], readClosedWorld: 'world1.data', writeData: 'global1.data');
await test(
['foo.dill', '${Flags.stage}=global-inference', '--out=/some/path/'],
readClosedWorld: '/some/path/world.data',
@ -368,24 +290,6 @@ main() {
writeCodegen: 'codegen1',
codegenShard: 10,
codegenShards: 11);
await test([
'${Flags.readModularAnalysis}=modular1.data,modular2.data',
'${Flags.readClosedWorld}=world1.data',
'${Flags.readData}=global1.data',
'${Flags.writeCodegen}=codegen1',
'${Flags.codegenShard}=10',
'${Flags.codegenShards}=11',
'foo.dill'
],
readModularAnalysis: [
'modular1.data',
'modular2.data'
],
readClosedWorld: 'world1.data',
readData: 'global1.data',
writeCodegen: 'codegen1',
codegenShard: 10,
codegenShards: 11);
await test([
'foo.dill',
'${Flags.stage}=codegen',
@ -527,23 +431,6 @@ main() {
readCodegen: 'codegen1',
codegenShards: 11,
out: 'out1.js');
await test([
'${Flags.readModularAnalysis}=modular1.data,modular2.data',
'${Flags.readClosedWorld}=world1.data',
'${Flags.readData}=global1.data',
'${Flags.readCodegen}=codegen1',
'${Flags.codegenShards}=11',
'foo.dill'
],
readModularAnalysis: [
'modular1.data',
'modular2.data'
],
readClosedWorld: 'world1.data',
readData: 'global1.data',
readCodegen: 'codegen1',
codegenShards: 11,
out: 'out.js');
await test([
'foo.dill',
'${Flags.stage}=emit-js',
@ -615,15 +502,6 @@ main() {
readClosedWorld: 'world1.data',
readData: 'global1.data',
out: 'out1.js');
await test([
'${Flags.readModularAnalysis}=modular1.data,modular2.data',
'${Flags.readClosedWorld}=world1.data',
'${Flags.readData}=global1.data',
'foo.dill'
], readModularAnalysis: [
'modular1.data',
'modular2.data'
], readClosedWorld: 'world1.data', readData: 'global1.data', out: 'out.js');
await test(
['foo.dill', '${Flags.stage}=codegen-emit-js', '--out=/some/path/'],
readClosedWorld: '/some/path/world.data',
@ -938,8 +816,6 @@ main() {
Future test(List<String> arguments,
{int? exitCode,
String? out,
List<String>? readModularAnalysis,
String? writeModularAnalysis,
bool allFromDill = false,
bool cfeFromDill = false,
bool cfeModularAnalysis = false,
@ -982,27 +858,6 @@ Future test(List<String> arguments,
if (cfeFromDill) {
Expect.equals(Dart2JSStage.cfeFromDill, options.stage);
}
if (readModularAnalysis != null) {
Expect.isNotNull(options.modularAnalysisInputs,
"modularAnalysisInputs expected to be non-null.");
Expect.listEquals(
readModularAnalysis.map(toUri).toList(),
options.modularAnalysisInputs!,
"Unexpected modularAnalysisInputs uri");
}
if (writeModularAnalysis == null) {
Expect.notEquals(options.stage, Dart2JSStage.modularAnalysis);
} else {
Expect.equals(
options.stage,
cfeModularAnalysis
? (cfeFromDill ? Dart2JSStage.cfeFromDill : Dart2JSStage.cfe)
: Dart2JSStage.modularAnalysis);
Expect.equals(
toUri(writeModularAnalysis),
options.dataOutputUriForStage(Dart2JSStage.modularAnalysis),
"Unexpected writeModularAnalysis uri");
}
if (writeDeferredLoadIds == null) {
Expect.notEquals(options.stage, Dart2JSStage.deferredLoadIds);
} else {

View File

@ -30,6 +30,8 @@ main() {
var cDill = await compileUnit(
['c2.dart'], {'c2.dart': sourceC, 'a.dill': aDill, 'b.dill': bDill},
deps: ['a.dill', 'b.dill']);
var unusedDill =
await compileUnit(['unused0.dart'], {'unused0.dart': unusedSource});
DiagnosticCollector diagnostics = DiagnosticCollector();
OutputCollector output = OutputCollector();
@ -38,10 +40,15 @@ main() {
entryPoint: entryPoint,
options: [
'--input-dill=memory:c.dill',
'--dill-dependencies=memory:a.dill,memory:b.dill',
'--dill-dependencies=memory:a.dill,memory:b.dill,memory:unused.dill',
'--sound-null-safety',
],
memorySourceFiles: {'a.dill': aDill, 'b.dill': bDill, 'c.dill': cDill},
memorySourceFiles: {
'a.dill': aDill,
'b.dill': bDill,
'c.dill': cDill,
'unused.dill': unusedDill
},
diagnosticHandler: diagnostics,
outputProvider: output);
load_kernel.Output result = (await load_kernel.run(load_kernel.Input(
@ -50,6 +57,9 @@ main() {
compiler.reporter,
compiler.initializedCompilerState,
false)))!;
// Make sure we trim the unused library.
Expect.isFalse(result.libraries!.any((l) => l.path == '/unused0.dart'));
compiler.frontendStrategy
.registerLoadedLibraries(result.component, result.libraries!);
@ -146,3 +156,7 @@ class C2 extends B1 {
main() => print(C2().foo.buffer.toString());
''';
const unusedSource = '''
void unused() => throw 'Unused';
''';

View File

@ -29,8 +29,9 @@ main() {
(fe.DiagnosticMessage message) {
message.plainTextFormatted.forEach(print);
Expect.notEquals(fe.Severity.error, message.severity);
}, [Uri.base.resolve('pkg/compiler/test/end_to_end/data/hello_world.dart')],
false))!;
}, [
Uri.base.resolve('pkg/compiler/test/end_to_end/data/hello_world.dart')
]))!;
Expect.isNotNull(new ir.CoreTypes(component).futureClass);
}

View File

@ -1,102 +0,0 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:expect/expect.dart';
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
import 'package:compiler/src/util/memory_compiler.dart';
const memorySourceFiles = const {
'main.dart': '''
// @dart=2.12
import 'used.dart';
void main() {
foo();
}
''',
'used.dart': '''
// @dart=2.12
void foo() {
print('foo');
}
''',
'unused.dart': '''
// @dart=2.12
void unused() {
throw 'unused';
}
'''
};
void verifyComponent(
ir.Component component, List<String> included, List<String> excluded) {
Set<String> uris = {};
component.libraries
.forEach((library) => uris.add(library.importUri.toString()));
for (String include in included) {
Expect.isTrue(uris.contains(include));
}
for (String exclude in excluded) {
Expect.isFalse(uris.contains(exclude));
}
}
Future<List<int>> buildDillAndVerify(
Map<String, dynamic> memorySourceFiles,
List<String> flags,
List<String> includedLibraries,
List<String> excludedLibraries) async {
final dillUri = Uri.parse('out.dill');
final collector = OutputCollector();
CompilationResult result = await runCompiler(
memorySourceFiles: memorySourceFiles,
options: flags,
outputProvider: collector);
Expect.isTrue(result.isSuccess);
Expect.isTrue(collector.binaryOutputMap.containsKey(dillUri));
List<int> bytes = collector.binaryOutputMap[dillUri]!.list;
Expect.isTrue(bytes.isNotEmpty);
ir.Component component = ir.Component();
BinaryBuilder(bytes).readComponent(component);
verifyComponent(component, includedLibraries, excludedLibraries);
return bytes;
}
Future<void> verifyComponentTrim() async {
List<String> buildDillFromSourceFlags = [
'--cfe-only',
'--out=out.dill',
'--sources=memory:main.dart,memory:used.dart,memory:unused.dart',
'--sound-null-safety',
];
List<int> bytes = await buildDillAndVerify(
memorySourceFiles,
buildDillFromSourceFlags,
['memory:main.dart', 'memory:used.dart', 'memory:unused.dart'],
[]);
// The combination of `--cfe-only` + `--entry-uri` should trigger the
// component trimming logic.
List<String> trimDillFlags = [
'--input-dill=memory:main.dill',
'--cfe-only',
'--out=out.dill',
'--entry-uri=memory:main.dart',
'--sound-null-safety',
];
List<int> newBytes = await buildDillAndVerify(
{'main.dill': bytes},
trimDillFlags,
['memory:main.dart', 'memory:used.dart'],
['memory:unused.dart']);
Expect.isTrue(newBytes.length < bytes.length);
}
void main() async {
await verifyComponentTrim();
}

View File

@ -2,7 +2,7 @@
// 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 the modular compilation pipeline of dart2js.
/// Test the compilation pipeline of dart2js.
///
/// This is a shell that runs multiple tests, one per folder under `data/`.
import 'dart:async';
@ -21,11 +21,9 @@ main(List<String> args) async {
options,
IOPipeline([
OutlineDillCompilationStep(),
FullDillCompilationStep(onlyOnSdk: true),
ModularAnalysisStep(onlyOnSdk: true),
ModularAnalysisStep(),
ConcatenateDillsStep(useModularAnalysis: true),
ComputeClosedWorldStep(useModularAnalysis: true),
FullDillCompilationStep(),
ConcatenateDillsStep(),
ComputeClosedWorldStep(),
GlobalAnalysisStep(),
Dart2jsCodegenStep(codeId0),
Dart2jsCodegenStep(codeId1),

View File

@ -28,9 +28,6 @@ late String _kernelWorkerScript;
const dillSummaryId = DataId("summary.dill");
const dillId = DataId("full.dill");
const fullDillId = DataId("concatenate.dill");
const modularUpdatedDillId = DataId("modular.dill");
const modularDataId = DataId("modular.data");
const modularFullDataId = DataId("concatenate.modular.data");
const closedWorldId = DataId("world");
const globalUpdatedDillId = DataId("global.dill");
const globalDataId = DataId("global.data");
@ -74,7 +71,7 @@ List<String> getSources(Module module) {
abstract class CFEStep extends IOModularStep {
final String stepName;
CFEStep(this.stepName, this.onlyOnSdk);
CFEStep(this.stepName);
@override
bool get needsSources => true;
@ -82,9 +79,6 @@ abstract class CFEStep extends IOModularStep {
@override
bool get onlyOnMain => false;
@override
final bool onlyOnSdk;
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
@ -181,7 +175,7 @@ class OutlineDillCompilationStep extends CFEStep {
@override
DataId get outputData => dillSummaryId;
OutlineDillCompilationStep() : super('outline-dill-compilation', false);
OutlineDillCompilationStep() : super('outline-dill-compilation');
}
// Step that compiles sources in a module to a .dill file.
@ -208,113 +202,14 @@ class FullDillCompilationStep extends CFEStep {
@override
DataId get outputData => dillId;
FullDillCompilationStep({bool onlyOnSdk = false})
: super('full-dill-compilation', onlyOnSdk);
}
class ModularAnalysisStep extends IOModularStep {
@override
List<DataId> get resultData => [modularDataId, modularUpdatedDillId];
@override
bool get needsSources => !onlyOnSdk;
/// The SDK has no dependencies, and for all other modules we only need
/// summaries.
@override
List<DataId> get dependencyDataNeeded => [dillSummaryId];
/// All non SDK modules only need sources for module data.
@override
List<DataId> get moduleDataNeeded => onlyOnSdk ? [dillId] : const [];
@override
bool get onlyOnMain => false;
@override
final bool onlyOnSdk;
@override
bool get notOnSdk => !onlyOnSdk;
// TODO(joshualitt): We currently special case the SDK both because it is not
// trivial to build it in the same fashion as other modules, and because it is
// a special case in other build environments. Eventually, we should
// standardize this a bit more and always build the SDK modularly, if we have
// to build it.
ModularAnalysisStep({this.onlyOnSdk = false});
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
if (_options.verbose) print("\nstep: modular analysis on $module");
Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
List<String> dillDependencies = [];
List<String> sources = [];
List<String> extraArgs = [];
if (!module.isSdk) {
await writePackageConfig(module, transitiveDependencies, root);
String rootScheme = getRootScheme(module);
sources = getSources(module);
dillDependencies = transitiveDependencies
.map((m) => '${toUri(m, dillSummaryId)}')
.toList();
extraArgs = [
'--packages=${root.resolve(packageConfigJsonPath)}',
'--multi-root=$root',
'--multi-root-scheme=$rootScheme',
];
}
List<String> args = [
'--packages=${sdkRoot.toFilePath()}/$packageConfigJsonPath',
_dart2jsScript,
Flags.soundNullSafety,
if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
if (_options.useSdk) '--invoker=modular_test',
// If we have sources, then we aren't building the SDK, otherwise we
// assume we are building the sdk and pass in a full dill.
if (sources.isNotEmpty)
'${Flags.sources}=${sources.join(',')}'
else
'${Flags.inputDill}=${toUri(module, dillId)}',
'${Flags.cfeConstants}',
if (dillDependencies.isNotEmpty)
'--dill-dependencies=${dillDependencies.join(',')}',
'--out=${toUri(module, modularUpdatedDillId)}',
'${Flags.writeModularAnalysis}=${toUri(module, modularDataId)}',
for (String flag in flags) '--enable-experiment=$flag',
...extraArgs
];
var result =
await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
_checkExitCode(result, this, module);
}
@override
void notifyCached(Module module) {
if (_options.verbose) {
print("cached step: dart2js modular analysis on $module");
}
}
FullDillCompilationStep() : super('full-dill-compilation');
}
class ConcatenateDillsStep extends IOModularStep {
final bool useModularAnalysis;
DataId get idForDill => useModularAnalysis ? modularUpdatedDillId : dillId;
List<DataId> get dependencies => [
idForDill,
if (useModularAnalysis) modularDataId,
];
List<DataId> get dependencies => [dillId];
@override
List<DataId> get resultData => [
fullDillId,
if (useModularAnalysis) modularFullDataId,
];
List<DataId> get resultData => [fullDillId];
@override
bool get needsSources => false;
@ -328,20 +223,15 @@ class ConcatenateDillsStep extends IOModularStep {
@override
bool get onlyOnMain => true;
ConcatenateDillsStep({required this.useModularAnalysis});
ConcatenateDillsStep();
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
if (_options.verbose) print("\nstep: dart2js concatenate dills on $module");
Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
DataId dillId = idForDill;
Iterable<String> dillDependencies =
transitiveDependencies.map((m) => '${toUri(m, dillId)}');
List<String> dataDependencies = transitiveDependencies
.map((m) => '${toUri(m, modularDataId)}')
.toList();
dataDependencies.add('${toUri(module, modularDataId)}');
List<String> args = [
'--packages=${sdkRoot.toFilePath()}/$packageConfigJsonPath',
_dart2jsScript,
@ -353,10 +243,6 @@ class ConcatenateDillsStep extends IOModularStep {
'${Flags.inputDill}=${toUri(module, dillId)}',
for (String flag in flags) '--enable-experiment=$flag',
'${Flags.dillDependencies}=${dillDependencies.join(',')}',
if (useModularAnalysis) ...[
'${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
'${Flags.writeModularAnalysis}=${toUri(module, modularFullDataId)}',
],
'${Flags.cfeOnly}',
'--out=${toUri(module, fullDillId)}',
];
@ -375,14 +261,9 @@ class ConcatenateDillsStep extends IOModularStep {
// Step that invokes the dart2js closed world computation.
class ComputeClosedWorldStep extends IOModularStep {
final bool useModularAnalysis;
ComputeClosedWorldStep();
ComputeClosedWorldStep({required this.useModularAnalysis});
List<DataId> get dependencies => [
fullDillId,
if (useModularAnalysis) modularFullDataId,
];
List<DataId> get dependencies => [fullDillId];
@override
List<DataId> get resultData => const [closedWorldId, globalUpdatedDillId];
@ -414,8 +295,6 @@ class ComputeClosedWorldStep extends IOModularStep {
'${Flags.entryUri}=$fakeRoot${module.mainSource}',
'${Flags.inputDill}=${toUri(module, fullDillId)}',
for (String flag in flags) '--enable-experiment=$flag',
if (useModularAnalysis)
'${Flags.readModularAnalysis}=${toUri(module, modularFullDataId)}',
'${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
Flags.noClosedWorldInData,
'--out=${toUri(module, globalUpdatedDillId)}',

View File

@ -1,34 +0,0 @@
// 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.
/// Test the modular compilation pipeline of dart2js.
///
/// This is a shell that runs multiple tests, one per folder under `data/`.
import 'dart:async';
import 'package:modular_test/src/io_pipeline.dart';
import 'package:modular_test/src/runner.dart';
import 'modular_test_suite_helper.dart';
main(List<String> args) async {
var options = Options.parse(args);
await resolveScripts(options);
await Future.wait([
runSuite(
sdkRoot.resolve('tests/modular/'),
'tests/modular',
options,
IOPipeline([
OutlineDillCompilationStep(),
FullDillCompilationStep(),
ConcatenateDillsStep(useModularAnalysis: false),
ComputeClosedWorldStep(useModularAnalysis: false),
GlobalAnalysisStep(),
Dart2jsCodegenStep(codeId0),
Dart2jsCodegenStep(codeId1),
Dart2jsEmissionStep(),
RunD8(),
], cacheSharedModules: true)),
]);
}

View File

@ -179,9 +179,8 @@ Future<Component?> compile(
bool verbose,
FileSystem fileSystem,
DiagnosticMessageHandler onDiagnostic,
List<Uri> inputs,
bool isModularCompile) async {
assert(inputs.length == 1 || isModularCompile);
List<Uri> inputs) async {
assert(inputs.length == 1);
CompilerOptions options = state.options;
options
..onDiagnostic = onDiagnostic
@ -198,7 +197,7 @@ Future<Component?> compile(
CompilerResult compilerResult = await generateKernelInternal();
Component? component = compilerResult.component;
if (component == null) return null;
if (component.mainMethod == null && !isModularCompile) {
if (component.mainMethod == null) {
context.options.report(
messageMissingMain.withLocation(inputs.single, -1, 0),
Severity.error);

View File

@ -2424,17 +2424,6 @@
"shards": 4,
"fileset": "js_platform_2"
},
{
"name": "dart2js legacy modular tests",
"script": "out/ReleaseX64/dart-sdk/bin/dart",
"testRunner": true,
"arguments": [
"pkg/compiler/tool/modular_test_suite_legacy.dart",
"-nweb-unittest-asserts-legacy-linux",
"--verbose",
"--use-sdk"
]
},
{
"name": "dart2js modular tests",
"script": "out/ReleaseX64/dart-sdk/bin/dart",