Chromium Code Reviews| Index: sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| index bf21636123cc218295666994b8b50fb01ddad721..77058e95a9a73eab3bf4d6d7dd55cbff8f491f8d 100644 |
| --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| @@ -3647,6 +3647,9 @@ LoadLibraryFunctionType _loadLibraryWrapper(String loadId) { |
| final Map<String, Future<Null>> _loadingLibraries = <String, Future<Null>>{}; |
| final Set<String> _loadedLibraries = new Set<String>(); |
| +/// Events used to diagnose failures from deferred loading requests. |
| +final List<String> _eventLog = <String>[]; |
|
sra1
2017/08/14 18:24:08
How do we get hold of this in a crash log?
Siggi Cherem (dart-lang)
2017/08/14 18:59:43
I'm attaching it on the exceptions that we throw e
sra1
2017/08/14 19:38:29
That is fine.
|
| + |
| typedef void DeferredLoadCallback(); |
| // Function that will be called every time a new deferred import is loaded. |
| @@ -3674,15 +3677,30 @@ Future<Null> loadDeferredLibrary(String loadId) { |
| // Now all hunks have been loaded, we run the needed initializers. |
| var isHunkInitialized = JS_EMBEDDED_GLOBAL('', IS_HUNK_INITIALIZED); |
| var initializer = JS_EMBEDDED_GLOBAL('', INITIALIZE_LOADED_HUNK); |
| - for (String hash in hashes) { |
| + for (int i = 0; i < hashes.length; ++i) { |
| // It is possible for a hash to be repeated. This happens when two |
| // different parts both end up empty. Checking in the loop rather than |
| // pre-filtering prevents duplicate hashes leading to duplicated |
| // initializations. |
| // TODO(29572): Merge small parts. |
| // TODO(29635): Remove duplicate parts from tables and output files. |
| - if (JS('bool', '#(#)', isHunkInitialized, hash)) continue; |
| - JS('void', '#(#)', initializer, hash); |
| + var uri = uris[i]; |
| + var hash = hashes[i]; |
| + if (JS('bool', '#(#)', isHunkInitialized, hash)) { |
| + _eventLog.add(' - already initialized: $uri ($hash)'); |
| + continue; |
| + } |
| + // On strange scenarios, e.g. if js encounters parse errors, we might get |
| + // an "success" callback on the script load but the hunk will be null. |
| + if (JS('bool', '#(#)', isHunkLoaded, hash)) { |
| + _eventLog.add(' - initialize: $uri ($hash)'); |
| + JS('void', '#(#)', initializer, hash); |
| + } else { |
| + _eventLog.add(' - missing hunk: $uri ($hash)'); |
| + throw new DeferredLoadException("Loading ${uris[i]} failed: " |
| + "the code with hash '${hash}' was not loaded.\n" |
| + "event log:\n${_eventLog.join("\n")}\n"); |
| + } |
| } |
| bool updated = _loadedLibraries.add(loadId); |
| if (updated && deferredLoadHook != null) { |
| @@ -3693,7 +3711,9 @@ Future<Null> loadDeferredLibrary(String loadId) { |
| Future<Null> _loadHunk(String hunkName) { |
| Future<Null> future = _loadingLibraries[hunkName]; |
| + _eventLog.add(' - _loadHunk: $hunkName'); |
| if (future != null) { |
| + _eventLog.add('reuse: $hunkName'); |
| return future.then((_) => null); |
| } |
| @@ -3701,23 +3721,29 @@ Future<Null> _loadHunk(String hunkName) { |
| int index = uri.lastIndexOf('/'); |
| uri = '${uri.substring(0, index + 1)}$hunkName'; |
| + _eventLog.add(' - download: $hunkName from $uri'); |
| var deferredLibraryLoader = JS('', 'self.dartDeferredLibraryLoader'); |
| Completer<Null> completer = new Completer<Null>(); |
| void success() { |
| + _eventLog.add(' - download success: $hunkName'); |
| completer.complete(null); |
| } |
| - void failure([error, StackTrace stackTrace]) { |
| + void failure(error, String context, StackTrace stackTrace) { |
| + _eventLog.add(' - download failed: $hunkName (context: $context)'); |
| _loadingLibraries[hunkName] = null; |
| - completer.completeError( |
| - new DeferredLoadException('Loading $uri failed: $error'), stackTrace); |
| + stackTrace ??= StackTrace.current; |
| + completer.completeError(new DeferredLoadException( |
| + 'Loading $uri failed: $error\n' |
| + 'event log:\n${_eventLog.join("\n")}\n'), stackTrace); |
| } |
| var jsSuccess = convertDartClosureToJS(success, 0); |
| var jsFailure = convertDartClosureToJS((error) { |
| - failure(unwrapException(error), getTraceFromException(error)); |
| + failure(unwrapException(error), 'js-failure-wrapper', |
| + getTraceFromException(error)); |
| }, 1); |
| if (JS('bool', 'typeof # === "function"', deferredLibraryLoader)) { |
| @@ -3725,7 +3751,7 @@ Future<Null> _loadHunk(String hunkName) { |
| JS('void', '#(#, #, #)', deferredLibraryLoader, uri, jsSuccess, |
| jsFailure); |
| } catch (error, stackTrace) { |
| - failure(error, stackTrace); |
| + failure(error, "invoking dartDeferredLibraryLoader hook", stackTrace); |
| } |
| } else if (isWorker()) { |
| // We are in a web worker. Load the code with an XMLHttpRequest. |
| @@ -3745,7 +3771,7 @@ Future<Null> _loadHunk(String hunkName) { |
| convertDartClosureToJS((event) { |
| int status = JS('int', '#.status', xhr); |
| if (status != 200) { |
| - failure('Request status: $status'); |
| + failure('Request status: $status', 'worker xhr', null); |
| } |
| String code = JS('String', '#.responseText', xhr); |
| try { |
| @@ -3754,12 +3780,16 @@ Future<Null> _loadHunk(String hunkName) { |
| JS('void', '(new Function(#))()', code); |
| success(); |
| } catch (error, stackTrace) { |
| - failure(error, stackTrace); |
| + failure(error, 'evaluating the code in worker xhr', stackTrace); |
| } |
| }, 1)); |
| - JS('void', '#.addEventListener("error", #, false)', xhr, failure); |
| - JS('void', '#.addEventListener("abort", #, false)', xhr, failure); |
| + JS('void', '#.addEventListener("error", #, false)', xhr, (e) { |
| + failure(e, 'xhr error handler', null); |
| + }); |
| + JS('void', '#.addEventListener("abort", #, false)', xhr, (e) { |
| + failure(e, 'xhr abort handler', null); |
| + }); |
| JS('void', '#.send()', xhr); |
| } else { |
| // We are in a dom-context. |