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' |
floitsch
2015/02/05 20:17:23
maybe not your code, but should be easy:
move "sho
sigurdm
2015/02/06 14:26:34
Done.
| |
29 show Future, | |
30 DeferredLoadException, | |
31 Completer, | |
32 StreamController, | |
33 Stream, | |
34 StreamSubscription, | |
35 scheduleMicrotask; | |
29 | 36 |
30 import 'dart:_foreign_helper' show | 37 import 'dart:_foreign_helper' show |
31 DART_CLOSURE_TO_JS, | 38 DART_CLOSURE_TO_JS, |
32 JS, | 39 JS, |
33 JS_CALL_IN_ISOLATE, | 40 JS_CALL_IN_ISOLATE, |
34 JS_CONST, | 41 JS_CONST, |
35 JS_CURRENT_ISOLATE, | 42 JS_CURRENT_ISOLATE, |
36 JS_CURRENT_ISOLATE_CONTEXT, | 43 JS_CURRENT_ISOLATE_CONTEXT, |
37 JS_DART_OBJECT_CONSTRUCTOR, | 44 JS_DART_OBJECT_CONSTRUCTOR, |
38 JS_EFFECT, | 45 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'."); | 3462 throw new MainError("No top-level function named 'main'."); |
3456 } | 3463 } |
3457 | 3464 |
3458 void badMain() { | 3465 void badMain() { |
3459 throw new MainError("'main' is not a function."); | 3466 throw new MainError("'main' is not a function."); |
3460 } | 3467 } |
3461 | 3468 |
3462 void mainHasTooManyParameters() { | 3469 void mainHasTooManyParameters() { |
3463 throw new MainError("'main' expects too many parameters."); | 3470 throw new MainError("'main' expects too many parameters."); |
3464 } | 3471 } |
3472 | |
3473 /// Runtime support for async-await transformation. | |
3474 /// | |
3475 /// This function is called by a transformed function on each await and return | |
3476 /// in the untransformed function, and before starting. | |
3477 /// | |
3478 /// If [object] is not a future it will be wrapped in a `new Future.value`. | |
3479 /// | |
3480 /// If [helperCallback] is null it indicates a return from the async function, | |
3481 /// and we complete the completer with object. | |
3482 /// | |
3483 /// Otherwise [helperCallback] is set up to be called when the future is | |
3484 /// successfull and [errorCallback] if it is completed with an error. | |
3485 /// | |
3486 /// If helperCallback or errorCallback throws we complete the completer with the | |
3487 /// error. | |
3488 /// | |
3489 /// Returns the future of the completer for convenience of the first call. | |
3490 dynamic thenHelper(dynamic object, | |
3491 dynamic /* js function */ helperCallback, | |
3492 Completer completer, | |
3493 dynamic /* js function */ errorCallback) { | |
3494 if (helperCallback == null) { | |
3495 completer.complete(object); | |
3496 return; | |
3497 } | |
3498 Future future = object is Future ? object : new Future.value(object); | |
3499 future.then(_wrapJsFunctionForThenHelper(helperCallback, completer), | |
3500 onError: (errorCallback == null) | |
3501 ? null | |
3502 : _wrapJsFunctionForThenHelper(errorCallback, completer)); | |
3503 return completer.future; | |
3504 } | |
3505 | |
3506 Function _wrapJsFunctionForThenHelper(dynamic /* js function */ function, | |
3507 Completer completer) { | |
3508 return (result) { | |
3509 try { | |
3510 JS('', '#(#)', function, result); | |
3511 } catch (e, st) { | |
3512 completer.completeError(e, st); | |
3513 } | |
3514 }; | |
3515 } | |
3516 | |
3517 | |
3518 /// Implements the runtime support for async* functions. | |
3519 /// | |
3520 /// Called by the transformed function for each original return, await, yield, | |
3521 /// yield* and before starting the function. | |
3522 /// | |
3523 /// When the async* function wants to return it call this with | |
floitsch
2015/02/05 20:17:23
calls this functions. The streamHelper takes this
sigurdm
2015/02/06 14:26:34
Done.
| |
3524 /// [helperCallback] == null, and the stream is closed. | |
3525 /// | |
3526 /// If the async* function wants to do a yield or yield* it will call this with | |
floitsch
2015/02/05 20:17:23
it calls this function
sigurdm
2015/02/06 14:26:34
Done.
| |
3527 /// [object] being an [IterationMarker]. In this case [errorCallback] has a | |
3528 /// special meaning; it is a callback that will run all enclosing finalizers. | |
3529 /// | |
3530 /// In the case of a yield or yield*, if the stream subscription has been | |
3531 /// canceled [errorCallback] is scheduled. | |
3532 /// | |
3533 /// If [object] is a single-yield [IterationMarker], add the value of the | |
floitsch
2015/02/05 20:17:23
adds
sigurdm
2015/02/06 14:26:34
Done.
| |
3534 /// [IterationMarker] to the stream, next: if the stream subscription has been | |
floitsch
2015/02/05 20:17:23
to the stream. If the stream...
(unless I'm misun
sigurdm
2015/02/06 14:26:34
Done.
| |
3535 /// paused, return early. Otherwise schedule the helper function to be | |
3536 /// executed again. | |
3537 /// | |
3538 /// If [object] is a yield-star [IterationMarker], start listening to the | |
floitsch
2015/02/05 20:17:23
starts ... and adds ... schedules
sigurdm
2015/02/06 14:26:34
Done.
| |
3539 /// yielded stream, and add all events and errors to our own controller (taking | |
3540 /// care if the subscription has been paused or canceled) - when the sub-stream | |
3541 /// is done, schedule [helperCallback] again. | |
3542 /// | |
3543 /// If the async* function wants to do an await it will call this with [object] | |
floitsch
2015/02/05 20:17:23
calls this function with [object] not an [Iteratio
sigurdm
2015/02/06 14:26:34
Done.
| |
3544 /// not being an [IterationMarker]. | |
3545 /// | |
3546 /// If [object] is not a [Future], it is wrapped in a `Future.value`. | |
3547 /// Then set up [helperCallback] to be called on successfull completion of the | |
floitsch
2015/02/05 20:17:23
The [helperCallback] is called on successful compl
sigurdm
2015/02/06 14:26:34
Done.
| |
3548 /// future. | |
3549 /// | |
3550 /// If [helperCallback] or [errorCallback] throws the error is added to the | |
3551 /// stream. | |
3552 dynamic streamHelper(dynamic object, | |
3553 dynamic /* js function */ helperCallback, | |
3554 AsyncStarStreamController controller, | |
3555 dynamic /* js function */ errorCallback) { | |
3556 if (helperCallback == null) { | |
3557 // This happens on return from the async* function. | |
3558 controller.close(); | |
3559 return null; | |
3560 } | |
3561 | |
3562 if (object is IterationMarker) { | |
3563 if (controller.stopRunning) { | |
3564 _wrapJsFunctionForStream(errorCallback, controller)(); | |
3565 return null; | |
3566 } | |
3567 if (object.state == IterationMarker.YIELD_SINGLE) { | |
3568 controller.add(object.value); | |
3569 if (controller.isPaused) { | |
3570 return null; | |
3571 } | |
3572 new Future.value(null).then( | |
floitsch
2015/02/05 20:17:23
Why?
Do we have to wait a microtask before we can
sigurdm
2015/02/06 14:26:35
I thought that it was necessary to suspend executi
| |
3573 _wrapJsFunctionForStream(helperCallback, controller)); | |
3574 return; | |
3575 } else if (object.state == IterationMarker.YIELD_STAR) { | |
3576 Stream stream = object.value; | |
3577 controller.isAdding = true; | |
3578 controller.addStream(stream).then((_) { | |
floitsch
2015/02/05 20:17:23
You might need to listen to the error too. I'm not
sigurdm
2015/02/06 14:26:34
As you saw below, I explicitly ask errors to be pa
| |
3579 controller.isAdding = false; | |
3580 _wrapJsFunctionForStream(helperCallback, controller)(null); | |
3581 }); | |
3582 return null; | |
3583 } | |
3584 } | |
3585 | |
3586 Future future = object is Future ? object : new Future.value(object); | |
3587 future.then(_wrapJsFunctionForStream(helperCallback, controller), | |
3588 onError: errorCallback == null | |
3589 ? null | |
3590 : _wrapJsFunctionForStream(errorCallback, controller)); | |
3591 return controller.stream; | |
3592 } | |
3593 | |
3594 /// A wrapper around a [StreamController] that remembers if that controller | |
3595 /// got a cancel. | |
3596 /// | |
3597 /// Also has a subSubscription that when not null will provide events for the | |
3598 /// stream, and will be paused and resumed along with this controller. | |
3599 class AsyncStarStreamController { | |
3600 StreamController controller; | |
3601 Stream get stream => controller.stream; | |
3602 bool stopRunning = false; | |
3603 bool isAdding = false; | |
3604 bool get isPaused => controller.isPaused; | |
3605 add(event) => controller.add(event); | |
3606 addStream(Stream stream) { | |
3607 return controller.addStream(stream, cancelOnError: false); | |
floitsch
2015/02/05 20:17:23
I see. So I guess the future cannot have an error.
sigurdm
2015/02/06 14:26:34
Done.
| |
3608 } | |
3609 addError(error, stackTrace) => controller.addError(error, stackTrace); | |
3610 close() => controller.close(); | |
3611 | |
3612 AsyncStarStreamController(helperCallback) { | |
3613 controller = new StreamController( | |
3614 onResume: () { | |
3615 if (!isAdding) { | |
floitsch
2015/02/05 20:17:23
Check with Lasse, if 'isPaused' on the controller
floitsch
2015/02/05 20:17:23
Add comment.
sigurdm
2015/02/06 14:26:34
Done.
sigurdm
2015/02/06 14:26:34
According to him, it is just as good.
| |
3616 streamHelper(null, helperCallback, this, null); | |
3617 } | |
3618 }, onCancel: () { | |
3619 stopRunning = true; | |
floitsch
2015/02/05 20:17:23
Please make sure (test), that an incoming stream i
sigurdm
2015/02/06 14:26:34
Modified asyncstar_yieldstar_test to do that.
| |
3620 }); | |
3621 } | |
3622 } | |
3623 | |
3624 makeAsyncStarController(helperCallback) { | |
3625 return new AsyncStarStreamController(helperCallback); | |
3626 } | |
3627 | |
3628 Function _wrapJsFunctionForStream(dynamic /* js function */ function, | |
3629 AsyncStarStreamController controller) { | |
3630 return (result) { | |
3631 try { | |
3632 JS('', '#(#)', function, result); | |
3633 } catch (e, st) { | |
3634 controller.addError(e, st); | |
3635 } | |
3636 }; | |
3637 } | |
3638 | |
3639 | |
3640 class IterationMarker { | |
3641 static const YIELD_SINGLE = 0; | |
3642 static const YIELD_STAR = 1; | |
3643 static const ITERATION_ENDED = 2; | |
3644 | |
3645 final value; | |
3646 final int state; | |
3647 | |
3648 IterationMarker._(this.state, this.value); | |
3649 | |
3650 static yieldStar(dynamic /* Iterable or Stream */ yielded) { | |
floitsch
2015/02/05 20:17:23
maybe just "values" ?
sigurdm
2015/02/06 14:26:34
Done.
| |
3651 return new IterationMarker._(YIELD_STAR, yielded); | |
3652 } | |
3653 | |
3654 static endOfIteration() { | |
3655 return new IterationMarker._(ITERATION_ENDED, null); | |
3656 } | |
3657 | |
3658 static yieldSingle(dynamic value) { | |
3659 return new IterationMarker._(YIELD_SINGLE, value); | |
3660 } | |
3661 | |
3662 toString() => "IterationMarker($state, $value)"; | |
3663 } | |
3664 | |
3665 class SyncStarIterator implements Iterator { | |
3666 final Function _helper; | |
3667 | |
3668 // If [runningNested] this is the nested iterator, otherwise it is the | |
3669 // current value. | |
3670 dynamic _current = null; | |
3671 bool _runningNested = false; | |
3672 | |
3673 get current => _runningNested ? _current.current : _current; | |
3674 | |
3675 SyncStarIterator(helper) | |
3676 : _helper = ((arg) => JS('', '#(#)', helper, arg)); | |
3677 | |
3678 bool moveNext() { | |
3679 if (_runningNested) { | |
3680 if (_current.moveNext()) { | |
3681 return true; | |
3682 } else { | |
3683 _runningNested = false; | |
3684 } | |
3685 } | |
3686 _current = _helper(null); | |
3687 if (_current is IterationMarker) { | |
3688 if (_current.state == IterationMarker.ITERATION_ENDED) { | |
3689 _current = null; | |
3690 // Rely on [_helper] to repeatedly return `ITERATION_ENDED`. | |
3691 return false; | |
3692 } else { | |
3693 assert(_current.state == IterationMarker.YIELD_STAR); | |
3694 _current = _current.value.iterator; | |
3695 _runningNested = true; | |
3696 return moveNext(); | |
3697 } | |
3698 } | |
3699 return true; | |
3700 } | |
3701 } | |
3702 | |
3703 /// An Iterable corresponding to a sync* method. | |
3704 /// | |
3705 /// Each invocation of a sync* method will return a new instance of this class. | |
3706 class SyncStarIterable extends IterableBase { | |
3707 // This is a function that will return a helper function that does the | |
3708 // iteration of the sync*. | |
3709 // | |
3710 // Each invocation should give a helper with fresh state. | |
3711 final dynamic /* js function */ _outerHelper; | |
3712 | |
3713 SyncStarIterable(this._outerHelper); | |
3714 | |
3715 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); | |
3716 } | |
3717 | |
OLD | NEW |