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

Side by Side Diff: dart/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart

Issue 11615023: Version 0.2.9.7 (Closed) Base URL: http://dart.googlecode.com/svn/trunk/
Patch Set: Created 8 years 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) 2012, 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 library _isolate_helper;
6
7 import 'dart:isolate';
8 import 'dart:uri';
9
10 /**
11 * Called by the compiler to support switching
12 * between isolates when we get a callback from the DOM.
13 */
14 void _callInIsolate(_IsolateContext isolate, Function function) {
15 isolate.eval(function);
16 _globalState.topEventLoop.run();
17 }
18
19 /**
20 * Called by the compiler to fetch the current isolate context.
21 */
22 _IsolateContext _currentIsolate() => _globalState.currentContext;
23
24 /********************************************************
25 Inserted from lib/isolate/dart2js/compiler_hooks.dart
26 ********************************************************/
27
28 /**
29 * Wrapper that takes the dart entry point and runs it within an isolate. The
30 * dart2js compiler will inject a call of the form
31 * [: startRootIsolate(main); :] when it determines that this wrapping
32 * is needed. For single-isolate applications (e.g. hello world), this
33 * call is not emitted.
34 */
35 void startRootIsolate(entry) {
36 _globalState = new _Manager();
37
38 // Don't start the main loop again, if we are in a worker.
39 if (_globalState.isWorker) return;
40 final rootContext = new _IsolateContext();
41 _globalState.rootContext = rootContext;
42 _fillStatics(rootContext);
43
44 // BUG(5151491): Setting currentContext should not be necessary, but
45 // because closures passed to the DOM as event handlers do not bind their
46 // isolate automatically we try to give them a reasonable context to live in
47 // by having a "default" isolate (the first one created).
48 _globalState.currentContext = rootContext;
49
50 rootContext.eval(entry);
51 _globalState.topEventLoop.run();
52 }
53
54 /********************************************************
55 Inserted from lib/isolate/dart2js/isolateimpl.dart
56 ********************************************************/
57
58 /**
59 * Concepts used here:
60 *
61 * "manager" - A manager contains one or more isolates, schedules their
62 * execution, and performs other plumbing on their behalf. The isolate
63 * present at the creation of the manager is designated as its "root isolate".
64 * A manager may, for example, be implemented on a web Worker.
65 *
66 * [_Manager] - State present within a manager (exactly once, as a global).
67 *
68 * [_ManagerStub] - A handle held within one manager that allows interaction
69 * with another manager. A target manager may be addressed by zero or more
70 * [_ManagerStub]s.
71 *
72 */
73
74 /**
75 * A native object that is shared across isolates. This object is visible to all
76 * isolates running under the same manager (either UI or background web worker).
77 *
78 * This is code that is intended to 'escape' the isolate boundaries in order to
79 * implement the semantics of isolates in JavaScript. Without this we would have
80 * been forced to implement more code (including the top-level event loop) in
81 * JavaScript itself.
82 */
83 // TODO(eub, sigmund): move the "manager" to be entirely in JS.
84 // Running any Dart code outside the context of an isolate gives it
85 // the change to break the isolate abstraction.
86 _Manager get _globalState => JS("_Manager", r"$globalState");
87 set _globalState(_Manager val) {
88 JS("void", r"$globalState = #", val);
89 }
90
91 void _fillStatics(context) {
92 JS("void", r"$globals = #.isolateStatics", context);
93 JS("void", r"$static_init()");
94 }
95
96 ReceivePort lazyPort;
97
98 /** State associated with the current manager. See [globalState]. */
99 // TODO(sigmund): split in multiple classes: global, thread, main-worker states?
100 class _Manager {
101
102 /** Next available isolate id within this [_Manager]. */
103 int nextIsolateId = 0;
104
105 /** id assigned to this [_Manager]. */
106 int currentManagerId = 0;
107
108 /**
109 * Next available manager id. Only used by the main manager to assign a unique
110 * id to each manager created by it.
111 */
112 int nextManagerId = 1;
113
114 /** Context for the currently running [Isolate]. */
115 _IsolateContext currentContext = null;
116
117 /** Context for the root [Isolate] that first run in this [_Manager]. */
118 _IsolateContext rootContext = null;
119
120 /** The top-level event loop. */
121 _EventLoop topEventLoop;
122
123 /** Whether this program is running from the command line. */
124 bool fromCommandLine;
125
126 /** Whether this [_Manager] is running as a web worker. */
127 bool isWorker;
128
129 /** Whether we support spawning web workers. */
130 bool supportsWorkers;
131
132 /**
133 * Whether to use web workers when implementing isolates. Set to false for
134 * debugging/testing.
135 */
136 bool get useWorkers => supportsWorkers;
137
138 /**
139 * Whether to use the web-worker JSON-based message serialization protocol. By
140 * default this is only used with web workers. For debugging, you can force
141 * using this protocol by changing this field value to [true].
142 */
143 bool get needSerialization => useWorkers;
144
145 /**
146 * Registry of isolates. Isolates must be registered if, and only if, receive
147 * ports are alive. Normally no open receive-ports means that the isolate is
148 * dead, but DOM callbacks could resurrect it.
149 */
150 Map<int, _IsolateContext> isolates;
151
152 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */
153 _ManagerStub mainManager;
154
155 /** Registry of active [_ManagerStub]s. Only used in the main [_Manager]. */
156 Map<int, _ManagerStub> managers;
157
158 _Manager() {
159 _nativeDetectEnvironment();
160 topEventLoop = new _EventLoop();
161 isolates = new Map<int, _IsolateContext>();
162 managers = new Map<int, _ManagerStub>();
163 if (isWorker) { // "if we are not the main manager ourself" is the intent.
164 mainManager = new _MainManagerStub();
165 _nativeInitWorkerMessageHandler();
166 }
167 }
168
169 void _nativeDetectEnvironment() {
170 isWorker = JS("bool", r"$isWorker");
171 supportsWorkers = JS("bool", r"$supportsWorkers");
172 fromCommandLine = JS("bool", r"typeof(window) == 'undefined'");
173 }
174
175 void _nativeInitWorkerMessageHandler() {
176 JS("void", r"""
177 $globalThis.onmessage = function (e) {
178 IsolateNatives._processWorkerMessage(this.mainManager, e);
179 }""");
180 }
181 /*: TODO: check that _processWorkerMessage is not discarded while treeshaking.
182 """ {
183 IsolateNatives._processWorkerMessage(null, null);
184 }
185 */
186
187
188 /** Close the worker running this code if all isolates are done. */
189 void maybeCloseWorker() {
190 if (isolates.isEmpty) {
191 mainManager.postMessage(_serializeMessage({'command': 'close'}));
192 }
193 }
194 }
195
196 /** Context information tracked for each isolate. */
197 class _IsolateContext {
198 /** Current isolate id. */
199 int id;
200
201 /** Registry of receive ports currently active on this isolate. */
202 Map<int, ReceivePort> ports;
203
204 /** Holds isolate globals (statics and top-level properties). */
205 var isolateStatics; // native object containing all globals of an isolate.
206
207 _IsolateContext() {
208 id = _globalState.nextIsolateId++;
209 ports = new Map<int, ReceivePort>();
210 initGlobals();
211 }
212
213 // these are filled lazily the first time the isolate starts running.
214 void initGlobals() { JS("void", r'$initGlobals(#)', this); }
215
216 /**
217 * Run [code] in the context of the isolate represented by [this]. Note this
218 * is called from JavaScript (see $wrap_call in corejs.dart).
219 */
220 dynamic eval(Function code) {
221 var old = _globalState.currentContext;
222 _globalState.currentContext = this;
223 this._setGlobals();
224 var result = null;
225 try {
226 result = code();
227 } finally {
228 _globalState.currentContext = old;
229 if (old != null) old._setGlobals();
230 }
231 return result;
232 }
233
234 void _setGlobals() { JS("void", r'$setGlobals(#)', this); }
235
236 /** Lookup a port registered for this isolate. */
237 ReceivePort lookup(int portId) => ports[portId];
238
239 /** Register a port on this isolate. */
240 void register(int portId, ReceivePort port) {
241 if (ports.containsKey(portId)) {
242 throw new Exception("Registry: ports must be registered only once.");
243 }
244 ports[portId] = port;
245 _globalState.isolates[id] = this; // indicate this isolate is active
246 }
247
248 /** Unregister a port on this isolate. */
249 void unregister(int portId) {
250 ports.remove(portId);
251 if (ports.isEmpty) {
252 _globalState.isolates.remove(id); // indicate this isolate is not active
253 }
254 }
255 }
256
257 /** Represent the event loop on a javascript thread (DOM or worker). */
258 class _EventLoop {
259 Queue<_IsolateEvent> events;
260
261 _EventLoop() : events = new Queue<_IsolateEvent>();
262
263 void enqueue(isolate, fn, msg) {
264 events.addLast(new _IsolateEvent(isolate, fn, msg));
265 }
266
267 _IsolateEvent dequeue() {
268 if (events.isEmpty) return null;
269 return events.removeFirst();
270 }
271
272 /** Process a single event, if any. */
273 bool runIteration() {
274 final event = dequeue();
275 if (event == null) {
276 if (_globalState.isWorker) {
277 _globalState.maybeCloseWorker();
278 } else if (_globalState.rootContext != null &&
279 _globalState.isolates.containsKey(
280 _globalState.rootContext.id) &&
281 _globalState.fromCommandLine &&
282 _globalState.rootContext.ports.isEmpty) {
283 // We want to reach here only on the main [_Manager] and only
284 // on the command-line. In the browser the isolate might
285 // still be alive due to DOM callbacks, but the presumption is
286 // that on the command-line, no future events can be injected
287 // into the event queue once it's empty. Node has setTimeout
288 // so this presumption is incorrect there. We think(?) that
289 // in d8 this assumption is valid.
290 throw new Exception("Program exited with open ReceivePorts.");
291 }
292 return false;
293 }
294 event.process();
295 return true;
296 }
297
298 /**
299 * Runs multiple iterations of the run-loop. If possible, each iteration is
300 * run asynchronously.
301 */
302 void _runHelper() {
303 if (hasWindow()) {
304 // Run each iteration from the browser's top event loop.
305 void next() {
306 if (!runIteration()) return;
307 _window.setTimeout(next, 0);
308 }
309 next();
310 } else {
311 // Run synchronously until no more iterations are available.
312 while (runIteration()) {}
313 }
314 }
315
316 /**
317 * Call [_runHelper] but ensure that worker exceptions are propragated. Note
318 * this is called from JavaScript (see $wrap_call in corejs.dart).
319 */
320 void run() {
321 if (!_globalState.isWorker) {
322 _runHelper();
323 } else {
324 try {
325 _runHelper();
326 } catch (e, trace) {
327 _globalState.mainManager.postMessage(_serializeMessage(
328 {'command': 'error', 'msg': '$e\n$trace' }));
329 }
330 }
331 }
332 }
333
334 /** An event in the top-level event queue. */
335 class _IsolateEvent {
336 _IsolateContext isolate;
337 Function fn;
338 String message;
339
340 _IsolateEvent(this.isolate, this.fn, this.message);
341
342 void process() {
343 isolate.eval(fn);
344 }
345 }
346
347 /** An interface for a stub used to interact with a manager. */
348 abstract class _ManagerStub {
349 get id;
350 void set id(int i);
351 void set onmessage(Function f);
352 void postMessage(msg);
353 void terminate();
354 }
355
356 /** A stub for interacting with the main manager. */
357 class _MainManagerStub implements _ManagerStub {
358 get id => 0;
359 void set id(int i) { throw new UnimplementedError(); }
360 void set onmessage(f) {
361 throw new Exception("onmessage should not be set on MainManagerStub");
362 }
363 void postMessage(msg) { JS("void", r"$globalThis.postMessage(#)", msg); }
364 void terminate() {} // Nothing useful to do here.
365 }
366
367 /**
368 * A stub for interacting with a manager built on a web worker. This
369 * definition uses a 'hidden' type (* prefix on the native name) to
370 * enforce that the type is defined dynamically only when web workers
371 * are actually available.
372 */
373 // @Native("*Worker");
374 class _WorkerStub implements _ManagerStub {
375 get id => JS("var", "#.id", this);
376 void set id(i) { JS("void", "#.id = #", this, i); }
377 void set onmessage(f) { JS("void", "#.onmessage = #", this, f); }
378 void postMessage(msg) => JS("void", "#.postMessage(#)", this, msg);
379 // terminate() is implemented by Worker.
380 void terminate();
381 }
382
383 const String _SPAWNED_SIGNAL = "spawned";
384
385 class IsolateNatives {
386
387 /**
388 * The src url for the script tag that loaded this code. Used to create
389 * JavaScript workers.
390 */
391 static String get _thisScript => JS("String", r"$thisScriptUrl");
392
393 /** Starts a new worker with the given URL. */
394 static _WorkerStub _newWorker(url) => JS("_WorkerStub", r"new Worker(#)", url) ;
395
396 /**
397 * Assume that [e] is a browser message event and extract its message data.
398 * We don't import the dom explicitly so, when workers are disabled, this
399 * library can also run on top of nodejs.
400 */
401 //static _getEventData(e) => JS("Object", "#.data", e);
402 static _getEventData(e) => JS("", "#.data", e);
403
404 /**
405 * Process messages on a worker, either to control the worker instance or to
406 * pass messages along to the isolate running in the worker.
407 */
408 static void _processWorkerMessage(sender, e) {
409 var msg = _deserializeMessage(_getEventData(e));
410 switch (msg['command']) {
411 case 'start':
412 _globalState.currentManagerId = msg['id'];
413 Function entryPoint = _getJSFunctionFromName(msg['functionName']);
414 var replyTo = _deserializeMessage(msg['replyTo']);
415 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
416 _startIsolate(entryPoint, replyTo);
417 }, 'worker-start');
418 _globalState.topEventLoop.run();
419 break;
420 case 'spawn-worker':
421 _spawnWorker(msg['functionName'], msg['uri'], msg['replyPort']);
422 break;
423 case 'message':
424 msg['port'].send(msg['msg'], msg['replyTo']);
425 _globalState.topEventLoop.run();
426 break;
427 case 'close':
428 _log("Closing Worker");
429 _globalState.managers.remove(sender.id);
430 sender.terminate();
431 _globalState.topEventLoop.run();
432 break;
433 case 'log':
434 _log(msg['msg']);
435 break;
436 case 'print':
437 if (_globalState.isWorker) {
438 _globalState.mainManager.postMessage(
439 _serializeMessage({'command': 'print', 'msg': msg}));
440 } else {
441 print(msg['msg']);
442 }
443 break;
444 case 'error':
445 throw msg['msg'];
446 }
447 }
448
449 /** Log a message, forwarding to the main [_Manager] if appropriate. */
450 static _log(msg) {
451 if (_globalState.isWorker) {
452 _globalState.mainManager.postMessage(
453 _serializeMessage({'command': 'log', 'msg': msg }));
454 } else {
455 try {
456 _consoleLog(msg);
457 } catch (e, trace) {
458 throw new Exception(trace);
459 }
460 }
461 }
462
463 static void _consoleLog(msg) {
464 JS("void", r"$globalThis.console.log(#)", msg);
465 }
466
467 /**
468 * Extract the constructor of runnable, so it can be allocated in another
469 * isolate.
470 */
471 static dynamic _getJSConstructor(Isolate runnable) {
472 return JS("Object", "#.constructor", runnable);
473 }
474
475 /** Extract the constructor name of a runnable */
476 // TODO(sigmund): find a browser-generic way to support this.
477 // TODO(floitsch): is this function still used? If yes, should we use
478 // Primitives.objectTypeName instead?
479 static dynamic _getJSConstructorName(Isolate runnable) {
480 return JS("Object", "#.constructor.name", runnable);
481 }
482
483 /** Find a constructor given its name. */
484 static dynamic _getJSConstructorFromName(String factoryName) {
485 return JS("Object", r"$globalThis[#]", factoryName);
486 }
487
488 static dynamic _getJSFunctionFromName(String functionName) {
489 return JS("Object", r"$globalThis[#]", functionName);
490 }
491
492 /**
493 * Get a string name for the function, if possible. The result for
494 * anonymous functions is browser-dependent -- it may be "" or "anonymous"
495 * but you should probably not count on this.
496 */
497 static String _getJSFunctionName(Function f) {
498 return JS("Object", r"(#.$name || #)", f, null);
499 }
500
501 /** Create a new JavaScript object instance given its constructor. */
502 static dynamic _allocate(var ctor) {
503 return JS("Object", "new #()", ctor);
504 }
505
506 static SendPort spawnFunction(void topLevelFunction()) {
507 final name = _getJSFunctionName(topLevelFunction);
508 if (name == null) {
509 throw new UnsupportedError(
510 "only top-level functions can be spawned.");
511 }
512 return spawn(name, null, false);
513 }
514
515 // TODO(sigmund): clean up above, after we make the new API the default:
516
517 static SendPort spawn(String functionName, String uri, bool isLight) {
518 Completer<SendPort> completer = new Completer<SendPort>();
519 ReceivePort port = new ReceivePort();
520 port.receive((msg, SendPort replyPort) {
521 port.close();
522 assert(msg == _SPAWNED_SIGNAL);
523 completer.complete(replyPort);
524 });
525
526 SendPort signalReply = port.toSendPort();
527
528 if (_globalState.useWorkers && !isLight) {
529 _startWorker(functionName, uri, signalReply);
530 } else {
531 _startNonWorker(functionName, uri, signalReply);
532 }
533 return new _BufferingSendPort(
534 _globalState.currentContext.id, completer.future);
535 }
536
537 static SendPort _startWorker(
538 String functionName, String uri, SendPort replyPort) {
539 if (_globalState.isWorker) {
540 _globalState.mainManager.postMessage(_serializeMessage({
541 'command': 'spawn-worker',
542 'functionName': functionName,
543 'uri': uri,
544 'replyPort': replyPort}));
545 } else {
546 _spawnWorker(functionName, uri, replyPort);
547 }
548 }
549
550 static SendPort _startNonWorker(
551 String functionName, String uri, SendPort replyPort) {
552 // TODO(eub): support IE9 using an iframe -- Dart issue 1702.
553 if (uri != null) throw new UnsupportedError(
554 "Currently spawnUri is not supported without web workers.");
555 _globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
556 final func = _getJSFunctionFromName(functionName);
557 _startIsolate(func, replyPort);
558 }, 'nonworker start');
559 }
560
561 static void _startIsolate(Function topLevel, SendPort replyTo) {
562 _fillStatics(_globalState.currentContext);
563 lazyPort = new ReceivePort();
564 replyTo.send(_SPAWNED_SIGNAL, port.toSendPort());
565
566 topLevel();
567 }
568
569 /**
570 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor
571 * name for the isolate entry point class.
572 */
573 static void _spawnWorker(functionName, uri, replyPort) {
574 if (functionName == null) functionName = 'main';
575 if (uri == null) uri = _thisScript;
576 if (!(new Uri.fromString(uri).isAbsolute())) {
577 // The constructor of dom workers requires an absolute URL. If we use a
578 // relative path we will get a DOM exception.
579 String prefix = _thisScript.substring(0, _thisScript.lastIndexOf('/'));
580 uri = "$prefix/$uri";
581 }
582 final worker = _newWorker(uri);
583 worker.onmessage = (e) { _processWorkerMessage(worker, e); };
584 var workerId = _globalState.nextManagerId++;
585 // We also store the id on the worker itself so that we can unregister it.
586 worker.id = workerId;
587 _globalState.managers[workerId] = worker;
588 worker.postMessage(_serializeMessage({
589 'command': 'start',
590 'id': workerId,
591 // Note: we serialize replyPort twice because the child worker needs to
592 // first deserialize the worker id, before it can correctly deserialize
593 // the port (port deserialization is sensitive to what is the current
594 // workerId).
595 'replyTo': _serializeMessage(replyPort),
596 'functionName': functionName }));
597 }
598 }
599
600 /********************************************************
601 Inserted from lib/isolate/dart2js/ports.dart
602 ********************************************************/
603
604 /** Common functionality to all send ports. */
605 class _BaseSendPort implements SendPort {
606 /** Id for the destination isolate. */
607 final int _isolateId;
608
609 const _BaseSendPort(this._isolateId);
610
611 void _checkReplyTo(SendPort replyTo) {
612 if (replyTo != null
613 && replyTo is! _NativeJsSendPort
614 && replyTo is! _WorkerSendPort
615 && replyTo is! _BufferingSendPort) {
616 throw new Exception("SendPort.send: Illegal replyTo port type");
617 }
618 }
619
620 Future call(var message) {
621 final completer = new Completer();
622 final port = new ReceivePortImpl();
623 send(message, port.toSendPort());
624 port.receive((value, ignoreReplyTo) {
625 port.close();
626 if (value is Exception) {
627 completer.completeException(value);
628 } else {
629 completer.complete(value);
630 }
631 });
632 return completer.future;
633 }
634
635 void send(var message, [SendPort replyTo]);
636 bool operator ==(var other);
637 int get hashCode;
638 }
639
640 /** A send port that delivers messages in-memory via native JavaScript calls. */
641 class _NativeJsSendPort extends _BaseSendPort implements SendPort {
642 final ReceivePortImpl _receivePort;
643
644 const _NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId);
645
646 void send(var message, [SendPort replyTo = null]) {
647 _waitForPendingPorts([message, replyTo], () {
648 _checkReplyTo(replyTo);
649 // Check that the isolate still runs and the port is still open
650 final isolate = _globalState.isolates[_isolateId];
651 if (isolate == null) return;
652 if (_receivePort._callback == null) return;
653
654 // We force serialization/deserialization as a simple way to ensure
655 // isolate communication restrictions are respected between isolates that
656 // live in the same worker. [_NativeJsSendPort] delivers both messages
657 // from the same worker and messages from other workers. In particular,
658 // messages sent from a worker via a [_WorkerSendPort] are received at
659 // [_processWorkerMessage] and forwarded to a native port. In such cases,
660 // here we'll see [_globalState.currentContext == null].
661 final shouldSerialize = _globalState.currentContext != null
662 && _globalState.currentContext.id != _isolateId;
663 var msg = message;
664 var reply = replyTo;
665 if (shouldSerialize) {
666 msg = _serializeMessage(msg);
667 reply = _serializeMessage(reply);
668 }
669 _globalState.topEventLoop.enqueue(isolate, () {
670 if (_receivePort._callback != null) {
671 if (shouldSerialize) {
672 msg = _deserializeMessage(msg);
673 reply = _deserializeMessage(reply);
674 }
675 _receivePort._callback(msg, reply);
676 }
677 }, 'receive $message');
678 });
679 }
680
681 bool operator ==(var other) => (other is _NativeJsSendPort) &&
682 (_receivePort == other._receivePort);
683
684 int get hashCode => _receivePort._id;
685 }
686
687 /** A send port that delivers messages via worker.postMessage. */
688 // TODO(eub): abstract this for iframes.
689 class _WorkerSendPort extends _BaseSendPort implements SendPort {
690 final int _workerId;
691 final int _receivePortId;
692
693 const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId)
694 : super(isolateId);
695
696 void send(var message, [SendPort replyTo = null]) {
697 _waitForPendingPorts([message, replyTo], () {
698 _checkReplyTo(replyTo);
699 final workerMessage = _serializeMessage({
700 'command': 'message',
701 'port': this,
702 'msg': message,
703 'replyTo': replyTo});
704
705 if (_globalState.isWorker) {
706 // communication from one worker to another go through the main worker:
707 _globalState.mainManager.postMessage(workerMessage);
708 } else {
709 _globalState.managers[_workerId].postMessage(workerMessage);
710 }
711 });
712 }
713
714 bool operator ==(var other) {
715 return (other is _WorkerSendPort) &&
716 (_workerId == other._workerId) &&
717 (_isolateId == other._isolateId) &&
718 (_receivePortId == other._receivePortId);
719 }
720
721 int get hashCode {
722 // TODO(sigmund): use a standard hash when we get one available in corelib.
723 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId;
724 }
725 }
726
727 /** A port that buffers messages until an underlying port gets resolved. */
728 class _BufferingSendPort extends _BaseSendPort implements SendPort {
729 /** Internal counter to assign unique ids to each port. */
730 static int _idCount = 0;
731
732 /** For implementing equals and hashcode. */
733 final int _id;
734
735 /** Underlying port, when resolved. */
736 SendPort _port;
737
738 /**
739 * Future of the underlying port, so that we can detect when this port can be
740 * sent on messages.
741 */
742 Future<SendPort> _futurePort;
743
744 /** Pending messages (and reply ports). */
745 List pending;
746
747 _BufferingSendPort(isolateId, this._futurePort)
748 : super(isolateId), _id = _idCount, pending = [] {
749 _idCount++;
750 _futurePort.then((p) {
751 _port = p;
752 for (final item in pending) {
753 p.send(item['message'], item['replyTo']);
754 }
755 pending = null;
756 });
757 }
758
759 _BufferingSendPort.fromPort(isolateId, this._port)
760 : super(isolateId), _id = _idCount {
761 _idCount++;
762 }
763
764 void send(var message, [SendPort replyTo]) {
765 if (_port != null) {
766 _port.send(message, replyTo);
767 } else {
768 pending.add({'message': message, 'replyTo': replyTo});
769 }
770 }
771
772 bool operator ==(var other) =>
773 other is _BufferingSendPort && _id == other._id;
774 int get hashCode => _id;
775 }
776
777 /** Implementation of a multi-use [ReceivePort] on top of JavaScript. */
778 class ReceivePortImpl implements ReceivePort {
779 int _id;
780 Function _callback;
781 static int _nextFreeId = 1;
782
783 ReceivePortImpl()
784 : _id = _nextFreeId++ {
785 _globalState.currentContext.register(_id, this);
786 }
787
788 void receive(void onMessage(var message, SendPort replyTo)) {
789 _callback = onMessage;
790 }
791
792 void close() {
793 _callback = null;
794 _globalState.currentContext.unregister(_id);
795 }
796
797 SendPort toSendPort() {
798 return new _NativeJsSendPort(this, _globalState.currentContext.id);
799 }
800 }
801
802 /** Wait until all ports in a message are resolved. */
803 _waitForPendingPorts(var message, void callback()) {
804 final finder = new _PendingSendPortFinder();
805 finder.traverse(message);
806 Futures.wait(finder.ports).then((_) => callback());
807 }
808
809
810 /** Visitor that finds all unresolved [SendPort]s in a message. */
811 class _PendingSendPortFinder extends _MessageTraverser {
812 List<Future<SendPort>> ports;
813 _PendingSendPortFinder() : super(), ports = [] {
814 _visited = new _JsVisitedMap();
815 }
816
817 visitPrimitive(x) {}
818
819 visitList(List list) {
820 final seen = _visited[list];
821 if (seen != null) return;
822 _visited[list] = true;
823 // TODO(sigmund): replace with the following: (bug #1660)
824 // list.forEach(_dispatch);
825 list.forEach((e) => _dispatch(e));
826 }
827
828 visitMap(Map map) {
829 final seen = _visited[map];
830 if (seen != null) return;
831
832 _visited[map] = true;
833 // TODO(sigmund): replace with the following: (bug #1660)
834 // map.values.forEach(_dispatch);
835 map.values.forEach((e) => _dispatch(e));
836 }
837
838 visitSendPort(SendPort port) {
839 if (port is _BufferingSendPort && port._port == null) {
840 ports.add(port._futurePort);
841 }
842 }
843 }
844
845 /********************************************************
846 Inserted from lib/isolate/dart2js/messages.dart
847 ********************************************************/
848
849 // Defines message visitors, serialization, and deserialization.
850
851 /** Serialize [message] (or simulate serialization). */
852 _serializeMessage(message) {
853 if (_globalState.needSerialization) {
854 return new _JsSerializer().traverse(message);
855 } else {
856 return new _JsCopier().traverse(message);
857 }
858 }
859
860 /** Deserialize [message] (or simulate deserialization). */
861 _deserializeMessage(message) {
862 if (_globalState.needSerialization) {
863 return new _JsDeserializer().deserialize(message);
864 } else {
865 // Nothing more to do.
866 return message;
867 }
868 }
869
870 class _JsSerializer extends _Serializer {
871
872 _JsSerializer() : super() { _visited = new _JsVisitedMap(); }
873
874 visitSendPort(SendPort x) {
875 if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
876 if (x is _WorkerSendPort) return visitWorkerSendPort(x);
877 if (x is _BufferingSendPort) return visitBufferingSendPort(x);
878 throw "Illegal underlying port $x";
879 }
880
881 visitNativeJsSendPort(_NativeJsSendPort port) {
882 return ['sendport', _globalState.currentManagerId,
883 port._isolateId, port._receivePort._id];
884 }
885
886 visitWorkerSendPort(_WorkerSendPort port) {
887 return ['sendport', port._workerId, port._isolateId, port._receivePortId];
888 }
889
890 visitBufferingSendPort(_BufferingSendPort port) {
891 if (port._port != null) {
892 return visitSendPort(port._port);
893 } else {
894 // TODO(floitsch): Use real exception (which one?).
895 throw
896 "internal error: must call _waitForPendingPorts to ensure all"
897 " ports are resolved at this point.";
898 }
899 }
900
901 }
902
903
904 class _JsCopier extends _Copier {
905
906 _JsCopier() : super() { _visited = new _JsVisitedMap(); }
907
908 visitSendPort(SendPort x) {
909 if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
910 if (x is _WorkerSendPort) return visitWorkerSendPort(x);
911 if (x is _BufferingSendPort) return visitBufferingSendPort(x);
912 throw "Illegal underlying port $p";
913 }
914
915 SendPort visitNativeJsSendPort(_NativeJsSendPort port) {
916 return new _NativeJsSendPort(port._receivePort, port._isolateId);
917 }
918
919 SendPort visitWorkerSendPort(_WorkerSendPort port) {
920 return new _WorkerSendPort(
921 port._workerId, port._isolateId, port._receivePortId);
922 }
923
924 SendPort visitBufferingSendPort(_BufferingSendPort port) {
925 if (port._port != null) {
926 return visitSendPort(port._port);
927 } else {
928 // TODO(floitsch): Use real exception (which one?).
929 throw
930 "internal error: must call _waitForPendingPorts to ensure all"
931 " ports are resolved at this point.";
932 }
933 }
934
935 }
936
937 class _JsDeserializer extends _Deserializer {
938
939 SendPort deserializeSendPort(List x) {
940 int managerId = x[1];
941 int isolateId = x[2];
942 int receivePortId = x[3];
943 // If two isolates are in the same manager, we use NativeJsSendPorts to
944 // deliver messages directly without using postMessage.
945 if (managerId == _globalState.currentManagerId) {
946 var isolate = _globalState.isolates[isolateId];
947 if (isolate == null) return null; // Isolate has been closed.
948 var receivePort = isolate.lookup(receivePortId);
949 return new _NativeJsSendPort(receivePort, isolateId);
950 } else {
951 return new _WorkerSendPort(managerId, isolateId, receivePortId);
952 }
953 }
954
955 }
956
957 class _JsVisitedMap implements _MessageTraverserVisitedMap {
958 List tagged;
959
960 /** Retrieves any information stored in the native object [object]. */
961 operator[](var object) {
962 return _getAttachedInfo(object);
963 }
964
965 /** Injects some information into the native [object]. */
966 void operator[]=(var object, var info) {
967 tagged.add(object);
968 _setAttachedInfo(object, info);
969 }
970
971 /** Get ready to rumble. */
972 void reset() {
973 assert(tagged == null);
974 tagged = new List();
975 }
976
977 /** Remove all information injected in the native objects. */
978 void cleanup() {
979 for (int i = 0, length = tagged.length; i < length; i++) {
980 _clearAttachedInfo(tagged[i]);
981 }
982 tagged = null;
983 }
984
985 void _clearAttachedInfo(var o) {
986 JS("void", "#['__MessageTraverser__attached_info__'] = #", o, null);
987 }
988
989 void _setAttachedInfo(var o, var info) {
990 JS("void", "#['__MessageTraverser__attached_info__'] = #", o, info);
991 }
992
993 _getAttachedInfo(var o) {
994 return JS("", "#['__MessageTraverser__attached_info__']", o);
995 }
996 }
997
998 // only visible for testing purposes
999 // TODO(sigmund): remove once we can disable privacy for testing (bug #1882)
1000 class TestingOnly {
1001 static copy(x) {
1002 return new _JsCopier().traverse(x);
1003 }
1004
1005 // only visible for testing purposes
1006 static serialize(x) {
1007 _Serializer serializer = new _JsSerializer();
1008 _Deserializer deserializer = new _JsDeserializer();
1009 return deserializer.deserialize(serializer.traverse(x));
1010 }
1011 }
1012
1013 /********************************************************
1014 Inserted from lib/isolate/serialization.dart
1015 ********************************************************/
1016
1017 class _MessageTraverserVisitedMap {
1018
1019 operator[](var object) => null;
1020 void operator[]=(var object, var info) { }
1021
1022 void reset() { }
1023 void cleanup() { }
1024
1025 }
1026
1027 /** Abstract visitor for dart objects that can be sent as isolate messages. */
1028 class _MessageTraverser {
1029
1030 _MessageTraverserVisitedMap _visited;
1031 _MessageTraverser() : _visited = new _MessageTraverserVisitedMap();
1032
1033 /** Visitor's entry point. */
1034 traverse(var x) {
1035 if (isPrimitive(x)) return visitPrimitive(x);
1036 _visited.reset();
1037 var result;
1038 try {
1039 result = _dispatch(x);
1040 } finally {
1041 _visited.cleanup();
1042 }
1043 return result;
1044 }
1045
1046 _dispatch(var x) {
1047 if (isPrimitive(x)) return visitPrimitive(x);
1048 if (x is List) return visitList(x);
1049 if (x is Map) return visitMap(x);
1050 if (x is SendPort) return visitSendPort(x);
1051 if (x is SendPortSync) return visitSendPortSync(x);
1052
1053 // Overridable fallback.
1054 return visitObject(x);
1055 }
1056
1057 visitPrimitive(x);
1058 visitList(List x);
1059 visitMap(Map x);
1060 visitSendPort(SendPort x);
1061 visitSendPortSync(SendPortSync x);
1062
1063 visitObject(Object x) {
1064 // TODO(floitsch): make this a real exception. (which one)?
1065 throw "Message serialization: Illegal value $x passed";
1066 }
1067
1068 static bool isPrimitive(x) {
1069 return (x == null) || (x is String) || (x is num) || (x is bool);
1070 }
1071 }
1072
1073
1074 /** A visitor that recursively copies a message. */
1075 class _Copier extends _MessageTraverser {
1076
1077 visitPrimitive(x) => x;
1078
1079 List visitList(List list) {
1080 List copy = _visited[list];
1081 if (copy != null) return copy;
1082
1083 int len = list.length;
1084
1085 // TODO(floitsch): we loose the generic type of the List.
1086 copy = new List(len);
1087 _visited[list] = copy;
1088 for (int i = 0; i < len; i++) {
1089 copy[i] = _dispatch(list[i]);
1090 }
1091 return copy;
1092 }
1093
1094 Map visitMap(Map map) {
1095 Map copy = _visited[map];
1096 if (copy != null) return copy;
1097
1098 // TODO(floitsch): we loose the generic type of the map.
1099 copy = new Map();
1100 _visited[map] = copy;
1101 map.forEach((key, val) {
1102 copy[_dispatch(key)] = _dispatch(val);
1103 });
1104 return copy;
1105 }
1106
1107 }
1108
1109 /** Visitor that serializes a message as a JSON array. */
1110 class _Serializer extends _MessageTraverser {
1111 int _nextFreeRefId = 0;
1112
1113 visitPrimitive(x) => x;
1114
1115 visitList(List list) {
1116 int copyId = _visited[list];
1117 if (copyId != null) return ['ref', copyId];
1118
1119 int id = _nextFreeRefId++;
1120 _visited[list] = id;
1121 var jsArray = _serializeList(list);
1122 // TODO(floitsch): we are losing the generic type.
1123 return ['list', id, jsArray];
1124 }
1125
1126 visitMap(Map map) {
1127 int copyId = _visited[map];
1128 if (copyId != null) return ['ref', copyId];
1129
1130 int id = _nextFreeRefId++;
1131 _visited[map] = id;
1132 var keys = _serializeList(map.keys);
1133 var values = _serializeList(map.values);
1134 // TODO(floitsch): we are losing the generic type.
1135 return ['map', id, keys, values];
1136 }
1137
1138 _serializeList(List list) {
1139 int len = list.length;
1140 var result = new List(len);
1141 for (int i = 0; i < len; i++) {
1142 result[i] = _dispatch(list[i]);
1143 }
1144 return result;
1145 }
1146 }
1147
1148 /** Deserializes arrays created with [_Serializer]. */
1149 class _Deserializer {
1150 Map<int, dynamic> _deserialized;
1151
1152 _Deserializer();
1153
1154 static bool isPrimitive(x) {
1155 return (x == null) || (x is String) || (x is num) || (x is bool);
1156 }
1157
1158 deserialize(x) {
1159 if (isPrimitive(x)) return x;
1160 // TODO(floitsch): this should be new HashMap<int, var|Dynamic>()
1161 _deserialized = new HashMap();
1162 return _deserializeHelper(x);
1163 }
1164
1165 _deserializeHelper(x) {
1166 if (isPrimitive(x)) return x;
1167 assert(x is List);
1168 switch (x[0]) {
1169 case 'ref': return _deserializeRef(x);
1170 case 'list': return _deserializeList(x);
1171 case 'map': return _deserializeMap(x);
1172 case 'sendport': return deserializeSendPort(x);
1173 default: return deserializeObject(x);
1174 }
1175 }
1176
1177 _deserializeRef(List x) {
1178 int id = x[1];
1179 var result = _deserialized[id];
1180 assert(result != null);
1181 return result;
1182 }
1183
1184 List _deserializeList(List x) {
1185 int id = x[1];
1186 // We rely on the fact that Dart-lists are directly mapped to Js-arrays.
1187 List dartList = x[2];
1188 _deserialized[id] = dartList;
1189 int len = dartList.length;
1190 for (int i = 0; i < len; i++) {
1191 dartList[i] = _deserializeHelper(dartList[i]);
1192 }
1193 return dartList;
1194 }
1195
1196 Map _deserializeMap(List x) {
1197 Map result = new Map();
1198 int id = x[1];
1199 _deserialized[id] = result;
1200 List keys = x[2];
1201 List values = x[3];
1202 int len = keys.length;
1203 assert(len == values.length);
1204 for (int i = 0; i < len; i++) {
1205 var key = _deserializeHelper(keys[i]);
1206 var value = _deserializeHelper(values[i]);
1207 result[key] = value;
1208 }
1209 return result;
1210 }
1211
1212 deserializeSendPort(List x);
1213
1214 deserializeObject(List x) {
1215 // TODO(floitsch): Use real exception (which one?).
1216 throw "Unexpected serialized object";
1217 }
1218 }
1219
1220 /********************************************************
1221 Inserted from lib/isolate/dart2js/timer_provider.dart
1222 ********************************************************/
1223
1224 // We don't want to import the DOM library just because of window.setTimeout,
1225 // so we reconstruct the Window class here. The only conflict that could happen
1226 // with the other DOMWindow class would be because of subclasses.
1227 // Currently, none of the two Dart classes have subclasses.
1228 typedef void _TimeoutHandler();
1229
1230 // @Native("*DOMWindow");
1231 class _Window {
1232 int setTimeout(_TimeoutHandler handler, int timeout) {
1233 return JS('int',
1234 '#.setTimeout(#, #)',
1235 this,
1236 convertDartClosureToJS(handler, 0),
1237 timeout);
1238 }
1239
1240 int setInterval(_TimeoutHandler handler, int timeout) {
1241 return JS('int',
1242 '#.setInterval(#, #)',
1243 this,
1244 convertDartClosureToJS(handler, 0),
1245 timeout);
1246 }
1247
1248 void clearTimeout(int handle) {
1249 JS('void', '#.clearTimeout(#)', this, handle);
1250 }
1251
1252 void clearInterval(int handle) {
1253 JS('void', '#.clearInterval(#)', this, handle);
1254 }
1255 }
1256
1257 _Window get _window =>
1258 JS('bool', 'typeof window != "undefined"') ? JS('_Window', 'window') : null;
1259
1260 bool hasWindow() => _window != null;
1261
1262 class TimerImpl implements Timer {
1263 final bool _once;
1264 int _handle;
1265
1266 TimerImpl(int milliseconds, void callback(Timer timer))
1267 : _once = true {
1268 _handle = _window.setTimeout(() => callback(this), milliseconds);
1269 }
1270
1271 TimerImpl.repeating(int milliseconds, void callback(Timer timer))
1272 : _once = false {
1273 _handle = _window.setInterval(() => callback(this), milliseconds);
1274 }
1275
1276 void cancel() {
1277 if (_once) {
1278 _window.clearTimeout(_handle);
1279 } else {
1280 _window.clearInterval(_handle);
1281 }
1282 }
1283 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698