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 |