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