Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(454)

Side by Side Diff: frog/lib/isolate.js

Issue 8577003: Convert most of the javascript isolate code into Dart, inject JS code only when (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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() {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698