OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of vmservice_io; | 5 part of vmservice_io; |
6 | 6 |
| 7 _sanitizeWindowsPath(path) { |
| 8 // For Windows we need to massage the paths a bit according to |
| 9 // http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx |
| 10 // |
| 11 // Convert |
| 12 // C:\one\two\three |
| 13 // to |
| 14 // /C:/one/two/three |
| 15 |
| 16 if (_isWindows == false) { |
| 17 // Do nothing when not running Windows. |
| 18 return path; |
| 19 } |
| 20 |
| 21 var fixedPath = "${path.replaceAll('\\', '/')}"; |
| 22 |
| 23 if ((path.length > 2) && (path[1] == ':')) { |
| 24 // Path begins with a drive letter. |
| 25 return '/$fixedPath'; |
| 26 } |
| 27 |
| 28 return fixedPath; |
| 29 } |
| 30 |
| 31 _trimWindowsPath(path) { |
| 32 // Convert /X:/ to X:/. |
| 33 if (_isWindows == false) { |
| 34 // Do nothing when not running Windows. |
| 35 return path; |
| 36 } |
| 37 if (!path.startsWith('/') || (path.length < 3)) { |
| 38 return path; |
| 39 } |
| 40 // Match '/?:'. |
| 41 if ((path[0] == '/') && (path[2] == ':')) { |
| 42 // Remove leading '/'. |
| 43 return path.substring(1); |
| 44 } |
| 45 return path; |
| 46 } |
| 47 |
| 48 // Ensure we have a trailing slash character. |
| 49 _enforceTrailingSlash(uri) { |
| 50 if (!uri.endsWith('/')) { |
| 51 return '$uri/'; |
| 52 } |
| 53 return uri; |
| 54 } |
| 55 |
| 56 // State associated with the isolate that is used for loading. |
| 57 class IsolateLoaderState extends IsolateEmbedderData { |
| 58 IsolateLoaderState(this.isolateId); |
| 59 |
| 60 final int isolateId; |
| 61 |
| 62 SendPort sp; |
| 63 |
| 64 void init(String packageRootFlag, |
| 65 String packagesConfigFlag, |
| 66 String workingDirectory, |
| 67 String rootScript) { |
| 68 // _workingDirectory must be set first. |
| 69 _workingDirectory = new Uri.directory(workingDirectory); |
| 70 if (rootScript != null) { |
| 71 _rootScript = Uri.parse(rootScript); |
| 72 } |
| 73 // If the --package-root flag was passed. |
| 74 if (packageRootFlag != null) { |
| 75 _setPackageRoot(packageRootFlag); |
| 76 } |
| 77 // If the --packages flag was passed. |
| 78 if (packagesConfigFlag != null) { |
| 79 _setPackagesConfig(packagesConfigFlag); |
| 80 } |
| 81 } |
| 82 |
| 83 void cleanup() { |
| 84 if (_packagesPort != null) { |
| 85 _packagesPort.close(); |
| 86 _packagesPort = null; |
| 87 } |
| 88 } |
| 89 |
| 90 // The working directory when the embedder started. |
| 91 Uri _workingDirectory; |
| 92 |
| 93 // The root script's uri. |
| 94 Uri _rootScript; |
| 95 |
| 96 bool _traceLoading = false; |
| 97 |
| 98 // Packages are either resolved looking up in a map or resolved from within a |
| 99 // package root. |
| 100 bool get _packagesReady => (_packageRoot != null) || |
| 101 (_packageMap != null) || |
| 102 (_packageError != null); |
| 103 |
| 104 // Error string set if there was an error resolving package configuration. |
| 105 // For example not finding a .packages file or packages/ directory, malformed |
| 106 // .packages file or any other related error. |
| 107 String _packageError = null; |
| 108 |
| 109 // The directory to look in to resolve "package:" scheme URIs. By default it |
| 110 // is the 'packages' directory right next to the script. |
| 111 Uri _packageRoot = null; |
| 112 |
| 113 // The map describing how certain package names are mapped to Uris. |
| 114 Uri _packageConfig = null; |
| 115 Map<String, Uri> _packageMap = null; |
| 116 |
| 117 _setPackageRoot(String packageRoot) { |
| 118 packageRoot = _enforceTrailingSlash(packageRoot); |
| 119 if (packageRoot.startsWith('file:') || |
| 120 packageRoot.startsWith('http:') || |
| 121 packageRoot.startsWith('https:')) { |
| 122 _packageRoot = _workingDirectory.resolve(packageRoot); |
| 123 } else { |
| 124 packageRoot = _sanitizeWindowsPath(packageRoot); |
| 125 packageRoot = _trimWindowsPath(packageRoot); |
| 126 _packageRoot = _workingDirectory.resolveUri(new Uri.file(packageRoot)); |
| 127 } |
| 128 } |
| 129 |
| 130 _setPackagesConfig(String packagesParam) { |
| 131 var packagesName = _sanitizeWindowsPath(packagesParam); |
| 132 var packagesUri = Uri.parse(packagesName); |
| 133 if (packagesUri.scheme == '') { |
| 134 // Script does not have a scheme, assume that it is a path, |
| 135 // resolve it against the working directory. |
| 136 packagesUri = _workingDirectory.resolveUri(packagesUri); |
| 137 } |
| 138 _requestPackagesMap(packagesUri); |
| 139 _pendingPackageLoads.add(() { |
| 140 // Dummy action. |
| 141 }); |
| 142 } |
| 143 |
| 144 // Handling of access to the package root or package map from user code. |
| 145 _triggerPackageResolution(action) { |
| 146 if (_packagesReady) { |
| 147 // Packages are ready. Execute the action now. |
| 148 action(); |
| 149 } else { |
| 150 if (_pendingPackageLoads.isEmpty) { |
| 151 // Package resolution has not been setup yet, and this is the first |
| 152 // request for package resolution & loading. |
| 153 _requestPackagesMap(); |
| 154 } |
| 155 // Register the action for when the package resolution is ready. |
| 156 _pendingPackageLoads.add(action); |
| 157 } |
| 158 } |
| 159 |
| 160 // A list of callbacks which should be invoked after the package map has been |
| 161 // loaded. |
| 162 List<Function> _pendingPackageLoads = []; |
| 163 |
| 164 // Given a uri with a 'package' scheme, return a Uri that is prefixed with |
| 165 // the package root. |
| 166 Uri _resolvePackageUri(Uri uri) { |
| 167 assert(uri.scheme == "package"); |
| 168 assert(_packagesReady); |
| 169 |
| 170 if (!uri.host.isEmpty) { |
| 171 var path = '${uri.host}${uri.path}'; |
| 172 var right = 'package:$path'; |
| 173 var wrong = 'package://$path'; |
| 174 |
| 175 throw "URIs using the 'package:' scheme should look like " |
| 176 "'$right', not '$wrong'."; |
| 177 } |
| 178 |
| 179 if (_traceLoading) { |
| 180 _log('Resolving package with uri path: ${uri.path}'); |
| 181 } |
| 182 var resolvedUri; |
| 183 if (_packageError != null) { |
| 184 if (_traceLoading) { |
| 185 _log("Resolving package with pending resolution error: $_packageError"); |
| 186 } |
| 187 throw _packageError; |
| 188 } else if (_packageRoot != null) { |
| 189 resolvedUri = _packageRoot.resolve(uri.path); |
| 190 } else { |
| 191 var packageName = uri.pathSegments[0]; |
| 192 var mapping = _packageMap[packageName]; |
| 193 if (_traceLoading) { |
| 194 _log("Mapped '$packageName' package to '$mapping'"); |
| 195 } |
| 196 if (mapping == null) { |
| 197 throw "No mapping for '$packageName' package when resolving '$uri'."; |
| 198 } |
| 199 var path; |
| 200 if (uri.path.length > packageName.length) { |
| 201 path = uri.path.substring(packageName.length + 1); |
| 202 } else { |
| 203 // Handle naked package resolution to the default package name: |
| 204 // package:foo is equivalent to package:foo/foo.dart |
| 205 assert(uri.path.length == packageName.length); |
| 206 path = "$packageName.dart"; |
| 207 } |
| 208 if (_traceLoading) { |
| 209 _log("Path to be resolved in package: $path"); |
| 210 } |
| 211 resolvedUri = mapping.resolve(path); |
| 212 } |
| 213 if (_traceLoading) { |
| 214 _log("Resolved '$uri' to '$resolvedUri'."); |
| 215 } |
| 216 return resolvedUri; |
| 217 } |
| 218 |
| 219 RawReceivePort _packagesPort; |
| 220 |
| 221 void _requestPackagesMap([Uri packageConfig]) { |
| 222 assert(_packagesPort == null); |
| 223 assert(_rootScript != null); |
| 224 // Create a port to receive the packages map on. |
| 225 _packagesPort = new RawReceivePort(_handlePackagesReply); |
| 226 var sp = _packagesPort.sendPort; |
| 227 |
| 228 if (packageConfig != null) { |
| 229 // Explicitly specified .packages path. |
| 230 _handlePackagesRequest(sp, |
| 231 _traceLoading, |
| 232 -2, |
| 233 packageConfig); |
| 234 } else { |
| 235 // Search for .packages or packages/ starting at the root script. |
| 236 _handlePackagesRequest(sp, |
| 237 _traceLoading, |
| 238 -1, |
| 239 _rootScript); |
| 240 } |
| 241 |
| 242 if (_traceLoading) { |
| 243 _log("Requested packages map for '$_rootScript'."); |
| 244 } |
| 245 } |
| 246 |
| 247 void _handlePackagesReply(msg) { |
| 248 assert(_packagesPort != null); |
| 249 // Make sure to close the _packagePort before any other action. |
| 250 _packagesPort.close(); |
| 251 _packagesPort = null; |
| 252 |
| 253 if (_traceLoading) { |
| 254 _log("Got packages reply: $msg"); |
| 255 } |
| 256 if (msg is String) { |
| 257 if (_traceLoading) { |
| 258 _log("Got failure response on package port: '$msg'"); |
| 259 } |
| 260 // Remember the error message. |
| 261 _packageError = msg; |
| 262 } else if (msg is List) { |
| 263 if (msg.length == 1) { |
| 264 if (_traceLoading) { |
| 265 _log("Received package root: '${msg[0]}'"); |
| 266 } |
| 267 _packageRoot = Uri.parse(msg[0]); |
| 268 } else { |
| 269 // First entry contains the location of the loaded .packages file. |
| 270 assert((msg.length % 2) == 0); |
| 271 assert(msg.length >= 2); |
| 272 assert(msg[1] == null); |
| 273 _packageConfig = Uri.parse(msg[0]); |
| 274 _packageMap = new Map<String, Uri>(); |
| 275 for (var i = 2; i < msg.length; i+=2) { |
| 276 // TODO(iposva): Complain about duplicate entries. |
| 277 _packageMap[msg[i]] = Uri.parse(msg[i+1]); |
| 278 } |
| 279 if (_traceLoading) { |
| 280 _log("Setup package map: $_packageMap"); |
| 281 } |
| 282 } |
| 283 } else { |
| 284 _packageError = "Bad type of packages reply: ${msg.runtimeType}"; |
| 285 if (_traceLoading) { |
| 286 _log(_packageError); |
| 287 } |
| 288 } |
| 289 |
| 290 // Resolve all pending package loads now that we know how to resolve them. |
| 291 while (_pendingPackageLoads.length > 0) { |
| 292 // Order does not matter as we queue all of the requests up right now. |
| 293 var req = _pendingPackageLoads.removeLast(); |
| 294 // Call the registered closure, to handle the delayed action. |
| 295 req(); |
| 296 } |
| 297 // Reset the pending package loads to empty. So that we eventually can |
| 298 // finish loading. |
| 299 _pendingPackageLoads = []; |
| 300 } |
| 301 |
| 302 } |
| 303 |
7 _log(msg) { | 304 _log(msg) { |
8 print("% $msg"); | 305 print("% $msg"); |
9 } | 306 } |
10 | 307 |
11 var _httpClient; | 308 var _httpClient; |
12 | 309 |
13 // Send a response to the requesting isolate. | 310 // Send a response to the requesting isolate. |
14 void _sendResourceResponse(SendPort sp, int id, dynamic data) { | 311 void _sendResourceResponse(SendPort sp, |
| 312 int tag, |
| 313 Uri uri, |
| 314 String libraryUrl, |
| 315 dynamic data) { |
15 assert((data is List<int>) || (data is String)); | 316 assert((data is List<int>) || (data is String)); |
16 var msg = new List(2); | 317 var msg = new List(4); |
17 msg[0] = id; | 318 if (data is String) { |
18 msg[1] = data; | 319 // We encountered an error, flip the sign of the tag to indicate that. |
| 320 tag = -tag; |
| 321 if (libraryUrl == null) { |
| 322 data = 'Could not load "$uri": $data'; |
| 323 } else { |
| 324 data = 'Could not import "$uri" from "$libraryUrl": $data'; |
| 325 } |
| 326 } |
| 327 msg[0] = tag; |
| 328 msg[1] = uri.toString(); |
| 329 msg[2] = libraryUrl; |
| 330 msg[3] = data; |
19 sp.send(msg); | 331 sp.send(msg); |
20 } | 332 } |
21 | 333 |
22 void _loadHttp(SendPort sp, int id, Uri uri) { | 334 void _loadHttp(SendPort sp, |
| 335 int tag, |
| 336 Uri uri, |
| 337 Uri resolvedUri, |
| 338 String libraryUrl) { |
23 if (_httpClient == null) { | 339 if (_httpClient == null) { |
24 _httpClient = new HttpClient()..maxConnectionsPerHost = 6; | 340 _httpClient = new HttpClient()..maxConnectionsPerHost = 6; |
25 } | 341 } |
26 _httpClient.getUrl(uri) | 342 _httpClient.getUrl(resolvedUri) |
27 .then((HttpClientRequest request) => request.close()) | 343 .then((HttpClientRequest request) => request.close()) |
28 .then((HttpClientResponse response) { | 344 .then((HttpClientResponse response) { |
29 var builder = new BytesBuilder(copy: false); | 345 var builder = new BytesBuilder(copy: false); |
30 response.listen( | 346 response.listen( |
31 builder.add, | 347 builder.add, |
32 onDone: () { | 348 onDone: () { |
33 if (response.statusCode != 200) { | 349 if (response.statusCode != 200) { |
34 var msg = "Failure getting $uri:\n" | 350 var msg = "Failure getting $resolvedUri:\n" |
35 " ${response.statusCode} ${response.reasonPhrase}"; | 351 " ${response.statusCode} ${response.reasonPhrase}"; |
36 _sendResourceResponse(sp, id, msg); | 352 _sendResourceResponse(sp, tag, uri, libraryUrl, msg); |
37 } else { | 353 } else { |
38 _sendResourceResponse(sp, id, builder.takeBytes()); | 354 _sendResourceResponse(sp, tag, uri, libraryUrl, |
| 355 builder.takeBytes()); |
39 } | 356 } |
40 }, | 357 }, |
41 onError: (e) { | 358 onError: (e) { |
42 _sendResourceResponse(sp, id, e.toString()); | 359 _sendResourceResponse(sp, tag, uri, libraryUrl, e.toString()); |
43 }); | 360 }); |
44 }) | 361 }) |
45 .catchError((e) { | 362 .catchError((e) { |
46 _sendResourceResponse(sp, id, e.toString()); | 363 _sendResourceResponse(sp, tag, uri, libraryUrl, e.toString()); |
47 }); | 364 }); |
48 // It's just here to push an event on the event loop so that we invoke the | 365 // It's just here to push an event on the event loop so that we invoke the |
49 // scheduled microtasks. | 366 // scheduled microtasks. |
50 Timer.run(() {}); | 367 Timer.run(() {}); |
51 } | 368 } |
52 | 369 |
53 void _loadFile(SendPort sp, int id, Uri uri) { | 370 void _loadFile(SendPort sp, |
54 var path = uri.toFilePath(); | 371 int tag, |
| 372 Uri uri, |
| 373 Uri resolvedUri, |
| 374 String libraryUrl) { |
| 375 var path = resolvedUri.toFilePath(); |
55 var sourceFile = new File(path); | 376 var sourceFile = new File(path); |
56 sourceFile.readAsBytes().then((data) { | 377 sourceFile.readAsBytes().then((data) { |
57 _sendResourceResponse(sp, id, data); | 378 _sendResourceResponse(sp, tag, uri, libraryUrl, data); |
58 }, | 379 }, |
59 onError: (e) { | 380 onError: (e) { |
60 _sendResourceResponse(sp, id, e.toString()); | 381 _sendResourceResponse(sp, tag, uri, libraryUrl, e.toString()); |
61 }); | 382 }); |
62 } | 383 } |
63 | 384 |
64 void _loadDataUri(SendPort sp, int id, Uri uri) { | 385 void _loadDataUri(SendPort sp, |
| 386 int tag, |
| 387 Uri uri, |
| 388 Uri resolvedUri, |
| 389 String libraryUrl) { |
65 try { | 390 try { |
66 var mime = uri.data.mimeType; | 391 var mime = uri.data.mimeType; |
67 if ((mime != "application/dart") && | 392 if ((mime != "application/dart") && |
68 (mime != "text/plain")) { | 393 (mime != "text/plain")) { |
69 throw "MIME-type must be application/dart or text/plain: $mime given."; | 394 throw "MIME-type must be application/dart or text/plain: $mime given."; |
70 } | 395 } |
71 var charset = uri.data.charset; | 396 var charset = uri.data.charset; |
72 if ((charset != "utf-8") && | 397 if ((charset != "utf-8") && |
73 (charset != "US-ASCII")) { | 398 (charset != "US-ASCII")) { |
74 // The C++ portion of the embedder assumes UTF-8. | 399 // The C++ portion of the embedder assumes UTF-8. |
75 throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; | 400 throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; |
76 } | 401 } |
77 _sendResourceResponse(sp, id, uri.data.contentAsBytes()); | 402 _sendResourceResponse(sp, tag, uri, libraryUrl, uri.data.contentAsBytes()); |
78 } catch (e) { | 403 } catch (e) { |
79 _sendResourceResponse(sp, id, "Invalid data uri ($uri):\n $e"); | 404 _sendResourceResponse(sp, tag, uri, libraryUrl, |
| 405 "Invalid data uri ($uri):\n $e"); |
80 } | 406 } |
81 } | 407 } |
82 | 408 |
83 _handleResourceRequest(SendPort sp, bool traceLoading, int id, Uri resource) { | 409 // Loading a package URI needs to first map the package name to a loadable |
84 if (resource.scheme == 'file') { | 410 // URI. |
85 _loadFile(sp, id, resource); | 411 _loadPackage(IsolateLoaderState loaderState, |
86 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { | 412 SendPort sp, |
87 _loadHttp(sp, id, resource); | 413 bool traceLoading, |
88 } else if ((resource.scheme == 'data')) { | 414 int tag, |
89 _loadDataUri(sp, id, resource); | 415 Uri uri, |
| 416 Uri resolvedUri, |
| 417 String libraryUrl) { |
| 418 if (loaderState._packagesReady) { |
| 419 var resolvedUri; |
| 420 try { |
| 421 resolvedUri = loaderState._resolvePackageUri(uri); |
| 422 } catch (e, s) { |
| 423 if (traceLoading) { |
| 424 _log("Exception ($e) when resolving package URI: $uri"); |
| 425 } |
| 426 // Report error. |
| 427 _sendResourceResponse(sp, |
| 428 tag, |
| 429 uri, |
| 430 libraryUrl, |
| 431 e.toString()); |
| 432 return; |
| 433 } |
| 434 // Recursively call with the new resolved uri. |
| 435 _handleResourceRequest(loaderState, |
| 436 sp, |
| 437 traceLoading, |
| 438 tag, |
| 439 uri, |
| 440 resolvedUri, |
| 441 libraryUrl); |
90 } else { | 442 } else { |
91 _sendResourceResponse(sp, id, | 443 if (loaderState._pendingPackageLoads.isEmpty) { |
92 'Unknown scheme (${resource.scheme}) for $resource'); | 444 // Package resolution has not been setup yet, and this is the first |
| 445 // request for package resolution & loading. |
| 446 loaderState._requestPackagesMap(); |
| 447 } |
| 448 // Register the action of loading this package once the package resolution |
| 449 // is ready. |
| 450 loaderState._pendingPackageLoads.add(() { |
| 451 _handleResourceRequest(loaderState, |
| 452 sp, |
| 453 traceLoading, |
| 454 tag, |
| 455 uri, |
| 456 uri, |
| 457 libraryUrl); |
| 458 }); |
| 459 if (traceLoading) { |
| 460 _log("Pending package load of '$uri': " |
| 461 "${loaderState._pendingPackageLoads.length} pending"); |
| 462 } |
93 } | 463 } |
94 } | 464 } |
95 | 465 |
| 466 // TODO(johnmccutchan): This and most other top level functions in this file |
| 467 // should be turned into methods on the IsolateLoaderState class. |
| 468 _handleResourceRequest(IsolateLoaderState loaderState, |
| 469 SendPort sp, |
| 470 bool traceLoading, |
| 471 int tag, |
| 472 Uri uri, |
| 473 Uri resolvedUri, |
| 474 String libraryUrl) { |
| 475 if (resolvedUri.scheme == 'file') { |
| 476 _loadFile(sp, tag, uri, resolvedUri, libraryUrl); |
| 477 } else if ((resolvedUri.scheme == 'http') || |
| 478 (resolvedUri.scheme == 'https')) { |
| 479 _loadHttp(sp, tag, uri, resolvedUri, libraryUrl); |
| 480 } else if ((resolvedUri.scheme == 'data')) { |
| 481 _loadDataUri(sp, tag, uri, resolvedUri, libraryUrl); |
| 482 } else if ((resolvedUri.scheme == 'package')) { |
| 483 _loadPackage(loaderState, |
| 484 sp, |
| 485 traceLoading, |
| 486 tag, |
| 487 uri, |
| 488 resolvedUri, |
| 489 libraryUrl); |
| 490 } else { |
| 491 _sendResourceResponse(sp, tag, |
| 492 uri, |
| 493 libraryUrl, |
| 494 'Unknown scheme (${resolvedUri.scheme}) for ' |
| 495 '$resolvedUri'); |
| 496 } |
| 497 } |
96 | 498 |
97 // Handling of packages requests. Finding and parsing of .packages file or | 499 // Handling of packages requests. Finding and parsing of .packages file or |
98 // packages/ directories. | 500 // packages/ directories. |
99 const _LF = 0x0A; | 501 const _LF = 0x0A; |
100 const _CR = 0x0D; | 502 const _CR = 0x0D; |
101 const _SPACE = 0x20; | 503 const _SPACE = 0x20; |
102 const _HASH = 0x23; | 504 const _HASH = 0x23; |
103 const _DOT = 0x2E; | 505 const _DOT = 0x2E; |
104 const _COLON = 0x3A; | 506 const _COLON = 0x3A; |
105 const _DEL = 0x7F; | 507 const _DEL = 0x7F; |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 } | 724 } |
323 sp.send("Could not resolve a package location for base at $base"); | 725 sp.send("Could not resolve a package location for base at $base"); |
324 } catch (e, s) { | 726 } catch (e, s) { |
325 if (traceLoading) { | 727 if (traceLoading) { |
326 _log("Error loading packages: $e\n$s"); | 728 _log("Error loading packages: $e\n$s"); |
327 } | 729 } |
328 sp.send("Uncaught error ($e) loading packages file."); | 730 sp.send("Uncaught error ($e) loading packages file."); |
329 } | 731 } |
330 } | 732 } |
331 | 733 |
332 | |
333 Future<bool> _loadHttpPackagesFile(SendPort sp, | 734 Future<bool> _loadHttpPackagesFile(SendPort sp, |
334 bool traceLoading, | 735 bool traceLoading, |
335 Uri resource) async { | 736 Uri resource) async { |
336 try { | 737 try { |
337 if (_httpClient == null) { | 738 if (_httpClient == null) { |
338 _httpClient = new HttpClient()..maxConnectionsPerHost = 6; | 739 _httpClient = new HttpClient()..maxConnectionsPerHost = 6; |
339 } | 740 } |
340 if (traceLoading) { | 741 if (traceLoading) { |
341 _log("Fetching packages file from '$resource'."); | 742 _log("Fetching packages file from '$resource'."); |
342 } | 743 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 (charset != "US-ASCII")) { | 780 (charset != "US-ASCII")) { |
380 // The C++ portion of the embedder assumes UTF-8. | 781 // The C++ portion of the embedder assumes UTF-8. |
381 throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; | 782 throw "Only utf-8 or US-ASCII encodings are supported: $charset given."; |
382 } | 783 } |
383 _parsePackagesFile(sp, traceLoading, resource, data.contentAsBytes()); | 784 _parsePackagesFile(sp, traceLoading, resource, data.contentAsBytes()); |
384 } catch (e) { | 785 } catch (e) { |
385 sp.send("Uncaught error ($e) loading packages data."); | 786 sp.send("Uncaught error ($e) loading packages data."); |
386 } | 787 } |
387 } | 788 } |
388 | 789 |
389 | 790 // This code used to exist in a second isolate and so it uses a SendPort to |
| 791 // report it's return value. This could be refactored so that it returns it's |
| 792 // value and the caller could wait on the future rather than a message on |
| 793 // SendPort. |
390 _handlePackagesRequest(SendPort sp, | 794 _handlePackagesRequest(SendPort sp, |
391 bool traceLoading, | 795 bool traceLoading, |
392 int id, | 796 int tag, |
393 Uri resource) async { | 797 Uri resource) async { |
394 try { | 798 try { |
395 if (id == -1) { | 799 if (tag == -1) { |
396 if (resource.scheme == 'file') { | 800 if (resource.scheme == 'file') { |
397 _findPackagesFile(sp, traceLoading, resource); | 801 _findPackagesFile(sp, traceLoading, resource); |
398 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { | 802 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { |
399 // Try to load the .packages file next to the resource. | 803 // Try to load the .packages file next to the resource. |
400 var packagesUri = resource.resolve(".packages"); | 804 var packagesUri = resource.resolve(".packages"); |
401 var exists = await _loadHttpPackagesFile(sp, traceLoading, packagesUri); | 805 var exists = await _loadHttpPackagesFile(sp, traceLoading, packagesUri); |
402 if (!exists) { | 806 if (!exists) { |
403 // If the loading of the .packages file failed for http/https based | 807 // If the loading of the .packages file failed for http/https based |
404 // scripts then setup the package root. | 808 // scripts then setup the package root. |
405 var packageRoot = resource.resolve('packages/'); | 809 var packageRoot = resource.resolve('packages/'); |
406 sp.send([packageRoot.toString()]); | 810 sp.send([packageRoot.toString()]); |
407 } | 811 } |
408 } else { | 812 } else { |
409 sp.send("Unsupported scheme used to locate .packages file: " | 813 sp.send("Unsupported scheme used to locate .packages file: " |
410 "'$resource'."); | 814 "'$resource'."); |
411 } | 815 } |
412 } else if (id == -2) { | 816 } else if (tag == -2) { |
413 if (traceLoading) { | 817 if (traceLoading) { |
414 _log("Handling load of packages map: '$resource'."); | 818 _log("Handling load of packages map: '$resource'."); |
415 } | 819 } |
416 if (resource.scheme == 'file') { | 820 if (resource.scheme == 'file') { |
417 var exists = await new File.fromUri(resource).exists(); | 821 var exists = await new File.fromUri(resource).exists(); |
418 if (exists) { | 822 if (exists) { |
419 _loadPackagesFile(sp, traceLoading, resource); | 823 _loadPackagesFile(sp, traceLoading, resource); |
420 } else { | 824 } else { |
421 sp.send("Packages file '$resource' not found."); | 825 sp.send("Packages file '$resource' not found."); |
422 } | 826 } |
423 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { | 827 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { |
424 var exists = await _loadHttpPackagesFile(sp, traceLoading, resource); | 828 var exists = await _loadHttpPackagesFile(sp, traceLoading, resource); |
425 if (!exists) { | 829 if (!exists) { |
426 sp.send("Packages file '$resource' not found."); | 830 sp.send("Packages file '$resource' not found."); |
427 } | 831 } |
428 } else if (resource.scheme == 'data') { | 832 } else if (resource.scheme == 'data') { |
429 _loadPackagesData(sp, traceLoading, resource); | 833 _loadPackagesData(sp, traceLoading, resource); |
430 } else { | 834 } else { |
431 sp.send("Unknown scheme (${resource.scheme}) for package file at " | 835 sp.send("Unknown scheme (${resource.scheme}) for package file at " |
432 "'$resource'."); | 836 "'$resource'."); |
433 } | 837 } |
434 } else { | 838 } else { |
435 sp.send("Unknown packages request id: $id for '$resource'."); | 839 sp.send("Unknown packages request tag: $tag for '$resource'."); |
436 } | 840 } |
437 } catch (e, s) { | 841 } catch (e, s) { |
438 if (traceLoading) { | 842 if (traceLoading) { |
439 _log("Error handling packages request: $e\n$s"); | 843 _log("Error handling packages request: $e\n$s"); |
440 } | 844 } |
441 sp.send("Uncaught error ($e) handling packages request."); | 845 sp.send("Uncaught error ($e) handling packages request."); |
442 } | 846 } |
443 } | 847 } |
444 | 848 |
| 849 // Shutdown all active loaders by sending an error message. |
| 850 void shutdownLoaders() { |
| 851 String message = 'Service shutdown'; |
| 852 if (_httpClient != null) { |
| 853 _httpClient.close(force: true); |
| 854 _httpClient = null; |
| 855 } |
| 856 isolateEmbedderData.values.toList().forEach((IsolateLoaderState ils) { |
| 857 ils.cleanup(); |
| 858 assert(ils.sp != null); |
| 859 _sendResourceResponse(ils.sp, 1, null, null, message); |
| 860 }); |
| 861 } |
| 862 |
| 863 // See Dart_LibraryTag in dart_api.h |
| 864 const _Dart_kCanonicalizeUrl = 0; // Canonicalize the URL. |
| 865 const _Dart_kScriptTag = 1; // Load the root script. |
| 866 const _Dart_kSourceTag = 2; // Load a part source. |
| 867 const _Dart_kImportTag = 3; // Import a library. |
| 868 |
| 869 // Extra requests. Keep these in sync between loader.dart and builtin.dart. |
| 870 const _Dart_kInitLoader = 4; // Initialize the loader. |
| 871 const _Dart_kResourceLoad = 5; // Resource class support. |
| 872 const _Dart_kGetPackageRootUri = 6; // Uri of the packages/ directory. |
| 873 const _Dart_kGetPackageConfigUri = 7; // Uri of the .packages file. |
| 874 const _Dart_kResolvePackageUri = 8; // Resolve a package: uri. |
445 | 875 |
446 // External entry point for loader requests. | 876 // External entry point for loader requests. |
447 _processLoadRequest(request) { | 877 _processLoadRequest(request) { |
448 SendPort sp = request[0]; | 878 assert(request is List); |
449 assert(sp != null); | 879 assert(request.length > 4); |
450 bool traceLoading = request[1]; | 880 |
451 assert(traceLoading != null); | 881 // Should we trace loading? |
452 int id = request[2]; | 882 bool traceLoading = request[0]; |
453 assert(id != null); | 883 |
454 String resource = request[3]; | 884 // This is the sending isolate's Dart_GetMainPortId(). |
455 assert(resource != null); | 885 int isolateId = request[1]; |
456 var uri = Uri.parse(resource); | 886 |
457 if (id >= 0) { | 887 // The tag describing the operation. |
458 _handleResourceRequest(sp, traceLoading, id, uri); | 888 int tag = request[2]; |
459 } else { | 889 |
460 _handlePackagesRequest(sp, traceLoading, id, uri); | 890 // The send port to send the response on. |
| 891 SendPort sp = request[3]; |
| 892 |
| 893 // Grab the loader state for the requesting isolate. |
| 894 IsolateLoaderState loaderState = isolateEmbedderData[isolateId]; |
| 895 |
| 896 // We are either about to initialize the loader, or, we already have. |
| 897 assert((tag == _Dart_kInitLoader) || (loaderState != null)); |
| 898 |
| 899 // Handle the request specified in the tag. |
| 900 switch (tag) { |
| 901 case _Dart_kScriptTag: { |
| 902 Uri uri = Uri.parse(request[4]); |
| 903 // Remember the root script. |
| 904 loaderState._rootScript = uri; |
| 905 _handleResourceRequest(loaderState, |
| 906 sp, |
| 907 traceLoading, |
| 908 tag, |
| 909 uri, |
| 910 uri, |
| 911 null); |
| 912 } |
| 913 break; |
| 914 case _Dart_kSourceTag: |
| 915 case _Dart_kImportTag: { |
| 916 // The url of the file being loaded. |
| 917 var uri = Uri.parse(request[4]); |
| 918 // The library that is importing/parting the file. |
| 919 String libraryUrl = request[5]; |
| 920 _handleResourceRequest(loaderState, |
| 921 sp, |
| 922 traceLoading, |
| 923 tag, |
| 924 uri, |
| 925 uri, |
| 926 libraryUrl); |
| 927 } |
| 928 break; |
| 929 case _Dart_kInitLoader: { |
| 930 String packageRoot = request[4]; |
| 931 String packagesFile = request[5]; |
| 932 String workingDirectory = request[6]; |
| 933 String rootScript = request[7]; |
| 934 if (loaderState == null) { |
| 935 loaderState = new IsolateLoaderState(isolateId); |
| 936 isolateEmbedderData[isolateId] = loaderState; |
| 937 loaderState.init(packageRoot, |
| 938 packagesFile, |
| 939 workingDirectory, |
| 940 rootScript); |
| 941 } |
| 942 loaderState.sp = sp; |
| 943 assert(isolateEmbedderData[isolateId] == loaderState); |
| 944 } |
| 945 break; |
| 946 case _Dart_kResourceLoad: { |
| 947 Uri uri = Uri.parse(request[4]); |
| 948 _handleResourceRequest(loaderState, |
| 949 sp, |
| 950 traceLoading, |
| 951 tag, |
| 952 uri, |
| 953 uri, |
| 954 null); |
| 955 } |
| 956 break; |
| 957 case _Dart_kGetPackageRootUri: |
| 958 loaderState._triggerPackageResolution(() { |
| 959 // Respond with the package root (if any) after package resolution. |
| 960 sp.send(loaderState._packageRoot); |
| 961 }); |
| 962 break; |
| 963 case _Dart_kGetPackageConfigUri: |
| 964 loaderState._triggerPackageResolution(() { |
| 965 // Respond with the packages config (if any) after package resolution. |
| 966 sp.send(loaderState._packageConfig); |
| 967 }); |
| 968 break; |
| 969 case _Dart_kResolvePackageUri: |
| 970 Uri uri = Uri.parse(request[4]); |
| 971 loaderState._triggerPackageResolution(() { |
| 972 // Respond with the resolved package uri after package resolution. |
| 973 Uri resolvedUri; |
| 974 try { |
| 975 resolvedUri = loaderState._resolvePackageUri(uri); |
| 976 } catch (e, s) { |
| 977 if (traceLoading) { |
| 978 _log("Exception ($e) when resolving package URI: $uri"); |
| 979 } |
| 980 resolvedUri = null; |
| 981 } |
| 982 sp.send(resolvedUri); |
| 983 }); |
| 984 break; |
| 985 default: |
| 986 _log('Unknown loader request tag=$tag from $isolateId'); |
461 } | 987 } |
462 } | 988 } |
OLD | NEW |