| 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;
 | 
|  }
 | 
| 
 |