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