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