| 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 |