// Copyright (c) 2015, 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. part of vmservice_io; _sanitizeWindowsPath(path) { // For Windows we need to massage the paths a bit according to // http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx // // Convert // C:\one\two\three // to // /C:/one/two/three if (_isWindows == false) { // Do nothing when not running Windows. return path; } var fixedPath = "${path.replaceAll('\\', '/')}"; if ((path.length > 2) && (path[1] == ':')) { // Path begins with a drive letter. return '/$fixedPath'; } return fixedPath; } _trimWindowsPath(path) { // Convert /X:/ to X:/. if (_isWindows == false) { // Do nothing when not running Windows. return path; } if (!path.startsWith('/') || (path.length < 3)) { return path; } // Match '/?:'. if ((path[0] == '/') && (path[2] == ':')) { // Remove leading '/'. return path.substring(1); } return path; } // Ensure we have a trailing slash character. _enforceTrailingSlash(uri) { if (!uri.endsWith('/')) { return '$uri/'; } return uri; } class FileRequest { final SendPort sp; final int tag; final Uri uri; final Uri resolvedUri; final String libraryUrl; FileRequest(this.sp, this.tag, this.uri, this.resolvedUri, this.libraryUrl); } @pragma("vm:entry-point") bool _traceLoading = false; @pragma("vm:entry-point") bool _deterministic = false; // State associated with the isolate that is used for loading. class IsolateLoaderState extends IsolateEmbedderData { IsolateLoaderState(this.isolateId); final int isolateId; bool _dead = false; SendPort sp; void init(String packageRootFlag, String packagesConfigFlag, String workingDirectory, String rootScript) { if (_dead) { return; } // _workingDirectory must be set first. _workingDirectory = new Uri.directory(workingDirectory); if (rootScript != null) { _rootScript = Uri.parse(rootScript); } // If the --package-root flag was passed. if (packageRootFlag != null) { _setPackageRoot(packageRootFlag); } // If the --packages flag was passed. if (packagesConfigFlag != null) { _setPackagesConfig(packagesConfigFlag); } } void updatePackageMap(String packagesConfigFlag) { if (packagesConfigFlag == null) { return; } _packageMap = null; _setPackagesConfig(packagesConfigFlag); } void cleanup() { _dead = true; if (_packagesPort != null) { _packagesPort.close(); _packagesPort = null; } } // The working directory when the embedder started. Uri _workingDirectory; // The root script's uri. Uri _rootScript; // Packages are either resolved looking up in a map or resolved from within a // package root. bool get _packagesReady => (_packageRoot != null) || (_packageMap != null) || (_packageError != null); // Error string set if there was an error resolving package configuration. // For example not finding a .packages file or packages/ directory, malformed // .packages file or any other related error. String _packageError = null; // The directory to look in to resolve "package:" scheme URIs. By default it // is the 'packages' directory right next to the script. Uri _packageRoot = null; // The map describing how certain package names are mapped to Uris. Uri _packageConfig = null; Map _packageMap = null; // We issue only 16 concurrent calls to File.readAsBytes() to stay within // platform-specific resource limits (e.g. max open files). The rest go on // _fileRequestQueue and are processed when we can safely issue them. static final int _maxFileRequests = _deterministic ? 1 : 16; int currentFileRequests = 0; final List _fileRequestQueue = new List(); bool get shouldIssueFileRequest => currentFileRequests < _maxFileRequests; void enqueueFileRequest(FileRequest fr) { _fileRequestQueue.add(fr); } FileRequest dequeueFileRequest() { if (_fileRequestQueue.length == 0) { return null; } return _fileRequestQueue.removeAt(0); } _setPackageRoot(String packageRoot) { packageRoot = _sanitizeWindowsPath(packageRoot); if (packageRoot.startsWith('file:') || packageRoot.startsWith('http:') || packageRoot.startsWith('https:')) { packageRoot = _enforceTrailingSlash(packageRoot); _packageRoot = _workingDirectory.resolve(packageRoot); } else { packageRoot = _sanitizeWindowsPath(packageRoot); packageRoot = _trimWindowsPath(packageRoot); _packageRoot = _workingDirectory.resolveUri(new Uri.directory(packageRoot)); } } _setPackagesConfig(String packagesParam) { 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); } _requestPackagesMap(packagesUri); _pendingPackageLoads.add(() { // Dummy action. }); } // Handling of access to the package root or package map from user code. _triggerPackageResolution(action) { if (_packagesReady) { // Packages are ready. Execute the action now. action(); } else { if (_pendingPackageLoads.isEmpty) { // Package resolution has not been setup yet, and this is the first // request for package resolution & loading. _requestPackagesMap(); } // Register the action for when the package resolution is ready. _pendingPackageLoads.add(action); } } // A list of callbacks which should be invoked after the package map has been // loaded. List _pendingPackageLoads = []; // Given a uri with a 'package' scheme, return a Uri that is prefixed with // the package root or resolved relative to the package configuration. Uri _resolvePackageUri(Uri uri) { assert(uri.scheme == "package"); assert(_packagesReady); if (uri.host.isNotEmpty) { var path = '${uri.host}${uri.path}'; var right = 'package:$path'; var wrong = 'package://$path'; throw "URIs using the 'package:' scheme should look like " "'$right', not '$wrong'."; } var packageNameEnd = uri.path.indexOf('/'); if (packageNameEnd == 0) { // Package URIs must have a non-empty package name (not start with "/"). throw "URIS using the 'package:' scheme should look like " "'package:packageName${uri.path}', not 'package:${uri.path}'"; } if (_traceLoading) { _log('Resolving package with uri path: ${uri.path}'); } var resolvedUri; if (_packageError != null) { if (_traceLoading) { _log("Resolving package with pending resolution error: $_packageError"); } throw _packageError; } else if (_packageRoot != null) { resolvedUri = _packageRoot.resolve(uri.path); } else { if (packageNameEnd < 0) { // Package URIs must have a path after the package name, even if it's // just "/". throw "URIS using the 'package:' scheme should look like " "'package:${uri.path}/', not 'package:${uri.path}'"; } var packageName = uri.path.substring(0, packageNameEnd); 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; assert(uri.path.length > packageName.length); path = uri.path.substring(packageName.length + 1); if (_traceLoading) { _log("Path to be resolved in package: $path"); } resolvedUri = mapping.resolve(path); } if (_traceLoading) { _log("Resolved '$uri' to '$resolvedUri'."); } return resolvedUri; } RawReceivePort _packagesPort; void _requestPackagesMap([Uri packageConfig]) { if (_packagesPort != null) { // Already scheduled. return; } // Create a port to receive the packages map on. _packagesPort = new RawReceivePort(_handlePackagesReply); var sp = _packagesPort.sendPort; if (packageConfig != null) { // Explicitly specified .packages path. _handlePackagesRequest(sp, _traceLoading, -2, packageConfig); } else { // Search for .packages starting at the root script. _handlePackagesRequest(sp, _traceLoading, -1, _rootScript); } if (_traceLoading) { _log("Requested packages map for '$_rootScript'."); } } void _handlePackagesReply(msg) { if (_packagesPort == null) { return; } // Make sure to close the _packagePort before any other action. _packagesPort.close(); _packagesPort = null; if (_traceLoading) { _log("Got packages reply: $msg"); } if (msg is String) { if (_traceLoading) { _log("Got failure response on package port: '$msg'"); } // Remember the error message. _packageError = msg; } else if (msg is List) { if (msg.length == 1) { if (_traceLoading) { _log("Received package root: '${msg[0]}'"); } _packageRoot = Uri.parse(msg[0]); } else { // First entry contains the location of the loaded .packages file. assert((msg.length % 2) == 0); assert(msg.length >= 2); assert(msg[1] == null); _packageConfig = Uri.parse(msg[0]); _packageMap = new Map(); for (var i = 2; i < msg.length; i += 2) { // TODO(iposva): Complain about duplicate entries. _packageMap[msg[i]] = Uri.parse(msg[i + 1]); } if (_traceLoading) { _log("Setup package map: $_packageMap"); } } } else { _packageError = "Bad type of packages reply: ${msg.runtimeType}"; if (_traceLoading) { _log(_packageError); } } // Resolve all pending package loads now that we know how to resolve them. while (_pendingPackageLoads.length > 0) { // Order does not matter as we queue all of the requests up right now. var req = _pendingPackageLoads.removeLast(); // Call the registered closure, to handle the delayed action. req(); } // Reset the pending package loads to empty. So that we eventually can // finish loading. _pendingPackageLoads = []; } } _log(msg) { print("% $msg"); } var _httpClient; // Send a response to the requesting isolate. void _sendResourceResponse(SendPort sp, int tag, Uri uri, Uri resolvedUri, String libraryUrl, dynamic data) { assert((data is List) || (data is String)); var msg = new List(5); if (data is String) { // We encountered an error, flip the sign of the tag to indicate that. tag = -tag; if (libraryUrl == null) { data = 'Could not load "$uri": $data'; } else { data = 'Could not import "$uri" from "$libraryUrl": $data'; } } msg[0] = tag; msg[1] = uri.toString(); msg[2] = resolvedUri.toString(); msg[3] = libraryUrl; msg[4] = data; sp.send(msg); } // Send a response to the requesting isolate. void _sendExtensionImportResponse( SendPort sp, Uri uri, String libraryUrl, String resolvedUri) { var msg = new List(5); int tag = _Dart_kImportExtension; if (resolvedUri == null) { // We could not resolve the dart-ext: uri. tag = -tag; resolvedUri = 'Could not resolve "$uri" from "$libraryUrl"'; } msg[0] = tag; msg[1] = uri.toString(); msg[2] = resolvedUri; msg[3] = libraryUrl; msg[4] = resolvedUri; sp.send(msg); } void _loadHttp( SendPort sp, int tag, Uri uri, Uri resolvedUri, String libraryUrl) { if (_httpClient == null) { _httpClient = new HttpClient()..maxConnectionsPerHost = 6; } _httpClient .getUrl(resolvedUri) .then((HttpClientRequest request) => request.close()) .then((HttpClientResponse response) { var builder = new BytesBuilder(copy: false); response.listen(builder.add, onDone: () { if (response.statusCode != 200) { var msg = "Failure getting $resolvedUri:\n" " ${response.statusCode} ${response.reasonPhrase}"; _sendResourceResponse(sp, tag, uri, resolvedUri, libraryUrl, msg); } else { _sendResourceResponse( sp, tag, uri, resolvedUri, libraryUrl, builder.takeBytes()); } }, onError: (e) { _sendResourceResponse( sp, tag, uri, resolvedUri, libraryUrl, e.toString()); }); }).catchError((e) { _sendResourceResponse(sp, tag, uri, resolvedUri, libraryUrl, e.toString()); }); // It's just here to push an event on the event loop so that we invoke the // scheduled microtasks. Timer.run(() {}); } void _loadFile(IsolateLoaderState loaderState, SendPort sp, int tag, Uri uri, Uri resolvedUri, String libraryUrl) { var path = resolvedUri.toFilePath(); var sourceFile = new File(path); sourceFile.readAsBytes().then((data) { _sendResourceResponse(sp, tag, uri, resolvedUri, libraryUrl, data); }, onError: (e) { _sendResourceResponse(sp, tag, uri, resolvedUri, libraryUrl, e.toString()); }).whenComplete(() { loaderState.currentFileRequests--; while (loaderState.shouldIssueFileRequest) { FileRequest fr = loaderState.dequeueFileRequest(); if (fr == null) { break; } _loadFile( loaderState, fr.sp, fr.tag, fr.uri, fr.resolvedUri, fr.libraryUrl); loaderState.currentFileRequests++; } }); } void _loadDataUri( SendPort sp, int tag, Uri uri, Uri resolvedUri, String libraryUrl) { try { var mime = uri.data.mimeType; if ((mime != "application/dart") && (mime != "text/plain")) { throw "MIME-type must be application/dart or text/plain: $mime given."; } var charset = uri.data.charset; if ((charset != "utf-8") && (charset != "US-ASCII")) { // The C++ portion of the embedder assumes UTF-8. throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; } _sendResourceResponse( sp, tag, uri, resolvedUri, libraryUrl, uri.data.contentAsBytes()); } catch (e) { _sendResourceResponse(sp, tag, uri, resolvedUri, libraryUrl, "Invalid data uri ($uri):\n $e"); } } // Loading a package URI needs to first map the package name to a loadable // URI. _loadPackage(IsolateLoaderState loaderState, SendPort sp, bool traceLoading, int tag, Uri uri, Uri resolvedUri, String libraryUrl) { if (loaderState._packagesReady) { var resolvedUri; try { resolvedUri = loaderState._resolvePackageUri(uri); } catch (e, s) { if (traceLoading) { _log("Exception ($e) when resolving package URI: $uri"); } // Report error. _sendResourceResponse( sp, tag, uri, resolvedUri, libraryUrl, e.toString()); return; } // Recursively call with the new resolved uri. _handleResourceRequest( loaderState, sp, traceLoading, tag, uri, resolvedUri, libraryUrl); } else { if (loaderState._pendingPackageLoads.isEmpty) { // Package resolution has not been setup yet, and this is the first // request for package resolution & loading. loaderState._requestPackagesMap(); } // Register the action of loading this package once the package resolution // is ready. loaderState._pendingPackageLoads.add(() { _handleResourceRequest( loaderState, sp, traceLoading, tag, uri, uri, libraryUrl); }); if (traceLoading) { _log("Pending package load of '$uri': " "${loaderState._pendingPackageLoads.length} pending"); } } } // TODO(johnmccutchan): This and most other top level functions in this file // should be turned into methods on the IsolateLoaderState class. _handleResourceRequest(IsolateLoaderState loaderState, SendPort sp, bool traceLoading, int tag, Uri uri, Uri resolvedUri, String libraryUrl) { if (resolvedUri.scheme == '' || resolvedUri.scheme == 'file') { if (loaderState.shouldIssueFileRequest) { _loadFile(loaderState, sp, tag, uri, resolvedUri, libraryUrl); loaderState.currentFileRequests++; } else { FileRequest fr = new FileRequest(sp, tag, uri, resolvedUri, libraryUrl); loaderState.enqueueFileRequest(fr); } } else if ((resolvedUri.scheme == 'http') || (resolvedUri.scheme == 'https')) { _loadHttp(sp, tag, uri, resolvedUri, libraryUrl); } else if ((resolvedUri.scheme == 'data')) { _loadDataUri(sp, tag, uri, resolvedUri, libraryUrl); } else if ((resolvedUri.scheme == 'package')) { _loadPackage( loaderState, sp, traceLoading, tag, uri, resolvedUri, libraryUrl); } else { _sendResourceResponse( sp, tag, uri, resolvedUri, libraryUrl, 'Unknown scheme (${resolvedUri.scheme}) for ' '$resolvedUri'); } } // Handling of packages requests. Finding and parsing of .packages file or // packages/ directories. const _LF = 0x0A; const _CR = 0x0D; const _SPACE = 0x20; const _HASH = 0x23; const _DOT = 0x2E; const _COLON = 0x3A; const _DEL = 0x7F; const _invalidPackageNameChars = const [ true, // space false, // ! true, // " true, // # false, // $ true, // % false, // & false, // ' false, // ( false, // ) false, // * false, // + false, // , false, // - false, // . true, // / false, // 0 false, // 1 false, // 2 false, // 3 false, // 4 false, // 5 false, // 6 false, // 7 false, // 8 false, // 9 true, // : false, // ; true, // < false, // = true, // > true, // ? false, // @ false, // A false, // B false, // C false, // D false, // E false, // F false, // G false, // H false, // I false, // J false, // K false, // L false, // M false, // N false, // O false, // P false, // Q false, // R false, // S false, // T false, // U false, // V false, // W false, // X false, // Y false, // Z true, // [ true, // \ true, // ] true, // ^ false, // _ true, // ` false, // a false, // b false, // c false, // d false, // e false, // f false, // g false, // h false, // i false, // j false, // k false, // l false, // m false, // n false, // o false, // p false, // q false, // r false, // s false, // t false, // u false, // v false, // w false, // x false, // y false, // z true, // { true, // | true, // } false, // ~ true, // DEL ]; _parsePackagesFile( SendPort sp, bool traceLoading, Uri packagesFile, List data) { // The first entry contains the location of the identified .packages file // instead of a mapping. var result = [packagesFile.toString(), null]; var index = 0; var len = data.length; while (index < len) { var start = index; var char = data[index]; if ((char == _CR) || (char == _LF)) { // Skipping empty lines. index++; continue; } // Identify split within the line and end of the line. var separator = -1; var end = len; // Verifying validity of package name while scanning the line. var nonDot = false; var invalidPackageName = false; // Scan to the end of the line or data. while (index < len) { char = data[index++]; // If we have not reached the separator yet, determine whether we are // scanning legal package name characters. if (separator == -1) { if ((char == _COLON)) { // The first colon on a line is the separator between package name and // related URI. separator = index - 1; } else { // Still scanning the package name part. Check for the validity of // the characters. nonDot = nonDot || (char != _DOT); invalidPackageName = invalidPackageName || (char < _SPACE) || (char > _DEL) || _invalidPackageNameChars[char - _SPACE]; } } // Identify end of line. if ((char == _CR) || (char == _LF)) { end = index - 1; break; } } // No further handling needed for comment lines. if (data[start] == _HASH) { if (traceLoading) { _log("Skipping comment in $packagesFile:\n" "${new String.fromCharCodes(data, start, end)}"); } continue; } // Check for a badly formatted line, starting with a ':'. if (separator == start) { var line = new String.fromCharCodes(data, start, end); if (traceLoading) { _log("Line starts with ':' in $packagesFile:\n" "$line"); } sp.send("Missing package name in $packagesFile:\n" "$line"); return; } // Ensure there is a separator on the line. if (separator == -1) { var line = new String.fromCharCodes(data, start, end); if (traceLoading) { _log("Line has no ':' in $packagesFile:\n" "$line"); } sp.send("Missing ':' separator in $packagesFile:\n" "$line"); return; } var packageName = new String.fromCharCodes(data, start, separator); // Check for valid package name. if (invalidPackageName || !nonDot) { var line = new String.fromCharCodes(data, start, end); if (traceLoading) { _log("Invalid package name $packageName in $packagesFile"); } sp.send("Invalid package name '$packageName' in $packagesFile:\n" "$line"); return; } if (traceLoading) { _log("packageName: $packageName"); } var packageUri = new String.fromCharCodes(data, separator + 1, end); if (traceLoading) { _log("original packageUri: $packageUri"); } // Ensure the package uri ends with a /. if (!packageUri.endsWith("/")) { packageUri = "$packageUri/"; } packageUri = packagesFile.resolve(packageUri).toString(); if (traceLoading) { _log("mapping: $packageName -> $packageUri"); } result.add(packageName); result.add(packageUri); } if (traceLoading) { _log("Parsed packages file at $packagesFile. Sending:\n$result"); } sp.send(result); } _loadPackagesFile(SendPort sp, bool traceLoading, Uri packagesFile) async { try { var data = await new File.fromUri(packagesFile).readAsBytes(); if (traceLoading) { _log("Loaded packages file from $packagesFile:\n" "${new String.fromCharCodes(data)}"); } _parsePackagesFile(sp, traceLoading, packagesFile, data); } catch (e, s) { if (traceLoading) { _log("Error loading packages: $e\n$s"); } sp.send("Uncaught error ($e) loading packages file."); } } _findPackagesFile(SendPort sp, bool traceLoading, Uri base) async { try { // Walk up the directory hierarchy to check for the existence of // .packages files in parent directories and for the existence of a // packages/ directory on the first iteration. var dir = new File.fromUri(base).parent; var prev = null; // Keep searching until we reach the root. while ((prev == null) || (prev.path != dir.path)) { // Check for the existence of a .packages file and if it exists try to // load and parse it. var dirUri = dir.uri; var packagesFile = dirUri.resolve(".packages"); if (traceLoading) { _log("Checking for $packagesFile file."); } var exists = await new File.fromUri(packagesFile).exists(); if (traceLoading) { _log("$packagesFile exists: $exists"); } if (exists) { _loadPackagesFile(sp, traceLoading, packagesFile); return; } // Move up one level. prev = dir; dir = dir.parent; } // No .packages file was found. if (traceLoading) { _log("Could not resolve a package location from $base"); } sp.send("Could not resolve a package location for base at $base"); } catch (e, s) { if (traceLoading) { _log("Error loading packages: $e\n$s"); } sp.send("Uncaught error ($e) loading packages file."); } } Future _loadHttpPackagesFile( SendPort sp, bool traceLoading, Uri resource) async { try { if (_httpClient == null) { _httpClient = new HttpClient()..maxConnectionsPerHost = 6; } if (traceLoading) { _log("Fetching packages file from '$resource'."); } var req = await _httpClient.getUrl(resource); var rsp = await req.close(); var builder = new BytesBuilder(copy: false); await for (var bytes in rsp) { builder.add(bytes); } if (rsp.statusCode != 200) { if (traceLoading) { _log("Got status ${rsp.statusCode} fetching '$resource'."); } return false; } var data = builder.takeBytes(); if (traceLoading) { _log("Loaded packages file from '$resource':\n" "${new String.fromCharCodes(data)}"); } _parsePackagesFile(sp, traceLoading, resource, data); } catch (e, s) { if (traceLoading) { _log("Error loading packages file from '$resource': $e\n$s"); } sp.send("Uncaught error ($e) loading packages file from '$resource'."); } return false; } _loadPackagesData(sp, traceLoading, resource) { try { var data = resource.data; var mime = data.mimeType; if (mime != "text/plain") { throw "MIME-type must be text/plain: $mime given."; } var charset = data.charset; if ((charset != "utf-8") && (charset != "US-ASCII")) { // The C++ portion of the embedder assumes UTF-8. throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; } _parsePackagesFile(sp, traceLoading, resource, data.contentAsBytes()); } catch (e) { sp.send("Uncaught error ($e) loading packages data."); } } // This code used to exist in a second isolate and so it uses a SendPort to // report it's return value. This could be refactored so that it returns it's // value and the caller could wait on the future rather than a message on // SendPort. _handlePackagesRequest( SendPort sp, bool traceLoading, int tag, Uri resource) async { try { if (tag == -1) { if (resource.scheme == '' || resource.scheme == 'file') { _findPackagesFile(sp, traceLoading, resource); } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { // Try to load the .packages file next to the resource. var packagesUri = resource.resolve(".packages"); var exists = await _loadHttpPackagesFile(sp, traceLoading, packagesUri); if (!exists) { // Loading of the .packages file failed for http/https based scripts sp.send([null]); } } else { sp.send("Unsupported scheme used to locate .packages file: " "'$resource'."); } } else if (tag == -2) { if (traceLoading) { _log("Handling load of packages map: '$resource'."); } if (resource.scheme == '' || resource.scheme == 'file') { var exists = await new File.fromUri(resource).exists(); if (exists) { _loadPackagesFile(sp, traceLoading, resource); } else { sp.send("Packages file '$resource' not found."); } } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { var exists = await _loadHttpPackagesFile(sp, traceLoading, resource); if (!exists) { sp.send("Packages file '$resource' not found."); } } else if (resource.scheme == 'data') { _loadPackagesData(sp, traceLoading, resource); } else { sp.send("Unknown scheme (${resource.scheme}) for package file at " "'$resource'."); } } else { sp.send("Unknown packages request tag: $tag for '$resource'."); } } catch (e, s) { if (traceLoading) { _log("Error handling packages request: $e\n$s"); } sp.send("Uncaught error ($e) handling packages request."); } } // Shutdown all active loaders by sending an error message. void shutdownLoaders() { String message = 'Service shutdown'; if (_httpClient != null) { _httpClient.close(force: true); _httpClient = null; } isolateEmbedderData.values.toList().forEach((ied) { IsolateLoaderState ils = ied; ils.cleanup(); assert(ils.sp != null); _sendResourceResponse(ils.sp, 1, null, null, null, message); }); } // See Dart_LibraryTag in dart_api.h const _Dart_kCanonicalizeUrl = 0; // Canonicalize the URL. const _Dart_kScriptTag = 1; // Load the root script. const _Dart_kSourceTag = 2; // Load a part source. const _Dart_kImportTag = 3; // Import a library. // Extra requests. Keep these in sync between loader.dart and builtin.dart. const _Dart_kInitLoader = 4; // Initialize the loader. const _Dart_kResourceLoad = 5; // Resource class support. const _Dart_kGetPackageRootUri = 6; // Uri of the packages/ directory. const _Dart_kGetPackageConfigUri = 7; // Uri of the .packages file. const _Dart_kResolvePackageUri = 8; // Resolve a package: uri. // Extra requests. Keep these in sync between loader.dart and loader.cc. const _Dart_kImportExtension = 9; // Import a dart-ext: file. const _Dart_kResolveAsFilePath = 10; // Resolve uri to file path. // External entry point for loader requests. _processLoadRequest(request) { assert(request is List); assert(request.length > 4); // Should we trace loading? bool traceLoading = request[0]; // This is the sending isolate's Dart_GetMainPortId(). int isolateId = request[1]; // The tag describing the operation. int tag = request[2]; // The send port to send the response on. SendPort sp = request[3]; // Grab the loader state for the requesting isolate. IsolateLoaderState loaderState = isolateEmbedderData[isolateId]; // We are either about to initialize the loader, or, we already have. assert((tag == _Dart_kInitLoader) || (loaderState != null)); // Handle the request specified in the tag. switch (tag) { case _Dart_kInitLoader: { String packageRoot = request[4]; String packagesFile = request[5]; String workingDirectory = request[6]; String rootScript = request[7]; bool isReloading = request[8]; if (loaderState == null) { loaderState = new IsolateLoaderState(isolateId); isolateEmbedderData[isolateId] = loaderState; loaderState.init( packageRoot, packagesFile, workingDirectory, rootScript); } else if (isReloading) { loaderState.updatePackageMap(packagesFile); } loaderState.sp = sp; assert(isolateEmbedderData[isolateId] == loaderState); } break; case _Dart_kResourceLoad: { Uri uri = Uri.parse(request[4]); _handleResourceRequest( loaderState, sp, traceLoading, tag, uri, uri, null); } break; case _Dart_kGetPackageRootUri: loaderState._triggerPackageResolution(() { // The package root is deprecated and now always returns null. sp.send(null); }); break; case _Dart_kGetPackageConfigUri: loaderState._triggerPackageResolution(() { // Respond with the packages config (if any) after package resolution. sp.send(loaderState._packageConfig); }); break; case _Dart_kResolvePackageUri: Uri uri = Uri.parse(request[4]); loaderState._triggerPackageResolution(() { // Respond with the resolved package uri after package resolution. Uri resolvedUri; try { resolvedUri = loaderState._resolvePackageUri(uri); } catch (e, s) { if (traceLoading) { _log("Exception ($e) when resolving package URI: $uri"); } resolvedUri = null; } sp.send(resolvedUri); }); break; case _Dart_kImportExtension: Uri uri = Uri.parse(request[4]); String libraryUri = request[5]; // Strip any filename off of the libraryUri's path. int index = libraryUri.lastIndexOf('/'); var path; if (index == -1) { path = './'; } else { path = libraryUri.substring(0, index + 1); } var pathUri = Uri.parse(path); switch (pathUri.scheme) { case '': case 'file': _sendExtensionImportResponse( sp, uri, libraryUri, pathUri.toFilePath()); break; case 'data': case 'http': case 'https': _sendExtensionImportResponse(sp, uri, libraryUri, pathUri.toString()); break; case 'package': // Start package resolution. loaderState._triggerPackageResolution(() { // Attempt to find the fully resolved uri of [path]. Uri resolvedUri; try { resolvedUri = loaderState._resolvePackageUri(pathUri); } catch (e, s) { if (traceLoading) { _log("Exception ($e) when resolving package URI: $uri"); } resolvedUri = null; } _sendExtensionImportResponse( sp, uri, libraryUri, resolvedUri.toString()); }); break; default: if (traceLoading) { _log('Unknown scheme (${pathUri.scheme}) in $pathUri.'); } _sendExtensionImportResponse(sp, uri, libraryUri, null); break; } break; case _Dart_kResolveAsFilePath: loaderState._triggerPackageResolution(() { String uri = request[4]; Uri resolvedUri = Uri.parse(_sanitizeWindowsPath(uri)); try { if (resolvedUri.scheme == 'package') { resolvedUri = loaderState._resolvePackageUri(resolvedUri); } if (resolvedUri.scheme == '' || resolvedUri.scheme == 'file') { resolvedUri = loaderState._workingDirectory.resolveUri(resolvedUri); var msg = new List(5); msg[0] = tag; msg[1] = uri; msg[2] = resolvedUri.toString(); msg[3] = null; msg[4] = resolvedUri.toFilePath(); sp.send(msg); } else { throw "Cannot resolve scheme (${resolvedUri.scheme}) to file path" " for $resolvedUri"; } } catch (e) { var msg = new List(5); msg[0] = -tag; msg[1] = uri; msg[2] = resolvedUri.toString(); msg[3] = null; msg[4] = e.toString(); sp.send(msg); } }); break; default: _log('Unknown loader request tag=$tag from $isolateId'); } }