blob: 25e576bc2a5eab8dd484349350c4ab1a9135156d [file] [log] [blame]
// Copyright (c) 2012, 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.
library dart2js.library_loader;
import 'dart:async';
import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
import 'package:front_end/src/fasta/util/link.dart' show Link;
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
import 'package:kernel/kernel.dart' hide LibraryDependency, Combinator;
import 'package:kernel/target/targets.dart';
import '../compiler_new.dart' as api;
import 'kernel/front_end_adapter.dart';
import 'kernel/dart2js_target.dart' show Dart2jsTarget;
import 'common/tasks.dart' show CompilerTask, Measurer;
import 'common.dart';
import 'elements/entities.dart' show LibraryEntity;
import 'kernel/element_map_impl.dart' show KernelToElementMapForImpactImpl;
import 'resolved_uri_translator.dart';
/**
* [CompilerTask] for loading libraries and setting up the import/export scopes.
*
* The library loader uses four different kinds of URIs in different parts of
* the loading process.
*
* ## User URI ##
*
* A 'user URI' is a URI provided by the user in code and as the main entry URI
* at the command line. These generally come in 3 versions:
*
* * A relative URI such as 'foo.dart', '../bar.dart', and 'baz/boz.dart'.
*
* * A dart URI such as 'dart:core' and 'dart:_js_helper'.
*
* * A package URI such as 'package:foo.dart' and 'package:bar/baz.dart'.
*
* A user URI can also be absolute, like 'file:///foo.dart' or
* 'http://example.com/bar.dart', but such URIs cannot necessarily be used for
* locating source files, since the scheme must be supported by the input
* provider. The standard input provider for dart2js only supports the 'file'
* and 'http' scheme.
*
* ## Resolved URI ##
*
* A 'resolved URI' is a (user) URI that has been resolved to an absolute URI
* based on the readable URI (see below) from which it was loaded. A URI with an
* explicit scheme (such as 'dart:', 'package:' or 'file:') is already resolved.
* A relative URI like for instance '../foo/bar.dart' is translated into an
* resolved URI in one of three ways:
*
* * If provided as the main entry URI at the command line, the URI is resolved
* relative to the current working directory, say
* 'file:///current/working/dir/', and the resolved URI is therefore
* 'file:///current/working/foo/bar.dart'.
*
* * If the relative URI is provided in an import, export or part tag, and the
* readable URI of the enclosing compilation unit is a file URI,
* 'file://some/path/baz.dart', then the resolved URI is
* 'file://some/foo/bar.dart'.
*
* * If the relative URI is provided in an import, export or part tag, and the
* readable URI of the enclosing compilation unit is a package URI,
* 'package:some/path/baz.dart', then the resolved URI is
* 'package:some/foo/bar.dart'.
*
* The resolved URI thus preserves the scheme through resolution: A readable
* file URI results in an resolved file URI and a readable package URI results
* in an resolved package URI. Note that since a dart URI is not a readable URI,
* import, export or part tags within platform libraries are not interpreted as
* dart URIs but instead relative to the library source file location.
*
* The resolved URI of a library is also used as the canonical URI
* ([LibraryElement.canonicalUri]) by which we identify which libraries are
* identical. This means that libraries loaded through the 'package' scheme will
* resolve to the same library when loaded from within using relative URIs (see
* for instance the test 'standalone/package/package1_test.dart'). But loading a
* platform library using a relative URI will _not_ result in the same library
* as when loaded through the dart URI.
*
* ## Readable URI ##
*
* A 'readable URI' is an absolute URI whose scheme is either 'package' or
* something supported by the input provider, normally 'file'. Dart URIs such as
* 'dart:core' and 'dart:_js_helper' are not readable themselves but are instead
* resolved into a readable URI using the library root URI provided from the
* command line and the list of platform libraries found in
* 'sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart'. This is done
* through a [ResolvedUriTranslator] provided from the compiler. The translator
* checks whether a library by that name exists and in case of internal
* libraries whether access is granted.
*
* ## Resource URI ##
*
* A 'resource URI' is an absolute URI with a scheme supported by the input
* provider. For the standard implementation this means a URI with the 'file'
* scheme. Readable URIs are converted into resource URIs as part of the
* [ScriptLoader.readScript] method. In the standard implementation the package
* URIs are converted to file URIs using the package root URI provided on the
* command line as base. If the package root URI is
* 'file:///current/working/dir/' then the package URI 'package:foo/bar.dart'
* will be resolved to the resource URI
* 'file:///current/working/dir/foo/bar.dart'.
*
* The distinction between readable URI and resource URI is necessary to ensure
* that these imports
*
* import 'package:foo.dart' as a;
* import 'packages/foo.dart' as b;
*
* do _not_ resolve to the same library when the package root URI happens to
* point to the 'packages' folder.
*
*/
abstract class LibraryLoaderTask implements LibraryProvider, CompilerTask {
/// Returns all libraries that have been loaded.
Iterable<LibraryEntity> get libraries;
/// Loads the library specified by the [resolvedUri] and returns the
/// [LoadedLibraries] that were loaded to load the specified uri. The
/// [LibraryElement] itself can be found by calling
/// `loadedLibraries.rootLibrary`.
///
/// If the library is not already loaded, the method creates the
/// [LibraryElement] for the library and computes the import/export scope,
/// loading and computing the import/export scopes of all required libraries
/// in the process. The method handles cyclic dependency between libraries.
Future<LoadedLibraries> loadLibrary(Uri resolvedUri);
}
/// Interface for an entity that provide libraries.
abstract class LibraryProvider {
/// Looks up the library with the [canonicalUri].
LibraryEntity lookupLibrary(Uri canonicalUri);
}
/// A loader that builds a kernel IR representation of the component.
///
/// It supports loading both .dart source files or pre-compiled .dill files.
/// When given .dart source files, it invokes the shared frontend
/// (`package:front_end`) to produce the corresponding kernel IR representation.
// TODO(sigmund): move this class to a new file under src/kernel/.
class KernelLibraryLoaderTask extends CompilerTask
implements LibraryLoaderTask {
final Uri librariesSpecification;
final Uri platformBinaries;
final Uri _packageConfig;
final DiagnosticReporter reporter;
final api.CompilerInput compilerInput;
/// Holds the mapping of Kernel IR to KElements that is constructed as a
/// result of loading a component.
final KernelToElementMapForImpactImpl _elementMap;
final bool verbose;
List<LibraryEntity> _allLoadedLibraries;
fe.InitializedCompilerState initializedCompilerState;
KernelLibraryLoaderTask(
this.librariesSpecification,
this.platformBinaries,
this._packageConfig,
this._elementMap,
this.compilerInput,
this.reporter,
Measurer measurer,
{this.verbose: false,
this.initializedCompilerState})
: _allLoadedLibraries = new List<LibraryEntity>(),
super(measurer);
/// Loads an entire Kernel [Component] from a file on disk (note, not just a
/// library, so this name is actually a bit of a misnomer).
// TODO(efortuna): Rename this once the Element library loader class goes
// away.
Future<LoadedLibraries> loadLibrary(Uri resolvedUri) {
return measure(() async {
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);
} else {
bool strongMode = _elementMap.options.strongMode;
String targetName =
_elementMap.options.compileForServer ? "dart2js_server" : "dart2js";
String platform = strongMode
? '${targetName}_platform_strong.dill'
: '${targetName}_platform.dill';
initializedCompilerState = fe.initializeCompiler(
initializedCompilerState,
new Dart2jsTarget(
targetName, new TargetFlags(strongMode: strongMode)),
librariesSpecification,
platformBinaries.resolve(platform),
_packageConfig);
component = await fe.compile(
initializedCompilerState,
verbose,
new CompilerFileSystem(compilerInput),
(e) => reportFrontEndMessage(reporter, e),
resolvedUri);
}
if (component == null) return null;
return createLoadedLibraries(component);
});
}
// Only visible for unit testing.
LoadedLibraries createLoadedLibraries(ir.Component component) {
_elementMap.addComponent(component);
LibraryEntity rootLibrary = null;
Iterable<ir.Library> libraries = component.libraries;
if (component.mainMethod != null) {
var root = component.mainMethod.enclosingLibrary;
rootLibrary = _elementMap.lookupLibrary(root.importUri);
// Filter unreachable libraries: [Component] was built by linking in the
// entire SDK libraries, not all of them are used. We include anything
// that is reachable from `main`. Note that all internal libraries that
// the compiler relies on are reachable from `dart:core`.
var seen = new Set<Library>();
search(ir.Library current) {
if (!seen.add(current)) return;
for (ir.LibraryDependency dep in current.dependencies) {
search(dep.targetLibrary);
}
}
search(root);
// Libraries dependencies do not show implicit imports to `dart:core`.
var dartCore = component.libraries.firstWhere((lib) {
return lib.importUri.scheme == 'dart' && lib.importUri.path == 'core';
});
search(dartCore);
libraries = libraries.where(seen.contains);
}
_allLoadedLibraries.addAll(
libraries.map((lib) => _elementMap.lookupLibrary(lib.importUri)));
return new _LoadedLibrariesAdapter(
rootLibrary, _allLoadedLibraries, _elementMap);
}
KernelToElementMapForImpactImpl get elementMap => _elementMap;
Iterable<LibraryEntity> get libraries => _allLoadedLibraries;
LibraryEntity lookupLibrary(Uri canonicalUri) {
return _elementMap?.lookupLibrary(canonicalUri);
}
}
/// Information on the set libraries loaded as a result of a call to
/// [LibraryLoader.loadLibrary].
abstract class LoadedLibraries {
/// The access the library object created corresponding to the library
/// passed to [LibraryLoader.loadLibrary].
LibraryEntity get rootLibrary;
/// Returns `true` if a library with canonical [uri] was loaded in this bulk.
bool containsLibrary(Uri uri);
/// Returns the library with canonical [uri] that was loaded in this bulk.
LibraryEntity getLibrary(Uri uri);
/// Applies all libraries in this bulk to [f].
void forEachLibrary(f(LibraryEntity library));
/// Applies all imports chains of [uri] in this bulk to [callback].
///
/// The argument [importChainReversed] to [callback] contains the chain of
/// imports uris that lead to importing [uri] starting in [uri] and ending in
/// the uri that was passed in with [loadLibrary].
///
/// [callback] is called once for each chain of imports leading to [uri] until
/// [callback] returns `false`.
void forEachImportChain(Uri uri,
{bool callback(Link<Uri> importChainReversed)});
}
/// Adapter class to mimic the behavior of LoadedLibraries for Kernel element
/// behavior. Ultimately we'll just access worldBuilder instead.
class _LoadedLibrariesAdapter implements LoadedLibraries {
final LibraryEntity rootLibrary;
final List<LibraryEntity> _newLibraries;
final KernelToElementMapForImpactImpl worldBuilder;
_LoadedLibrariesAdapter(
this.rootLibrary, this._newLibraries, this.worldBuilder) {
assert(rootLibrary != null);
}
bool containsLibrary(Uri uri) {
var lib = getLibrary(uri);
return lib != null && _newLibraries.contains(lib);
}
LibraryEntity getLibrary(Uri uri) => worldBuilder.lookupLibrary(uri);
void forEachLibrary(f(LibraryEntity library)) => _newLibraries.forEach(f);
void forEachImportChain(Uri uri,
{bool callback(Link<Uri> importChainReversed)}) {
// Currently a no-op. This seems wrong.
}
String toString() => 'root=$rootLibrary,libraries=${_newLibraries}';
}