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