OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart._isolate_helper; | 5 library dart._isolate_helper; |
6 | 6 |
7 import 'dart:_js_embedded_names' show | 7 import 'dart:_js_embedded_names' |
8 CLASS_ID_EXTRACTOR, | 8 show |
9 CLASS_FIELDS_EXTRACTOR, | 9 CLASS_ID_EXTRACTOR, |
10 CURRENT_SCRIPT, | 10 CLASS_FIELDS_EXTRACTOR, |
11 GLOBAL_FUNCTIONS, | 11 CURRENT_SCRIPT, |
12 INITIALIZE_EMPTY_INSTANCE, | 12 GLOBAL_FUNCTIONS, |
13 INSTANCE_FROM_CLASS_ID; | 13 INITIALIZE_EMPTY_INSTANCE, |
| 14 INSTANCE_FROM_CLASS_ID; |
14 | 15 |
15 import 'dart:async'; | 16 import 'dart:async'; |
16 import 'dart:collection' show Queue, HashMap; | 17 import 'dart:collection' show Queue, HashMap; |
17 import 'dart:isolate'; | 18 import 'dart:isolate'; |
18 import 'dart:_native_typed_data' show NativeByteBuffer, NativeTypedData; | 19 import 'dart:_native_typed_data' show NativeByteBuffer, NativeTypedData; |
19 | 20 |
20 import 'dart:_js_helper' show | 21 import 'dart:_js_helper' show InternalMap, Null, Primitives, random64; |
21 InternalMap, | |
22 Null, | |
23 Primitives, | |
24 random64; | |
25 | 22 |
26 import 'dart:_foreign_helper' show JS, | 23 import 'dart:_foreign_helper' |
27 JS_CREATE_ISOLATE, | 24 show |
28 JS_CURRENT_ISOLATE_CONTEXT, | 25 JS, |
29 JS_CURRENT_ISOLATE, | 26 JS_CREATE_ISOLATE, |
30 JS_EMBEDDED_GLOBAL, | 27 JS_CURRENT_ISOLATE_CONTEXT, |
31 JS_SET_CURRENT_ISOLATE, | 28 JS_CURRENT_ISOLATE, |
32 IsolateContext; | 29 JS_EMBEDDED_GLOBAL, |
| 30 JS_SET_CURRENT_ISOLATE, |
| 31 IsolateContext; |
33 | 32 |
34 import 'dart:_interceptors' show Interceptor, | 33 import 'dart:_interceptors' |
35 JSArray, | 34 show |
36 JSExtendableArray, | 35 Interceptor, |
37 JSFixedArray, | 36 JSArray, |
38 JSIndexable, | 37 JSExtendableArray, |
39 JSMutableArray, | 38 JSFixedArray, |
40 JSObject; | 39 JSIndexable, |
41 | 40 JSMutableArray, |
| 41 JSObject; |
42 | 42 |
43 part 'isolate_serialization.dart'; | 43 part 'isolate_serialization.dart'; |
44 | 44 |
45 /** | 45 /** |
46 * Called by the compiler to support switching | 46 * Called by the compiler to support switching |
47 * between isolates when we get a callback from the DOM. | 47 * between isolates when we get a callback from the DOM. |
48 */ | 48 */ |
49 _callInIsolate(_IsolateContext isolate, Function function) { | 49 _callInIsolate(_IsolateContext isolate, Function function) { |
50 var result = isolate.eval(function); | 50 var result = isolate.eval(function); |
51 _globalState.topEventLoop.run(); | 51 _globalState.topEventLoop.run(); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
103 if (_globalState.isWorker) return; | 103 if (_globalState.isWorker) return; |
104 final rootContext = new _IsolateContext(); | 104 final rootContext = new _IsolateContext(); |
105 _globalState.rootContext = rootContext; | 105 _globalState.rootContext = rootContext; |
106 | 106 |
107 // BUG(5151491): Setting currentContext should not be necessary, but | 107 // BUG(5151491): Setting currentContext should not be necessary, but |
108 // because closures passed to the DOM as event handlers do not bind their | 108 // because closures passed to the DOM as event handlers do not bind their |
109 // isolate automatically we try to give them a reasonable context to live in | 109 // isolate automatically we try to give them a reasonable context to live in |
110 // by having a "default" isolate (the first one created). | 110 // by having a "default" isolate (the first one created). |
111 _globalState.currentContext = rootContext; | 111 _globalState.currentContext = rootContext; |
112 if (entry is _MainFunctionArgs) { | 112 if (entry is _MainFunctionArgs) { |
113 rootContext.eval(() { entry(args); }); | 113 rootContext.eval(() { |
| 114 entry(args); |
| 115 }); |
114 } else if (entry is _MainFunctionArgsMessage) { | 116 } else if (entry is _MainFunctionArgsMessage) { |
115 rootContext.eval(() { entry(args, null); }); | 117 rootContext.eval(() { |
| 118 entry(args, null); |
| 119 }); |
116 } else { | 120 } else { |
117 rootContext.eval(entry); | 121 rootContext.eval(entry); |
118 } | 122 } |
119 _globalState.topEventLoop.run(); | 123 _globalState.topEventLoop.run(); |
120 } | 124 } |
121 | 125 |
122 /******************************************************** | 126 /******************************************************** |
123 Inserted from lib/isolate/dart2js/isolateimpl.dart | 127 Inserted from lib/isolate/dart2js/isolateimpl.dart |
124 ********************************************************/ | 128 ********************************************************/ |
125 | 129 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
159 | 163 |
160 set _globalState(_Manager val) { | 164 set _globalState(_Manager val) { |
161 // TODO(vsm): This was changed from init.globalState. | 165 // TODO(vsm): This was changed from init.globalState. |
162 // See: https://github.com/dart-lang/dev_compiler/issues/164 | 166 // See: https://github.com/dart-lang/dev_compiler/issues/164 |
163 JS("void", "dart.globalState = #", val); | 167 JS("void", "dart.globalState = #", val); |
164 } | 168 } |
165 | 169 |
166 /** State associated with the current manager. See [globalState]. */ | 170 /** State associated with the current manager. See [globalState]. */ |
167 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? | 171 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? |
168 class _Manager { | 172 class _Manager { |
169 | |
170 /** Next available isolate id within this [_Manager]. */ | 173 /** Next available isolate id within this [_Manager]. */ |
171 int nextIsolateId = 0; | 174 int nextIsolateId = 0; |
172 | 175 |
173 /** id assigned to this [_Manager]. */ | 176 /** id assigned to this [_Manager]. */ |
174 int currentManagerId = 0; | 177 int currentManagerId = 0; |
175 | 178 |
176 /** | 179 /** |
177 * Next available manager id. Only used by the main manager to assign a unique | 180 * Next available manager id. Only used by the main manager to assign a unique |
178 * id to each manager created by it. | 181 * id to each manager created by it. |
179 */ | 182 */ |
(...skipping 27 matching lines...) Expand all Loading... |
207 * Registry of isolates. Isolates must be registered if, and only if, receive | 210 * Registry of isolates. Isolates must be registered if, and only if, receive |
208 * ports are alive. Normally no open receive-ports means that the isolate is | 211 * ports are alive. Normally no open receive-ports means that the isolate is |
209 * dead, but DOM callbacks could resurrect it. | 212 * dead, but DOM callbacks could resurrect it. |
210 */ | 213 */ |
211 Map<int, _IsolateContext> isolates; | 214 Map<int, _IsolateContext> isolates; |
212 | 215 |
213 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */ | 216 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */ |
214 _MainManagerStub mainManager; | 217 _MainManagerStub mainManager; |
215 | 218 |
216 /// Registry of active Web Workers. Only used in the main [_Manager]. | 219 /// Registry of active Web Workers. Only used in the main [_Manager]. |
217 Map<int, dynamic /* Worker */> managers; | 220 Map<int, dynamic /* Worker */ > managers; |
218 | 221 |
219 /** The entry point given by [startRootIsolate]. */ | 222 /** The entry point given by [startRootIsolate]. */ |
220 final Function entry; | 223 final Function entry; |
221 | 224 |
222 _Manager(this.entry) { | 225 _Manager(this.entry) { |
223 _nativeDetectEnvironment(); | 226 _nativeDetectEnvironment(); |
224 topEventLoop = new _EventLoop(); | 227 topEventLoop = new _EventLoop(); |
225 isolates = new Map<int, _IsolateContext>(); | 228 isolates = new Map<int, _IsolateContext>(); |
226 managers = new Map<int, dynamic>(); | 229 managers = new Map<int, dynamic>(); |
227 if (isWorker) { // "if we are not the main manager ourself" is the intent. | 230 if (isWorker) { |
| 231 // "if we are not the main manager ourself" is the intent. |
228 mainManager = new _MainManagerStub(); | 232 mainManager = new _MainManagerStub(); |
229 _nativeInitWorkerMessageHandler(); | 233 _nativeInitWorkerMessageHandler(); |
230 } | 234 } |
231 } | 235 } |
232 | 236 |
233 void _nativeDetectEnvironment() { | 237 void _nativeDetectEnvironment() { |
234 bool isWindowDefined = globalWindow != null; | 238 bool isWindowDefined = globalWindow != null; |
235 bool isWorkerDefined = globalWorker != null; | 239 bool isWorkerDefined = globalWorker != null; |
236 | 240 |
237 isWorker = !isWindowDefined && globalPostMessageDefined; | 241 isWorker = !isWindowDefined && globalPostMessageDefined; |
238 supportsWorkers = isWorker | 242 supportsWorkers = |
239 || (isWorkerDefined && IsolateNatives.thisScript != null); | 243 isWorker || (isWorkerDefined && IsolateNatives.thisScript != null); |
240 fromCommandLine = !isWindowDefined && !isWorker; | 244 fromCommandLine = !isWindowDefined && !isWorker; |
241 } | 245 } |
242 | 246 |
243 void _nativeInitWorkerMessageHandler() { | 247 void _nativeInitWorkerMessageHandler() { |
244 var function = JS('', | 248 var function = JS( |
| 249 '', |
245 "(function (f, a) { return function (e) { f(a, e); }})(#, #)", | 250 "(function (f, a) { return function (e) { f(a, e); }})(#, #)", |
246 IsolateNatives._processWorkerMessage, | 251 IsolateNatives._processWorkerMessage, |
247 mainManager); | 252 mainManager); |
248 JS("void", r"#.onmessage = #", global, function); | 253 JS("void", r"#.onmessage = #", global, function); |
249 // We ensure dartPrint is defined so that the implementation of the Dart | 254 // We ensure dartPrint is defined so that the implementation of the Dart |
250 // print method knows what to call. | 255 // print method knows what to call. |
251 JS('', '''#.dartPrint = #.dartPrint || (function(serialize) { | 256 JS( |
| 257 '', |
| 258 '''#.dartPrint = #.dartPrint || (function(serialize) { |
252 return function (object) { | 259 return function (object) { |
253 var _self = #; | 260 var _self = #; |
254 if (_self.console && _self.console.log) { | 261 if (_self.console && _self.console.log) { |
255 _self.console.log(object) | 262 _self.console.log(object) |
256 } else { | 263 } else { |
257 _self.postMessage(serialize(object)); | 264 _self.postMessage(serialize(object)); |
258 } | 265 } |
259 } | 266 } |
260 })(#)''', global, global, global, _serializePrintMessage); | 267 })(#)''', |
| 268 global, |
| 269 global, |
| 270 global, |
| 271 _serializePrintMessage); |
261 } | 272 } |
262 | 273 |
263 static _serializePrintMessage(object) { | 274 static _serializePrintMessage(object) { |
264 return _serializeMessage({"command": "print", "msg": object}); | 275 return _serializeMessage({"command": "print", "msg": object}); |
265 } | 276 } |
266 | 277 |
267 /** | 278 /** |
268 * Close the worker running this code if all isolates are done and | 279 * Close the worker running this code if all isolates are done and |
269 * there are no active async JavaScript tasks still running. | 280 * there are no active async JavaScript tasks still running. |
270 */ | 281 */ |
271 void maybeCloseWorker() { | 282 void maybeCloseWorker() { |
272 if (isWorker | 283 if (isWorker && isolates.isEmpty && topEventLoop._activeJsAsyncCount == 0) { |
273 && isolates.isEmpty | |
274 && topEventLoop._activeJsAsyncCount == 0) { | |
275 mainManager.postMessage(_serializeMessage({'command': 'close'})); | 284 mainManager.postMessage(_serializeMessage({'command': 'close'})); |
276 } | 285 } |
277 } | 286 } |
278 } | 287 } |
279 | 288 |
280 /** Context information tracked for each isolate. */ | 289 /** Context information tracked for each isolate. */ |
281 class _IsolateContext implements IsolateContext { | 290 class _IsolateContext implements IsolateContext { |
282 /** Current isolate id. */ | 291 /** Current isolate id. */ |
283 final int id = _globalState.nextIsolateId++; | 292 final int id = _globalState.nextIsolateId++; |
284 | 293 |
285 /** Registry of receive ports currently active on this isolate. */ | 294 /** Registry of receive ports currently active on this isolate. */ |
286 final Map<int, RawReceivePortImpl> ports = new Map<int, RawReceivePortImpl>(); | 295 final Map<int, RawReceivePortImpl> ports = new Map<int, RawReceivePortImpl>(); |
287 | 296 |
288 /** Registry of weak receive ports currently active on this isolate. */ | 297 /** Registry of weak receive ports currently active on this isolate. */ |
289 final Set<int> weakPorts = new Set<int>(); | 298 final Set<int> weakPorts = new Set<int>(); |
290 | 299 |
291 /** Holds isolate globals (statics and top-level properties). */ | 300 /** Holds isolate globals (statics and top-level properties). */ |
292 // native object containing all globals of an isolate. | 301 // native object containing all globals of an isolate. |
293 final isolateStatics = JS_CREATE_ISOLATE(); | 302 final isolateStatics = JS_CREATE_ISOLATE(); |
294 | 303 |
295 final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort(); | 304 final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort(); |
296 | 305 |
297 final Capability pauseCapability = new Capability(); | 306 final Capability pauseCapability = new Capability(); |
298 final Capability terminateCapability = new Capability(); // License to kill. | 307 final Capability terminateCapability = new Capability(); // License to kill. |
299 | 308 |
300 /// Boolean flag set when the initial method of the isolate has been executed. | 309 /// Boolean flag set when the initial method of the isolate has been executed. |
301 /// | 310 /// |
302 /// Used to avoid considering the isolate dead when it has no open | 311 /// Used to avoid considering the isolate dead when it has no open |
303 /// receive ports and no scheduled timers, because it hasn't had time to | 312 /// receive ports and no scheduled timers, because it hasn't had time to |
304 /// create them yet. | 313 /// create them yet. |
305 bool initialized = false; | 314 bool initialized = false; |
306 | 315 |
307 // TODO(lrn): Store these in single "PauseState" object, so they don't take | 316 // TODO(lrn): Store these in single "PauseState" object, so they don't take |
308 // up as much room when not pausing. | 317 // up as much room when not pausing. |
(...skipping 29 matching lines...) Expand all Loading... |
338 if (pauseTokens.add(resume) && !isPaused) { | 347 if (pauseTokens.add(resume) && !isPaused) { |
339 isPaused = true; | 348 isPaused = true; |
340 } | 349 } |
341 _updateGlobalState(); | 350 _updateGlobalState(); |
342 } | 351 } |
343 | 352 |
344 void removePause(Capability resume) { | 353 void removePause(Capability resume) { |
345 if (!isPaused) return; | 354 if (!isPaused) return; |
346 pauseTokens.remove(resume); | 355 pauseTokens.remove(resume); |
347 if (pauseTokens.isEmpty) { | 356 if (pauseTokens.isEmpty) { |
348 while(delayedEvents.isNotEmpty) { | 357 while (delayedEvents.isNotEmpty) { |
349 _IsolateEvent event = delayedEvents.removeLast(); | 358 _IsolateEvent event = delayedEvents.removeLast(); |
350 _globalState.topEventLoop.prequeue(event); | 359 _globalState.topEventLoop.prequeue(event); |
351 } | 360 } |
352 isPaused = false; | 361 isPaused = false; |
353 } | 362 } |
354 _updateGlobalState(); | 363 _updateGlobalState(); |
355 } | 364 } |
356 | 365 |
357 void addDoneListener(SendPort responsePort) { | 366 void addDoneListener(SendPort responsePort) { |
358 if (doneHandlers == null) { | 367 if (doneHandlers == null) { |
(...skipping 10 matching lines...) Expand all Loading... |
369 doneHandlers.remove(responsePort); | 378 doneHandlers.remove(responsePort); |
370 } | 379 } |
371 | 380 |
372 void setErrorsFatal(Capability authentification, bool errorsAreFatal) { | 381 void setErrorsFatal(Capability authentification, bool errorsAreFatal) { |
373 if (terminateCapability != authentification) return; | 382 if (terminateCapability != authentification) return; |
374 this.errorsAreFatal = errorsAreFatal; | 383 this.errorsAreFatal = errorsAreFatal; |
375 } | 384 } |
376 | 385 |
377 void handlePing(SendPort responsePort, int pingType) { | 386 void handlePing(SendPort responsePort, int pingType) { |
378 if (pingType == Isolate.IMMEDIATE || | 387 if (pingType == Isolate.IMMEDIATE || |
379 (pingType == Isolate.BEFORE_NEXT_EVENT && | 388 (pingType == Isolate.BEFORE_NEXT_EVENT && !_isExecutingEvent)) { |
380 !_isExecutingEvent)) { | |
381 responsePort.send(null); | 389 responsePort.send(null); |
382 return; | 390 return; |
383 } | 391 } |
384 void respond() { responsePort.send(null); } | 392 void respond() { |
| 393 responsePort.send(null); |
| 394 } |
| 395 |
385 assert(pingType == Isolate.BEFORE_NEXT_EVENT); | 396 assert(pingType == Isolate.BEFORE_NEXT_EVENT); |
386 if (_scheduledControlEvents == null) { | 397 if (_scheduledControlEvents == null) { |
387 _scheduledControlEvents = new Queue(); | 398 _scheduledControlEvents = new Queue(); |
388 } | 399 } |
389 _scheduledControlEvents.addLast(respond); | 400 _scheduledControlEvents.addLast(respond); |
390 } | 401 } |
391 | 402 |
392 void handleKill(Capability authentification, int priority) { | 403 void handleKill(Capability authentification, int priority) { |
393 if (this.terminateCapability != authentification) return; | 404 if (this.terminateCapability != authentification) return; |
394 if (priority == Isolate.IMMEDIATE || | 405 if (priority == Isolate.IMMEDIATE || |
395 (priority == Isolate.BEFORE_NEXT_EVENT && | 406 (priority == Isolate.BEFORE_NEXT_EVENT && !_isExecutingEvent)) { |
396 !_isExecutingEvent)) { | |
397 kill(); | 407 kill(); |
398 return; | 408 return; |
399 } | 409 } |
400 assert(priority == Isolate.BEFORE_NEXT_EVENT); | 410 assert(priority == Isolate.BEFORE_NEXT_EVENT); |
401 if (_scheduledControlEvents == null) { | 411 if (_scheduledControlEvents == null) { |
402 _scheduledControlEvents = new Queue(); | 412 _scheduledControlEvents = new Queue(); |
403 } | 413 } |
404 _scheduledControlEvents.addLast(kill); | 414 _scheduledControlEvents.addLast(kill); |
405 } | 415 } |
406 | 416 |
(...skipping 17 matching lines...) Expand all Loading... |
424 } | 434 } |
425 if (JS('bool', '#.console && #.console.error', global, global)) { | 435 if (JS('bool', '#.console && #.console.error', global, global)) { |
426 JS('void', '#.console.error(#, #)', global, error, stackTrace); | 436 JS('void', '#.console.error(#, #)', global, error, stackTrace); |
427 } else { | 437 } else { |
428 print(error); | 438 print(error); |
429 if (stackTrace != null) print(stackTrace); | 439 if (stackTrace != null) print(stackTrace); |
430 } | 440 } |
431 return; | 441 return; |
432 } | 442 } |
433 List message = new List(2) | 443 List message = new List(2) |
434 ..[0] = error.toString() | 444 ..[0] = error.toString() |
435 ..[1] = (stackTrace == null) ? null : stackTrace.toString(); | 445 ..[1] = (stackTrace == null) ? null : stackTrace.toString(); |
436 for (SendPort port in errorPorts) port.send(message); | 446 for (SendPort port in errorPorts) port.send(message); |
437 } | 447 } |
438 | 448 |
439 /** | 449 /** |
440 * Run [code] in the context of the isolate represented by [this]. | 450 * Run [code] in the context of the isolate represented by [this]. |
441 */ | 451 */ |
442 dynamic eval(Function code) { | 452 dynamic eval(Function code) { |
443 var old = _globalState.currentContext; | 453 var old = _globalState.currentContext; |
444 _globalState.currentContext = this; | 454 _globalState.currentContext = this; |
445 this._setGlobals(); | 455 this._setGlobals(); |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 RawReceivePortImpl lookup(int portId) => ports[portId]; | 527 RawReceivePortImpl lookup(int portId) => ports[portId]; |
518 | 528 |
519 void _addRegistration(int portId, RawReceivePortImpl port) { | 529 void _addRegistration(int portId, RawReceivePortImpl port) { |
520 if (ports.containsKey(portId)) { | 530 if (ports.containsKey(portId)) { |
521 throw new Exception("Registry: ports must be registered only once."); | 531 throw new Exception("Registry: ports must be registered only once."); |
522 } | 532 } |
523 ports[portId] = port; | 533 ports[portId] = port; |
524 } | 534 } |
525 | 535 |
526 /** Registers a port on this isolate. */ | 536 /** Registers a port on this isolate. */ |
527 void register(int portId, RawReceivePortImpl port) { | 537 void register(int portId, RawReceivePortImpl port) { |
528 _addRegistration(portId, port); | 538 _addRegistration(portId, port); |
529 _updateGlobalState(); | 539 _updateGlobalState(); |
530 } | 540 } |
531 | 541 |
532 /** | 542 /** |
533 * Registers a weak port on this isolate. | 543 * Registers a weak port on this isolate. |
534 * | 544 * |
535 * The port does not keep the isolate active. | 545 * The port does not keep the isolate active. |
536 */ | 546 */ |
537 void registerWeak(int portId, RawReceivePortImpl port) { | 547 void registerWeak(int portId, RawReceivePortImpl port) { |
538 weakPorts.add(portId); | 548 weakPorts.add(portId); |
539 _addRegistration(portId, port); | 549 _addRegistration(portId, port); |
540 } | 550 } |
541 | 551 |
542 void _updateGlobalState() { | 552 void _updateGlobalState() { |
543 if (ports.length - weakPorts.length > 0 || isPaused || !initialized) { | 553 if (ports.length - weakPorts.length > 0 || isPaused || !initialized) { |
544 _globalState.isolates[id] = this; // indicate this isolate is active | 554 _globalState.isolates[id] = this; // indicate this isolate is active |
545 } else { | 555 } else { |
546 kill(); | 556 kill(); |
547 } | 557 } |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
600 void prequeue(_IsolateEvent event) { | 610 void prequeue(_IsolateEvent event) { |
601 events.addFirst(event); | 611 events.addFirst(event); |
602 } | 612 } |
603 | 613 |
604 _IsolateEvent dequeue() { | 614 _IsolateEvent dequeue() { |
605 if (events.isEmpty) return null; | 615 if (events.isEmpty) return null; |
606 return events.removeFirst(); | 616 return events.removeFirst(); |
607 } | 617 } |
608 | 618 |
609 void checkOpenReceivePortsFromCommandLine() { | 619 void checkOpenReceivePortsFromCommandLine() { |
610 if (_globalState.rootContext != null | 620 if (_globalState.rootContext != null && |
611 && _globalState.isolates.containsKey(_globalState.rootContext.id) | 621 _globalState.isolates.containsKey(_globalState.rootContext.id) && |
612 && _globalState.fromCommandLine | 622 _globalState.fromCommandLine && |
613 && _globalState.rootContext.ports.isEmpty) { | 623 _globalState.rootContext.ports.isEmpty) { |
614 // We want to reach here only on the main [_Manager] and only | 624 // We want to reach here only on the main [_Manager] and only |
615 // on the command-line. In the browser the isolate might | 625 // on the command-line. In the browser the isolate might |
616 // still be alive due to DOM callbacks, but the presumption is | 626 // still be alive due to DOM callbacks, but the presumption is |
617 // that on the command-line, no future events can be injected | 627 // that on the command-line, no future events can be injected |
618 // into the event queue once it's empty. Node has setTimeout | 628 // into the event queue once it's empty. Node has setTimeout |
619 // so this presumption is incorrect there. We think(?) that | 629 // so this presumption is incorrect there. We think(?) that |
620 // in d8 this assumption is valid. | 630 // in d8 this assumption is valid. |
621 throw new Exception("Program exited with open ReceivePorts."); | 631 throw new Exception("Program exited with open ReceivePorts."); |
622 } | 632 } |
623 } | 633 } |
(...skipping 14 matching lines...) Expand all Loading... |
638 * Runs multiple iterations of the run-loop. If possible, each iteration is | 648 * Runs multiple iterations of the run-loop. If possible, each iteration is |
639 * run asynchronously. | 649 * run asynchronously. |
640 */ | 650 */ |
641 void _runHelper() { | 651 void _runHelper() { |
642 if (globalWindow != null) { | 652 if (globalWindow != null) { |
643 // Run each iteration from the browser's top event loop. | 653 // Run each iteration from the browser's top event loop. |
644 next() { | 654 next() { |
645 if (!runIteration()) return; | 655 if (!runIteration()) return; |
646 Timer.run(next); | 656 Timer.run(next); |
647 } | 657 } |
| 658 |
648 next(); | 659 next(); |
649 } else { | 660 } else { |
650 // Run synchronously until no more iterations are available. | 661 // Run synchronously until no more iterations are available. |
651 while (runIteration()) {} | 662 while (runIteration()) {} |
652 } | 663 } |
653 } | 664 } |
654 | 665 |
655 /** | 666 /** |
656 * Call [_runHelper] but ensure that worker exceptions are propragated. | 667 * Call [_runHelper] but ensure that worker exceptions are propragated. |
657 */ | 668 */ |
658 void run() { | 669 void run() { |
659 if (!_globalState.isWorker) { | 670 if (!_globalState.isWorker) { |
660 _runHelper(); | 671 _runHelper(); |
661 } else { | 672 } else { |
662 try { | 673 try { |
663 _runHelper(); | 674 _runHelper(); |
664 } catch (e, trace) { | 675 } catch (e, trace) { |
665 _globalState.mainManager.postMessage(_serializeMessage( | 676 _globalState.mainManager.postMessage( |
666 {'command': 'error', 'msg': '$e\n$trace' })); | 677 _serializeMessage({'command': 'error', 'msg': '$e\n$trace'})); |
667 } | 678 } |
668 } | 679 } |
669 } | 680 } |
670 } | 681 } |
671 | 682 |
672 /** An event in the top-level event queue. */ | 683 /** An event in the top-level event queue. */ |
673 class _IsolateEvent { | 684 class _IsolateEvent { |
674 _IsolateContext isolate; | 685 _IsolateContext isolate; |
675 Function fn; | 686 Function fn; |
676 String message; | 687 String message; |
677 | 688 |
678 _IsolateEvent(this.isolate, this.fn, this.message); | 689 _IsolateEvent(this.isolate, this.fn, this.message); |
679 | 690 |
680 void process() { | 691 void process() { |
681 if (isolate.isPaused) { | 692 if (isolate.isPaused) { |
682 isolate.delayedEvents.add(this); | 693 isolate.delayedEvents.add(this); |
683 return; | 694 return; |
684 } | 695 } |
685 isolate.eval(fn); | 696 isolate.eval(fn); |
686 } | 697 } |
687 } | 698 } |
688 | 699 |
689 // "self" is a way to refer to the global context object that | 700 // "self" is a way to refer to the global context object that |
690 // works in HTML pages and in Web Workers. It does not work in d8, iojs | 701 // works in HTML pages and in Web Workers. It does not work in d8, iojs |
691 // and Firefox jsshell, because that would have been too easy. In iojs | 702 // and Firefox jsshell, because that would have been too easy. In iojs |
692 // "global" works. | 703 // "global" works. |
693 // | 704 // |
694 // See: http://www.w3.org/TR/workers/#the-global-scope | 705 // See: http://www.w3.org/TR/workers/#the-global-scope |
695 // and: http://www.w3.org/TR/Window/#dfn-self-attribute | 706 // and: http://www.w3.org/TR/Window/#dfn-self-attribute |
696 final global = | 707 final global = JS("", "typeof global == 'undefined' ? self : global"); |
697 JS("", "typeof global == 'undefined' ? self : global"); | |
698 | 708 |
699 /** A stub for interacting with the main manager. */ | 709 /** A stub for interacting with the main manager. */ |
700 class _MainManagerStub { | 710 class _MainManagerStub { |
701 void postMessage(msg) { | 711 void postMessage(msg) { |
702 JS("void", r"#.postMessage(#)", global, msg); | 712 JS("void", r"#.postMessage(#)", global, msg); |
703 } | 713 } |
704 } | 714 } |
705 | 715 |
706 const String _SPAWNED_SIGNAL = "spawned"; | 716 const String _SPAWNED_SIGNAL = "spawned"; |
707 const String _SPAWN_FAILED_SIGNAL = "spawn failed"; | 717 const String _SPAWN_FAILED_SIGNAL = "spawn failed"; |
708 | 718 |
709 get globalWindow { | 719 get globalWindow { |
710 return JS('', "#.window", global); | 720 return JS('', "#.window", global); |
711 } | 721 } |
712 | 722 |
713 get globalWorker { | 723 get globalWorker { |
714 return JS('', "#.Worker", global); | 724 return JS('', "#.Worker", global); |
715 } | 725 } |
| 726 |
716 bool get globalPostMessageDefined { | 727 bool get globalPostMessageDefined { |
717 return JS('bool', "!!#.postMessage", global); | 728 return JS('bool', "!!#.postMessage", global); |
718 } | 729 } |
719 | 730 |
720 typedef _MainFunction(); | 731 typedef _MainFunction(); |
721 typedef _MainFunctionArgs(args); | 732 typedef _MainFunctionArgs(args); |
722 typedef _MainFunctionArgsMessage(args, message); | 733 typedef _MainFunctionArgsMessage(args, message); |
723 | 734 |
724 /// Note: IsolateNatives depends on _globalState which is only set up correctly | 735 /// Note: IsolateNatives depends on _globalState which is only set up correctly |
725 /// when 'dart:isolate' has been imported. | 736 /// when 'dart:isolate' has been imported. |
726 class IsolateNatives { | 737 class IsolateNatives { |
727 | |
728 // We set [enableSpawnWorker] to true (not null) when calling isolate | 738 // We set [enableSpawnWorker] to true (not null) when calling isolate |
729 // primitives that require support for spawning workers. The field starts out | 739 // primitives that require support for spawning workers. The field starts out |
730 // by being null, and dart2js' type inference will track if it can have a | 740 // by being null, and dart2js' type inference will track if it can have a |
731 // non-null value. So by testing if this value is not null, we generate code | 741 // non-null value. So by testing if this value is not null, we generate code |
732 // that dart2js knows is dead when worker support isn't needed. | 742 // that dart2js knows is dead when worker support isn't needed. |
733 // TODO(herhut): Initialize this to false when able to track compile-time | 743 // TODO(herhut): Initialize this to false when able to track compile-time |
734 // constants. | 744 // constants. |
735 static var enableSpawnWorker; | 745 static var enableSpawnWorker; |
736 | 746 |
737 static String thisScript = computeThisScript(); | 747 static String thisScript = computeThisScript(); |
(...skipping 28 matching lines...) Expand all Loading... |
766 // TODO(ahe): The following is for supporting D8. We should move this code | 776 // TODO(ahe): The following is for supporting D8. We should move this code |
767 // to a helper library that is only loaded when testing on D8. | 777 // to a helper library that is only loaded when testing on D8. |
768 static String computeThisScriptD8() => computeThisScriptFromTrace(); | 778 static String computeThisScriptD8() => computeThisScriptFromTrace(); |
769 | 779 |
770 static String computeThisScriptFromTrace() { | 780 static String computeThisScriptFromTrace() { |
771 var stack = JS('String|Null', 'new Error().stack'); | 781 var stack = JS('String|Null', 'new Error().stack'); |
772 if (stack == null) { | 782 if (stack == null) { |
773 // According to Internet Explorer documentation, the stack | 783 // According to Internet Explorer documentation, the stack |
774 // property is not set until the exception is thrown. The stack | 784 // property is not set until the exception is thrown. The stack |
775 // property was not provided until IE10. | 785 // property was not provided until IE10. |
776 stack = JS('String|Null', | 786 stack = JS( |
777 '(function() {' | 787 'String|Null', |
778 'try { throw new Error() } catch(e) { return e.stack }' | 788 '(function() {' |
779 '})()'); | 789 'try { throw new Error() } catch(e) { return e.stack }' |
| 790 '})()'); |
780 if (stack == null) throw new UnsupportedError('No stack trace'); | 791 if (stack == null) throw new UnsupportedError('No stack trace'); |
781 } | 792 } |
782 var pattern, matches; | 793 var pattern, matches; |
783 | 794 |
784 // This pattern matches V8, Chrome, and Internet Explorer stack | 795 // This pattern matches V8, Chrome, and Internet Explorer stack |
785 // traces that look like this: | 796 // traces that look like this: |
786 // Error | 797 // Error |
787 // at methodName (URI:LINE:COLUMN) | 798 // at methodName (URI:LINE:COLUMN) |
788 pattern = JS('', | 799 pattern = |
789 r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")'); | 800 JS('', r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")'); |
790 | |
791 | 801 |
792 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); | 802 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); |
793 if (matches != null) return JS('String', '#[1]', matches); | 803 if (matches != null) return JS('String', '#[1]', matches); |
794 | 804 |
795 // This pattern matches Firefox stack traces that look like this: | 805 // This pattern matches Firefox stack traces that look like this: |
796 // methodName@URI:LINE | 806 // methodName@URI:LINE |
797 pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")'); | 807 pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")'); |
798 | 808 |
799 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); | 809 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); |
800 if (matches != null) return JS('String', '#[1]', matches); | 810 if (matches != null) return JS('String', '#[1]', matches); |
(...skipping 21 matching lines...) Expand all Loading... |
822 Function entryPoint = (functionName == null) | 832 Function entryPoint = (functionName == null) |
823 ? _globalState.entry | 833 ? _globalState.entry |
824 : _getJSFunctionFromName(functionName); | 834 : _getJSFunctionFromName(functionName); |
825 var args = msg['args']; | 835 var args = msg['args']; |
826 var message = _deserializeMessage(msg['msg']); | 836 var message = _deserializeMessage(msg['msg']); |
827 var isSpawnUri = msg['isSpawnUri']; | 837 var isSpawnUri = msg['isSpawnUri']; |
828 var startPaused = msg['startPaused']; | 838 var startPaused = msg['startPaused']; |
829 var replyTo = _deserializeMessage(msg['replyTo']); | 839 var replyTo = _deserializeMessage(msg['replyTo']); |
830 var context = new _IsolateContext(); | 840 var context = new _IsolateContext(); |
831 _globalState.topEventLoop.enqueue(context, () { | 841 _globalState.topEventLoop.enqueue(context, () { |
832 _startIsolate(entryPoint, args, message, | 842 _startIsolate( |
833 isSpawnUri, startPaused, replyTo); | 843 entryPoint, args, message, isSpawnUri, startPaused, replyTo); |
834 }, 'worker-start'); | 844 }, 'worker-start'); |
835 // Make sure we always have a current context in this worker. | 845 // Make sure we always have a current context in this worker. |
836 // TODO(7907): This is currently needed because we're using | 846 // TODO(7907): This is currently needed because we're using |
837 // Timers to implement Futures, and this isolate library | 847 // Timers to implement Futures, and this isolate library |
838 // implementation uses Futures. We should either stop using | 848 // implementation uses Futures. We should either stop using |
839 // Futures in this library, or re-adapt if Futures get a | 849 // Futures in this library, or re-adapt if Futures get a |
840 // different implementation. | 850 // different implementation. |
841 _globalState.currentContext = context; | 851 _globalState.currentContext = context; |
842 _globalState.topEventLoop.run(); | 852 _globalState.topEventLoop.run(); |
843 break; | 853 break; |
(...skipping 11 matching lines...) Expand all Loading... |
855 case 'close': | 865 case 'close': |
856 _globalState.managers.remove(workerIds[sender]); | 866 _globalState.managers.remove(workerIds[sender]); |
857 JS('void', '#.terminate()', sender); | 867 JS('void', '#.terminate()', sender); |
858 _globalState.topEventLoop.run(); | 868 _globalState.topEventLoop.run(); |
859 break; | 869 break; |
860 case 'log': | 870 case 'log': |
861 _log(msg['msg']); | 871 _log(msg['msg']); |
862 break; | 872 break; |
863 case 'print': | 873 case 'print': |
864 if (_globalState.isWorker) { | 874 if (_globalState.isWorker) { |
865 _globalState.mainManager.postMessage( | 875 _globalState.mainManager |
866 _serializeMessage({'command': 'print', 'msg': msg})); | 876 .postMessage(_serializeMessage({'command': 'print', 'msg': msg})); |
867 } else { | 877 } else { |
868 print(msg['msg']); | 878 print(msg['msg']); |
869 } | 879 } |
870 break; | 880 break; |
871 case 'error': | 881 case 'error': |
872 throw msg['msg']; | 882 throw msg['msg']; |
873 } | 883 } |
874 } | 884 } |
875 | 885 |
876 static handleSpawnWorkerRequest(msg) { | 886 static handleSpawnWorkerRequest(msg) { |
877 var replyPort = msg['replyPort']; | 887 var replyPort = msg['replyPort']; |
878 spawn(msg['functionName'], msg['uri'], | 888 spawn(msg['functionName'], msg['uri'], msg['args'], msg['msg'], false, |
879 msg['args'], msg['msg'], | 889 msg['isSpawnUri'], msg['startPaused']).then((msg) { |
880 false, msg['isSpawnUri'], msg['startPaused']).then((msg) { | |
881 replyPort.send(msg); | 890 replyPort.send(msg); |
882 }, onError: (String errorMessage) { | 891 }, onError: (String errorMessage) { |
883 replyPort.send([_SPAWN_FAILED_SIGNAL, errorMessage]); | 892 replyPort.send([_SPAWN_FAILED_SIGNAL, errorMessage]); |
884 }); | 893 }); |
885 } | 894 } |
886 | 895 |
887 /** Log a message, forwarding to the main [_Manager] if appropriate. */ | 896 /** Log a message, forwarding to the main [_Manager] if appropriate. */ |
888 static _log(msg) { | 897 static _log(msg) { |
889 if (_globalState.isWorker) { | 898 if (_globalState.isWorker) { |
890 _globalState.mainManager.postMessage( | 899 _globalState.mainManager |
891 _serializeMessage({'command': 'log', 'msg': msg })); | 900 .postMessage(_serializeMessage({'command': 'log', 'msg': msg})); |
892 } else { | 901 } else { |
893 try { | 902 try { |
894 _consoleLog(msg); | 903 _consoleLog(msg); |
895 } catch (e, trace) { | 904 } catch (e, trace) { |
896 throw new Exception(trace); | 905 throw new Exception(trace); |
897 } | 906 } |
898 } | 907 } |
899 } | 908 } |
900 | 909 |
901 static void _consoleLog(msg) { | 910 static void _consoleLog(msg) { |
(...skipping 12 matching lines...) Expand all Loading... |
914 */ | 923 */ |
915 static String _getJSFunctionName(Function f) { | 924 static String _getJSFunctionName(Function f) { |
916 return JS("String|Null", r'#.$name', f); | 925 return JS("String|Null", r'#.$name', f); |
917 } | 926 } |
918 | 927 |
919 /** Create a new JavaScript object instance given its constructor. */ | 928 /** Create a new JavaScript object instance given its constructor. */ |
920 static dynamic _allocate(var ctor) { | 929 static dynamic _allocate(var ctor) { |
921 return JS("", "new #()", ctor); | 930 return JS("", "new #()", ctor); |
922 } | 931 } |
923 | 932 |
924 static Future<List> spawnFunction(void topLevelFunction(message), | 933 static Future<List> spawnFunction( |
925 var message, | 934 void topLevelFunction(message), var message, bool startPaused) { |
926 bool startPaused) { | |
927 IsolateNatives.enableSpawnWorker = true; | 935 IsolateNatives.enableSpawnWorker = true; |
928 final name = _getJSFunctionName(topLevelFunction); | 936 final name = _getJSFunctionName(topLevelFunction); |
929 if (name == null) { | 937 if (name == null) { |
930 throw new UnsupportedError( | 938 throw new UnsupportedError("only top-level functions can be spawned."); |
931 "only top-level functions can be spawned."); | |
932 } | 939 } |
933 bool isLight = false; | 940 bool isLight = false; |
934 bool isSpawnUri = false; | 941 bool isSpawnUri = false; |
935 return spawn(name, null, null, message, isLight, isSpawnUri, startPaused); | 942 return spawn(name, null, null, message, isLight, isSpawnUri, startPaused); |
936 } | 943 } |
937 | 944 |
938 static Future<List> spawnUri(Uri uri, List<String> args, var message, | 945 static Future<List> spawnUri( |
939 bool startPaused) { | 946 Uri uri, List<String> args, var message, bool startPaused) { |
940 IsolateNatives.enableSpawnWorker = true; | 947 IsolateNatives.enableSpawnWorker = true; |
941 bool isLight = false; | 948 bool isLight = false; |
942 bool isSpawnUri = true; | 949 bool isSpawnUri = true; |
943 return spawn(null, uri.toString(), args, message, | 950 return spawn( |
944 isLight, isSpawnUri, startPaused); | 951 null, uri.toString(), args, message, isLight, isSpawnUri, startPaused); |
945 } | 952 } |
946 | 953 |
947 // TODO(sigmund): clean up above, after we make the new API the default: | 954 // TODO(sigmund): clean up above, after we make the new API the default: |
948 | 955 |
949 /// If [uri] is `null` it is replaced with the current script. | 956 /// If [uri] is `null` it is replaced with the current script. |
950 static Future<List> spawn(String functionName, String uri, | 957 static Future<List> spawn(String functionName, String uri, List<String> args, |
951 List<String> args, message, | 958 message, bool isLight, bool isSpawnUri, bool startPaused) { |
952 bool isLight, bool isSpawnUri, bool startPaused) { | |
953 // Assume that the compiled version of the Dart file lives just next to the | 959 // Assume that the compiled version of the Dart file lives just next to the |
954 // dart file. | 960 // dart file. |
955 // TODO(floitsch): support precompiled version of dart2js output. | 961 // TODO(floitsch): support precompiled version of dart2js output. |
956 if (uri != null && uri.endsWith(".dart")) uri += ".js"; | 962 if (uri != null && uri.endsWith(".dart")) uri += ".js"; |
957 | 963 |
958 ReceivePort port = new ReceivePort(); | 964 ReceivePort port = new ReceivePort(); |
959 Completer<List> completer = new Completer(); | 965 Completer<List> completer = new Completer(); |
960 port.first.then((msg) { | 966 port.first.then((msg) { |
961 if (msg[0] == _SPAWNED_SIGNAL) { | 967 if (msg[0] == _SPAWNED_SIGNAL) { |
962 completer.complete(msg); | 968 completer.complete(msg); |
963 } else { | 969 } else { |
964 assert(msg[0] == _SPAWN_FAILED_SIGNAL); | 970 assert(msg[0] == _SPAWN_FAILED_SIGNAL); |
965 completer.completeError(msg[1]); | 971 completer.completeError(msg[1]); |
966 } | 972 } |
967 }); | 973 }); |
968 | 974 |
969 SendPort signalReply = port.sendPort; | 975 SendPort signalReply = port.sendPort; |
970 | 976 |
971 if (_globalState.useWorkers && !isLight) { | 977 if (_globalState.useWorkers && !isLight) { |
972 _startWorker( | 978 _startWorker(functionName, uri, args, message, isSpawnUri, startPaused, |
973 functionName, uri, args, message, isSpawnUri, startPaused, | |
974 signalReply, (String message) => completer.completeError(message)); | 979 signalReply, (String message) => completer.completeError(message)); |
975 } else { | 980 } else { |
976 _startNonWorker( | 981 _startNonWorker(functionName, uri, args, message, isSpawnUri, startPaused, |
977 functionName, uri, args, message, isSpawnUri, startPaused, | |
978 signalReply); | 982 signalReply); |
979 } | 983 } |
980 return completer.future; | 984 return completer.future; |
981 } | 985 } |
982 | 986 |
983 static void _startWorker( | 987 static void _startWorker( |
984 String functionName, String uri, | 988 String functionName, |
985 List<String> args, message, | 989 String uri, |
| 990 List<String> args, |
| 991 message, |
986 bool isSpawnUri, | 992 bool isSpawnUri, |
987 bool startPaused, | 993 bool startPaused, |
988 SendPort replyPort, | 994 SendPort replyPort, |
989 void onError(String message)) { | 995 void onError(String message)) { |
990 // Make sure that the args list is a fresh generic list. A newly spawned | 996 // Make sure that the args list is a fresh generic list. A newly spawned |
991 // isolate should be able to assume that the arguments list is an | 997 // isolate should be able to assume that the arguments list is an |
992 // extendable list. | 998 // extendable list. |
993 if (args != null) args = new List<String>.from(args); | 999 if (args != null) args = new List<String>.from(args); |
994 if (_globalState.isWorker) { | 1000 if (_globalState.isWorker) { |
995 _globalState.mainManager.postMessage(_serializeMessage({ | 1001 _globalState.mainManager.postMessage(_serializeMessage({ |
996 'command': 'spawn-worker', | 1002 'command': 'spawn-worker', |
997 'functionName': functionName, | 1003 'functionName': functionName, |
998 'args': args, | 1004 'args': args, |
999 'msg': message, | 1005 'msg': message, |
1000 'uri': uri, | 1006 'uri': uri, |
1001 'isSpawnUri': isSpawnUri, | 1007 'isSpawnUri': isSpawnUri, |
1002 'startPaused': startPaused, | 1008 'startPaused': startPaused, |
1003 'replyPort': replyPort})); | 1009 'replyPort': replyPort |
| 1010 })); |
1004 } else { | 1011 } else { |
1005 _spawnWorker(functionName, uri, args, message, | 1012 _spawnWorker(functionName, uri, args, message, isSpawnUri, startPaused, |
1006 isSpawnUri, startPaused, replyPort, onError); | 1013 replyPort, onError); |
1007 } | 1014 } |
1008 } | 1015 } |
1009 | 1016 |
1010 static void _startNonWorker( | 1017 static void _startNonWorker( |
1011 String functionName, String uri, | 1018 String functionName, |
1012 List<String> args, var message, | 1019 String uri, |
| 1020 List<String> args, |
| 1021 var message, |
1013 bool isSpawnUri, | 1022 bool isSpawnUri, |
1014 bool startPaused, | 1023 bool startPaused, |
1015 SendPort replyPort) { | 1024 SendPort replyPort) { |
1016 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. | 1025 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. |
1017 if (uri != null) { | 1026 if (uri != null) { |
1018 throw new UnsupportedError( | 1027 throw new UnsupportedError( |
1019 "Currently spawnUri is not supported without web workers."); | 1028 "Currently spawnUri is not supported without web workers."); |
1020 } | 1029 } |
1021 // Clone the message to enforce the restrictions we have on isolate | 1030 // Clone the message to enforce the restrictions we have on isolate |
1022 // messages. | 1031 // messages. |
1023 message = _clone(message); | 1032 message = _clone(message); |
1024 // Make sure that the args list is a fresh generic list. A newly spawned | 1033 // Make sure that the args list is a fresh generic list. A newly spawned |
1025 // isolate should be able to assume that the arguments list is an | 1034 // isolate should be able to assume that the arguments list is an |
1026 // extendable list. | 1035 // extendable list. |
1027 if (args != null) args = new List<String>.from(args); | 1036 if (args != null) args = new List<String>.from(args); |
1028 _globalState.topEventLoop.enqueue(new _IsolateContext(), () { | 1037 _globalState.topEventLoop.enqueue(new _IsolateContext(), () { |
1029 final func = _getJSFunctionFromName(functionName); | 1038 final func = _getJSFunctionFromName(functionName); |
1030 _startIsolate(func, args, message, isSpawnUri, startPaused, replyPort); | 1039 _startIsolate(func, args, message, isSpawnUri, startPaused, replyPort); |
1031 }, 'nonworker start'); | 1040 }, 'nonworker start'); |
1032 } | 1041 } |
1033 | 1042 |
1034 static Isolate get currentIsolate { | 1043 static Isolate get currentIsolate { |
1035 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); | 1044 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); |
1036 return new Isolate(context.controlPort.sendPort, | 1045 return new Isolate(context.controlPort.sendPort, |
1037 pauseCapability: context.pauseCapability, | 1046 pauseCapability: context.pauseCapability, |
1038 terminateCapability: context.terminateCapability); | 1047 terminateCapability: context.terminateCapability); |
1039 } | 1048 } |
1040 | 1049 |
1041 static void _startIsolate(Function topLevel, | 1050 static void _startIsolate(Function topLevel, List<String> args, message, |
1042 List<String> args, message, | 1051 bool isSpawnUri, bool startPaused, SendPort replyTo) { |
1043 bool isSpawnUri, | |
1044 bool startPaused, | |
1045 SendPort replyTo) { | |
1046 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); | 1052 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); |
1047 Primitives.initializeStatics(context.id); | 1053 Primitives.initializeStatics(context.id); |
1048 // The isolate's port does not keep the isolate open. | 1054 // The isolate's port does not keep the isolate open. |
1049 replyTo.send([_SPAWNED_SIGNAL, | 1055 replyTo.send([ |
1050 context.controlPort.sendPort, | 1056 _SPAWNED_SIGNAL, |
1051 context.pauseCapability, | 1057 context.controlPort.sendPort, |
1052 context.terminateCapability]); | 1058 context.pauseCapability, |
| 1059 context.terminateCapability |
| 1060 ]); |
1053 | 1061 |
1054 void runStartFunction() { | 1062 void runStartFunction() { |
1055 context.initialized = true; | 1063 context.initialized = true; |
1056 if (!isSpawnUri) { | 1064 if (!isSpawnUri) { |
1057 topLevel(message); | 1065 topLevel(message); |
1058 } else if (topLevel is _MainFunctionArgsMessage) { | 1066 } else if (topLevel is _MainFunctionArgsMessage) { |
1059 topLevel(args, message); | 1067 topLevel(args, message); |
1060 } else if (topLevel is _MainFunctionArgs) { | 1068 } else if (topLevel is _MainFunctionArgs) { |
1061 topLevel(args); | 1069 topLevel(args); |
1062 } else { | 1070 } else { |
1063 topLevel(); | 1071 topLevel(); |
1064 } | 1072 } |
1065 } | 1073 } |
1066 | 1074 |
1067 if (startPaused) { | 1075 if (startPaused) { |
1068 context.addPause(context.pauseCapability, context.pauseCapability); | 1076 context.addPause(context.pauseCapability, context.pauseCapability); |
1069 _globalState.topEventLoop.enqueue(context, runStartFunction, | 1077 _globalState.topEventLoop |
1070 'start isolate'); | 1078 .enqueue(context, runStartFunction, 'start isolate'); |
1071 } else { | 1079 } else { |
1072 runStartFunction(); | 1080 runStartFunction(); |
1073 } | 1081 } |
1074 } | 1082 } |
1075 | 1083 |
1076 /** | 1084 /** |
1077 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor | 1085 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor |
1078 * name for the isolate entry point class. | 1086 * name for the isolate entry point class. |
1079 */ | 1087 */ |
1080 static void _spawnWorker(functionName, String uri, | 1088 static void _spawnWorker( |
1081 List<String> args, message, | 1089 functionName, |
1082 bool isSpawnUri, | 1090 String uri, |
1083 bool startPaused, | 1091 List<String> args, |
1084 SendPort replyPort, | 1092 message, |
1085 void onError(String message)) { | 1093 bool isSpawnUri, |
| 1094 bool startPaused, |
| 1095 SendPort replyPort, |
| 1096 void onError(String message)) { |
1086 if (uri == null) uri = thisScript; | 1097 if (uri == null) uri = thisScript; |
1087 final worker = JS('var', 'new Worker(#)', uri); | 1098 final worker = JS('var', 'new Worker(#)', uri); |
1088 // Trampolines are used when wanting to call a Dart closure from | 1099 // Trampolines are used when wanting to call a Dart closure from |
1089 // JavaScript. The helper function DART_CLOSURE_TO_JS only accepts | 1100 // JavaScript. The helper function DART_CLOSURE_TO_JS only accepts |
1090 // top-level or static methods, and the trampoline allows us to capture | 1101 // top-level or static methods, and the trampoline allows us to capture |
1091 // arguments and values which can be passed to a static method. | 1102 // arguments and values which can be passed to a static method. |
1092 final onerrorTrampoline = JS( | 1103 final onerrorTrampoline = JS( |
1093 '', | 1104 '', |
1094 ''' | 1105 ''' |
1095 (function (f, u, c) { | 1106 (function (f, u, c) { |
1096 return function(e) { | 1107 return function(e) { |
1097 return f(e, u, c) | 1108 return f(e, u, c) |
1098 } | 1109 } |
1099 })(#, #, #)''', | 1110 })(#, #, #)''', |
1100 workerOnError, uri, onError); | 1111 workerOnError, |
| 1112 uri, |
| 1113 onError); |
1101 JS('void', '#.onerror = #', worker, onerrorTrampoline); | 1114 JS('void', '#.onerror = #', worker, onerrorTrampoline); |
1102 | 1115 |
1103 var processWorkerMessageTrampoline = JS( | 1116 var processWorkerMessageTrampoline = JS( |
1104 '', | 1117 '', |
1105 """ | 1118 """ |
1106 (function (f, a) { | 1119 (function (f, a) { |
1107 return function (e) { | 1120 return function (e) { |
1108 // We can stop listening for errors when the first message is received as | 1121 // We can stop listening for errors when the first message is received as |
1109 // we only listen for messages to determine if the uri was bad. | 1122 // we only listen for messages to determine if the uri was bad. |
1110 e.onerror = null; | 1123 e.onerror = null; |
1111 return f(a, e); | 1124 return f(a, e); |
1112 } | 1125 } |
1113 })(#, #)""", | 1126 })(#, #)""", |
1114 _processWorkerMessage, | 1127 _processWorkerMessage, |
1115 worker); | 1128 worker); |
1116 JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline); | 1129 JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline); |
1117 var workerId = _globalState.nextManagerId++; | 1130 var workerId = _globalState.nextManagerId++; |
1118 // We also store the id on the worker itself so that we can unregister it. | 1131 // We also store the id on the worker itself so that we can unregister it. |
1119 workerIds[worker] = workerId; | 1132 workerIds[worker] = workerId; |
1120 _globalState.managers[workerId] = worker; | 1133 _globalState.managers[workerId] = worker; |
1121 JS('void', '#.postMessage(#)', worker, _serializeMessage({ | 1134 JS( |
1122 'command': 'start', | 1135 'void', |
1123 'id': workerId, | 1136 '#.postMessage(#)', |
1124 // Note: we serialize replyPort twice because the child worker needs to | 1137 worker, |
1125 // first deserialize the worker id, before it can correctly deserialize | 1138 _serializeMessage({ |
1126 // the port (port deserialization is sensitive to what is the current | 1139 'command': 'start', |
1127 // workerId). | 1140 'id': workerId, |
1128 'replyTo': _serializeMessage(replyPort), | 1141 // Note: we serialize replyPort twice because the child worker needs t
o |
1129 'args': args, | 1142 // first deserialize the worker id, before it can correctly deserializ
e |
1130 'msg': _serializeMessage(message), | 1143 // the port (port deserialization is sensitive to what is the current |
1131 'isSpawnUri': isSpawnUri, | 1144 // workerId). |
1132 'startPaused': startPaused, | 1145 'replyTo': _serializeMessage(replyPort), |
1133 'functionName': functionName })); | 1146 'args': args, |
| 1147 'msg': _serializeMessage(message), |
| 1148 'isSpawnUri': isSpawnUri, |
| 1149 'startPaused': startPaused, |
| 1150 'functionName': functionName |
| 1151 })); |
1134 } | 1152 } |
1135 | 1153 |
1136 static bool workerOnError( | 1154 static bool workerOnError( |
1137 /* Event */ event, | 1155 /* Event */ event, |
1138 String uri, | 1156 String uri, |
1139 void onError(String message)) { | 1157 void onError(String message)) { |
1140 // Attempt to shut up the browser, as the error has been handled. Chrome | 1158 // Attempt to shut up the browser, as the error has been handled. Chrome |
1141 // ignores this :-( | 1159 // ignores this :-( |
1142 JS('void', '#.preventDefault()', event); | 1160 JS('void', '#.preventDefault()', event); |
1143 String message = JS('String|Null', '#.message', event); | 1161 String message = JS('String|Null', '#.message', event); |
(...skipping 14 matching lines...) Expand all Loading... |
1158 ********************************************************/ | 1176 ********************************************************/ |
1159 | 1177 |
1160 /** Common functionality to all send ports. */ | 1178 /** Common functionality to all send ports. */ |
1161 abstract class _BaseSendPort implements SendPort { | 1179 abstract class _BaseSendPort implements SendPort { |
1162 /** Id for the destination isolate. */ | 1180 /** Id for the destination isolate. */ |
1163 final int _isolateId; | 1181 final int _isolateId; |
1164 | 1182 |
1165 const _BaseSendPort(this._isolateId); | 1183 const _BaseSendPort(this._isolateId); |
1166 | 1184 |
1167 void _checkReplyTo(SendPort replyTo) { | 1185 void _checkReplyTo(SendPort replyTo) { |
1168 if (replyTo != null | 1186 if (replyTo != null && |
1169 && replyTo is! _NativeJsSendPort | 1187 replyTo is! _NativeJsSendPort && |
1170 && replyTo is! _WorkerSendPort) { | 1188 replyTo is! _WorkerSendPort) { |
1171 throw new Exception("SendPort.send: Illegal replyTo port type"); | 1189 throw new Exception("SendPort.send: Illegal replyTo port type"); |
1172 } | 1190 } |
1173 } | 1191 } |
1174 | 1192 |
1175 void send(var message); | 1193 void send(var message); |
1176 bool operator ==(var other); | 1194 bool operator ==(var other); |
1177 int get hashCode; | 1195 int get hashCode; |
1178 } | 1196 } |
1179 | 1197 |
1180 /** A send port that delivers messages in-memory via native JavaScript calls. */ | 1198 /** A send port that delivers messages in-memory via native JavaScript calls. */ |
(...skipping 14 matching lines...) Expand all Loading... |
1195 isolate.handleControlMessage(msg); | 1213 isolate.handleControlMessage(msg); |
1196 return; | 1214 return; |
1197 } | 1215 } |
1198 _globalState.topEventLoop.enqueue(isolate, () { | 1216 _globalState.topEventLoop.enqueue(isolate, () { |
1199 if (!_receivePort._isClosed) { | 1217 if (!_receivePort._isClosed) { |
1200 _receivePort._add(msg); | 1218 _receivePort._add(msg); |
1201 } | 1219 } |
1202 }, 'receive $message'); | 1220 }, 'receive $message'); |
1203 } | 1221 } |
1204 | 1222 |
1205 bool operator ==(var other) => (other is _NativeJsSendPort) && | 1223 bool operator ==(var other) => |
1206 (_receivePort == other._receivePort); | 1224 (other is _NativeJsSendPort) && (_receivePort == other._receivePort); |
1207 | 1225 |
1208 int get hashCode => _receivePort._id; | 1226 int get hashCode => _receivePort._id; |
1209 } | 1227 } |
1210 | 1228 |
1211 /** A send port that delivers messages via worker.postMessage. */ | 1229 /** A send port that delivers messages via worker.postMessage. */ |
1212 // TODO(eub): abstract this for iframes. | 1230 // TODO(eub): abstract this for iframes. |
1213 class _WorkerSendPort extends _BaseSendPort implements SendPort { | 1231 class _WorkerSendPort extends _BaseSendPort implements SendPort { |
1214 final int _workerId; | 1232 final int _workerId; |
1215 final int _receivePortId; | 1233 final int _receivePortId; |
1216 | 1234 |
1217 const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId) | 1235 const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId) |
1218 : super(isolateId); | 1236 : super(isolateId); |
1219 | 1237 |
1220 void send(var message) { | 1238 void send(var message) { |
1221 final workerMessage = _serializeMessage({ | 1239 final workerMessage = |
1222 'command': 'message', | 1240 _serializeMessage({'command': 'message', 'port': this, 'msg': message}); |
1223 'port': this, | |
1224 'msg': message}); | |
1225 | 1241 |
1226 if (_globalState.isWorker) { | 1242 if (_globalState.isWorker) { |
1227 // Communication from one worker to another go through the | 1243 // Communication from one worker to another go through the |
1228 // main worker. | 1244 // main worker. |
1229 _globalState.mainManager.postMessage(workerMessage); | 1245 _globalState.mainManager.postMessage(workerMessage); |
1230 } else { | 1246 } else { |
1231 // Deliver the message only if the worker is still alive. | 1247 // Deliver the message only if the worker is still alive. |
1232 /* Worker */ var manager = _globalState.managers[_workerId]; | 1248 /* Worker */ var manager = _globalState.managers[_workerId]; |
1233 if (manager != null) { | 1249 if (manager != null) { |
1234 JS('void', '#.postMessage(#)', manager, workerMessage); | 1250 JS('void', '#.postMessage(#)', manager, workerMessage); |
(...skipping 25 matching lines...) Expand all Loading... |
1260 _globalState.currentContext.register(_id, this); | 1276 _globalState.currentContext.register(_id, this); |
1261 } | 1277 } |
1262 | 1278 |
1263 RawReceivePortImpl.weak(this._handler) : _id = _nextFreeId++ { | 1279 RawReceivePortImpl.weak(this._handler) : _id = _nextFreeId++ { |
1264 _globalState.currentContext.registerWeak(_id, this); | 1280 _globalState.currentContext.registerWeak(_id, this); |
1265 } | 1281 } |
1266 | 1282 |
1267 // Creates the control port of an isolate. | 1283 // Creates the control port of an isolate. |
1268 // This is created before the isolate context object itself, | 1284 // This is created before the isolate context object itself, |
1269 // so it cannot access the static _nextFreeId field. | 1285 // so it cannot access the static _nextFreeId field. |
1270 RawReceivePortImpl._controlPort() : _handler = null, _id = 0; | 1286 RawReceivePortImpl._controlPort() |
| 1287 : _handler = null, |
| 1288 _id = 0; |
1271 | 1289 |
1272 void set handler(Function newHandler) { | 1290 void set handler(Function newHandler) { |
1273 _handler = newHandler; | 1291 _handler = newHandler; |
1274 } | 1292 } |
1275 | 1293 |
1276 // Close the port without unregistering it. | 1294 // Close the port without unregistering it. |
1277 // Used by an isolate context to close all ports when shutting down. | 1295 // Used by an isolate context to close all ports when shutting down. |
1278 void _close() { | 1296 void _close() { |
1279 _isClosed = true; | 1297 _isClosed = true; |
1280 _handler = null; | 1298 _handler = null; |
(...skipping 24 matching lines...) Expand all Loading... |
1305 | 1323 |
1306 ReceivePortImpl.weak() | 1324 ReceivePortImpl.weak() |
1307 : this.fromRawReceivePort(new RawReceivePortImpl.weak(null)); | 1325 : this.fromRawReceivePort(new RawReceivePortImpl.weak(null)); |
1308 | 1326 |
1309 ReceivePortImpl.fromRawReceivePort(this._rawPort) { | 1327 ReceivePortImpl.fromRawReceivePort(this._rawPort) { |
1310 _controller = new StreamController(onCancel: close, sync: true); | 1328 _controller = new StreamController(onCancel: close, sync: true); |
1311 _rawPort.handler = _controller.add; | 1329 _rawPort.handler = _controller.add; |
1312 } | 1330 } |
1313 | 1331 |
1314 StreamSubscription listen(void onData(var event), | 1332 StreamSubscription listen(void onData(var event), |
1315 {Function onError, | 1333 {Function onError, void onDone(), bool cancelOnError}) { |
1316 void onDone(), | 1334 return _controller.stream.listen(onData, |
1317 bool cancelOnError}) { | 1335 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
1318 return _controller.stream.listen(onData, onError: onError, onDone: onDone, | |
1319 cancelOnError: cancelOnError); | |
1320 } | 1336 } |
1321 | 1337 |
1322 void close() { | 1338 void close() { |
1323 _rawPort.close(); | 1339 _rawPort.close(); |
1324 _controller.close(); | 1340 _controller.close(); |
1325 } | 1341 } |
1326 | 1342 |
1327 SendPort get sendPort => _rawPort.sendPort; | 1343 SendPort get sendPort => _rawPort.sendPort; |
1328 } | 1344 } |
1329 | 1345 |
1330 class TimerImpl implements Timer { | 1346 class TimerImpl implements Timer { |
1331 final bool _once; | 1347 final bool _once; |
1332 bool _inEventLoop = false; | 1348 bool _inEventLoop = false; |
1333 int _handle; | 1349 int _handle; |
1334 | 1350 |
1335 TimerImpl(int milliseconds, void callback()) | 1351 TimerImpl(int milliseconds, void callback()) : _once = true { |
1336 : _once = true { | |
1337 if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) { | 1352 if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) { |
1338 | |
1339 void internalCallback() { | 1353 void internalCallback() { |
1340 _handle = null; | 1354 _handle = null; |
1341 callback(); | 1355 callback(); |
1342 } | 1356 } |
1343 | 1357 |
1344 // Setting _handle to something different from null indicates that the | 1358 // Setting _handle to something different from null indicates that the |
1345 // callback has not been run. Hence, the choice of 1 is arbitrary. | 1359 // callback has not been run. Hence, the choice of 1 is arbitrary. |
1346 _handle = 1; | 1360 _handle = 1; |
1347 | 1361 |
1348 // This makes a dependency between the async library and the | 1362 // This makes a dependency between the async library and the |
1349 // event loop of the isolate library. The compiler makes sure | 1363 // event loop of the isolate library. The compiler makes sure |
1350 // that the event loop is compiled if [Timer] is used. | 1364 // that the event loop is compiled if [Timer] is used. |
1351 // TODO(7907): In case of web workers, we need to use the event | 1365 // TODO(7907): In case of web workers, we need to use the event |
1352 // loop instead of setTimeout, to make sure the futures get executed in | 1366 // loop instead of setTimeout, to make sure the futures get executed in |
1353 // order. | 1367 // order. |
1354 _globalState.topEventLoop.enqueue( | 1368 _globalState.topEventLoop |
1355 _globalState.currentContext, internalCallback, 'timer'); | 1369 .enqueue(_globalState.currentContext, internalCallback, 'timer'); |
1356 _inEventLoop = true; | 1370 _inEventLoop = true; |
1357 } else if (hasTimer()) { | 1371 } else if (hasTimer()) { |
1358 | |
1359 void internalCallback() { | 1372 void internalCallback() { |
1360 _handle = null; | 1373 _handle = null; |
1361 leaveJsAsync(); | 1374 leaveJsAsync(); |
1362 callback(); | 1375 callback(); |
1363 } | 1376 } |
1364 | 1377 |
1365 enterJsAsync(); | 1378 enterJsAsync(); |
1366 | 1379 |
1367 _handle = JS( | 1380 _handle = JS( |
1368 'int', '#.setTimeout(#, #)', global, internalCallback, milliseconds); | 1381 'int', '#.setTimeout(#, #)', global, internalCallback, milliseconds); |
1369 } else { | 1382 } else { |
1370 assert(milliseconds > 0); | 1383 assert(milliseconds > 0); |
1371 throw new UnsupportedError("Timer greater than 0."); | 1384 throw new UnsupportedError("Timer greater than 0."); |
1372 } | 1385 } |
1373 } | 1386 } |
1374 | 1387 |
1375 TimerImpl.periodic(int milliseconds, void callback(Timer timer)) | 1388 TimerImpl.periodic(int milliseconds, void callback(Timer timer)) |
1376 : _once = false { | 1389 : _once = false { |
1377 if (hasTimer()) { | 1390 if (hasTimer()) { |
1378 enterJsAsync(); | 1391 enterJsAsync(); |
1379 _handle = JS('int', '#.setInterval(#, #)', | 1392 _handle = JS('int', '#.setInterval(#, #)', global, () { |
1380 global, () { callback(this); }, milliseconds); | 1393 callback(this); |
| 1394 }, milliseconds); |
1381 } else { | 1395 } else { |
1382 throw new UnsupportedError("Periodic timer."); | 1396 throw new UnsupportedError("Periodic timer."); |
1383 } | 1397 } |
1384 } | 1398 } |
1385 | 1399 |
1386 void cancel() { | 1400 void cancel() { |
1387 if (hasTimer()) { | 1401 if (hasTimer()) { |
1388 if (_inEventLoop) { | 1402 if (_inEventLoop) { |
1389 throw new UnsupportedError("Timer in event loop cannot be canceled."); | 1403 throw new UnsupportedError("Timer in event loop cannot be canceled."); |
1390 } | 1404 } |
(...skipping 10 matching lines...) Expand all Loading... |
1401 } | 1415 } |
1402 } | 1416 } |
1403 | 1417 |
1404 bool get isActive => _handle != null; | 1418 bool get isActive => _handle != null; |
1405 } | 1419 } |
1406 | 1420 |
1407 bool hasTimer() { | 1421 bool hasTimer() { |
1408 return JS('', '#.setTimeout', global) != null; | 1422 return JS('', '#.setTimeout', global) != null; |
1409 } | 1423 } |
1410 | 1424 |
1411 | |
1412 /** | 1425 /** |
1413 * Implementation class for [Capability]. | 1426 * Implementation class for [Capability]. |
1414 * | 1427 * |
1415 * It has the same name to make it harder for users to distinguish. | 1428 * It has the same name to make it harder for users to distinguish. |
1416 */ | 1429 */ |
1417 class CapabilityImpl implements Capability { | 1430 class CapabilityImpl implements Capability { |
1418 /** Internal random secret identifying the capability. */ | 1431 /** Internal random secret identifying the capability. */ |
1419 final int _id; | 1432 final int _id; |
1420 | 1433 |
1421 CapabilityImpl() : this._internal(random64()); | 1434 CapabilityImpl() : this._internal(random64()); |
1422 | 1435 |
1423 CapabilityImpl._internal(this._id); | 1436 CapabilityImpl._internal(this._id); |
1424 | 1437 |
1425 int get hashCode { | 1438 int get hashCode { |
1426 // Thomas Wang 32 bit Mix. | 1439 // Thomas Wang 32 bit Mix. |
1427 // http://www.concentric.net/~Ttwang/tech/inthash.htm | 1440 // http://www.concentric.net/~Ttwang/tech/inthash.htm |
1428 // (via https://gist.github.com/badboy/6267743) | 1441 // (via https://gist.github.com/badboy/6267743) |
1429 int hash = _id; | 1442 int hash = _id; |
1430 hash = (hash >> 0) ^ (hash ~/ 0x100000000); // To 32 bit from ~64. | 1443 hash = (hash >> 0) ^ (hash ~/ 0x100000000); // To 32 bit from ~64. |
1431 hash = (~hash + (hash << 15)) & 0xFFFFFFFF; | 1444 hash = (~hash + (hash << 15)) & 0xFFFFFFFF; |
1432 hash ^= hash >> 12; | 1445 hash ^= hash >> 12; |
1433 hash = (hash * 5) & 0xFFFFFFFF; | 1446 hash = (hash * 5) & 0xFFFFFFFF; |
1434 hash ^= hash >> 4; | 1447 hash ^= hash >> 4; |
1435 hash = (hash * 2057) & 0xFFFFFFFF; | 1448 hash = (hash * 2057) & 0xFFFFFFFF; |
1436 hash ^= hash >> 16; | 1449 hash ^= hash >> 16; |
1437 return hash; | 1450 return hash; |
1438 } | 1451 } |
1439 | 1452 |
1440 bool operator==(Object other) { | 1453 bool operator ==(Object other) { |
1441 if (identical(other, this)) return true; | 1454 if (identical(other, this)) return true; |
1442 if (other is CapabilityImpl) { | 1455 if (other is CapabilityImpl) { |
1443 return identical(_id, other._id); | 1456 return identical(_id, other._id); |
1444 } | 1457 } |
1445 return false; | 1458 return false; |
1446 } | 1459 } |
1447 } | 1460 } |
OLD | NEW |