| Index: pkg/front_end/lib/src/base/processed_options.dart
|
| diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
|
| index a42d2b54caa8c554bb705ee450a79a3a61a8ab64..b2e93ab6008cb4553aec0ad17fb6d3d515a3733e 100644
|
| --- a/pkg/front_end/lib/src/base/processed_options.dart
|
| +++ b/pkg/front_end/lib/src/base/processed_options.dart
|
| @@ -4,17 +4,27 @@
|
|
|
| import 'dart:async';
|
|
|
| +import 'package:front_end/compilation_error.dart';
|
| import 'package:front_end/compiler_options.dart';
|
| import 'package:front_end/file_system.dart';
|
| -import 'package:front_end/src/fasta/translate_uri.dart';
|
| import 'package:front_end/src/base/performace_logger.dart';
|
| +import 'package:front_end/src/fasta/ticker.dart';
|
| +import 'package:front_end/src/fasta/translate_uri.dart';
|
| import 'package:front_end/src/incremental/byte_store.dart';
|
| -import 'package:front_end/src/simple_error.dart';
|
| +import 'package:kernel/kernel.dart'
|
| + show Program, loadProgramFromBytes, CanonicalName;
|
| +import 'package:kernel/target/targets.dart';
|
| +import 'package:kernel/target/vm_fasta.dart';
|
| import 'package:package_config/packages_file.dart' as package_config;
|
| -import 'package:kernel/kernel.dart' show Program, loadProgramFromBytes;
|
| +import 'package:source_span/source_span.dart' show SourceSpan;
|
|
|
| -/// Wrapper around [CompilerOptions] which exposes the options in a form useful
|
| -/// to the front end implementation.
|
| +/// All options needed for the front end implementation.
|
| +///
|
| +/// This includes: all of [CompilerOptions] in a form useful to the
|
| +/// implementation, default values for options that were not provided,
|
| +/// and information derived from how the compiler was invoked (like the
|
| +/// entry-points given to the compiler and whether a modular or whole-program
|
| +/// API was used).
|
| ///
|
| /// The intent is that the front end should immediately wrap any incoming
|
| /// [CompilerOptions] object in this class before doing further processing, and
|
| @@ -34,19 +44,58 @@ class ProcessedOptions {
|
| TranslateUri _uriTranslator;
|
|
|
| /// The SDK summary, or `null` if it has not been read yet.
|
| + ///
|
| + /// A summary, also referred to as "outline" internally, is a [Program] where
|
| + /// all method bodies are left out. In essence, it contains just API
|
| + /// signatures and constants. When strong-mode is enabled, the summary already
|
| + /// includes inferred types.
|
| Program _sdkSummaryProgram;
|
|
|
| /// The summary for each uri in `options.inputSummaries`.
|
| + ///
|
| + /// A summary, also referred to as "outline" internally, is a [Program] where
|
| + /// all method bodies are left out. In essence, it contains just API
|
| + /// signatures and constants. When strong-mode is enabled, the summary already
|
| + /// includes inferred types.
|
| List<Program> _inputSummariesPrograms;
|
|
|
| + /// Other programs that are meant to be linked and compiled with the input
|
| + /// sources.
|
| + List<Program> _linkedDependencies;
|
| +
|
| /// The location of the SDK, or `null` if the location hasn't been determined
|
| /// yet.
|
| Uri _sdkRoot;
|
| -
|
| Uri get sdkRoot => _sdkRoot ??= _normalizeSdkRoot();
|
|
|
| + Uri _sdkSummary;
|
| + Uri get sdkSummary => _sdkSummary ??= _computeSdkSummaryUri();
|
| +
|
| + Ticker ticker;
|
| +
|
| + bool get verbose => _raw.verbose;
|
| +
|
| + bool get verify => _raw.verify;
|
| +
|
| + bool get debugDump => _raw.debugDump;
|
| +
|
| + /// Like [CompilerOptions.chaseDependencies] but with the appropriate default
|
| + /// value filled in.
|
| + bool get chaseDependencies => _raw.chaseDependencies ?? !_modularApi;
|
| +
|
| + /// Whether the compiler was invoked with a modular API.
|
| + ///
|
| + /// Used to determine the default behavior for [chaseDependencies].
|
| + final bool _modularApi;
|
| +
|
| + /// The entry-points provided to the compiler.
|
| + final List<Uri> inputs;
|
| +
|
| /// Initializes a [ProcessedOptions] object wrapping the given [rawOptions].
|
| - ProcessedOptions(CompilerOptions rawOptions) : this._raw = rawOptions;
|
| + ProcessedOptions(CompilerOptions rawOptions,
|
| + [this._modularApi = false, this.inputs = const []])
|
| + : this._raw = rawOptions,
|
| + ticker = new Ticker(isVerbose: rawOptions.verbose);
|
|
|
| /// The logger to report compilation progress.
|
| PerformanceLog get logger {
|
| @@ -58,28 +107,40 @@ class ProcessedOptions {
|
| return _raw.byteStore;
|
| }
|
|
|
| + // TODO(sigmund): delete. We should use messages with error codes directly
|
| + // instead.
|
| + void reportError(String message) {
|
| + _raw.onError(new _CompilationError(message));
|
| + }
|
| +
|
| /// Runs various validations checks on the input options. For instance,
|
| /// if an option is a path to a file, it checks that the file exists.
|
| Future<bool> validateOptions() async {
|
| - var fs = _raw.fileSystem;
|
| - var root = _raw.sdkRoot;
|
| + for (var source in inputs) {
|
| + if (source.scheme == 'file' &&
|
| + !await fileSystem.entityForUri(source).exists()) {
|
| + reportError("Entry-point file not found: $source");
|
| + return false;
|
| + }
|
| + }
|
|
|
| - bool _report(String msg) {
|
| - _raw.onError(new SimpleError(msg));
|
| + if (_raw.sdkRoot != null &&
|
| + !await fileSystem.entityForUri(sdkRoot).exists()) {
|
| + reportError("SDK root directory not found: ${sdkRoot}");
|
| return false;
|
| }
|
|
|
| - if (root != null && !await fs.entityForUri(root).exists()) {
|
| - return _report("SDK root directory not found: ${_raw.sdkRoot}");
|
| + var summary = sdkSummary;
|
| + if (summary != null && !await fileSystem.entityForUri(summary).exists()) {
|
| + reportError("SDK summary not found: ${summary}");
|
| + return false;
|
| }
|
|
|
| - var summary = _raw.sdkSummary;
|
| - if (summary != null && !await fs.entityForUri(summary).exists()) {
|
| - return _report("SDK summary not found: ${_raw.sdkSummary}");
|
| + if (compileSdk && summary != null) {
|
| + reportError(
|
| + "The compileSdk and sdkSummary options are mutually exclusive");
|
| + return false;
|
| }
|
| -
|
| - // TODO(sigmund): add checks for options that are meant to be disjoint (like
|
| - // sdkRoot and sdkSummary).
|
| return true;
|
| }
|
|
|
| @@ -87,43 +148,67 @@ class ProcessedOptions {
|
| /// whole-program.
|
| bool get compileSdk => _raw.compileSdk;
|
|
|
| + FileSystem _fileSystem;
|
| +
|
| /// Get the [FileSystem] which should be used by the front end to access
|
| /// files.
|
| ///
|
| /// If the client supplied roots using [CompilerOptions.multiRoots], the
|
| /// returned [FileSystem] will automatically perform the appropriate mapping.
|
| - FileSystem get fileSystem {
|
| - // TODO(paulberry): support multiRoots.
|
| - assert(_raw.multiRoots.isEmpty);
|
| - return _raw.fileSystem;
|
| - }
|
| + FileSystem get fileSystem => _fileSystem ??= _createFileSystem();
|
|
|
| /// Whether to interpret Dart sources in strong-mode.
|
| bool get strongMode => _raw.strongMode;
|
|
|
| - /// Get an outline program that summarizes the SDK.
|
| - Future<Program> get sdkSummaryProgram async {
|
| + Target _target;
|
| + Target get target => _target ??=
|
| + _raw.target ?? new VmFastaTarget(new TargetFlags(strongMode: strongMode));
|
| +
|
| + /// Get an outline program that summarizes the SDK, if any.
|
| + // TODO(sigmund): move, this doesn't feel like an "option".
|
| + Future<Program> loadSdkSummary(CanonicalName nameRoot) async {
|
| if (_sdkSummaryProgram == null) {
|
| - if (_raw.sdkSummary == null) return null;
|
| - _sdkSummaryProgram = await _loadProgram(_raw.sdkSummary);
|
| + if (sdkSummary == null) return null;
|
| + var bytes = await fileSystem.entityForUri(sdkSummary).readAsBytes();
|
| + _sdkSummaryProgram = loadProgram(bytes, nameRoot);
|
| }
|
| return _sdkSummaryProgram;
|
| }
|
|
|
| /// Get the summary programs for each of the underlying `inputSummaries`
|
| /// provided via [CompilerOptions].
|
| - Future<List<Program>> get inputSummariesPrograms async {
|
| + // TODO(sigmund): move, this doesn't feel like an "option".
|
| + Future<List<Program>> loadInputSummaries(CanonicalName nameRoot) async {
|
| if (_inputSummariesPrograms == null) {
|
| var uris = _raw.inputSummaries;
|
| if (uris == null || uris.isEmpty) return const <Program>[];
|
| - _inputSummariesPrograms = await Future.wait(uris.map(_loadProgram));
|
| + // TODO(sigmund): throttle # of concurrent opreations.
|
| + var allBytes = await Future
|
| + .wait(uris.map((uri) => fileSystem.entityForUri(uri).readAsBytes()));
|
| + _inputSummariesPrograms =
|
| + allBytes.map((bytes) => loadProgram(bytes, nameRoot)).toList();
|
| }
|
| return _inputSummariesPrograms;
|
| }
|
|
|
| - Future<Program> _loadProgram(Uri uri) async {
|
| - var bytes = await fileSystem.entityForUri(uri).readAsBytes();
|
| - return loadProgramFromBytes(bytes)..unbindCanonicalNames();
|
| + /// Load each of the [CompilerOptions.linkedDependencies] programs.
|
| + // TODO(sigmund): move, this doesn't feel like an "option".
|
| + Future<List<Program>> loadLinkDependencies(CanonicalName nameRoot) async {
|
| + if (_linkedDependencies == null) {
|
| + var uris = _raw.linkedDependencies;
|
| + if (uris == null || uris.isEmpty) return const <Program>[];
|
| + // TODO(sigmund): throttle # of concurrent opreations.
|
| + var allBytes = await Future
|
| + .wait(uris.map((uri) => fileSystem.entityForUri(uri).readAsBytes()));
|
| + _linkedDependencies =
|
| + allBytes.map((bytes) => loadProgram(bytes, nameRoot)).toList();
|
| + }
|
| + return _linkedDependencies;
|
| + }
|
| +
|
| + /// Helper to load a .dill file from [uri] using the existing [nameRoot].
|
| + Program loadProgram(List<int> bytes, CanonicalName nameRoot) {
|
| + return loadProgramFromBytes(bytes, new Program(nameRoot: nameRoot));
|
| }
|
|
|
| /// Get the [TranslateUri] which resolves "package:" and "dart:" URIs.
|
| @@ -135,13 +220,19 @@ class ProcessedOptions {
|
| await _getPackages();
|
| // TODO(scheglov) Load SDK libraries from whatever format we decide.
|
| // TODO(scheglov) Remove the field "_raw.dartLibraries".
|
| - _uriTranslator = new TranslateUri(
|
| - _packages, _raw.dartLibraries, const <String, List<Uri>>{});
|
| - _uriTranslator.dartLibraries.addAll(_raw.dartLibraries);
|
| + var libraries = _raw.dartLibraries ?? await _parseLibraries();
|
| + _uriTranslator =
|
| + new TranslateUri(_packages, libraries, const <String, List<Uri>>{});
|
| + ticker.logMs("Read packages file");
|
| }
|
| return _uriTranslator;
|
| }
|
|
|
| + Future<Map<String, Uri>> _parseLibraries() async {
|
| + Uri librariesJson = _raw.sdkRoot?.resolve("lib/libraries.json");
|
| + return await computeLibraries(fileSystem, librariesJson);
|
| + }
|
| +
|
| /// Get the package map which maps package names to URIs.
|
| ///
|
| /// This is an asynchronous getter since file system operations may be
|
| @@ -149,7 +240,8 @@ class ProcessedOptions {
|
| Future<Map<String, Uri>> _getPackages() async {
|
| if (_packages == null) {
|
| if (_raw.packagesFileUri == null) {
|
| - throw new UnimplementedError(); // TODO(paulberry): search for .packages
|
| + // TODO(sigmund,paulberry): implement
|
| + throw new UnimplementedError('search for .packages');
|
| } else if (_raw.packagesFileUri.path.isEmpty) {
|
| _packages = {};
|
| } else {
|
| @@ -162,9 +254,6 @@ class ProcessedOptions {
|
| }
|
|
|
| /// Get the location of the SDK.
|
| - ///
|
| - /// This is an asynchronous getter since file system operations may be
|
| - /// required to locate the SDK.
|
| Uri _normalizeSdkRoot() {
|
| // If an SDK summary location was provided, the SDK itself should not be
|
| // needed.
|
| @@ -172,12 +261,89 @@ class ProcessedOptions {
|
| if (_raw.sdkRoot == null) {
|
| // TODO(paulberry): implement the algorithm for finding the SDK
|
| // automagically.
|
| - throw new UnimplementedError();
|
| + throw new UnimplementedError('infer the default sdk location');
|
| }
|
| var root = _raw.sdkRoot;
|
| if (!root.path.endsWith('/')) {
|
| - root = root.replace(path: _sdkRoot.path + '/');
|
| + root = root.replace(path: root.path + '/');
|
| }
|
| return root;
|
| }
|
| +
|
| + /// Get or infer the location of the SDK summary.
|
| + Uri _computeSdkSummaryUri() {
|
| + if (_raw.sdkSummary != null) return _raw.sdkSummary;
|
| +
|
| + // Infer based on the sdkRoot, but only when `compileSdk` is false,
|
| + // otherwise the default intent was to compile the sdk from sources and not
|
| + // to load an sdk summary file.
|
| + if (_raw.compileSdk) return null;
|
| + return sdkRoot.resolve('outline.dill');
|
| + }
|
| +
|
| + /// Create a [FileSystem] specific to the current options.
|
| + ///
|
| + /// If [chaseDependencies] is false, the resulting file system will be
|
| + /// hermetic.
|
| + FileSystem _createFileSystem() {
|
| + var result = _raw.fileSystem;
|
| + if (!chaseDependencies) {
|
| + var allInputs = inputs.toSet();
|
| + allInputs.addAll(_raw.inputSummaries);
|
| + allInputs.addAll(_raw.linkedDependencies);
|
| +
|
| + if (sdkSummary != null) allInputs.add(sdkSummary);
|
| +
|
| + if (_raw.sdkRoot != null) {
|
| + // TODO(sigmund): refine this, we should be more explicit about when
|
| + // sdkRoot and libraries.json are allowed to be used.
|
| + allInputs.add(sdkRoot);
|
| + allInputs.add(sdkRoot.resolve("lib/libraries.json"));
|
| + }
|
| +
|
| + /// Note: Searching the file-system for the package-config is not
|
| + /// supported in hermetic builds.
|
| + if (_raw.packagesFileUri != null) allInputs.add(_raw.packagesFileUri);
|
| + result = new HermeticFileSystem(allInputs, result);
|
| + }
|
| + // TODO(paulberry): support multiRoots.
|
| + assert(_raw.multiRoots.isEmpty);
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +/// A [FileSystem] that only allows access to files that have been explicitly
|
| +/// whitelisted.
|
| +class HermeticFileSystem implements FileSystem {
|
| + final Set<Uri> includedFiles;
|
| + final FileSystem _realFileSystem;
|
| +
|
| + HermeticFileSystem(this.includedFiles, this._realFileSystem);
|
| +
|
| + FileSystemEntity entityForUri(Uri uri) {
|
| + if (includedFiles.contains(uri)) return _realFileSystem.entityForUri(uri);
|
| + throw new HermeticAccessException(uri);
|
| + }
|
| +}
|
| +
|
| +class HermeticAccessException extends FileSystemException {
|
| + HermeticAccessException(Uri uri)
|
| + : super(
|
| + uri,
|
| + 'Invalid access to $uri: '
|
| + 'the file is accessed in a modular hermetic build, '
|
| + 'but it was not explicitly listed as an input.');
|
| +
|
| + @override
|
| + String toString() => message;
|
| +}
|
| +
|
| +/// An error that only contains a message and no error location.
|
| +class _CompilationError implements CompilationError {
|
| + String get correction => null;
|
| + SourceSpan get span => null;
|
| + final String message;
|
| + _CompilationError(this.message);
|
| +
|
| + String toString() => message;
|
| }
|
|
|