 Chromium Code Reviews
 Chromium Code Reviews Issue 1232593003:
  - Implement .packages specification.  (Closed) 
  Base URL: git@github.com:dart-lang/sdk.git@master
    
  
    Issue 1232593003:
  - Implement .packages specification.  (Closed) 
  Base URL: git@github.com:dart-lang/sdk.git@master| 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); | 
| } | 
| } |