Chromium Code Reviews| 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..e26c4b4a315614d0b748fb6893e48e6749996644 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:kernel/target/targets.dart'; |
| +import 'package:kernel/target/vm_fasta.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/fasta/ticker.dart'; |
| +import 'package:front_end/src/fasta/errors.dart'; |
| import 'package:front_end/src/base/performace_logger.dart'; |
| import 'package:front_end/src/incremental/byte_store.dart'; |
| import 'package:front_end/src/simple_error.dart'; |
| import 'package:package_config/packages_file.dart' as package_config; |
| -import 'package:kernel/kernel.dart' show Program, loadProgramFromBytes; |
| +import 'package:kernel/kernel.dart' |
| + show Program, loadProgramFromBytes, CanonicalName; |
| -/// 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; |
| + Ticker get ticker => _ticker ??= new Ticker(isVerbose: _raw.verbose); |
| + |
| + 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; |
|
Paul Berry
2017/07/04 15:12:14
Nit: consider making this private. Any reference
Siggi Cherem (dart-lang)
2017/07/05 18:42:12
Done.
|
| + |
| + /// 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; |
| /// The logger to report compilation progress. |
| PerformanceLog get logger { |
| @@ -58,28 +107,38 @@ class ProcessedOptions { |
| return _raw.byteStore; |
| } |
| + void reportError(String message) { |
| + _raw.onError(new SimpleError(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 +146,62 @@ 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. |
| + 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 { |
| + 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)); |
| + 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. |
| + Future<List<Program>> loadLinkDependencies(CanonicalName nameRoot) async { |
| + if (_linkedDependencies == null) { |
| + var uris = _raw.linkedDependencies; |
| + if (uris == null || uris.isEmpty) return const <Program>[]; |
| + 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 +213,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 |
| @@ -176,8 +260,69 @@ class ProcessedOptions { |
| } |
| 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); |
| + return inputError( |
| + null, |
| + -1, |
| + 'Invalid access to $uri: ' |
| + 'the file is accessed in a modular hermetic build ' |
| + '(where chaseDependencies is false), but it was not ' |
|
Paul Berry
2017/07/04 15:12:14
Is this going to be a user-visible error message?
Siggi Cherem (dart-lang)
2017/07/05 18:42:11
Good point. removed.
|
| + 'explicitly listed as an input.'); |
| + } |
| } |