Version 2.16.0-60.0.dev
Merge commit 'b735151bbccdcbe216f45eb511ab01d1650b0eb2' into 'dev'
diff --git a/pkg/analyzer/lib/src/services/available_declarations.dart b/pkg/analyzer/lib/src/services/available_declarations.dart
index 588b150..b5a3097 100644
--- a/pkg/analyzer/lib/src/services/available_declarations.dart
+++ b/pkg/analyzer/lib/src/services/available_declarations.dart
@@ -504,6 +504,10 @@
/// The list of changed file paths.
final List<String> _changedPaths = [];
+ /// While processing [_changedPaths] we accumulate libraries here.
+ /// Then we process all of them at once, and reset to the empty set.
+ Set<_File> _invalidatedLibraries = {};
+
/// The list of files scheduled for processing. It may include parts and
/// libraries, but parts are ignored when we detect them.
final List<_ScheduledFile> _scheduledFiles = [];
@@ -529,7 +533,9 @@
_whenKnownFilesPulled = now;
pullKnownFiles();
}
- return _changedPaths.isNotEmpty || _scheduledFiles.isNotEmpty;
+ return _changedPaths.isNotEmpty ||
+ _invalidatedLibraries.isNotEmpty ||
+ _scheduledFiles.isNotEmpty;
}
/// Add the [analysisContext], so that its libraries are reported via the
@@ -595,6 +601,13 @@
return;
}
+ // There are no more changes as far as we know.
+ // So, recompute exported declarations for all invalidated libraries.
+ if (_invalidatedLibraries.isNotEmpty) {
+ _processInvalidatedLibraries(_invalidatedLibraries);
+ _invalidatedLibraries = {};
+ }
+
if (_scheduledFiles.isNotEmpty) {
var scheduledFile = _scheduledFiles.removeLast();
var file = _getFileByPath(scheduledFile.context, [], scheduledFile.path)!;
@@ -794,11 +807,33 @@
_invalidateExportedDeclarations(invalidatedLibraries, newLibrary);
}
}
- _computeExportedDeclarations(invalidatedLibraries);
- var changedLibraries = <Library>[];
+ // Don't compute exported declarations now, there might be more changes.
+ // Instead, accumulate invalidated libraries, and recompute all later.
+ _invalidatedLibraries.addAll(invalidatedLibraries);
+
var removedLibraries = <int>[];
for (var libraryFile in invalidatedLibraries) {
+ if (!libraryFile.exists) {
+ _idToLibrary.remove(libraryFile.id);
+ removedLibraries.add(libraryFile.id);
+ }
+ }
+ for (var file in notLibraries) {
+ _idToLibrary.remove(file.id);
+ removedLibraries.add(file.id);
+ }
+ if (removedLibraries.isNotEmpty) {
+ _changesController.add(
+ LibraryChange._([], removedLibraries),
+ );
+ }
+ }
+
+ void _processInvalidatedLibraries(Set<_File> invalidatedLibraries) {
+ _computeExportedDeclarations(invalidatedLibraries);
+ var changedLibraries = <Library>[];
+ for (var libraryFile in invalidatedLibraries) {
if (libraryFile.exists) {
var library = Library._(
libraryFile.id,
@@ -809,17 +844,10 @@
);
_idToLibrary[library.id] = library;
changedLibraries.add(library);
- } else {
- _idToLibrary.remove(libraryFile.id);
- removedLibraries.add(libraryFile.id);
}
}
- for (var file in notLibraries) {
- _idToLibrary.remove(file.id);
- removedLibraries.add(file.id);
- }
_changesController.add(
- LibraryChange._(changedLibraries, removedLibraries),
+ LibraryChange._(changedLibraries, []),
);
}
diff --git a/pkg/analyzer/test/src/services/available_declarations_test.dart b/pkg/analyzer/test/src/services/available_declarations_test.dart
index 17908dd..357ca89 100644
--- a/pkg/analyzer/test/src/services/available_declarations_test.dart
+++ b/pkg/analyzer/test/src/services/available_declarations_test.dart
@@ -914,6 +914,75 @@
]);
}
+ /// https://github.com/dart-lang/sdk/issues/47804
+ test_updated_exported2() async {
+ var a = convertPath('/home/test/lib/a.dart');
+ var b = convertPath('/home/test/lib/b.dart');
+ var c = convertPath('/home/test/lib/c.dart');
+
+ newFile(a, content: r'''
+class A {}
+''');
+ newFile(b, content: r'''
+class B {}
+''');
+ newFile(c, content: r'''
+export 'a.dart';
+export 'b.dart';
+class C {}
+''');
+ tracker.addContext(testAnalysisContext);
+
+ await _doAllTrackerWork();
+ _assertHasLibrary('package:test/c.dart', declarations: [
+ _ExpectedDeclaration.class_('A', [
+ _ExpectedDeclaration.constructor(''),
+ ]),
+ _ExpectedDeclaration.class_('B', [
+ _ExpectedDeclaration.constructor(''),
+ ]),
+ _ExpectedDeclaration.class_('C', [
+ _ExpectedDeclaration.constructor(''),
+ ]),
+ ]);
+
+ changes.clear();
+
+ newFile(a, content: r'''
+class A2 {}
+''');
+ newFile(b, content: r'''
+class B2 {}
+''');
+ tracker.changeFile(a);
+ tracker.changeFile(b);
+ await _doAllTrackerWork();
+
+ // In general it is OK to get duplicate libraries.
+ // But here we notified about both `a.dart` and `b.dart` changes before
+ // performing any work. So, there is no reason do handle `c.dart` twice.
+ var uniquePathSet = <String>{};
+ for (var change in changes) {
+ for (var library in change.changed) {
+ if (!uniquePathSet.add(library.path)) {
+ fail('Not unique path: ${library.path}');
+ }
+ }
+ }
+
+ _assertHasLibrary('package:test/c.dart', declarations: [
+ _ExpectedDeclaration.class_('A2', [
+ _ExpectedDeclaration.constructor(''),
+ ]),
+ _ExpectedDeclaration.class_('B2', [
+ _ExpectedDeclaration.constructor(''),
+ ]),
+ _ExpectedDeclaration.class_('C', [
+ _ExpectedDeclaration.constructor(''),
+ ]),
+ ]);
+ }
+
test_updated_library() async {
var a = convertPath('/home/test/lib/a.dart');
var b = convertPath('/home/test/lib/b.dart');
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 9257730..7a20a67 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -5,44 +5,25 @@
/// Test the modular compilation pipeline of dart2js.
///
/// This is a shell that runs multiple tests, one per folder under `data/`.
-import 'dart:io';
import 'dart:async';
-import 'package:compiler/src/commandline_options.dart';
-import 'package:compiler/src/kernel/dart2js_target.dart';
-import 'package:front_end/src/compute_platform_binaries_location.dart'
- show computePlatformBinariesLocation;
import 'package:modular_test/src/io_pipeline.dart';
-import 'package:modular_test/src/pipeline.dart';
-import 'package:modular_test/src/suite.dart';
import 'package:modular_test/src/runner.dart';
-import 'package:package_config/package_config.dart';
+import 'modular_test_suite_helper.dart';
-String packageConfigJsonPath = ".dart_tool/package_config.json";
-Uri sdkRoot = Platform.script.resolve("../../../");
-Uri packageConfigUri = sdkRoot.resolve(packageConfigJsonPath);
-Options _options;
-String _dart2jsScript;
-String _kernelWorkerScript;
-
-// TODO(joshualitt): Figure out a way to support package configs in
-// tests/modular.
-PackageConfig _packageConfig;
main(List<String> args) async {
- _options = Options.parse(args);
- _packageConfig = await loadPackageConfigUri(packageConfigUri);
- await _resolveScripts();
+ var options = Options.parse(args);
+ await resolveScripts(options);
await Future.wait([
- // TODO(joshualitt): Factor modular steps into a library so we can test
- // modular analysis alongside the existing pipeline.
runSuite(
sdkRoot.resolve('tests/modular/'),
'tests/modular',
- _options,
+ options,
IOPipeline([
OutlineDillCompilationStep(),
FullDillCompilationStep(),
- ComputeClosedWorldStep(useModularAnalysis: false),
+ ModularAnalysisStep(),
+ ComputeClosedWorldStep(useModularAnalysis: true),
GlobalAnalysisStep(),
Dart2jsCodegenStep(codeId0),
Dart2jsCodegenStep(codeId1),
@@ -51,631 +32,3 @@
], cacheSharedModules: true)),
]);
}
-
-const dillSummaryId = DataId("sdill");
-const dillId = DataId("dill");
-const modularUpdatedDillId = DataId("mdill");
-const modularDataId = DataId("mdata");
-const closedWorldId = DataId("world");
-const globalUpdatedDillId = DataId("gdill");
-const globalDataId = DataId("gdata");
-const codeId = ShardsDataId("code", 2);
-const codeId0 = ShardDataId(codeId, 0);
-const codeId1 = ShardDataId(codeId, 1);
-const jsId = DataId("js");
-const txtId = DataId("txt");
-const fakeRoot = 'dev-dart-app:/';
-
-String _packageConfigEntry(String name, Uri root,
- {Uri packageRoot, LanguageVersion version}) {
- var fields = [
- '"name": "${name}"',
- '"rootUri": "$root"',
- if (packageRoot != null) '"packageUri": "$packageRoot"',
- if (version != null) '"languageVersion": "$version"'
- ];
- return '{${fields.join(',')}}';
-}
-
-abstract class CFEStep implements IOModularStep {
- final String stepName;
-
- CFEStep(this.stepName);
-
- @override
- bool get needsSources => true;
-
- @override
- bool get onlyOnMain => false;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("\nstep: $stepName on $module");
-
- // We use non file-URI schemes for representeing source locations in a
- // root-agnostic way. This allows us to refer to file across modules and
- // across steps without exposing the underlying temporary folders that are
- // created by the framework. In build systems like bazel this is especially
- // important because each step may be run on a different machine.
- //
- // Files in packages are defined in terms of `package:` URIs, while
- // non-package URIs are defined using the `dart-dev-app` scheme.
- String rootScheme = module.isSdk ? 'dart-dev-sdk' : 'dev-dart-app';
- String sourceToImportUri(Uri relativeUri) {
- if (module.isPackage) {
- var basePath = module.packageBase.path;
- var packageRelativePath = basePath == "./"
- ? relativeUri.path
- : relativeUri.path.substring(basePath.length);
- return 'package:${module.name}/$packageRelativePath';
- } else {
- return '$rootScheme:/$relativeUri';
- }
- }
-
- // We create both a .packages and package_config.json file which defines
- // the location of this module if it is a package. The CFE requires that
- // if a `package:` URI of a dependency is used in an import, then we need
- // that package entry in the associated file. However, after it checks that
- // the definition exists, the CFE will not actually use the resolved URI if
- // a library for the import URI is already found in one of the provide
- // .dill files of the dependencies. For that reason, and to ensure that
- // a step only has access to the files provided in a module, we generate a
- // config file with invalid folders for other packages.
- // TODO(sigmund): follow up with the CFE to see if we can remove the need
- // for these dummy entries..
- // TODO(joshualitt): Generate just the json file.
- var packagesJson = [];
- var packagesContents = StringBuffer();
- if (module.isPackage) {
- packagesContents.write('${module.name}:${module.packageBase}\n');
- packagesJson.add(_packageConfigEntry(
- module.name, Uri.parse('../${module.packageBase}')));
- }
-
- Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
- int unusedNum = 0;
- for (Module dependency in transitiveDependencies) {
- if (dependency.isPackage) {
- // rootUri should be ignored for dependent modules, so we pass in a
- // bogus value.
- var rootUri = Uri.parse('unused$unusedNum');
- unusedNum++;
-
- var dependentPackage = _packageConfig[dependency.name];
- var packageJson = dependentPackage == null
- ? _packageConfigEntry(dependency.name, rootUri)
- : _packageConfigEntry(dependentPackage.name, rootUri,
- version: dependentPackage.languageVersion);
- packagesJson.add(packageJson);
- packagesContents.write('${dependency.name}:$rootUri\n');
- }
- }
-
- if (module.isPackage) {
- await File.fromUri(root.resolve(packageConfigJsonPath))
- .create(recursive: true);
- await File.fromUri(root.resolve(packageConfigJsonPath)).writeAsString('{'
- ' "configVersion": ${_packageConfig.version},'
- ' "packages": [ ${packagesJson.join(',')} ]'
- '}');
- }
-
- await File.fromUri(root.resolve('.packages'))
- .writeAsString('$packagesContents');
-
- List<String> sources;
- List<String> extraArgs = ['--packages-file', '$rootScheme:/.packages'];
- if (module.isSdk) {
- // When no flags are passed, we can skip compilation and reuse the
- // platform.dill created by build.py.
- if (flags.isEmpty) {
- var platform = computePlatformBinariesLocation()
- .resolve("dart2js_platform_unsound.dill");
- var destination = root.resolveUri(toUri(module, outputData));
- if (_options.verbose) {
- print('command:\ncp $platform $destination');
- }
- await File.fromUri(platform).copy(destination.toFilePath());
- return;
- }
- sources = requiredLibraries['dart2js'] + ['dart:core'];
- extraArgs += [
- '--libraries-file',
- '$rootScheme:///sdk/lib/libraries.json'
- ];
- assert(transitiveDependencies.isEmpty);
- } else {
- sources = module.sources.map(sourceToImportUri).toList();
- }
-
- // TODO(joshualitt): Ensure the kernel worker has some way to specify
- // --no-sound-null-safety
- List<String> args = [
- _kernelWorkerScript,
- ...stepArguments,
- '--exclude-non-sources',
- '--multi-root',
- '$root',
- '--multi-root-scheme',
- rootScheme,
- ...extraArgs,
- '--output',
- '${toUri(module, outputData)}',
- ...(transitiveDependencies
- .expand((m) => ['--input-summary', '${toUri(m, inputData)}'])),
- ...(sources.expand((String uri) => ['--source', uri])),
- ...(flags.expand((String flag) => ['--enable-experiment', flag])),
- ];
-
- var result =
- await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
- _checkExitCode(result, this, module);
- }
-
- List<String> get stepArguments;
-
- DataId get inputData;
-
- DataId get outputData;
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose) print("\ncached step: $stepName on $module");
- }
-}
-
-// Step that compiles sources in a module to a summary .dill file.
-class OutlineDillCompilationStep extends CFEStep {
- @override
- List<DataId> get resultData => const [dillSummaryId];
-
- @override
- bool get needsSources => true;
-
- @override
- List<DataId> get dependencyDataNeeded => const [dillSummaryId];
-
- @override
- List<DataId> get moduleDataNeeded => const [];
-
- @override
- List<String> get stepArguments =>
- ['--target', 'dart2js_summary', '--summary-only'];
-
- @override
- DataId get inputData => dillSummaryId;
-
- @override
- DataId get outputData => dillSummaryId;
-
- OutlineDillCompilationStep() : super('outline-dill-compilation');
-}
-
-// Step that compiles sources in a module to a .dill file.
-class FullDillCompilationStep extends CFEStep {
- @override
- List<DataId> get resultData => const [dillId];
-
- @override
- bool get needsSources => true;
-
- @override
- List<DataId> get dependencyDataNeeded => const [dillSummaryId];
-
- @override
- List<DataId> get moduleDataNeeded => const [];
-
- @override
- List<String> get stepArguments =>
- ['--target', 'dart2js', '--no-summary', '--no-summary-only'];
-
- @override
- DataId get inputData => dillSummaryId;
-
- @override
- DataId get outputData => dillId;
-
- FullDillCompilationStep() : super('full-dill-compilation');
-}
-
-class ModularAnalysisStep implements IOModularStep {
- @override
- List<DataId> get resultData => const [modularDataId, modularUpdatedDillId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [dillId];
-
- @override
- List<DataId> get moduleDataNeeded => const [dillId];
-
- @override
- bool get onlyOnMain => 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);
- Iterable<String> dillDependencies =
- transitiveDependencies.map((m) => '${toUri(m, dillId)}');
- List<String> args = [
- '--packages=${sdkRoot.toFilePath()}/.packages',
- _dart2jsScript,
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.inputDill}=${toUri(module, dillId)}',
- if (dillDependencies.isNotEmpty)
- '--dill-dependencies=${dillDependencies.join(',')}',
- '--out=${toUri(module, modularUpdatedDillId)}',
- '${Flags.writeModularAnalysis}=${toUri(module, modularDataId)}',
- for (String flag in flags) '--enable-experiment=$flag',
- ];
- 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");
- }
- }
-}
-
-DataId idForDill({bool useModularAnalysis}) =>
- useModularAnalysis ? modularUpdatedDillId : dillId;
-
-List<DataId> inputFromAnalysis({bool useModularAnalysis = false}) => [
- idForDill(useModularAnalysis: useModularAnalysis),
- if (useModularAnalysis) modularDataId,
- ];
-
-// Step that invokes the dart2js closed world computation.
-class ComputeClosedWorldStep implements IOModularStep {
- final bool useModularAnalysis;
-
- ComputeClosedWorldStep({this.useModularAnalysis});
-
- @override
- List<DataId> get resultData => const [closedWorldId, globalUpdatedDillId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded =>
- inputFromAnalysis(useModularAnalysis: useModularAnalysis);
-
- @override
- List<DataId> get moduleDataNeeded =>
- inputFromAnalysis(useModularAnalysis: useModularAnalysis);
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose)
- print("\nstep: dart2js compute closed world on $module");
- Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
- DataId dillId = idForDill(useModularAnalysis: useModularAnalysis);
- 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()}/.packages',
- _dart2jsScript,
- // TODO(sigmund): remove this dependency on libraries.json
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.entryUri}=$fakeRoot${module.mainSource}',
- '${Flags.inputDill}=${toUri(module, dillId)}',
- for (String flag in flags) '--enable-experiment=$flag',
- '${Flags.dillDependencies}=${dillDependencies.join(',')}',
- if (useModularAnalysis)
- '${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
- '${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
- '--out=${toUri(module, globalUpdatedDillId)}',
- ];
- var result =
- await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
-
- _checkExitCode(result, this, module);
- }
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose)
- print("\ncached step: dart2js compute closed world on $module");
- }
-}
-
-// Step that runs the dart2js modular analysis.
-class GlobalAnalysisStep implements IOModularStep {
- @override
- List<DataId> get resultData => const [globalDataId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [globalUpdatedDillId];
-
- @override
- List<DataId> get moduleDataNeeded =>
- const [closedWorldId, globalUpdatedDillId];
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("\nstep: dart2js global analysis on $module");
- List<String> args = [
- '--packages=${sdkRoot.toFilePath()}/.packages',
- _dart2jsScript,
- // TODO(sigmund): remove this dependency on libraries.json
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.entryUri}=$fakeRoot${module.mainSource}',
- '${Flags.inputDill}=${toUri(module, globalUpdatedDillId)}',
- for (String flag in flags) '--enable-experiment=$flag',
- '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
- '${Flags.writeData}=${toUri(module, globalDataId)}',
- ];
- var result =
- await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
-
- _checkExitCode(result, this, module);
- }
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose)
- print("\ncached step: dart2js global analysis on $module");
- }
-}
-
-// Step that invokes the dart2js code generation on the main module given the
-// results of the global analysis step and produces one shard of the codegen
-// output.
-class Dart2jsCodegenStep implements IOModularStep {
- final ShardDataId codeId;
-
- Dart2jsCodegenStep(this.codeId);
-
- @override
- List<DataId> get resultData => [codeId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [];
-
- @override
- List<DataId> get moduleDataNeeded =>
- const [globalUpdatedDillId, closedWorldId, globalDataId];
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("\nstep: dart2js backend on $module");
- List<String> args = [
- '--packages=${sdkRoot.toFilePath()}/.packages',
- _dart2jsScript,
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.entryUri}=$fakeRoot${module.mainSource}',
- '${Flags.inputDill}=${toUri(module, globalUpdatedDillId)}',
- for (String flag in flags) '--enable-experiment=$flag',
- '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
- '${Flags.readData}=${toUri(module, globalDataId)}',
- '${Flags.writeCodegen}=${toUri(module, codeId.dataId)}',
- '${Flags.codegenShard}=${codeId.shard}',
- '${Flags.codegenShards}=${codeId.dataId.shards}',
- ];
- 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 backend on $module");
- }
-}
-
-// Step that invokes the dart2js codegen enqueuer and emitter on the main module
-// given the results of the global analysis step and codegen shards.
-class Dart2jsEmissionStep implements IOModularStep {
- @override
- List<DataId> get resultData => const [jsId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [];
-
- @override
- List<DataId> get moduleDataNeeded => const [
- globalUpdatedDillId,
- closedWorldId,
- globalDataId,
- codeId0,
- codeId1
- ];
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("step: dart2js backend on $module");
- List<String> args = [
- '--packages=${sdkRoot.toFilePath()}/.packages',
- _dart2jsScript,
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.entryUri}=$fakeRoot${module.mainSource}',
- '${Flags.inputDill}=${toUri(module, globalUpdatedDillId)}',
- for (String flag in flags) '${Flags.enableLanguageExperiments}=$flag',
- '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
- '${Flags.readData}=${toUri(module, globalDataId)}',
- '${Flags.readCodegen}=${toUri(module, codeId)}',
- '${Flags.codegenShards}=${codeId.shards}',
- '--out=${toUri(module, jsId)}',
- ];
- var result =
- await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
-
- _checkExitCode(result, this, module);
- }
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose) print("\ncached step: dart2js backend on $module");
- }
-}
-
-/// Step that runs the output of dart2js in d8 and saves the output.
-class RunD8 implements IOModularStep {
- @override
- List<DataId> get resultData => const [txtId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [];
-
- @override
- List<DataId> get moduleDataNeeded => const [jsId];
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("\nstep: d8 on $module");
- List<String> d8Args = [
- sdkRoot
- .resolve('sdk/lib/_internal/js_runtime/lib/preambles/d8.js')
- .toFilePath(),
- root.resolveUri(toUri(module, jsId)).toFilePath(),
- ];
- var result = await _runProcess(
- sdkRoot.resolve(_d8executable).toFilePath(), d8Args, root.toFilePath());
-
- _checkExitCode(result, this, module);
-
- await File.fromUri(root.resolveUri(toUri(module, txtId)))
- .writeAsString(result.stdout);
- }
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose) print("\ncached step: d8 on $module");
- }
-}
-
-void _checkExitCode(ProcessResult result, IOModularStep step, Module module) {
- if (result.exitCode != 0 || _options.verbose) {
- stdout.write(result.stdout);
- stderr.write(result.stderr);
- }
- if (result.exitCode != 0) {
- throw "${step.runtimeType} failed on $module:\n\n"
- "stdout:\n${result.stdout}\n\n"
- "stderr:\n${result.stderr}";
- }
-}
-
-Future<ProcessResult> _runProcess(
- String command, List<String> arguments, String workingDirectory) {
- if (_options.verbose) {
- print('command:\n$command ${arguments.join(' ')} from $workingDirectory');
- }
- return Process.run(command, arguments, workingDirectory: workingDirectory);
-}
-
-String get _d8executable {
- if (Platform.isWindows) {
- return 'third_party/d8/windows/d8.exe';
- } else if (Platform.isLinux) {
- return 'third_party/d8/linux/d8';
- } else if (Platform.isMacOS) {
- return 'third_party/d8/macos/d8';
- }
- throw UnsupportedError('Unsupported platform.');
-}
-
-class ShardsDataId implements DataId {
- @override
- final String name;
- final int shards;
-
- const ShardsDataId(this.name, this.shards);
-
- @override
- String toString() => name;
-}
-
-class ShardDataId implements DataId {
- final ShardsDataId dataId;
- final int _shard;
-
- const ShardDataId(this.dataId, this._shard);
-
- int get shard {
- assert(0 <= _shard && _shard < dataId.shards);
- return _shard;
- }
-
- @override
- String get name => '${dataId.name}${shard}';
-
- @override
- String toString() => name;
-}
-
-Future<void> _resolveScripts() async {
- Future<String> resolve(
- String sourceUriOrPath, String relativeSnapshotPath) async {
- Uri sourceUri = sdkRoot.resolve(sourceUriOrPath);
- String result =
- sourceUri.scheme == 'file' ? sourceUri.toFilePath() : sourceUriOrPath;
- if (_options.useSdk) {
- String snapshot = Uri.file(Platform.resolvedExecutable)
- .resolve(relativeSnapshotPath)
- .toFilePath();
- if (await File(snapshot).exists()) {
- return snapshot;
- }
- }
- return result;
- }
-
- _dart2jsScript = await resolve(
- 'package:compiler/src/dart2js.dart', 'snapshots/dart2js.dart.snapshot');
- _kernelWorkerScript = await resolve('utils/bazel/kernel_worker.dart',
- 'snapshots/kernel_worker.dart.snapshot');
-}
-
-String _librarySpecForSnapshot = Uri.file(Platform.resolvedExecutable)
- .resolve('../lib/libraries.json')
- .toFilePath();
diff --git a/pkg/compiler/tool/modular_test_suite_helper.dart b/pkg/compiler/tool/modular_test_suite_helper.dart
new file mode 100644
index 0000000..cd583ad
--- /dev/null
+++ b/pkg/compiler/tool/modular_test_suite_helper.dart
@@ -0,0 +1,662 @@
+// 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:io';
+import 'dart:async';
+
+import 'package:compiler/src/commandline_options.dart';
+import 'package:compiler/src/kernel/dart2js_target.dart';
+import 'package:front_end/src/compute_platform_binaries_location.dart'
+ show computePlatformBinariesLocation;
+import 'package:modular_test/src/io_pipeline.dart';
+import 'package:modular_test/src/pipeline.dart';
+import 'package:modular_test/src/suite.dart';
+import 'package:modular_test/src/runner.dart';
+import 'package:package_config/package_config.dart';
+
+String packageConfigJsonPath = ".dart_tool/package_config.json";
+Uri sdkRoot = Platform.script.resolve("../../../");
+Uri packageConfigUri = sdkRoot.resolve(packageConfigJsonPath);
+Options _options;
+String _dart2jsScript;
+String _kernelWorkerScript;
+
+const dillSummaryId = DataId("sdill");
+const dillId = DataId("dill");
+const modularUpdatedDillId = DataId("mdill");
+const modularDataId = DataId("mdata");
+const closedWorldId = DataId("world");
+const globalUpdatedDillId = DataId("gdill");
+const globalDataId = DataId("gdata");
+const codeId = ShardsDataId("code", 2);
+const codeId0 = ShardDataId(codeId, 0);
+const codeId1 = ShardDataId(codeId, 1);
+const jsId = DataId("js");
+const txtId = DataId("txt");
+const fakeRoot = 'dev-dart-app:/';
+
+String _packageConfigEntry(String name, Uri root,
+ {Uri packageRoot, LanguageVersion version}) {
+ var fields = [
+ '"name": "${name}"',
+ '"rootUri": "$root"',
+ if (packageRoot != null) '"packageUri": "$packageRoot"',
+ if (version != null) '"languageVersion": "$version"'
+ ];
+ return '{${fields.join(',')}}';
+}
+
+abstract class CFEStep implements IOModularStep {
+ final String stepName;
+
+ CFEStep(this.stepName);
+
+ @override
+ bool get needsSources => true;
+
+ @override
+ bool get onlyOnMain => false;
+
+ @override
+ Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+ List<String> flags) async {
+ if (_options.verbose) print("\nstep: $stepName on $module");
+
+ // TODO(joshualitt): Figure out a way to support package configs in
+ // tests/modular.
+ var packageConfig = await loadPackageConfigUri(packageConfigUri);
+
+ // We use non file-URI schemes for representeing source locations in a
+ // root-agnostic way. This allows us to refer to file across modules and
+ // across steps without exposing the underlying temporary folders that are
+ // created by the framework. In build systems like bazel this is especially
+ // important because each step may be run on a different machine.
+ //
+ // Files in packages are defined in terms of `package:` URIs, while
+ // non-package URIs are defined using the `dart-dev-app` scheme.
+ String rootScheme = module.isSdk ? 'dart-dev-sdk' : 'dev-dart-app';
+ String sourceToImportUri(Uri relativeUri) {
+ if (module.isPackage) {
+ var basePath = module.packageBase.path;
+ var packageRelativePath = basePath == "./"
+ ? relativeUri.path
+ : relativeUri.path.substring(basePath.length);
+ return 'package:${module.name}/$packageRelativePath';
+ } else {
+ return '$rootScheme:/$relativeUri';
+ }
+ }
+
+ // We create both a .packages and package_config.json file which defines
+ // the location of this module if it is a package. The CFE requires that
+ // if a `package:` URI of a dependency is used in an import, then we need
+ // that package entry in the associated file. However, after it checks that
+ // the definition exists, the CFE will not actually use the resolved URI if
+ // a library for the import URI is already found in one of the provide
+ // .dill files of the dependencies. For that reason, and to ensure that
+ // a step only has access to the files provided in a module, we generate a
+ // config file with invalid folders for other packages.
+ // TODO(sigmund): follow up with the CFE to see if we can remove the need
+ // for these dummy entries..
+ // TODO(joshualitt): Generate just the json file.
+ var packagesJson = [];
+ var packagesContents = StringBuffer();
+ if (module.isPackage) {
+ packagesContents.write('${module.name}:${module.packageBase}\n');
+ packagesJson.add(_packageConfigEntry(
+ module.name, Uri.parse('../${module.packageBase}')));
+ }
+
+ Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
+ int unusedNum = 0;
+ for (Module dependency in transitiveDependencies) {
+ if (dependency.isPackage) {
+ // rootUri should be ignored for dependent modules, so we pass in a
+ // bogus value.
+ var rootUri = Uri.parse('unused$unusedNum');
+ unusedNum++;
+
+ var dependentPackage = packageConfig[dependency.name];
+ var packageJson = dependentPackage == null
+ ? _packageConfigEntry(dependency.name, rootUri)
+ : _packageConfigEntry(dependentPackage.name, rootUri,
+ version: dependentPackage.languageVersion);
+ packagesJson.add(packageJson);
+ packagesContents.write('${dependency.name}:$rootUri\n');
+ }
+ }
+
+ if (module.isPackage) {
+ await File.fromUri(root.resolve(packageConfigJsonPath))
+ .create(recursive: true);
+ await File.fromUri(root.resolve(packageConfigJsonPath)).writeAsString('{'
+ ' "configVersion": ${packageConfig.version},'
+ ' "packages": [ ${packagesJson.join(',')} ]'
+ '}');
+ }
+
+ await File.fromUri(root.resolve('.packages'))
+ .writeAsString('$packagesContents');
+
+ List<String> sources;
+ List<String> extraArgs = ['--packages-file', '$rootScheme:/.packages'];
+ if (module.isSdk) {
+ // When no flags are passed, we can skip compilation and reuse the
+ // platform.dill created by build.py.
+ if (flags.isEmpty) {
+ var platform = computePlatformBinariesLocation()
+ .resolve("dart2js_platform_unsound.dill");
+ var destination = root.resolveUri(toUri(module, outputData));
+ if (_options.verbose) {
+ print('command:\ncp $platform $destination');
+ }
+ await File.fromUri(platform).copy(destination.toFilePath());
+ return;
+ }
+ sources = requiredLibraries['dart2js'] + ['dart:core'];
+ extraArgs += [
+ '--libraries-file',
+ '$rootScheme:///sdk/lib/libraries.json'
+ ];
+ assert(transitiveDependencies.isEmpty);
+ } else {
+ sources = module.sources.map(sourceToImportUri).toList();
+ }
+
+ // TODO(joshualitt): Ensure the kernel worker has some way to specify
+ // --no-sound-null-safety
+ List<String> args = [
+ _kernelWorkerScript,
+ ...stepArguments,
+ '--exclude-non-sources',
+ '--multi-root',
+ '$root',
+ '--multi-root-scheme',
+ rootScheme,
+ ...extraArgs,
+ '--output',
+ '${toUri(module, outputData)}',
+ ...(transitiveDependencies
+ .expand((m) => ['--input-summary', '${toUri(m, inputData)}'])),
+ ...(sources.expand((String uri) => ['--source', uri])),
+ ...(flags.expand((String flag) => ['--enable-experiment', flag])),
+ ];
+
+ var result =
+ await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+ _checkExitCode(result, this, module);
+ }
+
+ List<String> get stepArguments;
+
+ DataId get inputData;
+
+ DataId get outputData;
+
+ @override
+ void notifyCached(Module module) {
+ if (_options.verbose) print("\ncached step: $stepName on $module");
+ }
+}
+
+// Step that compiles sources in a module to a summary .dill file.
+class OutlineDillCompilationStep extends CFEStep {
+ @override
+ List<DataId> get resultData => const [dillSummaryId];
+
+ @override
+ bool get needsSources => true;
+
+ @override
+ List<DataId> get dependencyDataNeeded => const [dillSummaryId];
+
+ @override
+ List<DataId> get moduleDataNeeded => const [];
+
+ @override
+ List<String> get stepArguments =>
+ ['--target', 'dart2js_summary', '--summary-only'];
+
+ @override
+ DataId get inputData => dillSummaryId;
+
+ @override
+ DataId get outputData => dillSummaryId;
+
+ OutlineDillCompilationStep() : super('outline-dill-compilation');
+}
+
+// Step that compiles sources in a module to a .dill file.
+class FullDillCompilationStep extends CFEStep {
+ @override
+ List<DataId> get resultData => const [dillId];
+
+ @override
+ bool get needsSources => true;
+
+ @override
+ List<DataId> get dependencyDataNeeded => const [dillSummaryId];
+
+ @override
+ List<DataId> get moduleDataNeeded => const [];
+
+ @override
+ List<String> get stepArguments =>
+ ['--target', 'dart2js', '--no-summary', '--no-summary-only'];
+
+ @override
+ DataId get inputData => dillSummaryId;
+
+ @override
+ DataId get outputData => dillId;
+
+ FullDillCompilationStep() : super('full-dill-compilation');
+}
+
+class ModularAnalysisStep implements IOModularStep {
+ @override
+ List<DataId> get resultData => const [modularDataId, modularUpdatedDillId];
+
+ @override
+ bool get needsSources => false;
+
+ @override
+ List<DataId> get dependencyDataNeeded => const [dillId];
+
+ @override
+ List<DataId> get moduleDataNeeded => const [dillId];
+
+ @override
+ bool get onlyOnMain => 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);
+ Iterable<String> dillDependencies =
+ transitiveDependencies.map((m) => '${toUri(m, dillId)}');
+ List<String> args = [
+ '--packages=${sdkRoot.toFilePath()}/.packages',
+ _dart2jsScript,
+ if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+ '${Flags.inputDill}=${toUri(module, dillId)}',
+ if (dillDependencies.isNotEmpty)
+ '--dill-dependencies=${dillDependencies.join(',')}',
+ '--out=${toUri(module, modularUpdatedDillId)}',
+ '${Flags.writeModularAnalysis}=${toUri(module, modularDataId)}',
+ for (String flag in flags) '--enable-experiment=$flag',
+ ];
+ 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");
+ }
+ }
+}
+
+DataId idForDill({bool useModularAnalysis}) =>
+ useModularAnalysis ? modularUpdatedDillId : dillId;
+
+List<DataId> inputFromAnalysis({bool useModularAnalysis = false}) => [
+ idForDill(useModularAnalysis: useModularAnalysis),
+ if (useModularAnalysis) modularDataId,
+ ];
+
+// Step that invokes the dart2js closed world computation.
+class ComputeClosedWorldStep implements IOModularStep {
+ final bool useModularAnalysis;
+
+ ComputeClosedWorldStep({this.useModularAnalysis});
+
+ @override
+ List<DataId> get resultData => const [closedWorldId, globalUpdatedDillId];
+
+ @override
+ bool get needsSources => false;
+
+ @override
+ List<DataId> get dependencyDataNeeded =>
+ inputFromAnalysis(useModularAnalysis: useModularAnalysis);
+
+ @override
+ List<DataId> get moduleDataNeeded =>
+ inputFromAnalysis(useModularAnalysis: useModularAnalysis);
+
+ @override
+ bool get onlyOnMain => true;
+
+ @override
+ Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+ List<String> flags) async {
+ if (_options.verbose)
+ print("\nstep: dart2js compute closed world on $module");
+ Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
+ DataId dillId = idForDill(useModularAnalysis: useModularAnalysis);
+ 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()}/.packages',
+ _dart2jsScript,
+ // TODO(sigmund): remove this dependency on libraries.json
+ if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+ '${Flags.entryUri}=$fakeRoot${module.mainSource}',
+ '${Flags.inputDill}=${toUri(module, dillId)}',
+ for (String flag in flags) '--enable-experiment=$flag',
+ '${Flags.dillDependencies}=${dillDependencies.join(',')}',
+ if (useModularAnalysis)
+ '${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
+ '${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
+ Flags.noClosedWorldInData,
+ '--out=${toUri(module, globalUpdatedDillId)}',
+ ];
+ var result =
+ await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+
+ _checkExitCode(result, this, module);
+ }
+
+ @override
+ void notifyCached(Module module) {
+ if (_options.verbose)
+ print("\ncached step: dart2js compute closed world on $module");
+ }
+}
+
+// Step that runs the dart2js modular analysis.
+class GlobalAnalysisStep implements IOModularStep {
+ @override
+ List<DataId> get resultData => const [globalDataId];
+
+ @override
+ bool get needsSources => false;
+
+ @override
+ List<DataId> get dependencyDataNeeded => const [globalUpdatedDillId];
+
+ @override
+ List<DataId> get moduleDataNeeded =>
+ const [closedWorldId, globalUpdatedDillId];
+
+ @override
+ bool get onlyOnMain => true;
+
+ @override
+ Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+ List<String> flags) async {
+ if (_options.verbose) print("\nstep: dart2js global analysis on $module");
+ List<String> args = [
+ '--packages=${sdkRoot.toFilePath()}/.packages',
+ _dart2jsScript,
+ // TODO(sigmund): remove this dependency on libraries.json
+ if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+ '${Flags.entryUri}=$fakeRoot${module.mainSource}',
+ '${Flags.inputDill}=${toUri(module, globalUpdatedDillId)}',
+ for (String flag in flags) '--enable-experiment=$flag',
+ '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
+ '${Flags.writeData}=${toUri(module, globalDataId)}',
+ // TODO(joshualitt): delete this flag after google3 roll
+ '${Flags.noClosedWorldInData}',
+ ];
+ var result =
+ await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+
+ _checkExitCode(result, this, module);
+ }
+
+ @override
+ void notifyCached(Module module) {
+ if (_options.verbose)
+ print("\ncached step: dart2js global analysis on $module");
+ }
+}
+
+// Step that invokes the dart2js code generation on the main module given the
+// results of the global analysis step and produces one shard of the codegen
+// output.
+class Dart2jsCodegenStep implements IOModularStep {
+ final ShardDataId codeId;
+
+ Dart2jsCodegenStep(this.codeId);
+
+ @override
+ List<DataId> get resultData => [codeId];
+
+ @override
+ bool get needsSources => false;
+
+ @override
+ List<DataId> get dependencyDataNeeded => const [];
+
+ @override
+ List<DataId> get moduleDataNeeded =>
+ const [globalUpdatedDillId, closedWorldId, globalDataId];
+
+ @override
+ bool get onlyOnMain => true;
+
+ @override
+ Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+ List<String> flags) async {
+ if (_options.verbose) print("\nstep: dart2js backend on $module");
+ List<String> args = [
+ '--packages=${sdkRoot.toFilePath()}/.packages',
+ _dart2jsScript,
+ if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+ '${Flags.entryUri}=$fakeRoot${module.mainSource}',
+ '${Flags.inputDill}=${toUri(module, globalUpdatedDillId)}',
+ for (String flag in flags) '--enable-experiment=$flag',
+ '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
+ '${Flags.readData}=${toUri(module, globalDataId)}',
+ '${Flags.writeCodegen}=${toUri(module, codeId.dataId)}',
+ '${Flags.codegenShard}=${codeId.shard}',
+ '${Flags.codegenShards}=${codeId.dataId.shards}',
+ ];
+ 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 backend on $module");
+ }
+}
+
+// Step that invokes the dart2js codegen enqueuer and emitter on the main module
+// given the results of the global analysis step and codegen shards.
+class Dart2jsEmissionStep implements IOModularStep {
+ @override
+ List<DataId> get resultData => const [jsId];
+
+ @override
+ bool get needsSources => false;
+
+ @override
+ List<DataId> get dependencyDataNeeded => const [];
+
+ @override
+ List<DataId> get moduleDataNeeded => const [
+ globalUpdatedDillId,
+ closedWorldId,
+ globalDataId,
+ codeId0,
+ codeId1
+ ];
+
+ @override
+ bool get onlyOnMain => true;
+
+ @override
+ Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+ List<String> flags) async {
+ if (_options.verbose) print("step: dart2js backend on $module");
+ List<String> args = [
+ '--packages=${sdkRoot.toFilePath()}/.packages',
+ _dart2jsScript,
+ if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+ '${Flags.entryUri}=$fakeRoot${module.mainSource}',
+ '${Flags.inputDill}=${toUri(module, globalUpdatedDillId)}',
+ for (String flag in flags) '${Flags.enableLanguageExperiments}=$flag',
+ '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
+ '${Flags.readData}=${toUri(module, globalDataId)}',
+ '${Flags.readCodegen}=${toUri(module, codeId)}',
+ '${Flags.codegenShards}=${codeId.shards}',
+ '--out=${toUri(module, jsId)}',
+ ];
+ var result =
+ await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+
+ _checkExitCode(result, this, module);
+ }
+
+ @override
+ void notifyCached(Module module) {
+ if (_options.verbose) print("\ncached step: dart2js backend on $module");
+ }
+}
+
+/// Step that runs the output of dart2js in d8 and saves the output.
+class RunD8 implements IOModularStep {
+ @override
+ List<DataId> get resultData => const [txtId];
+
+ @override
+ bool get needsSources => false;
+
+ @override
+ List<DataId> get dependencyDataNeeded => const [];
+
+ @override
+ List<DataId> get moduleDataNeeded => const [jsId];
+
+ @override
+ bool get onlyOnMain => true;
+
+ @override
+ Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+ List<String> flags) async {
+ if (_options.verbose) print("\nstep: d8 on $module");
+ List<String> d8Args = [
+ sdkRoot
+ .resolve('sdk/lib/_internal/js_runtime/lib/preambles/d8.js')
+ .toFilePath(),
+ root.resolveUri(toUri(module, jsId)).toFilePath(),
+ ];
+ var result = await _runProcess(
+ sdkRoot.resolve(_d8executable).toFilePath(), d8Args, root.toFilePath());
+
+ _checkExitCode(result, this, module);
+
+ await File.fromUri(root.resolveUri(toUri(module, txtId)))
+ .writeAsString(result.stdout);
+ }
+
+ @override
+ void notifyCached(Module module) {
+ if (_options.verbose) print("\ncached step: d8 on $module");
+ }
+}
+
+void _checkExitCode(ProcessResult result, IOModularStep step, Module module) {
+ if (result.exitCode != 0 || _options.verbose) {
+ stdout.write(result.stdout);
+ stderr.write(result.stderr);
+ }
+ if (result.exitCode != 0) {
+ throw "${step.runtimeType} failed on $module:\n\n"
+ "stdout:\n${result.stdout}\n\n"
+ "stderr:\n${result.stderr}";
+ }
+}
+
+Future<ProcessResult> _runProcess(
+ String command, List<String> arguments, String workingDirectory) {
+ if (_options.verbose) {
+ print('command:\n$command ${arguments.join(' ')} from $workingDirectory');
+ }
+ return Process.run(command, arguments, workingDirectory: workingDirectory);
+}
+
+String get _d8executable {
+ if (Platform.isWindows) {
+ return 'third_party/d8/windows/d8.exe';
+ } else if (Platform.isLinux) {
+ return 'third_party/d8/linux/d8';
+ } else if (Platform.isMacOS) {
+ return 'third_party/d8/macos/d8';
+ }
+ throw UnsupportedError('Unsupported platform.');
+}
+
+class ShardsDataId implements DataId {
+ @override
+ final String name;
+ final int shards;
+
+ const ShardsDataId(this.name, this.shards);
+
+ @override
+ String toString() => name;
+}
+
+class ShardDataId implements DataId {
+ final ShardsDataId dataId;
+ final int _shard;
+
+ const ShardDataId(this.dataId, this._shard);
+
+ int get shard {
+ assert(0 <= _shard && _shard < dataId.shards);
+ return _shard;
+ }
+
+ @override
+ String get name => '${dataId.name}${shard}';
+
+ @override
+ String toString() => name;
+}
+
+Future<void> resolveScripts(Options options) async {
+ Future<String> resolve(
+ String sourceUriOrPath, String relativeSnapshotPath) async {
+ Uri sourceUri = sdkRoot.resolve(sourceUriOrPath);
+ String result =
+ sourceUri.scheme == 'file' ? sourceUri.toFilePath() : sourceUriOrPath;
+ if (_options.useSdk) {
+ String snapshot = Uri.file(Platform.resolvedExecutable)
+ .resolve(relativeSnapshotPath)
+ .toFilePath();
+ if (await File(snapshot).exists()) {
+ return snapshot;
+ }
+ }
+ return result;
+ }
+
+ _options = options;
+ _dart2jsScript = await resolve(
+ 'package:compiler/src/dart2js.dart', 'snapshots/dart2js.dart.snapshot');
+ _kernelWorkerScript = await resolve('utils/bazel/kernel_worker.dart',
+ 'snapshots/kernel_worker.dart.snapshot');
+}
+
+String _librarySpecForSnapshot = Uri.file(Platform.resolvedExecutable)
+ .resolve('../lib/libraries.json')
+ .toFilePath();
diff --git a/pkg/compiler/tool/modular_test_suite_legacy.dart b/pkg/compiler/tool/modular_test_suite_legacy.dart
new file mode 100644
index 0000000..875a64a
--- /dev/null
+++ b/pkg/compiler/tool/modular_test_suite_legacy.dart
@@ -0,0 +1,33 @@
+// 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(),
+ ComputeClosedWorldStep(useModularAnalysis: false),
+ GlobalAnalysisStep(),
+ Dart2jsCodegenStep(codeId0),
+ Dart2jsCodegenStep(codeId1),
+ Dart2jsEmissionStep(),
+ RunD8(),
+ ], cacheSharedModules: true)),
+ ]);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 64cee58..30279d0 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 59
+PRERELEASE 60
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 2396b82..c060e70 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -424,6 +424,17 @@
"use-sdk": true
}
},
+ "web-unittest-asserts-legacy-(linux|mac|win)": {
+ "options": {
+ "builder-tag": "web-legacy",
+ "compiler": "dartk",
+ "enable-asserts": true,
+ "mode": "release",
+ "runtime": "vm",
+ "timeout": 240,
+ "use-sdk": true
+ }
+ },
"unittest-asserts-(debug|product|release)-(linux|mac|win)": {
"options": {
"compiler": "dartk",
@@ -2401,6 +2412,17 @@
]
},
{
+ "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",
"testRunner": true,