dart2js support for loading .dill files built modularly
Change-Id: Ie360b13b5be786df9101c96982400136c63dff00
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97462
Commit-Queue: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 704cfe7..82b1fe8 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -79,6 +79,7 @@
static const String progress = '--show-internal-progress';
static const String version = '--version';
+ static const String dillDependencies = '--dill-dependencies';
static const String readData = '--read-data';
static const String writeData = '--write-data';
static const String cfeOnly = '--cfe-only';
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 0fe8224..3d64064 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -250,12 +250,18 @@
fail("Cannot read and write serialized simultaneously.");
}
if (argument != Flags.readData) {
- readDataUri = currentDirectory
- .resolve(nativeToUriPath(extractPath(argument, isDirectory: false)));
+ readDataUri = nativeToUri(extractPath(argument, isDirectory: false));
}
compilationStrategy = CompilationStrategy.fromData;
}
+ void setDillDependencies(String argument) {
+ String dependencies = extractParameter(argument);
+ String uriDependencies = dependencies.splitMapJoin(',',
+ onMatch: (_) => ',', onNonMatch: (p) => '${nativeToUri(p)}');
+ options.add('${Flags.dillDependencies}=${uriDependencies}');
+ }
+
void setCfeOnly(String argument) {
compilationStrategy = CompilationStrategy.toKernel;
}
@@ -265,8 +271,7 @@
fail("Cannot read and write serialized simultaneously.");
}
if (argument != Flags.writeData) {
- writeDataUri = currentDirectory
- .resolve(nativeToUriPath(extractPath(argument, isDirectory: false)));
+ writeDataUri = nativeToUri(extractPath(argument, isDirectory: false));
}
compilationStrategy = CompilationStrategy.toData;
}
@@ -341,6 +346,7 @@
new OptionHandler(Flags.version, (_) => wantVersion = true),
new OptionHandler('--library-root=.+', ignoreOption),
new OptionHandler('--libraries-spec=.+', setLibrarySpecificationUri),
+ new OptionHandler('${Flags.dillDependencies}=.+', setDillDependencies),
new OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
new OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
new OptionHandler(Flags.cfeOnly, setCfeOnly),
diff --git a/pkg/compiler/lib/src/filenames.dart b/pkg/compiler/lib/src/filenames.dart
index 32aca53..be671f9 100644
--- a/pkg/compiler/lib/src/filenames.dart
+++ b/pkg/compiler/lib/src/filenames.dart
@@ -31,4 +31,7 @@
final Uri currentDirectory = Uri.base;
+Uri nativeToUri(String filename) =>
+ currentDirectory.resolve(nativeToUriPath(filename));
+
String appendSlash(String path) => path.endsWith('/') ? path : '$path/';
diff --git a/pkg/compiler/lib/src/kernel/loader.dart b/pkg/compiler/lib/src/kernel/loader.dart
index b972976..f73fba4 100644
--- a/pkg/compiler/lib/src/kernel/loader.dart
+++ b/pkg/compiler/lib/src/kernel/loader.dart
@@ -57,22 +57,48 @@
/// Loads an entire Kernel [Component] from a file on disk.
Future<KernelResult> load(Uri resolvedUri) {
return measure(() async {
+ String targetName =
+ _options.compileForServer ? "dart2js_server" : "dart2js";
+ String platform = '${targetName}_platform.dill';
var isDill = resolvedUri.path.endsWith('.dill');
ir.Component component;
if (isDill) {
- api.Input input = await _compilerInput.readFromUri(resolvedUri,
- inputKind: api.InputKind.binary);
component = new ir.Component();
- new BinaryBuilder(input.data).readComponent(component);
+ Future<void> read(Uri uri) async {
+ api.Input input = await _compilerInput.readFromUri(uri,
+ inputKind: api.InputKind.binary);
+ new BinaryBuilder(input.data).readComponent(component);
+ }
+
+ await read(resolvedUri);
+ if (_options.dillDependencies != null) {
+ // Modular compiles do not include the platform on the input dill
+ // either.
+ await read(_options.platformBinaries.resolve(platform));
+ for (Uri dependency in _options.dillDependencies) {
+ await read(dependency);
+ }
+ }
+
+ // This is not expected to be null when creating a whole-program .dill
+ // file, but needs to be checked for modular inputs.
+ 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 null;
+ }
} else {
- String targetName =
- _options.compileForServer ? "dart2js_server" : "dart2js";
- String platform = '${targetName}_platform.dill';
+ List<Uri> dependencies = [_options.platformBinaries.resolve(platform)];
+ if (_options.dillDependencies != null) {
+ dependencies.addAll(_options.dillDependencies);
+ }
initializedCompilerState = fe.initializeCompiler(
initializedCompilerState,
new Dart2jsTarget(targetName, new TargetFlags()),
_options.librariesSpecificationUri,
- _options.platformBinaries.resolve(platform),
+ dependencies,
_options.packageConfig,
experimentalFlags: _options.languageExperiments);
component = await fe.compile(
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index a3acf0e..8a6e462 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -51,6 +51,19 @@
/// If not null then [packageRoot] should be null.
Uri packageConfig;
+ /// 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.
+ List<Uri> dillDependencies;
+
/// Location from which serialized inference data is read.
///
/// If this is set, the [entryPoint] is expected to be a .dill file and the
@@ -374,6 +387,8 @@
..useNewSourceInfo = _hasOption(options, Flags.useNewSourceInfo)
..verbose = _hasOption(options, Flags.verbose)
..showInternalProgress = _hasOption(options, Flags.progress)
+ ..dillDependencies =
+ _extractUriListOption(options, '${Flags.dillDependencies}')
..readDataUri = _extractUriOption(options, '${Flags.readData}=')
..writeDataUri = _extractUriOption(options, '${Flags.writeData}=')
..cfeOnly = _hasOption(options, Flags.cfeOnly)
@@ -526,6 +541,15 @@
return null;
}
+/// Extract list of comma separated Uris provided for [flag]. Returns an
+/// empty list if [option] contain [flag] without arguments. Returns `null` if
+/// [option] doesn't contain [flag] with or without arguments.
+List<Uri> _extractUriListOption(List<String> options, String flag) {
+ List<String> stringUris = _extractOptionalCsvOption(options, flag);
+ if (stringUris == null) return null;
+ return stringUris.map(Uri.parse).toList();
+}
+
Map<fe.ExperimentalFlag, bool> _extractExperiments(List<String> options) {
List<String> experiments =
_extractOptionalCsvOption(options, Flags.enableLanguageExperiments);
diff --git a/pkg/front_end/lib/src/api_unstable/dart2js.dart b/pkg/front_end/lib/src/api_unstable/dart2js.dart
index a08b77d..37129d0 100644
--- a/pkg/front_end/lib/src/api_unstable/dart2js.dart
+++ b/pkg/front_end/lib/src/api_unstable/dart2js.dart
@@ -109,9 +109,10 @@
InitializedCompilerState oldState,
Target target,
Uri librariesSpecificationUri,
- Uri sdkPlatformUri,
+ List<Uri> linkedDependencies,
Uri packagesFileUri,
- {Map<ExperimentalFlag, bool> experimentalFlags}) {
+ {List<Uri> dependencies,
+ Map<ExperimentalFlag, bool> experimentalFlags}) {
bool mapEqual(Map<ExperimentalFlag, bool> a, Map<ExperimentalFlag, bool> b) {
if (a == null || b == null) return a == b;
if (a.length != b.length) return false;
@@ -121,10 +122,20 @@
return true;
}
+ bool listEqual(List<Uri> a, List<Uri> b) {
+ if (a.length != b.length) return false;
+ for (int i = 0; i < a.length; ++i) {
+ if (a[i] != b[i]) return false;
+ }
+ return true;
+ }
+
+ linkedDependencies.sort((a, b) => a.toString().compareTo(b.toString()));
+
if (oldState != null &&
oldState.options.packagesFileUri == packagesFileUri &&
oldState.options.librariesSpecificationUri == librariesSpecificationUri &&
- oldState.options.linkedDependencies[0] == sdkPlatformUri &&
+ listEqual(oldState.options.linkedDependencies, linkedDependencies) &&
mapEqual(oldState.options.experimentalFlags, experimentalFlags)) {
return oldState;
}
@@ -132,7 +143,7 @@
CompilerOptions options = new CompilerOptions()
..target = target
..legacyMode = target.legacyMode
- ..linkedDependencies = [sdkPlatformUri]
+ ..linkedDependencies = linkedDependencies
..librariesSpecificationUri = librariesSpecificationUri
..packagesFileUri = packagesFileUri
..experimentalFlags = experimentalFlags;
diff --git a/tests/compiler/dart2js/end_to_end/modular_loader_test.dart b/tests/compiler/dart2js/end_to_end/modular_loader_test.dart
new file mode 100644
index 0000000..b1a1d98
--- /dev/null
+++ b/tests/compiler/dart2js/end_to_end/modular_loader_test.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../helpers/memory_compiler.dart';
+import 'package:async_helper/async_helper.dart';
+import 'package:compiler/src/apiimpl.dart' show CompilerImpl;
+import 'package:compiler/src/common_elements.dart';
+import 'package:compiler/src/elements/entities.dart'
+ show LibraryEntity, ClassEntity;
+import 'package:compiler/src/kernel/dart2js_target.dart';
+import 'package:compiler/src/kernel/loader.dart';
+import 'package:expect/expect.dart';
+import 'package:front_end/src/api_prototype/front_end.dart';
+import 'package:front_end/src/api_prototype/memory_file_system.dart';
+import 'package:front_end/src/api_prototype/standard_file_system.dart';
+import 'package:front_end/src/compute_platform_binaries_location.dart'
+ show computePlatformBinariesLocation;
+import 'package:front_end/src/fasta/kernel/utils.dart' show serializeComponent;
+import 'package:kernel/ast.dart';
+import 'package:kernel/target/targets.dart' show TargetFlags;
+
+/// Test that the compiler can load kernel in modular fragments.
+main() {
+ asyncTest(() async {
+ var aDill = await compileUnit(['a0.dart'], {'a0.dart': sourceA});
+ var bDill = await compileUnit(
+ ['b1.dart'], {'b1.dart': sourceB, 'a.dill': aDill},
+ deps: ['a.dill']);
+ var cDill = await compileUnit(
+ ['c2.dart'], {'c2.dart': sourceC, 'a.dill': aDill, 'b.dill': bDill},
+ deps: ['a.dill', 'b.dill']);
+
+ DiagnosticCollector diagnostics = new DiagnosticCollector();
+ OutputCollector output = new OutputCollector();
+ Uri entryPoint = Uri.parse('memory:c.dill');
+ CompilerImpl compiler = compilerFor(
+ entryPoint: entryPoint,
+ options: ['--dill-dependencies=memory:a.dill,memory:b.dill'],
+ memorySourceFiles: {'a.dill': aDill, 'b.dill': bDill, 'c.dill': cDill},
+ diagnosticHandler: diagnostics,
+ outputProvider: output);
+ await compiler.setupSdk();
+ KernelResult result = await compiler.kernelLoader.load(entryPoint);
+ compiler.frontendStrategy.registerLoadedLibraries(result);
+
+ Expect.equals(0, diagnostics.errors.length);
+ Expect.equals(0, diagnostics.warnings.length);
+
+ ElementEnvironment environment =
+ compiler.frontendStrategy.elementEnvironment;
+ LibraryEntity library = environment.lookupLibrary(toTestUri('b1.dart'));
+ Expect.isNotNull(library);
+ ClassEntity clss = environment.lookupClass(library, 'B1');
+ Expect.isNotNull(clss);
+ var member = environment.lookupClassMember(clss, 'foo');
+ Expect.isNotNull(member);
+ });
+}
+
+/// Generate a component for a modular complation unit.
+Future<List<int>> compileUnit(List<String> inputs, Map<String, dynamic> sources,
+ {List<String> deps: const []}) async {
+ var fs = new MemoryFileSystem(_defaultDir);
+ sources.forEach((name, data) {
+ var entity = fs.entityForUri(toTestUri(name));
+ if (data is String) {
+ entity.writeAsStringSync(data);
+ } else {
+ entity.writeAsBytesSync(data);
+ }
+ });
+ List<Uri> linkedDependencies = [
+ computePlatformBinariesLocation().resolve("dart2js_platform.dill"),
+ ]..addAll(deps.map(toTestUri));
+ fs.entityForUri(toTestUri('.packages')).writeAsStringSync('');
+ var options = new CompilerOptions()
+ ..target = new Dart2jsTarget("dart2js", new TargetFlags())
+ ..fileSystem = new TestFileSystem(fs)
+ ..linkedDependencies = linkedDependencies
+ ..packagesFileUri = toTestUri('.packages');
+ var inputUris = inputs.map(toTestUri).toList();
+ var inputUriSet = inputUris.toSet();
+ var component = await kernelForComponent(inputUris, options);
+ for (var lib in component.libraries) {
+ if (!inputUriSet.contains(lib.importUri)) {
+ component.root.getChildFromUri(lib.importUri).bindTo(lib.reference);
+ lib.computeCanonicalNames();
+ }
+ }
+ return serializeComponent(component,
+ filter: (Library lib) => inputUriSet.contains(lib.importUri));
+}
+
+Uri _defaultDir = Uri.parse('org-dartlang-test:///');
+
+Uri toTestUri(String relativePath) => _defaultDir.resolve(relativePath);
+
+class TestFileSystem implements FileSystem {
+ final MemoryFileSystem memory;
+ final FileSystem physical = StandardFileSystem.instance;
+
+ TestFileSystem(this.memory);
+
+ @override
+ FileSystemEntity entityForUri(Uri uri) {
+ if (uri.scheme == 'file') return physical.entityForUri(uri);
+ return memory.entityForUri(uri);
+ }
+}
+
+const sourceA = '''
+class A0 {
+ StringBuffer buffer = new StringBuffer();
+}
+''';
+
+const sourceB = '''
+import 'a0.dart';
+
+class B1 extends A0 {
+ A0 get foo => null;
+}
+
+A0 createA0() => new A0();
+''';
+
+const sourceC = '''
+import 'b1.dart';
+
+class C2 extends B1 {
+ final foo = createA0();
+}
+
+main() => print(new C2().foo.buffer.toString());
+''';