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

Side by Side Diff: test/generated_sdk/lib/_internal/compiler/js_lib/isolate_helper.dart

Issue 1162723007: remove generated_sdk from checked in code (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 6 months 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
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:_js_embedded_names' show
8 CLASS_ID_EXTRACTOR,
9 CLASS_FIELDS_EXTRACTOR,
10 CURRENT_SCRIPT,
11 GLOBAL_FUNCTIONS,
12 INITIALIZE_EMPTY_INSTANCE,
13 INSTANCE_FROM_CLASS_ID;
14
15 import 'dart:async';
16 import 'dart:collection' show Queue, HashMap;
17 import 'dart:isolate';
18 import 'dart:_native_typed_data' show NativeByteBuffer, NativeTypedData;
19
20 import 'dart:_js_helper' show
21 Closure,
22 InternalMap,
23 Null,
24 Primitives,
25 convertDartClosureToJS,
26 random64,
27 requiresPreamble;
28
29 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS,
30 JS,
31 JS_CREATE_ISOLATE,
32 JS_CURRENT_ISOLATE_CONTEXT,
33 JS_CURRENT_ISOLATE,
34 JS_EMBEDDED_GLOBAL,
35 JS_SET_CURRENT_ISOLATE,
36 IsolateContext;
37
38 import 'dart:_interceptors' show Interceptor,
39 JSArray,
40 JSExtendableArray,
41 JSFixedArray,
42 JSIndexable,
43 JSMutableArray,
44 JSObject;
45
46
47 part 'isolate_serialization.dart';
48
49 /**
50 * Called by the compiler to support switching
51 * between isolates when we get a callback from the DOM.
52 */
53 _callInIsolate(_IsolateContext isolate, Function function) {
54 var result = isolate.eval(function);
55 _globalState.topEventLoop.run();
56 return result;
57 }
58
59 /// Marks entering a JavaScript async operation to keep the worker alive.
60 ///
61 /// To be called by library code before starting an async operation controlled
62 /// by the JavaScript event handler.
63 ///
64 /// Also call [leaveJsAsync] in all callback handlers marking the end of that
65 /// async operation (also error handlers) so the worker can be released.
66 ///
67 /// These functions only has to be called for code that can be run from a
68 /// worker-isolate (so not for general dom operations).
69 enterJsAsync() {
70 _globalState.topEventLoop._activeJsAsyncCount++;
71 }
72
73 /// Marks leaving a javascript async operation.
74 ///
75 /// See [enterJsAsync].
76 leaveJsAsync() {
77 _globalState.topEventLoop._activeJsAsyncCount--;
78 assert(_globalState.topEventLoop._activeJsAsyncCount >= 0);
79 }
80
81 /// Returns true if we are currently in a worker context.
82 bool isWorker() => _globalState.isWorker;
83
84 /**
85 * Called by the compiler to fetch the current isolate context.
86 */
87 _IsolateContext _currentIsolate() => _globalState.currentContext;
88
89 /**
90 * Wrapper that takes the dart entry point and runs it within an isolate. The
91 * dart2js compiler will inject a call of the form
92 * [: startRootIsolate(main); :] when it determines that this wrapping
93 * is needed. For single-isolate applications (e.g. hello world), this
94 * call is not emitted.
95 */
96 void startRootIsolate(entry, args) {
97 // The dartMainRunner can inject a new arguments array. We pass the arguments
98 // through a "JS", so that the type-inferrer loses track of it.
99 args = JS("", "#", args);
100 if (args == null) args = [];
101 if (args is! List) {
102 throw new ArgumentError("Arguments to main must be a List: $args");
103 }
104 _globalState = new _Manager(entry);
105
106 // Don't start the main loop again, if we are in a worker.
107 if (_globalState.isWorker) return;
108 final rootContext = new _IsolateContext();
109 _globalState.rootContext = rootContext;
110
111 // BUG(5151491): Setting currentContext should not be necessary, but
112 // because closures passed to the DOM as event handlers do not bind their
113 // isolate automatically we try to give them a reasonable context to live in
114 // by having a "default" isolate (the first one created).
115 _globalState.currentContext = rootContext;
116 if (entry is _MainFunctionArgs) {
117 rootContext.eval(() { entry(args); });
118 } else if (entry is _MainFunctionArgsMessage) {
119 rootContext.eval(() { entry(args, null); });
120 } else {
121 rootContext.eval(entry);
122 }
123 _globalState.topEventLoop.run();
124 }
125
126 /********************************************************
127 Inserted from lib/isolate/dart2js/isolateimpl.dart
128 ********************************************************/
129
130 /**
131 * Concepts used here:
132 *
133 * "manager" - A manager contains one or more isolates, schedules their
134 * execution, and performs other plumbing on their behalf. The isolate
135 * present at the creation of the manager is designated as its "root isolate".
136 * A manager may, for example, be implemented on a web Worker.
137 *
138 * [_Manager] - State present within a manager (exactly once, as a global).
139 *
140 * [_ManagerStub] - A handle held within one manager that allows interaction
141 * with another manager. A target manager may be addressed by zero or more
142 * [_ManagerStub]s.
143 * TODO(ahe): The _ManagerStub concept is broken. It was an attempt
144 * to create a common interface between the native Worker class and
145 * _MainManagerStub.
146 */
147
148 /**
149 * A native object that is shared across isolates. This object is visible to all
150 * isolates running under the same manager (either UI or background web worker).
151 *
152 * This is code that is intended to 'escape' the isolate boundaries in order to
153 * implement the semantics of isolates in JavaScript. Without this we would have
154 * been forced to implement more code (including the top-level event loop) in
155 * JavaScript itself.
156 */
157 // TODO(eub, sigmund): move the "manager" to be entirely in JS.
158 // Running any Dart code outside the context of an isolate gives it
159 // the chance to break the isolate abstraction.
160 // TODO(vsm): This was changed from init.globalState.
161 // See: https://github.com/dart-lang/dev_compiler/issues/164
162 _Manager get _globalState => JS("_Manager", "dart.globalState");
163
164 set _globalState(_Manager val) {
165 // TODO(vsm): This was changed from init.globalState.
166 // See: https://github.com/dart-lang/dev_compiler/issues/164
167 JS("void", "dart.globalState = #", val);
168 }
169
170 /** State associated with the current manager. See [globalState]. */
171 // TODO(sigmund): split in multiple classes: global, thread, main-worker states?
172 class _Manager {
173
174 /** Next available isolate id within this [_Manager]. */
175 int nextIsolateId = 0;
176
177 /** id assigned to this [_Manager]. */
178 int currentManagerId = 0;
179
180 /**
181 * Next available manager id. Only used by the main manager to assign a unique
182 * id to each manager created by it.
183 */
184 int nextManagerId = 1;
185
186 /** Context for the currently running [Isolate]. */
187 _IsolateContext currentContext = null;
188
189 /** Context for the root [Isolate] that first run in this [_Manager]. */
190 _IsolateContext rootContext = null;
191
192 /** The top-level event loop. */
193 _EventLoop topEventLoop;
194
195 /** Whether this program is running from the command line. */
196 bool fromCommandLine;
197
198 /** Whether this [_Manager] is running as a web worker. */
199 bool isWorker;
200
201 /** Whether we support spawning web workers. */
202 bool supportsWorkers;
203
204 /**
205 * Whether to use web workers when implementing isolates. Set to false for
206 * debugging/testing.
207 */
208 bool get useWorkers => supportsWorkers;
209
210 /**
211 * Registry of isolates. Isolates must be registered if, and only if, receive
212 * ports are alive. Normally no open receive-ports means that the isolate is
213 * dead, but DOM callbacks could resurrect it.
214 */
215 Map<int, _IsolateContext> isolates;
216
217 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */
218 _MainManagerStub mainManager;
219
220 /// Registry of active Web Workers. Only used in the main [_Manager].
221 Map<int, dynamic /* Worker */> managers;
222
223 /** The entry point given by [startRootIsolate]. */
224 final Function entry;
225
226 _Manager(this.entry) {
227 _nativeDetectEnvironment();
228 topEventLoop = new _EventLoop();
229 isolates = new Map<int, _IsolateContext>();
230 managers = new Map<int, dynamic>();
231 if (isWorker) { // "if we are not the main manager ourself" is the intent.
232 mainManager = new _MainManagerStub();
233 _nativeInitWorkerMessageHandler();
234 }
235 }
236
237 void _nativeDetectEnvironment() {
238 bool isWindowDefined = globalWindow != null;
239 bool isWorkerDefined = globalWorker != null;
240
241 isWorker = !isWindowDefined && globalPostMessageDefined;
242 supportsWorkers = isWorker
243 || (isWorkerDefined && IsolateNatives.thisScript != null);
244 fromCommandLine = !isWindowDefined && !isWorker;
245 }
246
247 void _nativeInitWorkerMessageHandler() {
248 var function = JS('',
249 "(function (f, a) { return function (e) { f(a, e); }})(#, #)",
250 DART_CLOSURE_TO_JS(IsolateNatives._processWorkerMessage),
251 mainManager);
252 JS("void", r"self.onmessage = #", function);
253 // We ensure dartPrint is defined so that the implementation of the Dart
254 // print method knows what to call.
255 JS('', '''self.dartPrint = self.dartPrint || (function(serialize) {
256 return function (object) {
257 if (self.console && self.console.log) {
258 self.console.log(object)
259 } else {
260 self.postMessage(serialize(object));
261 }
262 }
263 })(#)''', DART_CLOSURE_TO_JS(_serializePrintMessage));
264 }
265
266 static _serializePrintMessage(object) {
267 return _serializeMessage({"command": "print", "msg": object});
268 }
269
270 /**
271 * Close the worker running this code if all isolates are done and
272 * there are no active async JavaScript tasks still running.
273 */
274 void maybeCloseWorker() {
275 if (isWorker
276 && isolates.isEmpty
277 && topEventLoop._activeJsAsyncCount == 0) {
278 mainManager.postMessage(_serializeMessage({'command': 'close'}));
279 }
280 }
281 }
282
283 /** Context information tracked for each isolate. */
284 class _IsolateContext implements IsolateContext {
285 /** Current isolate id. */
286 final int id = _globalState.nextIsolateId++;
287
288 /** Registry of receive ports currently active on this isolate. */
289 final Map<int, RawReceivePortImpl> ports = new Map<int, RawReceivePortImpl>();
290
291 /** Registry of weak receive ports currently active on this isolate. */
292 final Set<int> weakPorts = new Set<int>();
293
294 /** Holds isolate globals (statics and top-level properties). */
295 // native object containing all globals of an isolate.
296 final isolateStatics = JS_CREATE_ISOLATE();
297
298 final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort();
299
300 final Capability pauseCapability = new Capability();
301 final Capability terminateCapability = new Capability(); // License to kill.
302
303 /// Boolean flag set when the initial method of the isolate has been executed.
304 ///
305 /// Used to avoid considering the isolate dead when it has no open
306 /// receive ports and no scheduled timers, because it hasn't had time to
307 /// create them yet.
308 bool initialized = false;
309
310 // TODO(lrn): Store these in single "PauseState" object, so they don't take
311 // up as much room when not pausing.
312 bool isPaused = false;
313 List<_IsolateEvent> delayedEvents = [];
314 Set<Capability> pauseTokens = new Set();
315
316 // Container with the "on exit" handler send-ports.
317 var doneHandlers;
318
319 /**
320 * Queue of functions to call when the current event is complete.
321 *
322 * These events are not just put at the front of the event queue, because
323 * they represent control messages, and should be handled even if the
324 * event queue is paused.
325 */
326 var _scheduledControlEvents;
327 bool _isExecutingEvent = false;
328
329 /** Whether uncaught errors are considered fatal. */
330 bool errorsAreFatal = true;
331
332 // Set of ports that listen to uncaught errors.
333 Set<SendPort> errorPorts = new Set();
334
335 _IsolateContext() {
336 this.registerWeak(controlPort._id, controlPort);
337 }
338
339 void addPause(Capability authentification, Capability resume) {
340 if (pauseCapability != authentification) return;
341 if (pauseTokens.add(resume) && !isPaused) {
342 isPaused = true;
343 }
344 _updateGlobalState();
345 }
346
347 void removePause(Capability resume) {
348 if (!isPaused) return;
349 pauseTokens.remove(resume);
350 if (pauseTokens.isEmpty) {
351 while(delayedEvents.isNotEmpty) {
352 _IsolateEvent event = delayedEvents.removeLast();
353 _globalState.topEventLoop.prequeue(event);
354 }
355 isPaused = false;
356 }
357 _updateGlobalState();
358 }
359
360 void addDoneListener(SendPort responsePort) {
361 if (doneHandlers == null) {
362 doneHandlers = [];
363 }
364 // If necessary, we can switch doneHandlers to a Set if it gets larger.
365 // That is not expected to happen in practice.
366 if (doneHandlers.contains(responsePort)) return;
367 doneHandlers.add(responsePort);
368 }
369
370 void removeDoneListener(SendPort responsePort) {
371 if (doneHandlers == null) return;
372 doneHandlers.remove(responsePort);
373 }
374
375 void setErrorsFatal(Capability authentification, bool errorsAreFatal) {
376 if (terminateCapability != authentification) return;
377 this.errorsAreFatal = errorsAreFatal;
378 }
379
380 void handlePing(SendPort responsePort, int pingType) {
381 if (pingType == Isolate.IMMEDIATE ||
382 (pingType == Isolate.BEFORE_NEXT_EVENT &&
383 !_isExecutingEvent)) {
384 responsePort.send(null);
385 return;
386 }
387 void respond() { responsePort.send(null); }
388 if (pingType == Isolate.AS_EVENT) {
389 _globalState.topEventLoop.enqueue(this, respond, "ping");
390 return;
391 }
392 assert(pingType == Isolate.BEFORE_NEXT_EVENT);
393 if (_scheduledControlEvents == null) {
394 _scheduledControlEvents = new Queue();
395 }
396 _scheduledControlEvents.addLast(respond);
397 }
398
399 void handleKill(Capability authentification, int priority) {
400 if (this.terminateCapability != authentification) return;
401 if (priority == Isolate.IMMEDIATE ||
402 (priority == Isolate.BEFORE_NEXT_EVENT &&
403 !_isExecutingEvent)) {
404 kill();
405 return;
406 }
407 if (priority == Isolate.AS_EVENT) {
408 _globalState.topEventLoop.enqueue(this, kill, "kill");
409 return;
410 }
411 assert(priority == Isolate.BEFORE_NEXT_EVENT);
412 if (_scheduledControlEvents == null) {
413 _scheduledControlEvents = new Queue();
414 }
415 _scheduledControlEvents.addLast(kill);
416 }
417
418 void addErrorListener(SendPort port) {
419 errorPorts.add(port);
420 }
421
422 void removeErrorListener(SendPort port) {
423 errorPorts.remove(port);
424 }
425
426 /** Function called with an uncaught error. */
427 void handleUncaughtError(error, StackTrace stackTrace) {
428 // Just print the error if there is no error listener registered.
429 if (errorPorts.isEmpty) {
430 // An uncaught error in the root isolate will terminate the program?
431 if (errorsAreFatal && identical(this, _globalState.rootContext)) {
432 // The error will be rethrown to reach the global scope, so
433 // don't print it.
434 return;
435 }
436 if (JS('bool', 'self.console && self.console.error')) {
437 JS('void', 'self.console.error(#, #)', error, stackTrace);
438 } else {
439 print(error);
440 if (stackTrace != null) print(stackTrace);
441 }
442 return;
443 }
444 List message = new List(2)
445 ..[0] = error.toString()
446 ..[1] = (stackTrace == null) ? null : stackTrace.toString();
447 for (SendPort port in errorPorts) port.send(message);
448 }
449
450 /**
451 * Run [code] in the context of the isolate represented by [this].
452 */
453 dynamic eval(Function code) {
454 var old = _globalState.currentContext;
455 _globalState.currentContext = this;
456 this._setGlobals();
457 var result = null;
458 _isExecutingEvent = true;
459 try {
460 result = code();
461 } catch (e, s) {
462 handleUncaughtError(e, s);
463 if (errorsAreFatal) {
464 kill();
465 // An uncaught error in the root context terminates all isolates.
466 if (identical(this, _globalState.rootContext)) {
467 rethrow;
468 }
469 }
470 } finally {
471 _isExecutingEvent = false;
472 _globalState.currentContext = old;
473 if (old != null) old._setGlobals();
474 if (_scheduledControlEvents != null) {
475 while (_scheduledControlEvents.isNotEmpty) {
476 (_scheduledControlEvents.removeFirst())();
477 }
478 }
479 }
480 return result;
481 }
482
483 void _setGlobals() {
484 JS_SET_CURRENT_ISOLATE(isolateStatics);
485 }
486
487 /**
488 * Handle messages comming in on the control port.
489 *
490 * These events do not go through the event queue.
491 * The `_globalState.currentContext` context is not set to this context
492 * during the handling.
493 */
494 void handleControlMessage(message) {
495 switch (message[0]) {
496 case "pause":
497 addPause(message[1], message[2]);
498 break;
499 case "resume":
500 removePause(message[1]);
501 break;
502 case 'add-ondone':
503 addDoneListener(message[1]);
504 break;
505 case 'remove-ondone':
506 removeDoneListener(message[1]);
507 break;
508 case 'set-errors-fatal':
509 setErrorsFatal(message[1], message[2]);
510 break;
511 case "ping":
512 handlePing(message[1], message[2]);
513 break;
514 case "kill":
515 handleKill(message[1], message[2]);
516 break;
517 case "getErrors":
518 addErrorListener(message[1]);
519 break;
520 case "stopErrors":
521 removeErrorListener(message[1]);
522 break;
523 default:
524 }
525 }
526
527 /** Looks up a port registered for this isolate. */
528 RawReceivePortImpl lookup(int portId) => ports[portId];
529
530 void _addRegistration(int portId, RawReceivePortImpl port) {
531 if (ports.containsKey(portId)) {
532 throw new Exception("Registry: ports must be registered only once.");
533 }
534 ports[portId] = port;
535 }
536
537 /** Registers a port on this isolate. */
538 void register(int portId, RawReceivePortImpl port) {
539 _addRegistration(portId, port);
540 _updateGlobalState();
541 }
542
543 /**
544 * Registers a weak port on this isolate.
545 *
546 * The port does not keep the isolate active.
547 */
548 void registerWeak(int portId, RawReceivePortImpl port) {
549 weakPorts.add(portId);
550 _addRegistration(portId, port);
551 }
552
553 void _updateGlobalState() {
554 if (ports.length - weakPorts.length > 0 || isPaused || !initialized) {
555 _globalState.isolates[id] = this; // indicate this isolate is active
556 } else {
557 kill();
558 }
559 }
560
561 void kill() {
562 if (_scheduledControlEvents != null) {
563 // Kill all pending events.
564 _scheduledControlEvents.clear();
565 }
566 // Stop listening on all ports.
567 // This should happen before sending events to done handlers, in case
568 // we are listening on ourselves.
569 // Closes all ports, including control port.
570 for (var port in ports.values) {
571 port._close();
572 }
573 ports.clear();
574 weakPorts.clear();
575 _globalState.isolates.remove(id); // indicate this isolate is not active
576 errorPorts.clear();
577 if (doneHandlers != null) {
578 for (SendPort port in doneHandlers) {
579 port.send(null);
580 }
581 doneHandlers = null;
582 }
583 }
584
585 /** Unregister a port on this isolate. */
586 void unregister(int portId) {
587 ports.remove(portId);
588 weakPorts.remove(portId);
589 _updateGlobalState();
590 }
591 }
592
593 /** Represent the event loop on a javascript thread (DOM or worker). */
594 class _EventLoop {
595 final Queue<_IsolateEvent> events = new Queue<_IsolateEvent>();
596
597 /// The number of waiting callbacks not controlled by the dart event loop.
598 ///
599 /// This could be timers or http requests. The worker will only be killed if
600 /// this count reaches 0.
601 /// Access this by using [enterJsAsync] before starting a JavaScript async
602 /// operation and [leaveJsAsync] when the callback has fired.
603 int _activeJsAsyncCount = 0;
604
605 _EventLoop();
606
607 void enqueue(isolate, fn, msg) {
608 events.addLast(new _IsolateEvent(isolate, fn, msg));
609 }
610
611 void prequeue(_IsolateEvent event) {
612 events.addFirst(event);
613 }
614
615 _IsolateEvent dequeue() {
616 if (events.isEmpty) return null;
617 return events.removeFirst();
618 }
619
620 void checkOpenReceivePortsFromCommandLine() {
621 if (_globalState.rootContext != null
622 && _globalState.isolates.containsKey(_globalState.rootContext.id)
623 && _globalState.fromCommandLine
624 && _globalState.rootContext.ports.isEmpty) {
625 // We want to reach here only on the main [_Manager] and only
626 // on the command-line. In the browser the isolate might
627 // still be alive due to DOM callbacks, but the presumption is
628 // that on the command-line, no future events can be injected
629 // into the event queue once it's empty. Node has setTimeout
630 // so this presumption is incorrect there. We think(?) that
631 // in d8 this assumption is valid.
632 throw new Exception("Program exited with open ReceivePorts.");
633 }
634 }
635
636 /** Process a single event, if any. */
637 bool runIteration() {
638 final event = dequeue();
639 if (event == null) {
640 checkOpenReceivePortsFromCommandLine();
641 _globalState.maybeCloseWorker();
642 return false;
643 }
644 event.process();
645 return true;
646 }
647
648 /**
649 * Runs multiple iterations of the run-loop. If possible, each iteration is
650 * run asynchronously.
651 */
652 void _runHelper() {
653 if (globalWindow != null) {
654 // Run each iteration from the browser's top event loop.
655 next() {
656 if (!runIteration()) return;
657 Timer.run(next);
658 }
659 next();
660 } else {
661 // Run synchronously until no more iterations are available.
662 while (runIteration()) {}
663 }
664 }
665
666 /**
667 * Call [_runHelper] but ensure that worker exceptions are propragated.
668 */
669 void run() {
670 if (!_globalState.isWorker) {
671 _runHelper();
672 } else {
673 try {
674 _runHelper();
675 } catch (e, trace) {
676 _globalState.mainManager.postMessage(_serializeMessage(
677 {'command': 'error', 'msg': '$e\n$trace' }));
678 }
679 }
680 }
681 }
682
683 /** An event in the top-level event queue. */
684 class _IsolateEvent {
685 _IsolateContext isolate;
686 Function fn;
687 String message;
688
689 _IsolateEvent(this.isolate, this.fn, this.message);
690
691 void process() {
692 if (isolate.isPaused) {
693 isolate.delayedEvents.add(this);
694 return;
695 }
696 isolate.eval(fn);
697 }
698 }
699
700 /** A stub for interacting with the main manager. */
701 class _MainManagerStub {
702 void postMessage(msg) {
703 // "self" is a way to refer to the global context object that
704 // works in HTML pages and in Web Workers. It does not work in d8
705 // and Firefox jsshell, because that would have been too easy.
706 //
707 // See: http://www.w3.org/TR/workers/#the-global-scope
708 // and: http://www.w3.org/TR/Window/#dfn-self-attribute
709 requiresPreamble();
710 JS("void", r"self.postMessage(#)", msg);
711 }
712 }
713
714 const String _SPAWNED_SIGNAL = "spawned";
715 const String _SPAWN_FAILED_SIGNAL = "spawn failed";
716
717 get globalWindow {
718 requiresPreamble();
719 return JS('', "self.window");
720 }
721
722 get globalWorker {
723 requiresPreamble();
724 return JS('', "self.Worker");
725 }
726 bool get globalPostMessageDefined {
727 requiresPreamble();
728 return JS('bool', "!!self.postMessage");
729 }
730
731 typedef _MainFunction();
732 typedef _MainFunctionArgs(args);
733 typedef _MainFunctionArgsMessage(args, message);
734
735 /// Note: IsolateNatives depends on _globalState which is only set up correctly
736 /// when 'dart:isolate' has been imported.
737 class IsolateNatives {
738
739 // We set [enableSpawnWorker] to true (not null) when calling isolate
740 // primitives that require support for spawning workers. The field starts out
741 // by being null, and dart2js' type inference will track if it can have a
742 // non-null value. So by testing if this value is not null, we generate code
743 // that dart2js knows is dead when worker support isn't needed.
744 // TODO(herhut): Initialize this to false when able to track compile-time
745 // constants.
746 static var enableSpawnWorker;
747
748 static String thisScript = computeThisScript();
749
750 /// Associates an ID with a native worker object.
751 static final Expando<int> workerIds = new Expando<int>();
752
753 /**
754 * The src url for the script tag that loaded this Used to create
755 * JavaScript workers.
756 */
757 static String computeThisScript() {
758 // See: https://github.com/dart-lang/dev_compiler/issues/164
759 // var currentScript = JS_EMBEDDED_GLOBAL('', CURRENT_SCRIPT);
760 var currentScript = JS('var', 'document.currentScript');
761 if (currentScript != null) {
762 return JS('String', 'String(#.src)', currentScript);
763 }
764 if (Primitives.isD8) return computeThisScriptD8();
765 if (Primitives.isJsshell) return computeThisScriptJsshell();
766 // A worker has no script tag - so get an url from a stack-trace.
767 if (_globalState.isWorker) return computeThisScriptFromTrace();
768 return null;
769 }
770
771 static String computeThisScriptJsshell() {
772 return JS('String|Null', 'thisFilename()');
773 }
774
775 // TODO(ahe): The following is for supporting D8. We should move this code
776 // to a helper library that is only loaded when testing on D8.
777 static String computeThisScriptD8() => computeThisScriptFromTrace();
778
779 static String computeThisScriptFromTrace() {
780 var stack = JS('String|Null', 'new Error().stack');
781 if (stack == null) {
782 // According to Internet Explorer documentation, the stack
783 // property is not set until the exception is thrown. The stack
784 // property was not provided until IE10.
785 stack = JS('String|Null',
786 '(function() {'
787 'try { throw new Error() } catch(e) { return e.stack }'
788 '})()');
789 if (stack == null) throw new UnsupportedError('No stack trace');
790 }
791 var pattern, matches;
792
793 // This pattern matches V8, Chrome, and Internet Explorer stack
794 // traces that look like this:
795 // Error
796 // at methodName (URI:LINE:COLUMN)
797 pattern = JS('',
798 r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")');
799
800
801 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern);
802 if (matches != null) return JS('String', '#[1]', matches);
803
804 // This pattern matches Firefox stack traces that look like this:
805 // methodName@URI:LINE
806 pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")');
807
808 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern);
809 if (matches != null) return JS('String', '#[1]', matches);
810
811 throw new UnsupportedError('Cannot extract URI from "$stack"');
812 }
813
814 /**
815 * Assume that [e] is a browser message event and extract its message data.
816 * We don't import the dom explicitly so, when workers are disabled, this
817 * library can also run on top of nodejs.
818 */
819 static _getEventData(e) => JS("", "#.data", e);
820
821 /**
822 * Process messages on a worker, either to control the worker instance or to
823 * pass messages along to the isolate running in the worker.
824 */
825 static void _processWorkerMessage(/* Worker */ sender, e) {
826 var msg = _deserializeMessage(_getEventData(e));
827 switch (msg['command']) {
828 case 'start':
829 _globalState.currentManagerId = msg['id'];
830 String functionName = msg['functionName'];
831 Function entryPoint = (functionName == null)
832 ? _globalState.entry
833 : _getJSFunctionFromName(functionName);
834 var args = msg['args'];
835 var message = _deserializeMessage(msg['msg']);
836 var isSpawnUri = msg['isSpawnUri'];
837 var startPaused = msg['startPaused'];
838 var replyTo = _deserializeMessage(msg['replyTo']);
839 var context = new _IsolateContext();
840 _globalState.topEventLoop.enqueue(context, () {
841 _startIsolate(entryPoint, args, message,
842 isSpawnUri, startPaused, replyTo);
843 }, 'worker-start');
844 // Make sure we always have a current context in this worker.
845 // TODO(7907): This is currently needed because we're using
846 // Timers to implement Futures, and this isolate library
847 // implementation uses Futures. We should either stop using
848 // Futures in this library, or re-adapt if Futures get a
849 // different implementation.
850 _globalState.currentContext = context;
851 _globalState.topEventLoop.run();
852 break;
853 case 'spawn-worker':
854 if (enableSpawnWorker != null) handleSpawnWorkerRequest(msg);
855 break;
856 case 'message':
857 SendPort port = msg['port'];
858 // If the port has been closed, we ignore the message.
859 if (port != null) {
860 msg['port'].send(msg['msg']);
861 }
862 _globalState.topEventLoop.run();
863 break;
864 case 'close':
865 _globalState.managers.remove(workerIds[sender]);
866 JS('void', '#.terminate()', sender);
867 _globalState.topEventLoop.run();
868 break;
869 case 'log':
870 _log(msg['msg']);
871 break;
872 case 'print':
873 if (_globalState.isWorker) {
874 _globalState.mainManager.postMessage(
875 _serializeMessage({'command': 'print', 'msg': msg}));
876 } else {
877 print(msg['msg']);
878 }
879 break;
880 case 'error':
881 throw msg['msg'];
882 }
883 }
884
885 static handleSpawnWorkerRequest(msg) {
886 var replyPort = msg['replyPort'];
887 spawn(msg['functionName'], msg['uri'],
888 msg['args'], msg['msg'],
889 false, msg['isSpawnUri'], msg['startPaused']).then((msg) {
890 replyPort.send(msg);
891 }, onError: (String errorMessage) {
892 replyPort.send([_SPAWN_FAILED_SIGNAL, errorMessage]);
893 });
894 }
895
896 /** Log a message, forwarding to the main [_Manager] if appropriate. */
897 static _log(msg) {
898 if (_globalState.isWorker) {
899 _globalState.mainManager.postMessage(
900 _serializeMessage({'command': 'log', 'msg': msg }));
901 } else {
902 try {
903 _consoleLog(msg);
904 } catch (e, trace) {
905 throw new Exception(trace);
906 }
907 }
908 }
909
910 static void _consoleLog(msg) {
911 requiresPreamble();
912 JS("void", r"self.console.log(#)", msg);
913 }
914
915 static _getJSFunctionFromName(String functionName) {
916 var globalFunctionsContainer = JS_EMBEDDED_GLOBAL("", GLOBAL_FUNCTIONS);
917 return JS("", "#[#]()", globalFunctionsContainer, functionName);
918 }
919
920 /**
921 * Get a string name for the function, if possible. The result for
922 * anonymous functions is browser-dependent -- it may be "" or "anonymous"
923 * but you should probably not count on this.
924 */
925 static String _getJSFunctionName(Function f) {
926 return (f is Closure) ? JS("String|Null", r'#.$name', f) : null;
927 }
928
929 /** Create a new JavaScript object instance given its constructor. */
930 static dynamic _allocate(var ctor) {
931 return JS("", "new #()", ctor);
932 }
933
934 static Future<List> spawnFunction(void topLevelFunction(message),
935 var message,
936 bool startPaused) {
937 IsolateNatives.enableSpawnWorker = true;
938 final name = _getJSFunctionName(topLevelFunction);
939 if (name == null) {
940 throw new UnsupportedError(
941 "only top-level functions can be spawned.");
942 }
943 bool isLight = false;
944 bool isSpawnUri = false;
945 return spawn(name, null, null, message, isLight, isSpawnUri, startPaused);
946 }
947
948 static Future<List> spawnUri(Uri uri, List<String> args, var message,
949 bool startPaused) {
950 IsolateNatives.enableSpawnWorker = true;
951 bool isLight = false;
952 bool isSpawnUri = true;
953 return spawn(null, uri.toString(), args, message,
954 isLight, isSpawnUri, startPaused);
955 }
956
957 // TODO(sigmund): clean up above, after we make the new API the default:
958
959 /// If [uri] is `null` it is replaced with the current script.
960 static Future<List> spawn(String functionName, String uri,
961 List<String> args, message,
962 bool isLight, bool isSpawnUri, bool startPaused) {
963 // Assume that the compiled version of the Dart file lives just next to the
964 // dart file.
965 // TODO(floitsch): support precompiled version of dart2js output.
966 if (uri != null && uri.endsWith(".dart")) uri += ".js";
967
968 ReceivePort port = new ReceivePort();
969 Completer<List> completer = new Completer();
970 port.first.then((msg) {
971 if (msg[0] == _SPAWNED_SIGNAL) {
972 completer.complete(msg);
973 } else {
974 assert(msg[0] == _SPAWN_FAILED_SIGNAL);
975 completer.completeError(msg[1]);
976 }
977 });
978
979 SendPort signalReply = port.sendPort;
980
981 if (_globalState.useWorkers && !isLight) {
982 _startWorker(
983 functionName, uri, args, message, isSpawnUri, startPaused,
984 signalReply, (String message) => completer.completeError(message));
985 } else {
986 _startNonWorker(
987 functionName, uri, args, message, isSpawnUri, startPaused,
988 signalReply);
989 }
990 return completer.future;
991 }
992
993 static void _startWorker(
994 String functionName, String uri,
995 List<String> args, message,
996 bool isSpawnUri,
997 bool startPaused,
998 SendPort replyPort,
999 void onError(String message)) {
1000 // Make sure that the args list is a fresh generic list. A newly spawned
1001 // isolate should be able to assume that the arguments list is an
1002 // extendable list.
1003 if (args != null) args = new List<String>.from(args);
1004 if (_globalState.isWorker) {
1005 _globalState.mainManager.postMessage(_serializeMessage({
1006 'command': 'spawn-worker',
1007 'functionName': functionName,
1008 'args': args,
1009 'msg': message,
1010 'uri': uri,
1011 'isSpawnUri': isSpawnUri,
1012 'startPaused': startPaused,
1013 'replyPort': replyPort}));
1014 } else {
1015 _spawnWorker(functionName, uri, args, message,
1016 isSpawnUri, startPaused, replyPort, onError);
1017 }
1018 }
1019
1020 static void _startNonWorker(
1021 String functionName, String uri,
1022 List<String> args, var message,
1023 bool isSpawnUri,
1024 bool startPaused,
1025 SendPort replyPort) {
1026 // TODO(eub): support IE9 using an iframe -- Dart issue 1702.
1027 if (uri != null) {
1028 throw new UnsupportedError(
1029 "Currently spawnUri is not supported without web workers.");
1030 }
1031 // Clone the message to enforce the restrictions we have on isolate
1032 // messages.
1033 message = _clone(message);
1034 // Make sure that the args list is a fresh generic list. A newly spawned
1035 // isolate should be able to assume that the arguments list is an
1036 // extendable list.
1037 if (args != null) args = new List<String>.from(args);
1038 _globalState.topEventLoop.enqueue(new _IsolateContext(), () {
1039 final func = _getJSFunctionFromName(functionName);
1040 _startIsolate(func, args, message, isSpawnUri, startPaused, replyPort);
1041 }, 'nonworker start');
1042 }
1043
1044 static Isolate get currentIsolate {
1045 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT();
1046 return new Isolate(context.controlPort.sendPort,
1047 pauseCapability: context.pauseCapability,
1048 terminateCapability: context.terminateCapability);
1049 }
1050
1051 static void _startIsolate(Function topLevel,
1052 List<String> args, message,
1053 bool isSpawnUri,
1054 bool startPaused,
1055 SendPort replyTo) {
1056 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT();
1057 Primitives.initializeStatics(context.id);
1058 // The isolate's port does not keep the isolate open.
1059 replyTo.send([_SPAWNED_SIGNAL,
1060 context.controlPort.sendPort,
1061 context.pauseCapability,
1062 context.terminateCapability]);
1063
1064 void runStartFunction() {
1065 context.initialized = true;
1066 if (!isSpawnUri) {
1067 topLevel(message);
1068 } else if (topLevel is _MainFunctionArgsMessage) {
1069 topLevel(args, message);
1070 } else if (topLevel is _MainFunctionArgs) {
1071 topLevel(args);
1072 } else {
1073 topLevel();
1074 }
1075 }
1076
1077 if (startPaused) {
1078 context.addPause(context.pauseCapability, context.pauseCapability);
1079 _globalState.topEventLoop.enqueue(context, runStartFunction,
1080 'start isolate');
1081 } else {
1082 runStartFunction();
1083 }
1084 }
1085
1086 /**
1087 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor
1088 * name for the isolate entry point class.
1089 */
1090 static void _spawnWorker(functionName, String uri,
1091 List<String> args, message,
1092 bool isSpawnUri,
1093 bool startPaused,
1094 SendPort replyPort,
1095 void onError(String message)) {
1096 if (uri == null) uri = thisScript;
1097 final worker = JS('var', 'new Worker(#)', uri);
1098 // Trampolines are used when wanting to call a Dart closure from
1099 // JavaScript. The helper function DART_CLOSURE_TO_JS only accepts
1100 // top-level or static methods, and the trampoline allows us to capture
1101 // arguments and values which can be passed to a static method.
1102 final onerrorTrampoline = JS(
1103 '',
1104 '''
1105 (function (f, u, c) {
1106 return function(e) {
1107 return f(e, u, c)
1108 }
1109 })(#, #, #)''',
1110 DART_CLOSURE_TO_JS(workerOnError), uri, onError);
1111 JS('void', '#.onerror = #', worker, onerrorTrampoline);
1112
1113 var processWorkerMessageTrampoline = JS(
1114 '',
1115 """
1116 (function (f, a) {
1117 return function (e) {
1118 // We can stop listening for errors when the first message is received as
1119 // we only listen for messages to determine if the uri was bad.
1120 e.onerror = null;
1121 return f(a, e);
1122 }
1123 })(#, #)""",
1124 DART_CLOSURE_TO_JS(_processWorkerMessage),
1125 worker);
1126 JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline);
1127 var workerId = _globalState.nextManagerId++;
1128 // We also store the id on the worker itself so that we can unregister it.
1129 workerIds[worker] = workerId;
1130 _globalState.managers[workerId] = worker;
1131 JS('void', '#.postMessage(#)', worker, _serializeMessage({
1132 'command': 'start',
1133 'id': workerId,
1134 // Note: we serialize replyPort twice because the child worker needs to
1135 // first deserialize the worker id, before it can correctly deserialize
1136 // the port (port deserialization is sensitive to what is the current
1137 // workerId).
1138 'replyTo': _serializeMessage(replyPort),
1139 'args': args,
1140 'msg': _serializeMessage(message),
1141 'isSpawnUri': isSpawnUri,
1142 'startPaused': startPaused,
1143 'functionName': functionName }));
1144 }
1145
1146 static bool workerOnError(
1147 /* Event */ event,
1148 String uri,
1149 void onError(String message)) {
1150 // Attempt to shut up the browser, as the error has been handled. Chrome
1151 // ignores this :-(
1152 JS('void', '#.preventDefault()', event);
1153 String message = JS('String|Null', '#.message', event);
1154 if (message == null) {
1155 // Some browsers, including Chrome, fail to provide a proper error
1156 // event.
1157 message = 'Error spawning worker for $uri';
1158 } else {
1159 message = 'Error spawning worker for $uri ($message)';
1160 }
1161 onError(message);
1162 return true;
1163 }
1164 }
1165
1166 /********************************************************
1167 Inserted from lib/isolate/dart2js/ports.dart
1168 ********************************************************/
1169
1170 /** Common functionality to all send ports. */
1171 abstract class _BaseSendPort implements SendPort {
1172 /** Id for the destination isolate. */
1173 final int _isolateId;
1174
1175 const _BaseSendPort(this._isolateId);
1176
1177 void _checkReplyTo(SendPort replyTo) {
1178 if (replyTo != null
1179 && replyTo is! _NativeJsSendPort
1180 && replyTo is! _WorkerSendPort) {
1181 throw new Exception("SendPort.send: Illegal replyTo port type");
1182 }
1183 }
1184
1185 void send(var message);
1186 bool operator ==(var other);
1187 int get hashCode;
1188 }
1189
1190 /** A send port that delivers messages in-memory via native JavaScript calls. */
1191 class _NativeJsSendPort extends _BaseSendPort implements SendPort {
1192 final RawReceivePortImpl _receivePort;
1193
1194 const _NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId);
1195
1196 void send(var message) {
1197 // Check that the isolate still runs and the port is still open
1198 final isolate = _globalState.isolates[_isolateId];
1199 if (isolate == null) return;
1200 if (_receivePort._isClosed) return;
1201 // Clone the message to enforce the restrictions we have on isolate
1202 // messages.
1203 var msg = _clone(message);
1204 if (isolate.controlPort == _receivePort) {
1205 isolate.handleControlMessage(msg);
1206 return;
1207 }
1208 _globalState.topEventLoop.enqueue(isolate, () {
1209 if (!_receivePort._isClosed) {
1210 _receivePort._add(msg);
1211 }
1212 }, 'receive $message');
1213 }
1214
1215 bool operator ==(var other) => (other is _NativeJsSendPort) &&
1216 (_receivePort == other._receivePort);
1217
1218 int get hashCode => _receivePort._id;
1219 }
1220
1221 /** A send port that delivers messages via worker.postMessage. */
1222 // TODO(eub): abstract this for iframes.
1223 class _WorkerSendPort extends _BaseSendPort implements SendPort {
1224 final int _workerId;
1225 final int _receivePortId;
1226
1227 const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId)
1228 : super(isolateId);
1229
1230 void send(var message) {
1231 final workerMessage = _serializeMessage({
1232 'command': 'message',
1233 'port': this,
1234 'msg': message});
1235
1236 if (_globalState.isWorker) {
1237 // Communication from one worker to another go through the
1238 // main worker.
1239 _globalState.mainManager.postMessage(workerMessage);
1240 } else {
1241 // Deliver the message only if the worker is still alive.
1242 /* Worker */ var manager = _globalState.managers[_workerId];
1243 if (manager != null) {
1244 JS('void', '#.postMessage(#)', manager, workerMessage);
1245 }
1246 }
1247 }
1248
1249 bool operator ==(var other) {
1250 return (other is _WorkerSendPort) &&
1251 (_workerId == other._workerId) &&
1252 (_isolateId == other._isolateId) &&
1253 (_receivePortId == other._receivePortId);
1254 }
1255
1256 int get hashCode {
1257 // TODO(sigmund): use a standard hash when we get one available in corelib.
1258 return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId;
1259 }
1260 }
1261
1262 class RawReceivePortImpl implements RawReceivePort {
1263 static int _nextFreeId = 1;
1264
1265 final int _id;
1266 Function _handler;
1267 bool _isClosed = false;
1268
1269 RawReceivePortImpl(this._handler) : _id = _nextFreeId++ {
1270 _globalState.currentContext.register(_id, this);
1271 }
1272
1273 RawReceivePortImpl.weak(this._handler) : _id = _nextFreeId++ {
1274 _globalState.currentContext.registerWeak(_id, this);
1275 }
1276
1277 // Creates the control port of an isolate.
1278 // This is created before the isolate context object itself,
1279 // so it cannot access the static _nextFreeId field.
1280 RawReceivePortImpl._controlPort() : _handler = null, _id = 0;
1281
1282 void set handler(Function newHandler) {
1283 _handler = newHandler;
1284 }
1285
1286 // Close the port without unregistering it.
1287 // Used by an isolate context to close all ports when shutting down.
1288 void _close() {
1289 _isClosed = true;
1290 _handler = null;
1291 }
1292
1293 void close() {
1294 if (_isClosed) return;
1295 _isClosed = true;
1296 _handler = null;
1297 _globalState.currentContext.unregister(_id);
1298 }
1299
1300 void _add(dataEvent) {
1301 if (_isClosed) return;
1302 _handler(dataEvent);
1303 }
1304
1305 SendPort get sendPort {
1306 return new _NativeJsSendPort(this, _globalState.currentContext.id);
1307 }
1308 }
1309
1310 class ReceivePortImpl extends Stream implements ReceivePort {
1311 final RawReceivePort _rawPort;
1312 StreamController _controller;
1313
1314 ReceivePortImpl() : this.fromRawReceivePort(new RawReceivePortImpl(null));
1315
1316 ReceivePortImpl.weak()
1317 : this.fromRawReceivePort(new RawReceivePortImpl.weak(null));
1318
1319 ReceivePortImpl.fromRawReceivePort(this._rawPort) {
1320 _controller = new StreamController(onCancel: close, sync: true);
1321 _rawPort.handler = _controller.add;
1322 }
1323
1324 StreamSubscription listen(void onData(var event),
1325 {Function onError,
1326 void onDone(),
1327 bool cancelOnError}) {
1328 return _controller.stream.listen(onData, onError: onError, onDone: onDone,
1329 cancelOnError: cancelOnError);
1330 }
1331
1332 void close() {
1333 _rawPort.close();
1334 _controller.close();
1335 }
1336
1337 SendPort get sendPort => _rawPort.sendPort;
1338 }
1339
1340 class TimerImpl implements Timer {
1341 final bool _once;
1342 bool _inEventLoop = false;
1343 int _handle;
1344
1345 TimerImpl(int milliseconds, void callback())
1346 : _once = true {
1347 if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) {
1348
1349 void internalCallback() {
1350 _handle = null;
1351 callback();
1352 }
1353
1354 // Setting _handle to something different from null indicates that the
1355 // callback has not been run. Hence, the choice of 1 is arbitrary.
1356 _handle = 1;
1357
1358 // This makes a dependency between the async library and the
1359 // event loop of the isolate library. The compiler makes sure
1360 // that the event loop is compiled if [Timer] is used.
1361 // TODO(7907): In case of web workers, we need to use the event
1362 // loop instead of setTimeout, to make sure the futures get executed in
1363 // order.
1364 _globalState.topEventLoop.enqueue(
1365 _globalState.currentContext, internalCallback, 'timer');
1366 _inEventLoop = true;
1367 } else if (hasTimer()) {
1368
1369 void internalCallback() {
1370 _handle = null;
1371 leaveJsAsync();
1372 callback();
1373 }
1374
1375 enterJsAsync();
1376
1377 _handle = JS('int', 'self.setTimeout(#, #)',
1378 convertDartClosureToJS(internalCallback, 0),
1379 milliseconds);
1380 } else {
1381 assert(milliseconds > 0);
1382 throw new UnsupportedError("Timer greater than 0.");
1383 }
1384 }
1385
1386 TimerImpl.periodic(int milliseconds, void callback(Timer timer))
1387 : _once = false {
1388 if (hasTimer()) {
1389 enterJsAsync();
1390 _handle = JS('int', 'self.setInterval(#, #)',
1391 convertDartClosureToJS(() { callback(this); }, 0),
1392 milliseconds);
1393 } else {
1394 throw new UnsupportedError("Periodic timer.");
1395 }
1396 }
1397
1398 void cancel() {
1399 if (hasTimer()) {
1400 if (_inEventLoop) {
1401 throw new UnsupportedError("Timer in event loop cannot be canceled.");
1402 }
1403 if (_handle == null) return;
1404 leaveJsAsync();
1405 if (_once) {
1406 JS('void', 'self.clearTimeout(#)', _handle);
1407 } else {
1408 JS('void', 'self.clearInterval(#)', _handle);
1409 }
1410 _handle = null;
1411 } else {
1412 throw new UnsupportedError("Canceling a timer.");
1413 }
1414 }
1415
1416 bool get isActive => _handle != null;
1417 }
1418
1419 bool hasTimer() {
1420 requiresPreamble();
1421 return JS('', 'self.setTimeout') != null;
1422 }
1423
1424
1425 /**
1426 * Implementation class for [Capability].
1427 *
1428 * It has the same name to make it harder for users to distinguish.
1429 */
1430 class CapabilityImpl implements Capability {
1431 /** Internal random secret identifying the capability. */
1432 final int _id;
1433
1434 CapabilityImpl() : this._internal(random64());
1435
1436 CapabilityImpl._internal(this._id);
1437
1438 int get hashCode {
1439 // Thomas Wang 32 bit Mix.
1440 // http://www.concentric.net/~Ttwang/tech/inthash.htm
1441 // (via https://gist.github.com/badboy/6267743)
1442 int hash = _id;
1443 hash = (hash >> 0) ^ (hash ~/ 0x100000000); // To 32 bit from ~64.
1444 hash = (~hash + (hash << 15)) & 0xFFFFFFFF;
1445 hash ^= hash >> 12;
1446 hash = (hash * 5) & 0xFFFFFFFF;
1447 hash ^= hash >> 4;
1448 hash = (hash * 2057) & 0xFFFFFFFF;
1449 hash ^= hash >> 16;
1450 return hash;
1451 }
1452
1453 bool operator==(Object other) {
1454 if (identical(other, this)) return true;
1455 if (other is CapabilityImpl) {
1456 return identical(_id, other._id);
1457 }
1458 return false;
1459 }
1460 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698