Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
|
jimhug
2011/11/16 18:00:45
Yay!
| |
| 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. | |
| 4 | |
| 5 var isolate$current = null; | |
| 6 var isolate$rootIsolate = null; // Will only be set in the main worker. | |
| 7 var isolate$inits = []; | |
| 8 var isolate$globalThis = this; | |
| 9 | |
| 10 var isolate$inWorker = | |
| 11 (typeof isolate$globalThis['importScripts']) != "undefined"; | |
| 12 var isolate$supportsWorkers = | |
| 13 isolate$inWorker || ((typeof isolate$globalThis['Worker']) != 'undefined'); | |
| 14 | |
| 15 var isolate$MAIN_WORKER_ID = 0; | |
| 16 // Non-main workers will update the id variable. | |
| 17 var isolate$thisWorkerId = isolate$MAIN_WORKER_ID; | |
| 18 | |
| 19 // Whether to use web workers when implementing isolates. | |
| 20 var isolate$useWorkers = isolate$supportsWorkers; | |
| 21 // Uncomment this to not use web workers even if they're available. | |
| 22 // isolate$useWorkers = false; | |
| 23 | |
| 24 // Whether to use the web-worker JSON-based message serialization protocol, | |
| 25 // even if not using web workers. | |
| 26 var isolate$useWorkerSerializationProtocol = false; | |
| 27 // Uncomment this to always use the web-worker JSON-based message | |
| 28 // serialization protocol, e.g. for testing purposes. | |
| 29 // isolate$useWorkerSerializationProtocol = true; | |
| 30 | |
| 31 | |
| 32 // ------- SendPort ------- | |
| 33 | |
| 34 function isolate$receiveMessage(port, isolate, | |
| 35 serializedMessage, serializedReplyTo) { | |
| 36 isolate$IsolateEvent.enqueue(isolate, function() { | |
| 37 var message = isolate$deserializeMessage(serializedMessage); | |
| 38 var replyTo = isolate$deserializeMessage(serializedReplyTo); | |
| 39 if (port._callback) port._callback(message, replyTo); | |
| 40 }); | |
| 41 } | |
| 42 | |
| 43 // -------- Registry --------- | |
| 44 function isolate$Registry() { | |
| 45 this.map = {}; | |
| 46 this.count = 0; | |
| 47 } | |
| 48 | |
| 49 isolate$Registry.prototype.register = function(id, val) { | |
| 50 if (this.map[id]) { | |
| 51 throw Error("Registry: Elements must be registered only once."); | |
| 52 } | |
| 53 this.map[id] = val; | |
| 54 this.count++; | |
| 55 }; | |
| 56 | |
| 57 isolate$Registry.prototype.unregister = function(id) { | |
| 58 if (id in this.map) { | |
| 59 delete this.map[id]; | |
| 60 this.count--; | |
| 61 } | |
| 62 }; | |
| 63 | |
| 64 isolate$Registry.prototype.get = function(id) { | |
| 65 return this.map[id]; | |
| 66 }; | |
| 67 | |
| 68 isolate$Registry.prototype.isEmpty = function() { | |
| 69 return this.count === 0; | |
| 70 }; | |
| 71 | |
| 72 | |
| 73 // ------- Worker registry ------- | |
| 74 // Only used in the main worker. | |
| 75 var isolate$workerRegistry = new isolate$Registry(); | |
| 76 | |
| 77 // ------- Isolate registry ------- | |
| 78 // Isolates must be registered if, and only if, receive ports are alive. | |
| 79 // Normally no open receive-ports means that the isolate is dead, but | |
| 80 // DOM callbacks could resurrect it. | |
| 81 var isolate$isolateRegistry = new isolate$Registry(); | |
| 82 | |
| 83 // ------- Debugging log function ------- | |
| 84 function isolate$log(msg) { | |
| 85 return; | |
| 86 if (isolate$inWorker) { | |
| 87 isolate$mainWorker.postMessage({ command: 'log', msg: msg }); | |
| 88 } else { | |
| 89 try { | |
| 90 isolate$globalThis.console.log(msg); | |
| 91 } catch(e) { | |
| 92 throw String(e.stack); | |
| 93 } | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 function isolate$initializeWorker(workerId) { | |
| 98 isolate$thisWorkerId = workerId; | |
| 99 } | |
| 100 | |
| 101 var isolate$workerPrint = false; | |
| 102 if (isolate$inWorker) { | |
| 103 isolate$workerPrint = function(msg){ | |
| 104 isolate$mainWorker.postMessage({ command: 'print', msg: msg }); | |
| 105 } | |
| 106 } | |
| 107 | |
| 108 // ------- Message handler ------- | |
| 109 function isolate$processWorkerMessage(sender, e) { | |
| 110 var msg = e.data; | |
| 111 switch (msg.command) { | |
| 112 case 'start': | |
| 113 isolate$log("starting worker: " + msg.id + " " + msg.factoryName); | |
| 114 isolate$initializeWorker(msg.id); | |
| 115 var runnerObject = new (isolate$globalThis[msg.factoryName])(); | |
| 116 var serializedReplyTo = msg.replyTo; | |
| 117 isolate$IsolateEvent.enqueue(new isolate$Isolate(), function() { | |
| 118 var replyTo = isolate$deserializeMessage(serializedReplyTo); | |
| 119 _IsolateJsUtil._startIsolate(runnerObject, replyTo); | |
| 120 }); | |
| 121 isolate$runEventLoop(); | |
| 122 break; | |
| 123 case 'spawn-worker': | |
| 124 isolate$spawnWorker(msg.factoryName, msg.replyPort); | |
| 125 break; | |
| 126 case 'message': | |
| 127 IsolateNatives.sendMessage( | |
| 128 msg.workerId, msg.isolateId, msg.portId, msg.msg, msg.replyTo); | |
| 129 isolate$runEventLoop(); | |
| 130 break; | |
| 131 case 'close': | |
| 132 isolate$log("Closing Worker"); | |
| 133 isolate$workerRegistry.unregister(sender.id); | |
| 134 sender.terminate(); | |
| 135 isolate$runEventLoop(); | |
| 136 break; | |
| 137 case 'log': | |
| 138 isolate$log(msg.msg); | |
| 139 break; | |
| 140 case 'print': | |
| 141 _IsolateJsUtil._print(msg.msg); | |
| 142 break; | |
| 143 case 'error': | |
| 144 throw msg.msg; | |
| 145 break; | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 | |
| 150 if (isolate$supportsWorkers) { | |
| 151 isolate$globalThis.onmessage = function(e) { | |
| 152 isolate$processWorkerMessage(isolate$mainWorker, e); | |
| 153 }; | |
| 154 } | |
| 155 | |
| 156 // ------- Default Worker ------- | |
| 157 function isolate$MainWorker() { | |
| 158 this.id = isolate$MAIN_WORKER_ID; | |
| 159 } | |
| 160 | |
| 161 var isolate$mainWorker = new isolate$MainWorker(); | |
| 162 isolate$mainWorker.postMessage = function(msg) { | |
| 163 isolate$globalThis.postMessage(msg); | |
| 164 }; | |
| 165 | |
| 166 var isolate$nextFreeIsolateId = 1; | |
| 167 | |
| 168 // Native methods for isolate functionality. | |
| 169 /** | |
| 170 * @constructor | |
| 171 */ | |
| 172 function isolate$Isolate() { | |
| 173 // The isolate ids is only unique within the current worker and frame. | |
| 174 this.id = isolate$nextFreeIsolateId++; | |
| 175 // When storing information on DOM nodes the isolate's id is not enough. | |
| 176 // We instead use a token with a hashcode. The token can be stored in the | |
| 177 // DOM node (since it is small and will not keep much data alive). | |
| 178 this.token = new Object(); | |
| 179 this.token.hashCode = (Math.random() * 0xFFFFFFF) >>> 0; | |
| 180 this.receivePorts = new isolate$Registry(); | |
| 181 this.run(function() { | |
| 182 // The Dart-to-JavaScript compiler builds a list of functions that | |
| 183 // need to run for each isolate to setup the state of static | |
| 184 // variables. Run through the list and execute each function. | |
| 185 for (var i = 0, len = isolate$inits.length; i < len; i++) { | |
| 186 isolate$inits[i](); | |
| 187 } | |
| 188 }); | |
| 189 } | |
| 190 | |
| 191 // It is allowed to stack 'run' calls. The stacked isolates can be different. | |
| 192 // That is Isolate1.run could call the DOM which then calls Isolate2.run. | |
| 193 isolate$Isolate.prototype.run = function(code) { | |
| 194 var old = isolate$current; | |
| 195 isolate$current = this; | |
| 196 var result = null; | |
| 197 try { | |
| 198 result = code(); | |
| 199 } finally { | |
| 200 isolate$current = old; | |
| 201 } | |
| 202 return result; | |
| 203 }; | |
| 204 | |
| 205 isolate$Isolate.prototype.registerReceivePort = function(id, port) { | |
| 206 if (this.receivePorts.isEmpty()) { | |
| 207 isolate$isolateRegistry.register(this.id, this); | |
| 208 } | |
| 209 this.receivePorts.register(id, port); | |
| 210 }; | |
| 211 | |
| 212 isolate$Isolate.prototype.unregisterReceivePort = function(id) { | |
| 213 this.receivePorts.unregister(id); | |
| 214 if (this.receivePorts.isEmpty()) { | |
| 215 isolate$isolateRegistry.unregister(this.id); | |
| 216 } | |
| 217 }; | |
| 218 | |
| 219 isolate$Isolate.prototype.getReceivePortForId = function(id) { | |
| 220 return this.receivePorts.get(id); | |
| 221 }; | |
| 222 | |
| 223 var isolate$events = []; | |
| 224 | |
| 225 /** | |
| 226 * @constructor | |
| 227 */ | |
| 228 function isolate$IsolateEvent(isolate, fn) { | |
| 229 this.isolate = isolate; | |
| 230 this.fn = fn; | |
| 231 } | |
| 232 | |
| 233 isolate$IsolateEvent.prototype.process = function() { | |
| 234 this.isolate.run(this.fn); | |
| 235 }; | |
| 236 | |
| 237 isolate$IsolateEvent.enqueue = function(isolate, fn) { | |
| 238 isolate$events.push(new isolate$IsolateEvent(isolate, fn)); | |
| 239 }; | |
| 240 | |
| 241 | |
| 242 isolate$IsolateEvent.dequeue = function() { | |
| 243 if (isolate$events.length == 0) return null; | |
| 244 var result = isolate$events[0]; | |
| 245 isolate$events.splice(0, 1); | |
| 246 return result; | |
| 247 }; | |
| 248 | |
| 249 function IsolateNatives() {} | |
| 250 | |
| 251 IsolateNatives.sendMessage = function (workerId, isolateId, receivePortId, | |
| 252 message, replyTo) { | |
| 253 // Both, the message and the replyTo are already serialized. | |
| 254 if (workerId == isolate$thisWorkerId) { | |
| 255 var isolate = isolate$isolateRegistry.get(isolateId); | |
| 256 if (!isolate) return; // Isolate has been closed. | |
| 257 var receivePort = isolate.getReceivePortForId(receivePortId); | |
| 258 if (!receivePort) return; // ReceivePort has been closed. | |
| 259 isolate$receiveMessage(receivePort, isolate, message, replyTo); | |
| 260 } else { | |
| 261 var worker; | |
| 262 if (isolate$inWorker) { | |
| 263 worker = isolate$mainWorker; | |
| 264 } else { | |
| 265 worker = isolate$workerRegistry.get(workerId); | |
| 266 } | |
| 267 worker.postMessage({ command: 'message', | |
| 268 workerId: workerId, | |
| 269 isolateId: isolateId, | |
| 270 portId: receivePortId, | |
| 271 msg: message, | |
| 272 replyTo: replyTo }); | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 // Wrap a 0-arg dom-callback to bind it with the current isolate: | |
| 277 function $wrap_call$0(fn) { return fn && fn.wrap$call$0(); } | |
| 278 Function.prototype.wrap$call$0 = function() { | |
| 279 var isolate = isolate$current; | |
| 280 var self = this; | |
| 281 this.wrap$0 = function() { | |
| 282 isolate.run(function() { | |
| 283 self(); | |
| 284 }); | |
| 285 isolate$runEventLoop(); | |
| 286 }; | |
| 287 this.wrap$call$0 = function() { return this.wrap$0; }; | |
| 288 return this.wrap$0; | |
| 289 } | |
| 290 | |
| 291 // Wrap a 1-arg dom-callback to bind it with the current isolate: | |
| 292 function $wrap_call$1(fn) { return fn && fn.wrap$call$1(); } | |
| 293 Function.prototype.wrap$call$1 = function() { | |
| 294 var isolate = isolate$current; | |
| 295 var self = this; | |
| 296 this.wrap$1 = function(arg) { | |
| 297 isolate.run(function() { | |
| 298 self(arg); | |
| 299 }); | |
| 300 isolate$runEventLoop(); | |
| 301 }; | |
| 302 this.wrap$call$1 = function() { return this.wrap$1; }; | |
| 303 return this.wrap$1; | |
| 304 } | |
| 305 | |
| 306 IsolateNatives._spawn = function(runnable, light, replyPort) { | |
| 307 // TODO(floitsch): throw exception if runnable's class doesn't have a | |
| 308 // default constructor. | |
| 309 if (isolate$useWorkers && !light) { | |
| 310 isolate$startWorker(runnable, replyPort); | |
| 311 } else { | |
| 312 isolate$startNonWorker(runnable, replyPort); | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 IsolateNatives.get$shouldSerialize = function() { | |
| 317 return isolate$useWorkers || isolate$useWorkerSerializationProtocol; | |
| 318 } | |
| 319 | |
| 320 IsolateNatives.registerPort = function(id, port) { | |
| 321 isolate$current.registerReceivePort(id, port); | |
| 322 } | |
| 323 | |
| 324 IsolateNatives.unregisterPort = function(id) { | |
| 325 isolate$current.unregisterReceivePort(id); | |
| 326 } | |
| 327 | |
| 328 IsolateNatives._currentWorkerId = function() { | |
| 329 return isolate$thisWorkerId; | |
| 330 } | |
| 331 | |
| 332 IsolateNatives._currentIsolateId = function() { | |
| 333 return isolate$current.id; | |
| 334 } | |
| 335 | |
| 336 function isolate$startNonWorker(runnable, replyTo) { | |
| 337 // Spawn a new isolate and create the receive port in it. | |
| 338 var spawned = new isolate$Isolate(); | |
| 339 | |
| 340 // Instead of just running the provided runnable, we create a | |
| 341 // new cloned instance of it with a fresh state in the spawned | |
| 342 // isolate. This way, we do not get cross-isolate references | |
| 343 // through the runnable. | |
| 344 var ctor = runnable.constructor; | |
| 345 isolate$IsolateEvent.enqueue(spawned, function() { | |
| 346 _IsolateJsUtil._startIsolate(new ctor(), replyTo); | |
| 347 }); | |
| 348 } | |
| 349 | |
| 350 // This field is only used by the main worker. | |
| 351 var isolate$nextFreeWorkerId = isolate$thisWorkerId + 1; | |
| 352 | |
| 353 var isolate$thisScript = function() { | |
| 354 if (!isolate$supportsWorkers || isolate$inWorker) return null; | |
| 355 | |
| 356 // TODO(5334778): Find a cross-platform non-brittle way of getting the | |
| 357 // currently running script. | |
| 358 var scripts = document.getElementsByTagName('script'); | |
| 359 // The scripts variable only contains the scripts that have already been | |
| 360 // executed. The last one is the currently running script. | |
| 361 var script = scripts[scripts.length - 1]; | |
| 362 var src = script.src; | |
| 363 if (!src) { | |
| 364 // TODO() | |
| 365 src = "FIXME:5407062" + "_" + Math.random().toString(); | |
| 366 script.src = src; | |
| 367 } | |
| 368 return src; | |
| 369 }(); | |
| 370 | |
| 371 function isolate$startWorker(runnable, replyPort) { | |
| 372 // TODO(sigmund): make this browser independent | |
| 373 var factoryName = runnable.constructor.name; | |
| 374 var serializedReplyPort = isolate$serializeMessage(replyPort); | |
| 375 if (isolate$inWorker) { | |
| 376 isolate$mainWorker.postMessage({ command: 'spawn-worker', | |
| 377 factoryName: factoryName, | |
| 378 replyPort: serializedReplyPort } ); | |
| 379 } else { | |
| 380 isolate$spawnWorker(factoryName, serializedReplyPort); | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 function isolate$spawnWorker(factoryName, serializedReplyPort) { | |
| 385 var worker = new Worker(isolate$thisScript); | |
| 386 worker.onmessage = function(e) { | |
| 387 isolate$processWorkerMessage(worker, e); | |
| 388 }; | |
| 389 var workerId = isolate$nextFreeWorkerId++; | |
| 390 // We also store the id on the worker itself so that we can unregister it. | |
| 391 worker.id = workerId; | |
| 392 isolate$workerRegistry.register(workerId, worker); | |
| 393 worker.postMessage({ command: 'start', | |
| 394 id: workerId, | |
| 395 replyTo: serializedReplyPort, | |
| 396 factoryName: factoryName }); | |
| 397 } | |
| 398 | |
| 399 function isolate$closeWorkerIfNecessary() { | |
| 400 if (!isolate$isolateRegistry.isEmpty()) return; | |
| 401 isolate$mainWorker.postMessage( { command: 'close' } ); | |
| 402 } | |
| 403 | |
| 404 function isolate$doOneEventLoopIteration() { | |
| 405 var CONTINUE_LOOP = true; | |
| 406 var STOP_LOOP = false; | |
| 407 var event = isolate$IsolateEvent.dequeue(); | |
| 408 if (!event) { | |
| 409 if (isolate$inWorker) { | |
| 410 isolate$closeWorkerIfNecessary(); | |
| 411 } else if (!isolate$isolateRegistry.isEmpty() && | |
| 412 isolate$workerRegistry.isEmpty() && | |
| 413 !isolate$supportsWorkers && (typeof(window) == 'undefined')) { | |
| 414 // This should only trigger when running on the command-line. | |
| 415 // We don't want this check to execute in the browser where the isolate | |
| 416 // might still be alive due to DOM callbacks. | |
| 417 // throw Error("Program exited with open ReceivePorts."); | |
| 418 } | |
| 419 return STOP_LOOP; | |
| 420 } else { | |
| 421 event.process(); | |
| 422 return CONTINUE_LOOP; | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 function isolate$doRunEventLoop() { | |
| 427 if (typeof window != 'undefined' && window.setTimeout) { | |
| 428 (function next() { | |
| 429 var continueLoop = isolate$doOneEventLoopIteration(); | |
| 430 if (!continueLoop) return; | |
| 431 // TODO(kasperl): It might turn out to be too expensive to call | |
| 432 // setTimeout for every single event. This needs more investigation. | |
| 433 window.setTimeout(next, 0); | |
| 434 })(); | |
| 435 } else { | |
| 436 while (true) { | |
| 437 var continueLoop = isolate$doOneEventLoopIteration(); | |
| 438 if (!continueLoop) break; | |
| 439 } | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 function isolate$runEventLoop() { | |
| 444 if (!isolate$inWorker) { | |
| 445 isolate$doRunEventLoop(); | |
| 446 } else { | |
| 447 try { | |
| 448 isolate$doRunEventLoop(); | |
| 449 } catch(e) { | |
| 450 // TODO(floitsch): try to send stack-trace to the other side. | |
| 451 isolate$mainWorker.postMessage({ command: 'error', msg: "" + e }); | |
| 452 } | |
| 453 } | |
| 454 } | |
| 455 | |
| 456 function RunEntry(entry, args) { | |
| 457 // Don't start the main loop again, if we are in a worker. | |
| 458 if (isolate$inWorker) return; | |
| 459 var isolate = new isolate$Isolate(); | |
| 460 isolate$rootIsolate = isolate; | |
| 461 isolate$IsolateEvent.enqueue(isolate, function() { | |
| 462 entry(args); | |
| 463 }); | |
| 464 isolate$runEventLoop(); | |
| 465 | |
| 466 // BUG(5151491): This should not be necessary, but because closures | |
| 467 // passed to the DOM as event handlers do not bind their isolate | |
| 468 // automatically we try to give them a reasonable context to live in | |
| 469 // by having a "default" isolate (the first one created). | |
| 470 isolate$current = isolate; | |
| 471 } | |
| 472 | |
| 473 // ------- Message Serializing and Deserializing ------- | |
| 474 | |
| 475 function isolate$serializeMessage(message) { | |
| 476 if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) { | |
| 477 return _IsolateJsUtil._serializeObject(message); | |
| 478 } else { | |
| 479 return _IsolateJsUtil._copyObject(message); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 function isolate$deserializeMessage(message_) { | |
| 484 if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) { | |
| 485 return _IsolateJsUtil._deserializeMessage(message_); | |
| 486 } else { | |
| 487 // Nothing more to do. | |
| 488 return message_; | |
| 489 } | |
| 490 } | |
| 491 | |
| 492 function _IsolateJsUtil() {} | |
| OLD | NEW |