Index: runtime/bin/builtin.dart |
diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart |
index 940556a1acd647494ef13ced93019689835a8d72..96f1bdae2b9bd50fbcaea9057867c6d661a390fd 100644 |
--- a/runtime/bin/builtin.dart |
+++ b/runtime/bin/builtin.dart |
@@ -48,6 +48,10 @@ _getUriBaseClosure() => _uriBase; |
// Asynchronous loading of resources. |
// The embedder forwards most loading requests to this library. |
+_log(msg) { |
+ _print("* $msg"); |
+} |
+ |
// See Dart_LibraryTag in dart_api.h |
const Dart_kScriptTag = null; |
const Dart_kImportTag = 0; |
@@ -78,9 +82,22 @@ Uri _workingDirectory; |
// package imports can be resolved relative to it. The root script is the basis |
// for the root library in the VM. |
Uri _rootScript; |
+ |
+// Packages are either resolved looking up in a map or resolved from within a |
+// package root. |
+bool _packagesReady() => (_packageRoot != null) || (_packageMap != null); |
// The directory to look in to resolve "package:" scheme URIs. By detault it is |
// the 'packages' directory right next to the script. |
-Uri _packageRoot = _rootScript.resolve('packages/'); |
+Uri _packageRoot = null; // Used to be _rootScript.resolve('packages/'); |
+// The map describing how certain package names are mapped to Uris. |
+Map<String, Uri> _packageMap = null; |
+// A list of pending packags which have been requested while resolving the |
+// location of the package root or the contents of the package map. |
+List<_LoadRequest> _pendingPackageLoads = []; |
+ |
+// If we have outstanding loads or pending package loads waiting for resolution, |
+// then we do have pending loads. |
+bool _pendingLoads() => !_reqMap.isEmpty || !_pendingPackageLoads.isEmpty; |
// Special handling for Windows paths so that they are compatible with URI |
// handling. |
@@ -103,8 +120,15 @@ class _LoadRequest { |
final int _tag; |
final String _uri; |
final String _libraryUri; |
+ final Uri _resourceUri; |
+ |
+ _LoadRequest(this._id, |
+ this._tag, |
+ this._uri, |
+ this._libraryUri, |
+ this._resourceUri); |
- _LoadRequest(this._id, this._tag, this._uri, this._libraryUri); |
+ toString() => "LoadRequest($_id, $_tag, $_uri, $_libraryUri, $_resourceUri)"; |
} |
@@ -172,11 +196,11 @@ _enforceTrailingSlash(uri) { |
// The embedder calls this method with the current working directory. |
void _setWorkingDirectory(cwd) { |
if (_traceLoading) { |
- _print('# Setting working directory: $cwd'); |
+ _log('Setting working directory: $cwd'); |
} |
_workingDirectory = new Uri.directory(cwd); |
if (_traceLoading) { |
- _print('# Working directory URI: $_workingDirectory'); |
+ _log('Working directory URI: $_workingDirectory'); |
} |
} |
@@ -185,7 +209,7 @@ void _setWorkingDirectory(cwd) { |
// The embedder calls this method with a custom package root. |
_setPackageRoot(String packageRoot) { |
if (_traceLoading) { |
- _print('# Setting package root: $packageRoot'); |
+ _log('Setting package root: $packageRoot'); |
} |
packageRoot = _enforceTrailingSlash(packageRoot); |
if (packageRoot.startsWith('file:') || |
@@ -198,7 +222,7 @@ _setPackageRoot(String packageRoot) { |
_packageRoot = _workingDirectory.resolveUri(new Uri.file(packageRoot)); |
} |
if (_traceLoading) { |
- _print('# Package root URI: $_packageRoot'); |
+ _log('Package root URI: $_packageRoot'); |
} |
} |
@@ -216,16 +240,36 @@ Uri _resolvePackageUri(Uri uri) { |
} |
if (_traceLoading) { |
- _print('# Package root: $_packageRoot'); |
- _print('# uri path: ${uri.path}'); |
+ _log('Resolving package with uri path: ${uri.path}'); |
} |
- return _packageRoot.resolve(uri.path); |
+ var resolvedUri; |
+ if (_packageRoot != null) { |
+ resolvedUri = _packageRoot.resolve(uri.path); |
+ } else { |
+ var packageName = uri.pathSegments[0]; |
+ var mapping = _packageMap[packageName]; |
+ if (_traceLoading) { |
+ _log("Mapped '$packageName' package to '$mapping'"); |
+ } |
+ if (mapping == null) { |
+ throw "No mapping for '$packageName' package when resolving '$uri'."; |
+ } |
+ var path = uri.path.substring(packageName.length + 1); |
+ resolvedUri = mapping.resolve(path); |
+ } |
+ if (_traceLoading) { |
+ _log("Resolved '$uri' to '$resolvedUri'."); |
+ } |
+ return resolvedUri; |
} |
// Resolves the script uri in the current working directory iff the given uri |
// did not specify a scheme (e.g. a path to a script file on the command line). |
Uri _resolveScriptUri(String scriptName) { |
+ if (_traceLoading) { |
+ _log("Resolving script: $scriptName"); |
+ } |
if (_workingDirectory == null) { |
throw 'No current working directory set.'; |
} |
@@ -243,7 +287,7 @@ Uri _resolveScriptUri(String scriptName) { |
_rootScript = scriptUri; |
if (_traceLoading) { |
- _print('# Resolved entry point to: $_rootScript'); |
+ _log('Resolved entry point to: $_rootScript'); |
} |
return scriptUri; |
} |
@@ -254,13 +298,14 @@ void _finishLoadRequest(_LoadRequest req) { |
var tmp = _reqMap.remove(req._id); |
assert(tmp == req); |
if (_traceLoading) { |
- _print("Loading of ${req._uri} finished, " |
- "${_reqMap.length} requests remaining"); |
+ _log("Loading of ${req._uri} finished: " |
+ "${_reqMap.length} requests remaining, " |
+ "${_pendingPackageLoads.length} packages pending."); |
} |
- if (_reqMap.isEmpty) { |
+ if (!_pendingLoads()) { |
if (_traceLoading) { |
- _print("Closing loading port."); |
+ _log("Closing loading port."); |
} |
_receivePort.close(); |
_receivePort = null; |
@@ -298,9 +343,9 @@ void _startLoadRequest(int tag, |
String uri, |
String libraryUri, |
Uri resourceUri) { |
- if (_reqMap.isEmpty) { |
+ if (_receivePort == null) { |
if (_traceLoading) { |
- _print("Initializing load port."); |
+ _log("Initializing load port."); |
} |
assert(_receivePort == null); |
assert(_sendPort == null); |
@@ -311,17 +356,135 @@ void _startLoadRequest(int tag, |
var curId = _reqId++; |
assert(_reqMap[curId] == null); |
- _reqMap[curId] = new _LoadRequest(curId, tag, uri, libraryUri); |
+ _reqMap[curId] = new _LoadRequest(curId, tag, uri, libraryUri, resourceUri); |
- var msg = new List(3); |
+ assert(_receivePort != null); |
+ assert(_sendPort != null); |
+ |
+ var msg = new List(4); |
msg[0] = _sendPort; |
- msg[1] = curId; |
- msg[2] = resourceUri.toString(); |
+ msg[1] = _traceLoading; |
+ msg[2] = curId; |
+ msg[3] = resourceUri.toString(); |
+ _loadPort.send(msg); |
+ |
+ if (_traceLoading) { |
+ _log("Loading of $resourceUri for $uri started with id: $curId, " |
+ "${_reqMap.length} requests outstanding"); |
+ } |
+} |
+ |
+ |
+RawReceivePort _packagesPort; |
+ |
+void _handlePackagesReply(msg) { |
+ // Make sure to close the _packagePort before any other action. |
+ _packagesPort.close(); |
+ |
+ if (_traceLoading) { |
+ _log("Got packages reply: $msg"); |
+ } |
+ if (msg is String) { |
+ if (_traceLoading) { |
+ _log("Got failure response on package port: '$msg'"); |
+ } |
+ throw msg; |
+ } |
+ if (msg.length == 1) { |
+ if (_traceLoading) { |
+ _log("Received package root: '${msg[0]}'"); |
+ } |
+ _packageRoot = Uri.parse(msg[0]); |
+ } else { |
+ assert((msg.length % 2) == 0); |
+ _packageMap = new Map<String, Uri>(); |
+ for (var i = 0; i < msg.length; i+=2) { |
+ _packageMap[msg[i]] = Uri.parse(msg[i+1]); |
+ } |
+ if (_traceLoading) { |
+ _log("Setup package map: $_packageMap"); |
+ } |
+ } |
+ |
+ // Resolve all pending package loads now that we know how to resolve them. |
+ for (var i = 0; i < _pendingPackageLoads.length; i++) { |
+ var req = _pendingPackageLoads[i]; |
+ if (req != null) { |
+ if (_traceLoading) { |
+ _log("Handling deferred load request: $req"); |
+ } |
+ _loadPackage(req._tag, req._uri, req._libraryUri, req._resourceUri); |
+ } |
+ } |
+ // Reset the pending package loads to empty. So that we eventually can |
+ // finish loading. |
+ _pendingPackageLoads = []; |
+} |
+ |
+ |
+void _requestPackagesMap() { |
+ assert(_packagesPort == null); |
+ assert(_rootScript != null); |
+ // Create a port to receive the packages map on. |
+ _packagesPort = new RawReceivePort(_handlePackagesReply); |
+ var sp = _packagesPort.sendPort; |
+ |
+ var msg = new List(4); |
+ msg[0] = sp; |
+ msg[1] = _traceLoading; |
+ msg[2] = -1; |
+ msg[3] = _rootScript.toString(); |
_loadPort.send(msg); |
if (_traceLoading) { |
- _print("Loading of $resourceUri for $uri started with id: $curId, " |
- "${_reqMap.length} requests outstanding"); |
+ _log("Requested packages map for '$_rootScript'."); |
+ } |
+} |
+ |
+ |
+// Embedder Entrypoint: |
+// Request the load of a particular packages map. |
+void _loadPackagesMap(String packagesParam) { |
+ // First convert the packages parameter from the command line to a URI which |
+ // can be handled by the loader code. |
+ // TODO(iposva): Consider refactoring the common code below which is almost |
+ // shared with resolution of the root script. |
+ if (_traceLoading) { |
+ _log("Resolving packages map: $packagesParam"); |
+ } |
+ if (_workingDirectory == null) { |
+ throw 'No current working directory set.'; |
+ } |
+ var packagesName = _sanitizeWindowsPath(packagesParam); |
+ var packagesUri = Uri.parse(packagesName); |
+ if (packagesUri.scheme == '') { |
+ // Script does not have a scheme, assume that it is a path, |
+ // resolve it against the working directory. |
+ packagesUri = _workingDirectory.resolveUri(packagesUri); |
+ } |
+ if (_traceLoading) { |
+ _log('Resolved packages map to: $packagesUri'); |
+ } |
+ |
+ // Request the loading and parsing of the packages map at the specified URI. |
+ // Create a port to receive the packages map on. |
+ assert(_packagesPort == null); |
+ _packagesPort = new RawReceivePort(_handlePackagesReply); |
+ var sp = _packagesPort.sendPort; |
+ |
+ var msg = new List(4); |
+ msg[0] = sp; |
+ msg[1] = _traceLoading; |
+ msg[2] = -2; |
+ msg[3] = packagesUri.toString(); |
+ _loadPort.send(msg); |
+ |
+ // Signal that the resolution of the packages map has started. But in this |
+ // case it is not tied to a particular request. |
+ _pendingPackageLoads.add(null); |
+ |
+ if (_traceLoading) { |
+ _log("Requested packages map at '$packagesUri'."); |
} |
} |
@@ -337,7 +500,7 @@ void _loadScript(_LoadRequest req, Uint8List data) { |
void _asyncLoadError(_LoadRequest req, _LoadError error) { |
if (_traceLoading) { |
- _print("_asyncLoadError(${req._uri}), error: $error"); |
+ _log("_asyncLoadError(${req._uri}), error: $error"); |
} |
var libraryUri = req._libraryUri; |
if (req._tag == Dart_kImportTag) { |
@@ -358,7 +521,7 @@ _loadDataFromLoadPort(int tag, |
_startLoadRequest(tag, uri, libraryUri, resourceUri); |
} catch (e) { |
if (_traceLoading) { |
- _print("Exception when communicating with service isolate: $e"); |
+ _log("Exception when communicating with service isolate: $e"); |
} |
// Wrap inside a _LoadError unless we are already propagating a previously |
// seen _LoadError. |
@@ -368,6 +531,40 @@ _loadDataFromLoadPort(int tag, |
} |
+// Loading a package URI needs to first map the package name to a loadable |
+// URI. |
+_loadPackage(int tag, String uri, String libraryUri, Uri resourceUri) { |
+ if (_packagesReady()) { |
+ _loadData(tag, uri, libraryUri, _resolvePackageUri(resourceUri)); |
+ } else { |
+ if (_pendingPackageLoads.isEmpty) { |
+ // Package resolution has not been setup yet, and this is the first |
+ // request for package resolution & loading. |
+ _requestPackagesMap(); |
+ } |
+ var req = new _LoadRequest(-1, tag, uri, libraryUri, resourceUri); |
+ _pendingPackageLoads.add(req); |
+ if (_traceLoading) { |
+ _log("Pending package load of '$uri': " |
+ "${_pendingPackageLoads.length} pending"); |
+ } |
+ } |
+} |
+ |
+ |
+// Load the data associated with the resourceUri. |
+_loadData(int tag, String uri, String libraryUri, Uri resourceUri) { |
+ if (resourceUri.scheme == 'package') { |
+ // package based uris need to be resolved to the correct loadable location. |
+ // The logic of which is handled seperately, and then _loadData is called |
+ // recursively. |
+ _loadPackage(tag, uri, libraryUri, resourceUri); |
+ } else { |
+ _loadDataFromLoadPort(tag, uri, libraryUri, resourceUri); |
+ } |
+} |
+ |
+ |
// Embedder Entrypoint: |
// Asynchronously loads script data through a http[s] or file uri. |
_loadDataAsync(int tag, String uri, String libraryUri) { |
@@ -378,13 +575,7 @@ _loadDataAsync(int tag, String uri, String libraryUri) { |
} else { |
resourceUri = Uri.parse(uri); |
} |
- |
- // package based uris need to be resolved to the correct loadable location. |
- if (resourceUri.scheme == 'package') { |
- resourceUri = _resolvePackageUri(resourceUri); |
- } |
- |
- _loadDataFromLoadPort(tag, uri, libraryUri, resourceUri); |
+ _loadData(tag, uri, libraryUri, resourceUri); |
} |
@@ -393,7 +584,7 @@ _loadDataAsync(int tag, String uri, String libraryUri) { |
// Dart_kCanonicalizeUrl from the tag handler. |
String _resolveUri(String base, String userString) { |
if (_traceLoading) { |
- _print('# Resolving: $userString from $base'); |
+ _log('Resolving: $userString from $base'); |
} |
var baseUri = Uri.parse(base); |
var result; |
@@ -404,7 +595,7 @@ String _resolveUri(String base, String userString) { |
result = baseUri.resolve(userString).toString(); |
} |
if (_traceLoading) { |
- _print('Resolved $userString in $base to $result'); |
+ _log('Resolved $userString in $base to $result'); |
} |
return result; |
} |
@@ -425,7 +616,7 @@ String _resolveInWorkingDirectory(String fileName) { |
uri = _workingDirectory.resolveUri(uri); |
if (_traceLoading) { |
- _print('# Resolved in working directory: $fileName -> $uri'); |
+ _log('Resolved in working directory: $fileName -> $uri'); |
} |
return uri.toString(); |
} |
@@ -453,7 +644,7 @@ String _platformExtensionFileName(String name) { |
String _filePathFromUri(String userUri) { |
var uri = Uri.parse(userUri); |
if (_traceLoading) { |
- _print('# Getting file path from: $uri'); |
+ _log('Getting file path from: $uri'); |
} |
var path; |
@@ -471,7 +662,7 @@ String _filePathFromUri(String userUri) { |
// Only handling file, http, and package URIs |
// in standalone binary. |
if (_traceLoading) { |
- _print('# Unknown scheme (${uri.scheme}) in $uri.'); |
+ _log('Unknown scheme (${uri.scheme}) in $uri.'); |
} |
throw 'Not a known scheme: $uri'; |
} |