OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 library _isolate_helper; | 5 library _isolate_helper; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:isolate'; | 8 import 'dart:isolate'; |
9 | 9 |
| 10 ReceivePort lazyPort; |
| 11 |
10 /** | 12 /** |
11 * Called by the compiler to support switching | 13 * Called by the compiler to support switching |
12 * between isolates when we get a callback from the DOM. | 14 * between isolates when we get a callback from the DOM. |
13 */ | 15 */ |
14 void _callInIsolate(_IsolateContext isolate, Function function) { | 16 void _callInIsolate(_IsolateContext isolate, Function function) { |
15 isolate.eval(function); | 17 isolate.eval(function); |
16 _globalState.topEventLoop.run(); | 18 _globalState.topEventLoop.run(); |
17 } | 19 } |
18 | 20 |
19 /** | 21 /** |
20 * Called by the compiler to fetch the current isolate context. | 22 * Called by the compiler to fetch the current isolate context. |
21 */ | 23 */ |
22 _IsolateContext _currentIsolate() => _globalState.currentContext; | 24 _IsolateContext _currentIsolate() => _globalState.currentContext; |
23 | 25 |
24 /******************************************************** | |
25 Inserted from lib/isolate/dart2js/compiler_hooks.dart | |
26 ********************************************************/ | |
27 | |
28 /** | 26 /** |
29 * Wrapper that takes the dart entry point and runs it within an isolate. The | 27 * Wrapper that takes the dart entry point and runs it within an isolate. The |
30 * dart2js compiler will inject a call of the form | 28 * dart2js compiler will inject a call of the form |
31 * [: startRootIsolate(main); :] when it determines that this wrapping | 29 * [: startRootIsolate(main); :] when it determines that this wrapping |
32 * is needed. For single-isolate applications (e.g. hello world), this | 30 * is needed. For single-isolate applications (e.g. hello world), this |
33 * call is not emitted. | 31 * call is not emitted. |
34 */ | 32 */ |
35 void startRootIsolate(entry) { | 33 void startRootIsolate(entry) { |
36 _globalState = new _Manager(); | 34 _globalState = new _Manager(); |
37 | 35 |
38 // Don't start the main loop again, if we are in a worker. | 36 // Don't start the main loop again, if we are in a worker. |
39 if (_globalState.isWorker) return; | 37 if (_globalState.isWorker) return; |
40 final rootContext = new _IsolateContext(); | 38 final rootContext = new _IsolateContext(); |
41 _globalState.rootContext = rootContext; | 39 _globalState.rootContext = rootContext; |
42 _fillStatics(rootContext); | |
43 | 40 |
44 // BUG(5151491): Setting currentContext should not be necessary, but | 41 // BUG(5151491): Setting currentContext should not be necessary, but |
45 // because closures passed to the DOM as event handlers do not bind their | 42 // because closures passed to the DOM as event handlers do not bind their |
46 // isolate automatically we try to give them a reasonable context to live in | 43 // isolate automatically we try to give them a reasonable context to live in |
47 // by having a "default" isolate (the first one created). | 44 // by having a "default" isolate (the first one created). |
48 _globalState.currentContext = rootContext; | 45 _globalState.currentContext = rootContext; |
49 | 46 |
50 rootContext.eval(entry); | 47 rootContext.eval(entry); |
51 _globalState.topEventLoop.run(); | 48 _globalState.topEventLoop.run(); |
52 } | 49 } |
(...skipping 22 matching lines...) Expand all Loading... |
75 * A native object that is shared across isolates. This object is visible to all | 72 * A native object that is shared across isolates. This object is visible to all |
76 * isolates running under the same manager (either UI or background web worker). | 73 * isolates running under the same manager (either UI or background web worker). |
77 * | 74 * |
78 * This is code that is intended to 'escape' the isolate boundaries in order to | 75 * This is code that is intended to 'escape' the isolate boundaries in order to |
79 * implement the semantics of isolates in JavaScript. Without this we would have | 76 * implement the semantics of isolates in JavaScript. Without this we would have |
80 * been forced to implement more code (including the top-level event loop) in | 77 * been forced to implement more code (including the top-level event loop) in |
81 * JavaScript itself. | 78 * JavaScript itself. |
82 */ | 79 */ |
83 // TODO(eub, sigmund): move the "manager" to be entirely in JS. | 80 // TODO(eub, sigmund): move the "manager" to be entirely in JS. |
84 // Running any Dart code outside the context of an isolate gives it | 81 // Running any Dart code outside the context of an isolate gives it |
85 // the change to break the isolate abstraction. | 82 // the chance to break the isolate abstraction. |
86 _Manager get _globalState => JS("_Manager", r"$globalState"); | 83 _Manager get _globalState => JS("_Manager", r"$globalState"); |
| 84 |
87 set _globalState(_Manager val) { | 85 set _globalState(_Manager val) { |
88 JS("void", r"$globalState = #", val); | 86 JS("void", r"$globalState = #", val); |
89 } | 87 } |
90 | 88 |
91 void _fillStatics(context) { | |
92 JS("void", r"$globals = #.isolateStatics", context); | |
93 JS("void", r"$static_init()"); | |
94 } | |
95 | |
96 ReceivePort lazyPort; | |
97 | |
98 /** State associated with the current manager. See [globalState]. */ | 89 /** State associated with the current manager. See [globalState]. */ |
99 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? | 90 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? |
100 class _Manager { | 91 class _Manager { |
101 | 92 |
102 /** Next available isolate id within this [_Manager]. */ | 93 /** Next available isolate id within this [_Manager]. */ |
103 int nextIsolateId = 0; | 94 int nextIsolateId = 0; |
104 | 95 |
105 /** id assigned to this [_Manager]. */ | 96 /** id assigned to this [_Manager]. */ |
106 int currentManagerId = 0; | 97 int currentManagerId = 0; |
107 | 98 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 topEventLoop = new _EventLoop(); | 151 topEventLoop = new _EventLoop(); |
161 isolates = new Map<int, _IsolateContext>(); | 152 isolates = new Map<int, _IsolateContext>(); |
162 managers = new Map<int, _ManagerStub>(); | 153 managers = new Map<int, _ManagerStub>(); |
163 if (isWorker) { // "if we are not the main manager ourself" is the intent. | 154 if (isWorker) { // "if we are not the main manager ourself" is the intent. |
164 mainManager = new _MainManagerStub(); | 155 mainManager = new _MainManagerStub(); |
165 _nativeInitWorkerMessageHandler(); | 156 _nativeInitWorkerMessageHandler(); |
166 } | 157 } |
167 } | 158 } |
168 | 159 |
169 void _nativeDetectEnvironment() { | 160 void _nativeDetectEnvironment() { |
170 isWorker = JS("bool", r"$isWorker"); | 161 bool isWindowDefined = globalWindow != null; |
171 supportsWorkers = JS("bool", r"$supportsWorkers"); | 162 bool isWorkerDefined = globalWorker != null; |
172 fromCommandLine = JS("bool", r"typeof(window) == 'undefined'"); | 163 |
| 164 isWorker = !isWindowDefined && globalPostMessageDefined; |
| 165 supportsWorkers = isWorker |
| 166 || (isWorkerDefined && IsolateNatives.thisScript != null); |
| 167 fromCommandLine = !isWindowDefined && !isWorker; |
173 } | 168 } |
174 | 169 |
175 void _nativeInitWorkerMessageHandler() { | 170 void _nativeInitWorkerMessageHandler() { |
176 JS("void", r""" | 171 var function = JS('', |
177 $globalThis.onmessage = function (e) { | 172 "function (e) { #(#, e); }", |
178 IsolateNatives._processWorkerMessage(this.mainManager, e); | 173 DART_CLOSURE_TO_JS(IsolateNatives._processWorkerMessage), |
179 }"""); | 174 mainManager); |
| 175 JS("void", r"#.onmessage = #", globalThis, function); |
| 176 // We define dartPrint so that the implementation of the Dart |
| 177 // print method knows what to call. |
| 178 // TODO(ngeoffray): Should we forward to the main isolate? What if |
| 179 // it exited? |
| 180 JS('void', r'#.dartPrint = function (object) {}', globalThis); |
180 } | 181 } |
181 /*: TODO: check that _processWorkerMessage is not discarded while treeshaking. | |
182 """ { | |
183 IsolateNatives._processWorkerMessage(null, null); | |
184 } | |
185 */ | |
186 | 182 |
187 | 183 |
188 /** Close the worker running this code if all isolates are done. */ | 184 /** Close the worker running this code if all isolates are done. */ |
189 void maybeCloseWorker() { | 185 void maybeCloseWorker() { |
190 if (isolates.isEmpty) { | 186 if (isolates.isEmpty) { |
191 mainManager.postMessage(_serializeMessage({'command': 'close'})); | 187 mainManager.postMessage(_serializeMessage({'command': 'close'})); |
192 } | 188 } |
193 } | 189 } |
194 } | 190 } |
195 | 191 |
196 /** Context information tracked for each isolate. */ | 192 /** Context information tracked for each isolate. */ |
197 class _IsolateContext { | 193 class _IsolateContext { |
198 /** Current isolate id. */ | 194 /** Current isolate id. */ |
199 int id; | 195 int id; |
200 | 196 |
201 /** Registry of receive ports currently active on this isolate. */ | 197 /** Registry of receive ports currently active on this isolate. */ |
202 Map<int, ReceivePort> ports; | 198 Map<int, ReceivePort> ports; |
203 | 199 |
204 /** Holds isolate globals (statics and top-level properties). */ | 200 /** Holds isolate globals (statics and top-level properties). */ |
205 var isolateStatics; // native object containing all globals of an isolate. | 201 var isolateStatics; // native object containing all globals of an isolate. |
206 | 202 |
207 _IsolateContext() { | 203 _IsolateContext() { |
208 id = _globalState.nextIsolateId++; | 204 id = _globalState.nextIsolateId++; |
209 ports = new Map<int, ReceivePort>(); | 205 ports = new Map<int, ReceivePort>(); |
210 initGlobals(); | 206 isolateStatics = JS_CREATE_ISOLATE(); |
211 } | 207 } |
212 | 208 |
213 // these are filled lazily the first time the isolate starts running. | |
214 void initGlobals() { JS("void", r'$initGlobals(#)', this); } | |
215 | |
216 /** | 209 /** |
217 * Run [code] in the context of the isolate represented by [this]. Note this | 210 * Run [code] in the context of the isolate represented by [this]. Note this |
218 * is called from JavaScript (see $wrap_call in corejs.dart). | 211 * is called from JavaScript (see $wrap_call in corejs.dart). |
219 */ | 212 */ |
220 dynamic eval(Function code) { | 213 dynamic eval(Function code) { |
221 var old = _globalState.currentContext; | 214 var old = _globalState.currentContext; |
222 _globalState.currentContext = this; | 215 _globalState.currentContext = this; |
223 this._setGlobals(); | 216 this._setGlobals(); |
224 var result = null; | 217 var result = null; |
225 try { | 218 try { |
226 result = code(); | 219 result = code(); |
227 } finally { | 220 } finally { |
228 _globalState.currentContext = old; | 221 _globalState.currentContext = old; |
229 if (old != null) old._setGlobals(); | 222 if (old != null) old._setGlobals(); |
230 } | 223 } |
231 return result; | 224 return result; |
232 } | 225 } |
233 | 226 |
234 void _setGlobals() { JS("void", r'$setGlobals(#)', this); } | 227 void _setGlobals() { |
| 228 JS_SET_CURRENT_ISOLATE(isolateStatics); |
| 229 } |
235 | 230 |
236 /** Lookup a port registered for this isolate. */ | 231 /** Lookup a port registered for this isolate. */ |
237 ReceivePort lookup(int portId) => ports[portId]; | 232 ReceivePort lookup(int portId) => ports[portId]; |
238 | 233 |
239 /** Register a port on this isolate. */ | 234 /** Register a port on this isolate. */ |
240 void register(int portId, ReceivePort port) { | 235 void register(int portId, ReceivePort port) { |
241 if (ports.containsKey(portId)) { | 236 if (ports.containsKey(portId)) { |
242 throw new Exception("Registry: ports must be registered only once."); | 237 throw new Exception("Registry: ports must be registered only once."); |
243 } | 238 } |
244 ports[portId] = port; | 239 ports[portId] = port; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
293 } | 288 } |
294 event.process(); | 289 event.process(); |
295 return true; | 290 return true; |
296 } | 291 } |
297 | 292 |
298 /** | 293 /** |
299 * Runs multiple iterations of the run-loop. If possible, each iteration is | 294 * Runs multiple iterations of the run-loop. If possible, each iteration is |
300 * run asynchronously. | 295 * run asynchronously. |
301 */ | 296 */ |
302 void _runHelper() { | 297 void _runHelper() { |
303 if (JS('String', 'typeof window') != 'undefined') { | 298 if (globalWindow != null) { |
304 // Run each iteration from the browser's top event loop. | 299 // Run each iteration from the browser's top event loop. |
305 void next() { | 300 void next() { |
306 if (!runIteration()) return; | 301 if (!runIteration()) return; |
307 JS('void', 'window.setTimeout(#, 0)', convertDartClosureToJS(next, 0)); | 302 JS('void', 'window.setTimeout(#, 0)', convertDartClosureToJS(next, 0)); |
308 } | 303 } |
309 next(); | 304 next(); |
310 } else { | 305 } else { |
311 // Run synchronously until no more iterations are available. | 306 // Run synchronously until no more iterations are available. |
312 while (runIteration()) {} | 307 while (runIteration()) {} |
313 } | 308 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
353 void terminate(); | 348 void terminate(); |
354 } | 349 } |
355 | 350 |
356 /** A stub for interacting with the main manager. */ | 351 /** A stub for interacting with the main manager. */ |
357 class _MainManagerStub implements _ManagerStub { | 352 class _MainManagerStub implements _ManagerStub { |
358 get id => 0; | 353 get id => 0; |
359 void set id(int i) { throw new UnimplementedError(); } | 354 void set id(int i) { throw new UnimplementedError(); } |
360 void set onmessage(f) { | 355 void set onmessage(f) { |
361 throw new Exception("onmessage should not be set on MainManagerStub"); | 356 throw new Exception("onmessage should not be set on MainManagerStub"); |
362 } | 357 } |
363 void postMessage(msg) { JS("void", r"$globalThis.postMessage(#)", msg); } | 358 void postMessage(msg) { |
| 359 JS("void", r"#.postMessage(#)", globalThis, msg); |
| 360 } |
364 void terminate() {} // Nothing useful to do here. | 361 void terminate() {} // Nothing useful to do here. |
365 } | 362 } |
366 | 363 |
367 /** | 364 /** |
368 * A stub for interacting with a manager built on a web worker. This | 365 * A stub for interacting with a manager built on a web worker. This |
369 * definition uses a 'hidden' type (* prefix on the native name) to | 366 * definition uses a 'hidden' type (* prefix on the native name) to |
370 * enforce that the type is defined dynamically only when web workers | 367 * enforce that the type is defined dynamically only when web workers |
371 * are actually available. | 368 * are actually available. |
372 */ | 369 */ |
373 // TODO(ngeoffray): We comment this annotation for now because the | |
374 // native feature uses a keyword that this file does not have access | |
375 // to. We should remove the comment once the native feature uses | |
376 // annotations. | |
377 // @Native("*Worker"); | 370 // @Native("*Worker"); |
378 class _WorkerStub implements _ManagerStub { | 371 class _WorkerStub implements _ManagerStub { |
379 get id => JS("var", "#.id", this); | 372 get id => JS("", "#.id", this); |
380 void set id(i) { JS("void", "#.id = #", this, i); } | 373 void set id(i) { JS("void", "#.id = #", this, i); } |
381 void set onmessage(f) { JS("void", "#.onmessage = #", this, f); } | 374 void set onmessage(f) { JS("void", "#.onmessage = #", this, f); } |
382 void postMessage(msg) => JS("void", "#.postMessage(#)", this, msg); | 375 void postMessage(msg) { JS("void", "#.postMessage(#)", this, msg); } |
383 // terminate() is implemented by Worker. | 376 void terminate() { JS("void", "#.terminate()", this); } |
384 void terminate(); | |
385 } | 377 } |
386 | 378 |
387 const String _SPAWNED_SIGNAL = "spawned"; | 379 const String _SPAWNED_SIGNAL = "spawned"; |
388 | 380 |
389 var globalThis = JS('', 'function() { return this; }()'); | 381 var globalThis = IsolateNatives.computeGlobalThis(); |
| 382 var globalWindow = JS('', "#['window']", globalThis); |
| 383 var globalWorker = JS('', "#['Worker']", globalThis); |
| 384 bool globalPostMessageDefined = |
| 385 JS('', "#['postMessage'] !== (void 0)", globalThis); |
390 | 386 |
391 class IsolateNatives { | 387 class IsolateNatives { |
392 | 388 |
| 389 static String thisScript = computeThisScript(); |
| 390 |
393 /** | 391 /** |
394 * The src url for the script tag that loaded this code. Used to create | 392 * The src url for the script tag that loaded this code. Used to create |
395 * JavaScript workers. | 393 * JavaScript workers. |
396 */ | 394 */ |
397 static String get _thisScript => JS("String", r"$thisScriptUrl"); | 395 static String computeThisScript() { |
| 396 // TODO(7369): Find a cross-platform non-brittle way of getting the |
| 397 // currently running script. |
| 398 var scripts = JS('=List', r"document.getElementsByTagName('script')"); |
| 399 // The scripts variable only contains the scripts that have already been |
| 400 // executed. The last one is the currently running script. |
| 401 for (var script in scripts) { |
| 402 var src = JS('String|Null', '# && #.src', script, script); |
| 403 if (src != null |
| 404 && !src.endsWith('test_controller.js') |
| 405 && !new RegExp('client.dart\.js').hasMatch(src)) { |
| 406 return src; |
| 407 } |
| 408 } |
| 409 return null; |
| 410 } |
| 411 |
| 412 static computeGlobalThis() => JS('', 'function() { return this; }()'); |
398 | 413 |
399 /** Starts a new worker with the given URL. */ | 414 /** Starts a new worker with the given URL. */ |
400 static _WorkerStub _newWorker(url) => JS("_WorkerStub", r"new Worker(#)", url)
; | 415 static _WorkerStub _newWorker(url) => JS("_WorkerStub", r"new Worker(#)", url)
; |
401 | 416 |
402 /** | 417 /** |
403 * Assume that [e] is a browser message event and extract its message data. | 418 * Assume that [e] is a browser message event and extract its message data. |
404 * We don't import the dom explicitly so, when workers are disabled, this | 419 * We don't import the dom explicitly so, when workers are disabled, this |
405 * library can also run on top of nodejs. | 420 * library can also run on top of nodejs. |
406 */ | 421 */ |
407 //static _getEventData(e) => JS("Object", "#.data", e); | |
408 static _getEventData(e) => JS("", "#.data", e); | 422 static _getEventData(e) => JS("", "#.data", e); |
409 | 423 |
410 /** | 424 /** |
411 * Process messages on a worker, either to control the worker instance or to | 425 * Process messages on a worker, either to control the worker instance or to |
412 * pass messages along to the isolate running in the worker. | 426 * pass messages along to the isolate running in the worker. |
413 */ | 427 */ |
414 static void _processWorkerMessage(sender, e) { | 428 static void _processWorkerMessage(sender, e) { |
415 var msg = _deserializeMessage(_getEventData(e)); | 429 var msg = _deserializeMessage(_getEventData(e)); |
416 switch (msg['command']) { | 430 switch (msg['command']) { |
417 case 'start': | 431 case 'start': |
418 _globalState.currentManagerId = msg['id']; | 432 _globalState.currentManagerId = msg['id']; |
419 Function entryPoint = _getJSFunctionFromName(msg['functionName']); | 433 Function entryPoint = _getJSFunctionFromName(msg['functionName']); |
420 var replyTo = _deserializeMessage(msg['replyTo']); | 434 var replyTo = _deserializeMessage(msg['replyTo']); |
421 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { | 435 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { |
422 _startIsolate(entryPoint, replyTo); | 436 _startIsolate(entryPoint, replyTo); |
423 }, 'worker-start'); | 437 }, 'worker-start'); |
424 _globalState.topEventLoop.run(); | 438 _globalState.topEventLoop.run(); |
425 break; | 439 break; |
426 case 'spawn-worker': | 440 case 'spawn-worker': |
427 _spawnWorker(msg['functionName'], msg['uri'], msg['replyPort']); | 441 _spawnWorker(msg['functionName'], msg['uri'], msg['replyPort']); |
428 break; | 442 break; |
429 case 'message': | 443 case 'message': |
430 msg['port'].send(msg['msg'], msg['replyTo']); | 444 SendPort port = msg['port']; |
| 445 // If the port has been closed, we ignore the message. |
| 446 if (port != null) { |
| 447 msg['port'].send(msg['msg'], msg['replyTo']); |
| 448 } |
431 _globalState.topEventLoop.run(); | 449 _globalState.topEventLoop.run(); |
432 break; | 450 break; |
433 case 'close': | 451 case 'close': |
434 _log("Closing Worker"); | 452 _log("Closing Worker"); |
435 _globalState.managers.remove(sender.id); | 453 _globalState.managers.remove(sender.id); |
436 sender.terminate(); | 454 sender.terminate(); |
437 _globalState.topEventLoop.run(); | 455 _globalState.topEventLoop.run(); |
438 break; | 456 break; |
439 case 'log': | 457 case 'log': |
440 _log(msg['msg']); | 458 _log(msg['msg']); |
(...skipping 19 matching lines...) Expand all Loading... |
460 } else { | 478 } else { |
461 try { | 479 try { |
462 _consoleLog(msg); | 480 _consoleLog(msg); |
463 } catch (e, trace) { | 481 } catch (e, trace) { |
464 throw new Exception(trace); | 482 throw new Exception(trace); |
465 } | 483 } |
466 } | 484 } |
467 } | 485 } |
468 | 486 |
469 static void _consoleLog(msg) { | 487 static void _consoleLog(msg) { |
470 JS("void", r"$globalThis.console.log(#)", msg); | 488 JS("void", r"#.console.log(#)", globalThis, msg); |
471 } | 489 } |
472 | 490 |
473 /** | 491 /** |
474 * Extract the constructor of runnable, so it can be allocated in another | 492 * Extract the constructor of runnable, so it can be allocated in another |
475 * isolate. | 493 * isolate. |
476 */ | 494 */ |
477 static dynamic _getJSConstructor(Isolate runnable) { | 495 static dynamic _getJSConstructor(Isolate runnable) { |
478 return JS("Object", "#.constructor", runnable); | 496 return JS("", "#.constructor", runnable); |
479 } | 497 } |
480 | 498 |
481 /** Extract the constructor name of a runnable */ | 499 /** Extract the constructor name of a runnable */ |
482 // TODO(sigmund): find a browser-generic way to support this. | 500 // TODO(sigmund): find a browser-generic way to support this. |
483 // TODO(floitsch): is this function still used? If yes, should we use | 501 // TODO(floitsch): is this function still used? If yes, should we use |
484 // Primitives.objectTypeName instead? | 502 // Primitives.objectTypeName instead? |
485 static dynamic _getJSConstructorName(Isolate runnable) { | 503 static dynamic _getJSConstructorName(Isolate runnable) { |
486 return JS("Object", "#.constructor.name", runnable); | 504 return JS("", "#.constructor.name", runnable); |
487 } | 505 } |
488 | 506 |
489 /** Find a constructor given its name. */ | 507 /** Find a constructor given its name. */ |
490 static dynamic _getJSConstructorFromName(String factoryName) { | 508 static dynamic _getJSConstructorFromName(String factoryName) { |
491 return JS("Object", r"$globalThis[#]", factoryName); | 509 return JS("", r"$[#]", factoryName); |
492 } | 510 } |
493 | 511 |
494 static dynamic _getJSFunctionFromName(String functionName) { | 512 static dynamic _getJSFunctionFromName(String functionName) { |
495 return JS("Object", r"$globalThis[#]", functionName); | 513 return JS("", r"$[#]", functionName); |
496 } | 514 } |
497 | 515 |
498 /** | 516 /** |
499 * Get a string name for the function, if possible. The result for | 517 * Get a string name for the function, if possible. The result for |
500 * anonymous functions is browser-dependent -- it may be "" or "anonymous" | 518 * anonymous functions is browser-dependent -- it may be "" or "anonymous" |
501 * but you should probably not count on this. | 519 * but you should probably not count on this. |
502 */ | 520 */ |
503 static String _getJSFunctionName(Function f) { | 521 static String _getJSFunctionName(Function f) { |
504 return JS("Object", r"(#.$name || #)", f, null); | 522 return JS("String|Null", r"(#.$name || #)", f, null); |
505 } | 523 } |
506 | 524 |
507 /** Create a new JavaScript object instance given its constructor. */ | 525 /** Create a new JavaScript object instance given its constructor. */ |
508 static dynamic _allocate(var ctor) { | 526 static dynamic _allocate(var ctor) { |
509 return JS("Object", "new #()", ctor); | 527 return JS("", "new #()", ctor); |
510 } | 528 } |
511 | 529 |
512 static SendPort spawnFunction(void topLevelFunction()) { | 530 static SendPort spawnFunction(void topLevelFunction()) { |
513 final name = _getJSFunctionName(topLevelFunction); | 531 final name = _getJSFunctionName(topLevelFunction); |
514 if (name == null) { | 532 if (name == null) { |
515 throw new UnsupportedError( | 533 throw new UnsupportedError( |
516 "only top-level functions can be spawned."); | 534 "only top-level functions can be spawned."); |
517 } | 535 } |
518 return spawn(name, null, false); | 536 return spawn(name, null, false); |
519 } | 537 } |
520 | 538 |
| 539 static SendPort spawnDomFunction(void topLevelFunction()) { |
| 540 final name = _getJSFunctionName(topLevelFunction); |
| 541 if (name == null) { |
| 542 throw new UnsupportedError( |
| 543 "only top-level functions can be spawned."); |
| 544 } |
| 545 return spawn(name, null, true); |
| 546 } |
| 547 |
521 // TODO(sigmund): clean up above, after we make the new API the default: | 548 // TODO(sigmund): clean up above, after we make the new API the default: |
522 | 549 |
523 static SendPort spawn(String functionName, String uri, bool isLight) { | 550 static spawn(String functionName, String uri, bool isLight) { |
524 Completer<SendPort> completer = new Completer<SendPort>(); | 551 Completer<SendPort> completer = new Completer<SendPort>(); |
525 ReceivePort port = new ReceivePort(); | 552 ReceivePort port = new ReceivePort(); |
526 port.receive((msg, SendPort replyPort) { | 553 port.receive((msg, SendPort replyPort) { |
527 port.close(); | 554 port.close(); |
528 assert(msg == _SPAWNED_SIGNAL); | 555 assert(msg == _SPAWNED_SIGNAL); |
529 completer.complete(replyPort); | 556 completer.complete(replyPort); |
530 }); | 557 }); |
531 | 558 |
532 SendPort signalReply = port.toSendPort(); | 559 SendPort signalReply = port.toSendPort(); |
533 | 560 |
(...skipping 24 matching lines...) Expand all Loading... |
558 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. | 585 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. |
559 if (uri != null) throw new UnsupportedError( | 586 if (uri != null) throw new UnsupportedError( |
560 "Currently spawnUri is not supported without web workers."); | 587 "Currently spawnUri is not supported without web workers."); |
561 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { | 588 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() { |
562 final func = _getJSFunctionFromName(functionName); | 589 final func = _getJSFunctionFromName(functionName); |
563 _startIsolate(func, replyPort); | 590 _startIsolate(func, replyPort); |
564 }, 'nonworker start'); | 591 }, 'nonworker start'); |
565 } | 592 } |
566 | 593 |
567 static void _startIsolate(Function topLevel, SendPort replyTo) { | 594 static void _startIsolate(Function topLevel, SendPort replyTo) { |
568 _fillStatics(_globalState.currentContext); | |
569 lazyPort = new ReceivePort(); | 595 lazyPort = new ReceivePort(); |
570 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); | 596 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); |
571 | |
572 topLevel(); | 597 topLevel(); |
573 } | 598 } |
574 | 599 |
575 /** | 600 /** |
576 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor | 601 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor |
577 * name for the isolate entry point class. | 602 * name for the isolate entry point class. |
578 */ | 603 */ |
579 static void _spawnWorker(functionName, uri, replyPort) { | 604 static void _spawnWorker(functionName, uri, replyPort) { |
580 if (functionName == null) functionName = 'main'; | 605 if (functionName == null) functionName = 'main'; |
581 if (uri == null) uri = _thisScript; | 606 if (uri == null) uri = thisScript; |
582 final worker = _newWorker(uri); | 607 final worker = _newWorker(uri); |
583 worker.onmessage = (e) { _processWorkerMessage(worker, e); }; | 608 worker.onmessage = JS('', |
| 609 'function(e) { #(#, e); }', |
| 610 DART_CLOSURE_TO_JS(_processWorkerMessage), |
| 611 worker); |
584 var workerId = _globalState.nextManagerId++; | 612 var workerId = _globalState.nextManagerId++; |
585 // We also store the id on the worker itself so that we can unregister it. | 613 // We also store the id on the worker itself so that we can unregister it. |
586 worker.id = workerId; | 614 worker.id = workerId; |
587 _globalState.managers[workerId] = worker; | 615 _globalState.managers[workerId] = worker; |
588 worker.postMessage(_serializeMessage({ | 616 worker.postMessage(_serializeMessage({ |
589 'command': 'start', | 617 'command': 'start', |
590 'id': workerId, | 618 'id': workerId, |
591 // Note: we serialize replyPort twice because the child worker needs to | 619 // Note: we serialize replyPort twice because the child worker needs to |
592 // first deserialize the worker id, before it can correctly deserialize | 620 // first deserialize the worker id, before it can correctly deserialize |
593 // the port (port deserialization is sensitive to what is the current | 621 // the port (port deserialization is sensitive to what is the current |
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
939 SendPort deserializeSendPort(List x) { | 967 SendPort deserializeSendPort(List x) { |
940 int managerId = x[1]; | 968 int managerId = x[1]; |
941 int isolateId = x[2]; | 969 int isolateId = x[2]; |
942 int receivePortId = x[3]; | 970 int receivePortId = x[3]; |
943 // If two isolates are in the same manager, we use NativeJsSendPorts to | 971 // If two isolates are in the same manager, we use NativeJsSendPorts to |
944 // deliver messages directly without using postMessage. | 972 // deliver messages directly without using postMessage. |
945 if (managerId == _globalState.currentManagerId) { | 973 if (managerId == _globalState.currentManagerId) { |
946 var isolate = _globalState.isolates[isolateId]; | 974 var isolate = _globalState.isolates[isolateId]; |
947 if (isolate == null) return null; // Isolate has been closed. | 975 if (isolate == null) return null; // Isolate has been closed. |
948 var receivePort = isolate.lookup(receivePortId); | 976 var receivePort = isolate.lookup(receivePortId); |
| 977 if (receivePort == null) return null; // Port has been closed. |
949 return new _NativeJsSendPort(receivePort, isolateId); | 978 return new _NativeJsSendPort(receivePort, isolateId); |
950 } else { | 979 } else { |
951 return new _WorkerSendPort(managerId, isolateId, receivePortId); | 980 return new _WorkerSendPort(managerId, isolateId, receivePortId); |
952 } | 981 } |
953 } | 982 } |
954 | 983 |
955 } | 984 } |
956 | 985 |
957 class _JsVisitedMap implements _MessageTraverserVisitedMap { | 986 class _JsVisitedMap implements _MessageTraverserVisitedMap { |
958 List tagged; | 987 List tagged; |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1240 void cancel() { | 1269 void cancel() { |
1241 if (_once) { | 1270 if (_once) { |
1242 JS('void', '#.clearTimeout(#)', globalThis, _handle); | 1271 JS('void', '#.clearTimeout(#)', globalThis, _handle); |
1243 } else { | 1272 } else { |
1244 JS('void', '#.clearInterval(#)', globalThis, _handle); | 1273 JS('void', '#.clearInterval(#)', globalThis, _handle); |
1245 } | 1274 } |
1246 } | 1275 } |
1247 } | 1276 } |
1248 | 1277 |
1249 bool hasTimer() => JS('', '#.setTimeout', globalThis) != null; | 1278 bool hasTimer() => JS('', '#.setTimeout', globalThis) != null; |
OLD | NEW |