Chromium Code Reviews| Index: runtime/bin/vmservice/loader.dart |
| diff --git a/runtime/bin/vmservice/loader.dart b/runtime/bin/vmservice/loader.dart |
| index a95df089b91bb5319f2b9687e8cf8cf951d68653..4fafa00ed1657b6b1bf7386cac4fa754de120f84 100644 |
| --- a/runtime/bin/vmservice/loader.dart |
| +++ b/runtime/bin/vmservice/loader.dart |
| @@ -4,10 +4,14 @@ |
| part of vmservice_io; |
| +_log(msg) { |
| + print("% $msg"); |
| +} |
| + |
| var _httpClient; |
| // Send a response to the requesting isolate. |
| -void _sendResponse(SendPort sp, int id, dynamic data) { |
| +void _sendResourceResponse(SendPort sp, int id, dynamic data) { |
| assert((data is List<int>) || (data is String)); |
| var msg = new List(2); |
| msg[0] = id; |
| @@ -29,17 +33,17 @@ void _loadHttp(SendPort sp, int id, Uri uri) { |
| if (response.statusCode != 200) { |
| var msg = "Failure getting $uri:\n" |
| " ${response.statusCode} ${response.reasonPhrase}"; |
| - _sendResponse(sp, id, msg); |
| + _sendResourceResponse(sp, id, msg); |
| } else { |
| - _sendResponse(sp, id, builder.takeBytes()); |
| + _sendResourceResponse(sp, id, builder.takeBytes()); |
| } |
| }, |
| onError: (e) { |
| - _sendResponse(sp, d, e.toString()); |
| + _sendResourceResponse(sp, id, e.toString()); |
| }); |
| }) |
| .catchError((e) { |
| - _sendResponse(sp, id, e.toString()); |
| + _sendResourceResponse(sp, id, e.toString()); |
| }); |
| // It's just here to push an event on the event loop so that we invoke the |
| // scheduled microtasks. |
| @@ -50,11 +54,11 @@ void _loadFile(SendPort sp, int id, Uri uri) { |
| var path = uri.toFilePath(); |
| var sourceFile = new File(path); |
| sourceFile.readAsBytes().then((data) { |
| - _sendResponse(sp, id, data); |
| + _sendResourceResponse(sp, id, data); |
| }, |
| onError: (e) { |
| var err = "Error loading $uri:\n $e"; |
| - _sendResponse(sp, id, err); |
| + _sendResourceResponse(sp, id, err); |
| }); |
| } |
| @@ -83,24 +87,303 @@ void _loadDataUri(SendPort sp, int id, Uri uri) { |
| } |
| var data = UTF8.encode(Uri.decodeComponent(encodedData)); |
| - _sendResponse(sp, id, data); |
| + _sendResourceResponse(sp, id, data); |
| } catch (e) { |
| - _sendResponse(sp, id, "Invalid data uri ($uri):\n $e"); |
| + _sendResourceResponse(sp, id, "Invalid data uri ($uri):\n $e"); |
| + } |
| +} |
| + |
| +_handleResourceRequest(SendPort sp, bool traceLoading, int id, Uri resource) { |
| + if (resource.scheme == 'file') { |
| + _loadFile(sp, id, resource); |
| + } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { |
| + _loadHttp(sp, id, resource); |
| + } else if ((resource.scheme == 'data')) { |
| + _loadDataUri(sp, id, resource); |
| + } else { |
| + _sendResourceResponse(sp, id, |
| + 'Unknown scheme (${resource.scheme}) for $resource'); |
| } |
| } |
| + |
| +// 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 [ |
| + // space ! " # $ % & ' |
| + true , false, true , true , false, true , false, false, |
| + // ( ) * + , - . / |
| + false, false, false, false, false, false, false, true , |
| + // 0 1 2 3 4 5 6 7 |
| + false, false, false, false, false, false, false, false, |
| + // 8 9 : ; < = > ? |
| + false, false, true , false, true , false, true , true , |
| + // @ A B C D E F G |
| + false, false, false, false, false, false, false, false, |
| + // H I J K L M N O |
| + false, false, false, false, false, false, false, false, |
| + // P Q R S T U V W |
| + false, false, false, false, false, false, false, false, |
| + // X Y Z [ \ ] ^ _ |
| + false, false, false, true , true , true , true , false, |
| + // ` a b c d e f g |
| + true , false, false, false, false, false, false, false, |
| + // h i j k l m n o |
| + false, false, false, false, false, false, false, false, |
| + // p q r s t u v w |
| + false, false, false, false, false, false, false, false, |
| + // x y z { | } ~ DEL |
| + false, false, false, true , true , true , false, true |
| +]; |
| + |
| +_parsePackagesFile(SendPort sp, |
| + bool traceLoading, |
| + Uri packagesFile, |
| + List<int> data) { |
| + var result = []; |
| + 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 (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]; |
| + } |
| + } else if ((char == _CR) || (char == _LF)) { |
| + // Identify end of line. |
| + 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); |
|
siva
2015/07/28 23:50:09
packageName duplicate check (needs to be a warning
Ivan Posva
2015/07/29 18:22:18
Added TODO in builtin.dart where we are adding the
|
| + |
| + // 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 packags file."); |
| + } |
| +} |
| + |
| +_findPackagesFile(SendPort sp, bool traceLoading, Uri base) async { |
| + try { |
| + // Check for the existence of a .packages file and if it exists try to load |
| + // and parse it. |
| + var packagesFile = base.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; |
| + } |
| + |
| + // Try whether there is a packages/ directory instead. |
| + var packageRoot = base.resolve("packages/"); |
| + if (traceLoading) { |
| + _log("Checking for $packageRoot directory."); |
| + } |
| + exists = await new Directory.fromUri(packageRoot).exists(); |
| + if (traceLoading) { |
| + _log("$packageRoot exists: $exists"); |
| + } |
| + if (exists) { |
| + if (traceLoading) { |
| + _log("Found a package root at: $packageRoot"); |
| + } |
| + sp.send([packageRoot.toString()]); |
| + return; |
| + } |
| + |
| + // Walk up the directory hierarchy to check for the existence of |
| + // .packages files in parent directories. |
| + var dir = new File.fromUri(base).parent; |
| + var parent = dir.parent; |
| + // Keep searching until we reach the root. |
| + while (parent.path != dir.path) { |
| + // Already looked in dir: Move up one level. |
| + dir = parent; |
| + parent = dir.parent; |
| + packagesFile = dir.uri.resolve(".packages"); |
| + if (traceLoading) { |
| + _log("Checking for $packagesFile file."); |
| + } |
| + exists = await new File.fromUri(packagesFile).exists(); |
| + if (traceLoading) { |
| + _log("$packagesFile exists: $exists"); |
| + } |
| + if (exists) { |
| + _loadPackagesFile(sp, traceLoading, packagesFile); |
| + return; |
| + } |
|
siva
2015/07/28 23:50:09
This chunk of code for finiding ".packages" file c
Ivan Posva
2015/07/29 18:22:18
Refactored into a single loop. It probably now mak
|
| + } |
| + |
| + // 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."); |
| + } |
| +} |
| + |
| +_handlePackagesRequest(SendPort sp, bool traceLoading, int id, Uri resource) { |
| + if (id == -1) { |
|
siva
2015/07/28 23:50:09
-1 and -2 seem like magic numbers maybe we can hav
|
| + if (resource.scheme == 'file') { |
| + _findPackagesFile(sp, traceLoading, resource); |
| + } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { |
| + // TODO(iposva): Check for the existence of a .packages file when loading |
| + // from http or https. |
| + var packageRoot = resource.resolve('packages/'); |
| + sp.send([packageRoot.toString()]); |
| + } else { |
| + sp.send("Unsupported base URI to identify .packages file: '$resource'."); |
| + } |
| + } else if (id == -2) { |
| + if (traceLoading) { |
| + _log("Handling load of packages map: '$resource'."); |
| + } |
| + _loadPackagesFile(sp, traceLoading, resource); |
| + } else { |
| + sp.send("Unknown packages request id: $id for '$resource'."); |
| + } |
| +} |
| + |
| + |
| +// External entry point for loader requests. |
| _processLoadRequest(request) { |
| SendPort sp = request[0]; |
| - int id = request[1]; |
| - String resource = request[2]; |
| - var uri = Uri.parse(request[2]); |
| - if (uri.scheme == 'file') { |
| - _loadFile(sp, id, uri); |
| - } else if ((uri.scheme == 'http') || (uri.scheme == 'https')) { |
| - _loadHttp(sp, id, uri); |
| - } else if ((uri.scheme == 'data')) { |
| - _loadDataUri(sp, id, uri); |
| + assert(sp != null); |
| + bool traceLoading = request[1]; |
| + assert(traceLoading != null); |
| + int id = request[2]; |
| + assert(id != null); |
| + String resource = request[3]; |
| + assert(resource != null); |
| + var uri = Uri.parse(resource); |
| + if (id >= 0) { |
| + _handleResourceRequest(sp, traceLoading, id, uri); |
| } else { |
| - sp.send([id, 'Unknown scheme (${uri.scheme}) for $uri']); |
| + _handlePackagesRequest(sp, traceLoading, id, uri); |
| } |
| } |