Index: dart/sdk/lib/_internal/lib/isolate_helper.dart |
diff --git a/dart/sdk/lib/_internal/lib/isolate_helper.dart b/dart/sdk/lib/_internal/lib/isolate_helper.dart |
index cfb0a96d6b29e04db6760038aad96a41cd24c736..06176b2ac9c95fc10a6f6e67f13c2776d8de19e8 100644 |
--- a/dart/sdk/lib/_internal/lib/isolate_helper.dart |
+++ b/dart/sdk/lib/_internal/lib/isolate_helper.dart |
@@ -683,6 +683,7 @@ class _MainManagerStub { |
} |
const String _SPAWNED_SIGNAL = "spawned"; |
+const String _SPAWN_FAILED_SIGNAL = "spawn failed"; |
var globalThis = Primitives.computeGlobalThis(); |
var globalWindow = JS('', "#.window", globalThis); |
@@ -802,10 +803,14 @@ class IsolateNatives { |
_globalState.topEventLoop.run(); |
break; |
case 'spawn-worker': |
- _spawnWorker(msg['functionName'], msg['uri'], |
- msg['args'], msg['msg'], |
- msg['isSpawnUri'], msg['startPaused'], |
- msg['replyPort']); |
+ var replyPort = msg['replyPort']; |
+ spawn(msg['functionName'], msg['uri'], |
+ msg['args'], msg['msg'], |
+ false, msg['isSpawnUri'], msg['startPaused']).then((msg) { |
+ replyPort.send(msg); |
+ }, onError: (String errorMessage) { |
+ replyPort.send([_SPAWN_FAILED_SIGNAL, errorMessage]); |
+ }); |
break; |
case 'message': |
SendPort port = msg['port']; |
@@ -905,22 +910,28 @@ class IsolateNatives { |
if (uri != null && uri.endsWith(".dart")) uri += ".js"; |
ReceivePort port = new ReceivePort(); |
- Future<List> result = port.first.then((msg) { |
- assert(msg[0] == _SPAWNED_SIGNAL); |
- return msg; |
+ Completer<List> completer = new Completer(); |
+ port.first.then((msg) { |
+ if (msg[0] == _SPAWNED_SIGNAL) { |
+ completer.complete(msg); |
+ } else { |
+ assert(msg[0] == _SPAWN_FAILED_SIGNAL); |
+ completer.completeError(msg[1]); |
+ } |
}); |
SendPort signalReply = port.sendPort; |
if (_globalState.useWorkers && !isLight) { |
- _startWorker(functionName, uri, args, message, isSpawnUri, startPaused, |
- signalReply); |
+ _startWorker( |
+ functionName, uri, args, message, isSpawnUri, startPaused, |
+ signalReply, (String message) => completer.completeError(message)); |
} else { |
_startNonWorker( |
functionName, uri, args, message, isSpawnUri, startPaused, |
signalReply); |
} |
- return result; |
+ return completer.future; |
} |
static void _startWorker( |
@@ -928,7 +939,8 @@ class IsolateNatives { |
List<String> args, message, |
bool isSpawnUri, |
bool startPaused, |
- SendPort replyPort) { |
+ SendPort replyPort, |
+ void onError(String message)) { |
if (_globalState.isWorker) { |
_globalState.mainManager.postMessage(_serializeMessage({ |
'command': 'spawn-worker', |
@@ -941,7 +953,7 @@ class IsolateNatives { |
'replyPort': replyPort})); |
} else { |
_spawnWorker(functionName, uri, args, message, |
- isSpawnUri, startPaused, replyPort); |
+ isSpawnUri, startPaused, replyPort, onError); |
} |
} |
@@ -1007,14 +1019,38 @@ class IsolateNatives { |
List<String> args, message, |
bool isSpawnUri, |
bool startPaused, |
- SendPort replyPort) { |
+ SendPort replyPort, |
+ void onError(String message)) { |
if (uri == null) uri = thisScript; |
final worker = JS('var', 'new Worker(#)', uri); |
- |
- var processWorkerMessageTrampoline = |
- JS('', "(function (f, a) { return function (e) { f(a, e); }})(#, #)", |
- DART_CLOSURE_TO_JS(_processWorkerMessage), |
- worker); |
+ // Trampolines are used when wanting to call a Dart closure from |
+ // JavaScript. The helper function DART_CLOSURE_TO_JS only accepts |
+ // top-level or static methods, and the trampoline allows us to capture |
+ // arguments and values which can be passed to a static method. |
+ final onerrorTrampoline = JS( |
+ '', |
+ ''' |
+(function (f, u, c) { |
+ return function(e) { |
+ return f(e, u, c) |
+ } |
+})(#, #, #)''', |
+ DART_CLOSURE_TO_JS(workerOnError), uri, onError); |
+ JS('void', '#.onerror = #', worker, onerrorTrampoline); |
+ |
+ var processWorkerMessageTrampoline = JS( |
+ '', |
+ """ |
+(function (f, a) { |
+ return function (e) { |
+ // We can stop listening for errors when the first message is received as |
+ // we only listen for messages to determine if the uri was bad. |
+ e.onerror = null; |
+ return f(a, e); |
+ } |
+})(#, #)""", |
+ DART_CLOSURE_TO_JS(_processWorkerMessage), |
+ worker); |
JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline); |
var workerId = _globalState.nextManagerId++; |
// We also store the id on the worker itself so that we can unregister it. |
@@ -1034,6 +1070,25 @@ class IsolateNatives { |
'startPaused': startPaused, |
'functionName': functionName })); |
} |
+ |
+ static bool workerOnError( |
+ /* Event */ event, |
+ String uri, |
+ void onError(String message)) { |
+ // Attempt to shut up the browser, as the error has been handled. Chrome |
+ // ignores this :-( |
+ JS('void', '#.preventDefault()', event); |
+ String message = JS('String|Null', '#.message', event); |
+ if (message == null) { |
+ // Some browsers, including Chrome, fail to provide a proper error |
+ // event. |
+ message = 'Error spawning worker for $uri'; |
+ } else { |
+ message = 'Error spawning worker for $uri ($message)'; |
+ } |
+ onError(message); |
+ return true; |
+ } |
} |
/******************************************************** |