OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 _js_helper; | 5 library _js_helper; |
6 | 6 |
7 import 'dart:_js_embedded_names' show | 7 import 'dart:_js_embedded_names' show |
8 GET_TYPE_FROM_NAME, | 8 GET_TYPE_FROM_NAME, |
9 GET_ISOLATE_TAG, | 9 GET_ISOLATE_TAG, |
10 INTERCEPTED_NAMES, | 10 INTERCEPTED_NAMES, |
11 INTERCEPTORS_BY_TAG, | 11 INTERCEPTORS_BY_TAG, |
12 LEAF_TAGS, | 12 LEAF_TAGS, |
13 METADATA, | 13 METADATA, |
14 DEFERRED_LIBRARY_URIS, | 14 DEFERRED_LIBRARY_URIS, |
15 DEFERRED_LIBRARY_HASHES, | 15 DEFERRED_LIBRARY_HASHES, |
16 INITIALIZE_LOADED_HUNK, | 16 INITIALIZE_LOADED_HUNK, |
17 IS_HUNK_LOADED, | 17 IS_HUNK_LOADED, |
18 IS_HUNK_INITIALIZED, | 18 IS_HUNK_INITIALIZED, |
19 NATIVE_SUPERCLASS_TAG_NAME; | 19 NATIVE_SUPERCLASS_TAG_NAME; |
20 | 20 |
21 import 'dart:collection'; | 21 import 'dart:collection'; |
22 import 'dart:_isolate_helper' show | 22 import 'dart:_isolate_helper' show |
23 IsolateNatives, | 23 IsolateNatives, |
24 leaveJsAsync, | 24 leaveJsAsync, |
25 enterJsAsync, | 25 enterJsAsync, |
26 isWorker; | 26 isWorker; |
27 | 27 |
28 import 'dart:async' show Future, DeferredLoadException, Completer; | 28 import 'dart:async' |
29 show Future, | |
30 DeferredLoadException, | |
Lasse Reichstein Nielsen
2015/02/13 11:13:20
Indent to align with Future?
Or at least by four.
sigurdm
2015/02/13 11:40:16
Will do in future CL
| |
31 Completer, | |
32 StreamController, | |
33 Stream, | |
34 StreamSubscription; | |
29 | 35 |
30 import 'dart:_foreign_helper' show | 36 import 'dart:_foreign_helper' show |
31 DART_CLOSURE_TO_JS, | 37 DART_CLOSURE_TO_JS, |
32 JS, | 38 JS, |
33 JS_CALL_IN_ISOLATE, | 39 JS_CALL_IN_ISOLATE, |
34 JS_CONST, | 40 JS_CONST, |
35 JS_CURRENT_ISOLATE, | 41 JS_CURRENT_ISOLATE, |
36 JS_CURRENT_ISOLATE_CONTEXT, | 42 JS_CURRENT_ISOLATE_CONTEXT, |
37 JS_DART_OBJECT_CONSTRUCTOR, | 43 JS_DART_OBJECT_CONSTRUCTOR, |
38 JS_EFFECT, | 44 JS_EFFECT, |
(...skipping 3416 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3455 throw new MainError("No top-level function named 'main'."); | 3461 throw new MainError("No top-level function named 'main'."); |
3456 } | 3462 } |
3457 | 3463 |
3458 void badMain() { | 3464 void badMain() { |
3459 throw new MainError("'main' is not a function."); | 3465 throw new MainError("'main' is not a function."); |
3460 } | 3466 } |
3461 | 3467 |
3462 void mainHasTooManyParameters() { | 3468 void mainHasTooManyParameters() { |
3463 throw new MainError("'main' expects too many parameters."); | 3469 throw new MainError("'main' expects too many parameters."); |
3464 } | 3470 } |
3471 | |
3472 /// Runtime support for async-await transformation. | |
3473 /// | |
3474 /// This function is called by a transformed function on each await and return | |
3475 /// in the untransformed function, and before starting. | |
3476 /// | |
3477 /// If [object] is not a future it will be wrapped in a `new Future.value`. | |
3478 /// | |
3479 /// If [helperCallback] is null it indicates a return from the async function, | |
3480 /// and we complete the completer with object. | |
3481 /// | |
3482 /// Otherwise [helperCallback] is set up to be called when the future is | |
3483 /// successfull and [errorCallback] if it is completed with an error. | |
3484 /// | |
3485 /// If helperCallback or errorCallback throws we complete the completer with the | |
3486 /// error. | |
3487 /// | |
3488 /// Returns the future of the completer for convenience of the first call. | |
3489 dynamic thenHelper(dynamic object, | |
3490 dynamic /* js function */ helperCallback, | |
3491 Completer completer, | |
3492 dynamic /* js function */ errorCallback) { | |
3493 if (helperCallback == null) { | |
3494 completer.complete(object); | |
3495 return; | |
3496 } | |
3497 Future future = object is Future ? object : new Future.value(object); | |
Lasse Reichstein Nielsen
2015/02/13 11:13:20
Maybe just:
if (object is Future) {
object.
sigurdm
2015/02/13 11:40:16
That would not suspend execution?
The spec is spec
| |
3498 future.then(_wrapJsFunction(helperCallback, completer), | |
3499 onError: _wrapJsFunction(errorCallback, completer)); | |
3500 return completer.future; | |
3501 } | |
3502 | |
3503 // Used to avoid creating two identical closure classes in [thenHelper]. | |
3504 Function _wrapJsFunction(dynamic /* js function */ function, | |
3505 Completer completer) { | |
3506 return (result) { | |
3507 try { | |
3508 JS('', '#(#)', function, result); | |
3509 } catch (e, st) { | |
3510 completer.completeError(e, st); | |
3511 } | |
3512 }; | |
3513 } | |
3514 | |
3515 | |
3516 /// Runtime support for async*. | |
3517 /// | |
3518 /// Called by the transformed function for each original return, await, yield, | |
3519 /// yield* and before starting the function. | |
3520 /// | |
3521 /// On a return call with helperCallback == null, and we close the stream. | |
3522 /// | |
3523 /// If object is an [IterationMarker] it indicates a yield or yield*. | |
3524 /// If the stream subscription has been canceled we schedule the errorCallback | |
3525 /// that should run through enclosing finally blocks before exiting. | |
3526 /// | |
3527 /// If it is a single-yield we add the value to the stream. | |
3528 /// If the stream subscription has been paused we return early, otherwise | |
3529 /// we schedule the helper function to be executed again. | |
3530 /// | |
3531 /// If it is a yield* we start listening to the yielded stream, and add all | |
3532 /// events and errors to our own controller (taking care if the subscription has | |
3533 /// been paused or canceled) - when the sub-stream is done we schedule the | |
3534 /// helper function again. | |
3535 /// | |
3536 /// If object is not and [IterationMarker] it indicates an await. | |
3537 /// If object is not a Future, it is wrapped in a `Future.value`. | |
3538 /// Then set up helperCallBack to be called on successfull completion of the | |
3539 /// error | |
3540 /// | |
3541 /// If helperCallback or errorCallback throws we add the error to the stream | |
3542 /// If the object is a YIELD_SINGLE we add the object to the stream, and if the | |
3543 /// stream has been canceled call the errorCallBack. | |
floitsch
2015/02/04 17:06:10
errorCallback
sigurdm
2015/02/05 14:06:07
Done.
| |
3544 dynamic streamHelper(dynamic object, | |
3545 dynamic /* js function */ helperCallback, | |
3546 AsyncStarStreamController controller, | |
3547 dynamic /* js function */ errorCallback) { | |
3548 if (helperCallback == null) { | |
3549 // This happens on return from the async* function. | |
3550 controller.close(); | |
3551 return; | |
3552 } | |
3553 | |
3554 if (object is IterationMarker) { | |
3555 if (controller.stopRunning) { | |
3556 _wrapJsFunctionForStream(errorCallback, controller)(); | |
3557 return; | |
3558 } | |
3559 if (object.state == IterationMarker.YIELD_SINGLE) { | |
3560 controller.add(object.value); | |
3561 } else if (object.state == IterationMarker.YIELD_STAR) { | |
3562 Stream stream = object.value; | |
3563 controller.subSubscription = stream.listen((value) { | |
3564 if (controller.stopRunning) { | |
3565 controller.subSubscription.cancel(); | |
3566 _wrapJsFunctionForStream(errorCallback, controller)(null); | |
3567 return; | |
3568 } | |
3569 controller.add(value); | |
3570 }, onError: (error, stackTrace) { | |
3571 if (controller.stopRunning) { | |
3572 controller.subSubscription.cancel(); | |
3573 // This will run the enclosing finally blocks. | |
3574 _wrapJsFunctionForStream(errorCallback, controller)(null); | |
3575 return; | |
3576 } | |
3577 controller.addError(error, stackTrace); | |
3578 if (controller.isPaused) { | |
3579 controller.subSubscription.pause(); | |
3580 } | |
3581 }, onDone: () { | |
3582 controller.subSubscription = null; | |
3583 // Resume producing. | |
3584 streamHelper(null, helperCallback, controller, null); | |
3585 }); | |
3586 return; | |
3587 } | |
3588 if (controller.isPaused) { | |
3589 return; | |
3590 } | |
3591 object = null; | |
3592 } | |
3593 Future future = object is Future ? object : new Future.value(object); | |
3594 future.then(_wrapJsFunctionForStream(helperCallback, controller), | |
3595 onError: errorCallback = null | |
3596 ? null | |
3597 : _wrapJsFunctionForStream(errorCallback, controller)); | |
3598 return controller.stream; | |
3599 } | |
3600 | |
3601 /// A wrapper around a [StreamController] that remembers if that controller | |
3602 /// got a cancel. | |
3603 /// | |
3604 /// Also has a subSubscription that when not null will provide events for the | |
3605 /// stream, and will be paused and resumed along with this controller. | |
3606 class AsyncStarStreamController { | |
3607 StreamController controller; | |
3608 StreamSubscription subSubscription = null; | |
3609 Stream get stream => controller.stream; | |
3610 bool stopRunning = false; | |
3611 bool get isPaused => controller.isPaused; | |
3612 add(event) => controller.add(event); | |
3613 addError(error, stackTrace) => controller.addError(error, stackTrace); | |
3614 close() => controller.close(); | |
3615 | |
3616 AsyncStarStreamController(helperCallBack) { | |
3617 controller = new StreamController( | |
3618 onPause: () { | |
3619 if (subSubscription != null) { | |
3620 subSubscription.pause(); | |
3621 } | |
3622 }, | |
3623 onResume: () { | |
3624 if (subSubscription == null) { | |
3625 helperCallBack(null, helperCallBack, controller, null); | |
3626 } else { | |
3627 subSubscription.resume(); | |
3628 } | |
3629 }, onCancel: () { | |
3630 stopRunning = true; | |
3631 }); | |
3632 } | |
3633 } | |
3634 | |
3635 makeAsyncStarController(helperCallback) { | |
3636 return new AsyncStarStreamController(helperCallback); | |
3637 } | |
3638 | |
3639 // Used to avoid creating identical closure classes in [streamHelper]. | |
3640 Function _wrapJsFunctionForStream(dynamic /* js function */ function, | |
3641 AsyncStarStreamController controller) { | |
3642 return (result) { | |
3643 try { | |
3644 JS('', '#(#)', function, result); | |
3645 } catch (e, st) { | |
3646 controller.addError(e, st); | |
3647 } | |
3648 }; | |
3649 } | |
3650 | |
3651 | |
3652 class IterationMarker { | |
3653 static const YIELD_SINGLE = 0; | |
3654 static const YIELD_STAR = 1; | |
3655 static const ITERATION_ENDED = 2; | |
3656 | |
3657 final value; | |
3658 final int state; | |
3659 | |
3660 IterationMarker._(this.state, this.value); | |
3661 | |
3662 static yieldStar(Iterable iterable) { | |
3663 return new IterationMarker._(YIELD_STAR, iterable); | |
3664 } | |
3665 | |
3666 static endOfIteration() { | |
3667 return new IterationMarker._(ITERATION_ENDED, null); | |
3668 } | |
3669 | |
3670 static yieldSingle(value) { | |
3671 return new IterationMarker._(YIELD_SINGLE, value); | |
3672 } | |
3673 | |
3674 toString() => "IterationMarker($state, $value)"; | |
3675 } | |
3676 | |
3677 class SyncStarIterator implements Iterator { | |
3678 final Function _helper; | |
3679 | |
3680 // if [runningNested] this will be the nested iterator, otherwise it will be | |
3681 // the current value. | |
3682 dynamic _current = null; | |
3683 bool _runningNested = false; | |
3684 | |
3685 get current => _runningNested ? _current.current : _current; | |
3686 | |
3687 SyncStarIterator(helper) | |
3688 : _helper = ((arg) => JS('', '#(#)', helper, arg)); | |
3689 | |
3690 bool moveNext() { | |
3691 if (_runningNested) { | |
3692 if (_current.moveNext()) { | |
3693 return true; | |
3694 } else { | |
3695 _runningNested = false; | |
3696 } | |
3697 } | |
3698 _current = _helper(null); | |
3699 if (_current is IterationMarker) { | |
3700 if (_current.state == IterationMarker.ITERATION_ENDED) { | |
3701 _current = null; | |
3702 // We rely on [_helper] to repeatedly return `ITERATION_ENDED`. | |
3703 return false; | |
3704 } else { | |
3705 assert(_current.state == IterationMarker.YIELD_STAR); | |
3706 _current = _current.value.iterator; | |
3707 _runningNested = true; | |
3708 return moveNext(); | |
3709 } | |
3710 } | |
3711 return true; | |
3712 } | |
3713 } | |
3714 | |
3715 /// An Iterable corresponding to a sync* method. | |
3716 /// | |
3717 /// Each invocation of a sync* method will return a new instance of this class. | |
3718 class SyncStarIterable extends IterableBase { | |
3719 // This is a function that will return a helper function that does the | |
3720 // iteration of the sync*. | |
3721 // | |
3722 // Each invocation should give a helper with fresh state. | |
3723 final dynamic /* js function */ _outerHelper; | |
3724 | |
3725 SyncStarIterable(this._outerHelper); | |
3726 | |
3727 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); | |
3728 } | |
3729 | |
OLD | NEW |