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

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: Address review 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 3624 matching lines...) Expand 10 before | Expand all | Expand 10 after
3635 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { 3635 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) {
3636 // The error is a js-error. 3636 // The error is a js-error.
3637 completer.completeError(unwrapException(object), 3637 completer.completeError(unwrapException(object),
3638 getTraceFromException(object)); 3638 getTraceFromException(object));
3639 return; 3639 return;
3640 } 3640 }
3641 Future future = object is Future ? object : new Future.value(object); 3641 Future future = object is Future ? object : new Future.value(object);
3642 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3642 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3643 async_error_codes.SUCCESS), 3643 async_error_codes.SUCCESS),
3644 onError: (dynamic error, StackTrace stackTrace) { 3644 onError: (dynamic error, StackTrace stackTrace) {
3645 ExceptionAndStackTrace wrapped = 3645 ExceptionAndStackTrace wrappedException =
3646 new ExceptionAndStackTrace(error, stackTrace); 3646 new ExceptionAndStackTrace(error, stackTrace);
3647 return _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3647 Function wrapped =_wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3648 async_error_codes.ERROR)(wrapped); 3648 async_error_codes.ERROR);
3649 wrapped(wrappedException);
3649 }); 3650 });
3650 return completer.future; 3651 return completer.future;
3651 } 3652 }
3652 3653
3653 Function _wrapJsFunctionForAsync(dynamic /* js function */ function, 3654 Function _wrapJsFunctionForAsync(dynamic /* js function */ function,
3654 int errorCode) { 3655 int errorCode) {
3655 var protected = JS('', """ 3656 var protected = JS('', """
3656 // Invokes [function] with [errorCode] and [result]. 3657 // Invokes [function] with [errorCode] and [result].
3657 // 3658 //
3658 // If (and as long as) the invocation throws, calls [function] again, 3659 // If (and as long as) the invocation throws, calls [function] again,
(...skipping 48 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 3708 /// If the async* function wants to do an await it calls this function with
3708 /// [object] not and [IterationMarker]. 3709 /// [object] not and [IterationMarker].
3709 /// 3710 ///
3710 /// If [object] is not a [Future], it is wrapped in a `Future.value`. 3711 /// 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]. 3712 /// The [asyncBody] is called on completion of the future (see [asyncHelper].
3712 void asyncStarHelper(dynamic object, 3713 void asyncStarHelper(dynamic object,
3713 dynamic /* int | js function */ bodyFunctionOrErrorCode, 3714 dynamic /* int | js function */ bodyFunctionOrErrorCode,
3714 AsyncStarStreamController controller) { 3715 AsyncStarStreamController controller) {
3715 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { 3716 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) {
3716 // This happens on return from the async* function. 3717 // This happens on return from the async* function.
3717 if (controller.cancelationCompleter != null) { 3718 if (controller.isCanceled) {
3718 controller.cancelationCompleter.complete(); 3719 controller.cancelationCompleter.complete();
3719 } else { 3720 } else {
3720 controller.close(); 3721 controller.close();
3721 } 3722 }
3722 return; 3723 return;
3723 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { 3724 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) {
3724 // The error is a js-error. 3725 // The error is a js-error.
3725 if (controller.cancelationCompleter != null) { 3726 if (controller.isCanceled) {
3726 controller.cancelationCompleter.completeError( 3727 controller.cancelationCompleter.completeError(
3727 unwrapException(object), 3728 unwrapException(object),
3728 getTraceFromException(object)); 3729 getTraceFromException(object));
3729 } else { 3730 } else {
3730 controller.addError(unwrapException(object), 3731 controller.addError(unwrapException(object),
3731 getTraceFromException(object)); 3732 getTraceFromException(object));
3732 controller.close(); 3733 controller.close();
3733 } 3734 }
3734 return; 3735 return;
3735 } 3736 }
3736 3737
3737 if (object is IterationMarker) { 3738 if (object is IterationMarker) {
3738 if (controller.cancelationCompleter != null) { 3739 if (controller.isCanceled) {
3739 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3740 Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3740 async_error_codes.STREAM_WAS_CANCELED)(null); 3741 async_error_codes.STREAM_WAS_CANCELED);
3742 wrapped(null);
3741 return; 3743 return;
3742 } 3744 }
3743 if (object.state == IterationMarker.YIELD_SINGLE) { 3745 if (object.state == IterationMarker.YIELD_SINGLE) {
3744 controller.add(object.value); 3746 controller.add(object.value);
3745 // If the controller is paused we stop producing more values. 3747
3746 if (controller.isPaused) {
3747 return;
3748 }
3749 // TODO(sigurdm): We should not suspend here according to the spec.
3750 scheduleMicrotask(() { 3748 scheduleMicrotask(() {
3751 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3749 if (controller.isPaused) {
3752 async_error_codes.SUCCESS) 3750 // We only suspend the thread inside the microtask in order to allow
3753 (null); 3751 // listeners on the output stream to pause in response to the just
3752 // output value, and have the stream immediately stop producing.
3753 controller.isSuspended = true;
3754 return;
3755 }
3756 Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3757 async_error_codes.SUCCESS);
3758 wrapped(null);
3754 }); 3759 });
3755 return; 3760 return;
3756 } else if (object.state == IterationMarker.YIELD_STAR) { 3761 } else if (object.state == IterationMarker.YIELD_STAR) {
3757 Stream stream = object.value; 3762 Stream stream = object.value;
3758 controller.isAdding = true;
3759 // Errors of [stream] are passed though to the main stream. (see 3763 // Errors of [stream] are passed though to the main stream. (see
3760 // [AsyncStreamController.addStream]. 3764 // [AsyncStreamController.addStream]).
3761 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. 3765 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad.
3762 controller.addStream(stream).then((_) { 3766 controller.addStream(stream).then((_) {
3763 controller.isAdding = false; 3767 // No check for isPaused here because the spec 17.16.2 only
3764 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3768 // demands checks *before* each element in [stream] not after the last
3765 async_error_codes.SUCCESS)(null); 3769 // one. On the other hand we check for isCanceled, as that check happens
3770 // after insertion of each element.
3771 int errorCode = controller.isCanceled
3772 ? async_error_codes.STREAM_WAS_CANCELED
3773 : async_error_codes.SUCCESS;
3774 Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3775 errorCode);
3776 wrapped(null);
3766 }); 3777 });
3767 return; 3778 return;
3768 } 3779 }
3769 } 3780 }
3770 3781
3771 Future future = object is Future ? object : new Future.value(object); 3782 Future future = object is Future ? object : new Future.value(object);
3772 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3783 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode,
3773 async_error_codes.SUCCESS), 3784 async_error_codes.SUCCESS),
3774 onError: (error, StackTrace stackTrace) { 3785 onError: (error, StackTrace stackTrace) {
3775 ExceptionAndStackTrace wrapped = 3786 ExceptionAndStackTrace wrappedException =
3776 new ExceptionAndStackTrace(error, stackTrace); 3787 new ExceptionAndStackTrace(error, stackTrace);
3777 return _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, 3788 Function wrapped = _wrapJsFunctionForAsync(
3778 async_error_codes.ERROR) 3789 bodyFunctionOrErrorCode, async_error_codes.ERROR);
3779 (wrapped); 3790 return wrapped(wrappedException);
3780 }); 3791 });
3781 } 3792 }
3782 3793
3783 Stream streamOfController(AsyncStarStreamController controller) { 3794 Stream streamOfController(AsyncStarStreamController controller) {
3784 return controller.stream; 3795 return controller.stream;
3785 } 3796 }
3786 3797
3787 /// A wrapper around a [StreamController] that remembers if that controller 3798 /// A wrapper around a [StreamController] that keeps track of the state of
3788 /// got a cancel. 3799 /// the execution of an async* function.
3800 /// It can be in 1 of 3 states:
3789 /// 3801 ///
3790 /// Also has a subSubscription that when not null will provide events for the 3802 /// - running/scheduled
3791 /// stream, and will be paused and resumed along with this controller. 3803 /// - suspended
3804 /// - canceled
3805 ///
3806 /// If yielding while the subscription is paused it will become suspended. And
3807 /// only resume after the subscription is resumed or canceled.
3792 class AsyncStarStreamController { 3808 class AsyncStarStreamController {
3793 StreamController controller; 3809 StreamController controller;
3794 Stream get stream => controller.stream; 3810 Stream get stream => controller.stream;
3811
3812 /// True when the async* function has yielded while being paused.
3813 /// When true execution will only resume after a `onResume` or `onCancel`
3814 /// event.
3815 bool isSuspended = false;
3816
3817 bool get isPaused => controller.isPaused;
3818
3795 Completer cancelationCompleter = null; 3819 Completer cancelationCompleter = null;
3820
3821 /// True after the StreamSubscription has been cancelled.
3822 /// When this is true, errors thrown from the async* body should go to the
3823 /// [cancelationCompleter] instead of adding them to [controller], and
3824 /// returning from the async function should complete [cancelationCompleter].
3796 bool get isCanceled => cancelationCompleter != null; 3825 bool get isCanceled => cancelationCompleter != null;
3797 bool isAdding = false; 3826
3798 bool isPaused = false;
3799 add(event) => controller.add(event); 3827 add(event) => controller.add(event);
3828
3800 addStream(Stream stream) { 3829 addStream(Stream stream) {
3801 return controller.addStream(stream, cancelOnError: false); 3830 return controller.addStream(stream, cancelOnError: false);
3802 } 3831 }
3832
3803 addError(error, stackTrace) => controller.addError(error, stackTrace); 3833 addError(error, stackTrace) => controller.addError(error, stackTrace);
3834
3804 close() => controller.close(); 3835 close() => controller.close();
3805 3836
3806 AsyncStarStreamController(body) { 3837 AsyncStarStreamController(body) {
3838
3839 _resumeBody() {
3840 scheduleMicrotask(() {
3841 Function wrapped =
3842 _wrapJsFunctionForAsync(body, async_error_codes.SUCCESS);
3843 wrapped(null);
3844 });
3845 }
3846
3807 controller = new StreamController( 3847 controller = new StreamController(
3808 onListen: () { 3848 onListen: () {
3809 scheduleMicrotask(() { 3849 _resumeBody();
3810 Function wrapped = _wrapJsFunctionForAsync(body,
3811 async_error_codes.SUCCESS);
3812 wrapped(null);
3813 });
3814 },
3815 onPause: () {
3816 isPaused = true;
3817 }, onResume: () { 3850 }, onResume: () {
3818 isPaused = false; 3851 // Only schedule again if the async* function actually is suspended.
3819 if (!isAdding) { 3852 // Resume directly instead of scheduling, so that the sequence
3820 asyncStarHelper(null, body, this); 3853 // `pause-resume-pause` will result in one extra event produced.
3854 if (isSuspended) {
3855 isSuspended = false;
3856 _resumeBody();
3821 } 3857 }
3822 }, onCancel: () { 3858 }, onCancel: () {
3859 // If the async* is finished we ignore cancel events.
3823 if (!controller.isClosed) { 3860 if (!controller.isClosed) {
3824 cancelationCompleter = new Completer(); 3861 cancelationCompleter = new Completer();
3825 if (isPaused) asyncStarHelper(null, body, this); 3862 if (isSuspended) {
3826 3863 // Resume the suspended async* function to run finalizers.
3864 isSuspended = false;
3865 scheduleMicrotask(() {
3866 Function wrapped =_wrapJsFunctionForAsync(body,
3867 async_error_codes.STREAM_WAS_CANCELED);
3868 wrapped(null);
3869 });
3870 }
3827 return cancelationCompleter.future; 3871 return cancelationCompleter.future;
3828 } 3872 }
3829 }); 3873 });
3830 } 3874 }
3831 } 3875 }
3832 3876
3833 makeAsyncStarController(body) { 3877 makeAsyncStarController(body) {
3834 return new AsyncStarStreamController(body); 3878 return new AsyncStarStreamController(body);
3835 } 3879 }
3836 3880
(...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 3976 // This is a function that will return a helper function that does the
3933 // iteration of the sync*. 3977 // iteration of the sync*.
3934 // 3978 //
3935 // Each invocation should give a body with fresh state. 3979 // Each invocation should give a body with fresh state.
3936 final dynamic /* js function */ _outerHelper; 3980 final dynamic /* js function */ _outerHelper;
3937 3981
3938 SyncStarIterable(this._outerHelper); 3982 SyncStarIterable(this._outerHelper);
3939 3983
3940 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); 3984 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper));
3941 } 3985 }
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