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

Side by Side Diff: sdk/lib/_internal/compiler/js_lib/js_helper.dart

Issue 1070733002: Dart2js async* functions only resume execution of the body when suspended on yield. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rewrite addressing points from Lasse Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/language/async_star_regression_23116_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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:_async_await_error_codes' as async_error_codes; 7 import 'dart:_async_await_error_codes' as async_error_codes;
8 8
9 import 'dart:_js_embedded_names' show 9 import 'dart:_js_embedded_names' show
10 DEFERRED_LIBRARY_URIS, 10 DEFERRED_LIBRARY_URIS,
(...skipping 3696 matching lines...) Expand 10 before | Expand all | Expand 10 after
3707 /// If the async* function wants to do an await it calls this function with 3707 /// If the async* function wants to do an await it calls this function with
3708 /// [object] not and [IterationMarker]. 3708 /// [object] not and [IterationMarker].
3709 /// 3709 ///
3710 /// If [object] is not a [Future], it is wrapped in a `Future.value`. 3710 /// If [object] is not a [Future], it is wrapped in a `Future.value`.
3711 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. 3711 /// The [asyncBody] is called on completion of the future (see [asyncHelper].
3712 void asyncStarHelper(dynamic object, 3712 void asyncStarHelper(dynamic object,
3713 dynamic /* int | js function */ bodyFunctionOrErrorCode, 3713 dynamic /* int | js function */ bodyFunctionOrErrorCode,
3714 AsyncStarStreamController controller) { 3714 AsyncStarStreamController controller) {
3715 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { 3715 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) {
3716 // This happens on return from the async* function. 3716 // This happens on return from the async* function.
3717 if (controller.cancelationCompleter != null) { 3717 if (controller.isCanceled) {
3718 controller.cancelationCompleter.complete(); 3718 controller.cancelationCompleter.complete();
3719 } else { 3719 } else {
3720 controller.close(); 3720 controller.close();
3721 } 3721 }
3722 return; 3722 return;
3723 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { 3723 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) {
3724 // The error is a js-error. 3724 // The error is a js-error.
3725 if (controller.cancelationCompleter != null) { 3725 if (controller.isCanceled) {
3726 controller.cancelationCompleter.completeError( 3726 controller.cancelationCompleter.completeError(
3727 unwrapException(object), 3727 unwrapException(object),
3728 getTraceFromException(object)); 3728 getTraceFromException(object));
3729 } else { 3729 } else {
3730 controller.addError(unwrapException(object), 3730 controller.addError(unwrapException(object),
3731 getTraceFromException(object)); 3731 getTraceFromException(object));
3732 controller.close(); 3732 controller.close();
3733 } 3733 }
3734 return; 3734 return;
3735 } 3735 }
3736 3736
3737 if (object is IterationMarker) { 3737 if (object is IterationMarker) {
3738 if (controller.cancelationCompleter != null) { 3738 if (controller.cancelationCompleter != null) {
Lasse Reichstein Nielsen 2015/04/09 12:22:09 if (controller.isCanceled) { ?
sigurdm 2015/04/10 08:13:24 Done.
3739 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3739 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3740 async_error_codes.STREAM_WAS_CANCELED)(null); 3740 async_error_codes.STREAM_WAS_CANCELED)
3741 (null);
Lasse Reichstein Nielsen 2015/04/09 12:22:09 Odd linebreak. Could (null) be on the previous lin
sigurdm 2015/04/10 08:13:25 It is to emphasize the call of the returned functi
3741 return; 3742 return;
3742 } 3743 }
3743 if (object.state == IterationMarker.YIELD_SINGLE) { 3744 if (object.state == IterationMarker.YIELD_SINGLE) {
3744 controller.add(object.value); 3745 controller.add(object.value);
3745 // If the controller is paused we stop producing more values. 3746
3746 if (controller.isPaused) {
3747 return;
3748 }
3749 // TODO(sigurdm): We should not suspend here according to the spec.
3750 scheduleMicrotask(() { 3747 scheduleMicrotask(() {
3748 if (controller.isPaused) {
3749 // We only suspend the thread inside the microtask in order to allow
3750 // listeners on the output stream to pause in response to the just
3751 // output value, and have the stream immediately stop producing.
3752 controller.isSuspended = true;
3753 return;
3754 }
3751 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3755 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3752 async_error_codes.SUCCESS) 3756 async_error_codes.SUCCESS)
3753 (null); 3757 (null);
3754 }); 3758 });
3755 return; 3759 return;
3756 } else if (object.state == IterationMarker.YIELD_STAR) { 3760 } else if (object.state == IterationMarker.YIELD_STAR) {
3757 Stream stream = object.value; 3761 Stream stream = object.value;
3758 controller.isAdding = true;
3759 // Errors of [stream] are passed though to the main stream. (see 3762 // Errors of [stream] are passed though to the main stream. (see
3760 // [AsyncStreamController.addStream]. 3763 // [AsyncStreamController.addStream].
3761 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. 3764 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad.
3762 controller.addStream(stream).then((_) { 3765 controller.addStream(stream).then((_) {
3763 controller.isAdding = false; 3766 // We do not check for isPaused here because the spec 17.16.2 only
3767 // demands checks for each element in [stream] not after the last one.
Lasse Reichstein Nielsen 2015/04/09 12:22:09 ... demands checks *before* each element ...
sigurdm 2015/04/10 08:13:25 Done.
3764 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3768 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3765 async_error_codes.SUCCESS)(null); 3769 async_error_codes.SUCCESS)
3770 (null);
3766 }); 3771 });
3767 return; 3772 return;
3768 } 3773 }
3769 } 3774 }
3770 3775
3771 Future future = object is Future ? object : new Future.value(object); 3776 Future future = object is Future ? object : new Future.value(object);
3772 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3777 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3773 async_error_codes.SUCCESS), 3778 async_error_codes.SUCCESS),
3774 onError: (error, StackTrace stackTrace) { 3779 onError: (error, StackTrace stackTrace) {
3775 ExceptionAndStackTrace wrapped = 3780 ExceptionAndStackTrace wrapped =
3776 new ExceptionAndStackTrace(error, stackTrace); 3781 new ExceptionAndStackTrace(error, stackTrace);
3777 return _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3782 return _wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3778 async_error_codes.ERROR) 3783 async_error_codes.ERROR)
3779 (wrapped); 3784 (wrapped);
3780 }); 3785 });
3781 } 3786 }
3782 3787
3783 Stream streamOfController(AsyncStarStreamController controller) { 3788 Stream streamOfController(AsyncStarStreamController controller) {
3784 return controller.stream; 3789 return controller.stream;
3785 } 3790 }
3786 3791
3787 /// A wrapper around a [StreamController] that remembers if that controller 3792 /// A wrapper around a [StreamController] that keeps track of the state of
3788 /// got a cancel. 3793 /// the execution of an async* function.
3794 /// It can be in 1 of 3 states:
3789 /// 3795 ///
3790 /// Also has a subSubscription that when not null will provide events for the 3796 /// - running/scheduled
3791 /// stream, and will be paused and resumed along with this controller. 3797 /// - suspended
3798 /// - canceled
Lasse Reichstein Nielsen 2015/04/09 12:22:09 I guess the "done" state is implicit.
sigurdm 2015/04/10 08:13:25 Yes - it corresponds to controller.isClosed
3799 ///
3800 /// If yielding while the subscription is paused it will become suspended. And
3801 /// only resume after the subscription is resumed or canceled.
3792 class AsyncStarStreamController { 3802 class AsyncStarStreamController {
3793 StreamController controller; 3803 StreamController controller;
3794 Stream get stream => controller.stream; 3804 Stream get stream => controller.stream;
3805
3806 /// True when the async* function has yielded while being paused.
3807 /// Or if it has yielded a
Lasse Reichstein Nielsen 2015/04/09 12:22:09 incomplete sentence.
sigurdm 2015/04/10 08:13:25 Thanks
3808 /// When true execution will only resume after a `onResume` or `onCancel`
3809 /// event.
3810 bool isSuspended = false;
3811
3812 bool get isPaused => controller.isPaused;
3813
3795 Completer cancelationCompleter = null; 3814 Completer cancelationCompleter = null;
3815
3816 /// True after the StreamSubscription has been cancelled.
3817 /// When this is true, errors thrown from the async* body should go to the
3818 /// [cancelationCompleter] instead of adding them to [controller], and
3819 /// returning from the async function should complete [cancelationCompleter].
3796 bool get isCanceled => cancelationCompleter != null; 3820 bool get isCanceled => cancelationCompleter != null;
3797 bool isAdding = false; 3821
3798 bool isPaused = false;
3799 add(event) => controller.add(event); 3822 add(event) => controller.add(event);
3823
3800 addStream(Stream stream) { 3824 addStream(Stream stream) {
3801 return controller.addStream(stream, cancelOnError: false); 3825 return controller.addStream(stream, cancelOnError: false);
3802 } 3826 }
3827
3803 addError(error, stackTrace) => controller.addError(error, stackTrace); 3828 addError(error, stackTrace) => controller.addError(error, stackTrace);
3829
3804 close() => controller.close(); 3830 close() => controller.close();
3805 3831
3806 AsyncStarStreamController(body) { 3832 AsyncStarStreamController(body) {
3833
3834 _resumeBody() {
3835 Function wrapped =
3836 _wrapJsFunctionForAsync(body, async_error_codes.SUCCESS);
3837 wrapped(null);
3838 }
3839
3807 controller = new StreamController( 3840 controller = new StreamController(
3808 onListen: () { 3841 onListen: () {
3809 scheduleMicrotask(() { 3842 scheduleMicrotask(_resumeBody);
3810 Function wrapped = _wrapJsFunctionForAsync(body,
3811 async_error_codes.SUCCESS);
3812 wrapped(null);
3813 });
3814 },
3815 onPause: () {
3816 isPaused = true;
3817 }, onResume: () { 3843 }, onResume: () {
3818 isPaused = false; 3844 // Only schedule again if the async* function actually os suspended.
Lasse Reichstein Nielsen 2015/04/09 12:22:09 os -> is
sigurdm 2015/04/10 08:13:25 Done.
3819 if (!isAdding) { 3845 // Resume directly instead of scheduling, so that the sequence
3820 asyncStarHelper(null, body, this); 3846 // `pause-resume-pause` will result in one extra event produced.
3847 if (isSuspended) {
3848 isSuspended = false;
3849 _resumeBody();
Lasse Reichstein Nielsen 2015/04/09 12:22:09 I think you still need to do a scheduleMicrotask (
sigurdm 2015/04/10 08:13:25 Ah - now I understand what you meant. I thought it
3821 } 3850 }
3822 }, onCancel: () { 3851 }, onCancel: () {
3852 // If the async* is finished we ignore cancel events.
3823 if (!controller.isClosed) { 3853 if (!controller.isClosed) {
3824 cancelationCompleter = new Completer(); 3854 cancelationCompleter = new Completer();
3825 if (isPaused) asyncStarHelper(null, body, this); 3855 if (isSuspended) {
3826 3856 // Resume the suspended async* function to run finalizers.
3857 isSuspended = false;
3858 scheduleMicrotask(_resumeBody);
Lasse Reichstein Nielsen 2015/04/09 12:22:09 Shouldn't the body be called with async_error_code
sigurdm 2015/04/10 08:13:25 Good catch - Done
3859 }
3827 return cancelationCompleter.future; 3860 return cancelationCompleter.future;
3828 } 3861 }
3829 }); 3862 });
3830 } 3863 }
3831 } 3864 }
3832 3865
3833 makeAsyncStarController(body) { 3866 makeAsyncStarController(body) {
3834 return new AsyncStarStreamController(body); 3867 return new AsyncStarStreamController(body);
3835 } 3868 }
3836 3869
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
3932 // This is a function that will return a helper function that does the 3965 // This is a function that will return a helper function that does the
3933 // iteration of the sync*. 3966 // iteration of the sync*.
3934 // 3967 //
3935 // Each invocation should give a body with fresh state. 3968 // Each invocation should give a body with fresh state.
3936 final dynamic /* js function */ _outerHelper; 3969 final dynamic /* js function */ _outerHelper;
3937 3970
3938 SyncStarIterable(this._outerHelper); 3971 SyncStarIterable(this._outerHelper);
3939 3972
3940 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); 3973 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper));
3941 } 3974 }
OLDNEW
« no previous file with comments | « no previous file | tests/language/async_star_regression_23116_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698