| 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:_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 18 matching lines...) Expand all Loading... |
| 29 isWorker, | 29 isWorker, |
| 30 leaveJsAsync; | 30 leaveJsAsync; |
| 31 | 31 |
| 32 import 'dart:async' show | 32 import 'dart:async' show |
| 33 Completer, | 33 Completer, |
| 34 DeferredLoadException, | 34 DeferredLoadException, |
| 35 Future, | 35 Future, |
| 36 StreamController, | 36 StreamController, |
| 37 Stream, | 37 Stream, |
| 38 StreamSubscription, | 38 StreamSubscription, |
| 39 scheduleMicrotask; | 39 scheduleMicrotask, |
| 40 Zone; |
| 40 | 41 |
| 41 import 'dart:_foreign_helper' show | 42 import 'dart:_foreign_helper' show |
| 42 DART_CLOSURE_TO_JS, | 43 DART_CLOSURE_TO_JS, |
| 43 JS, | 44 JS, |
| 44 JS_BUILTIN, | 45 JS_BUILTIN, |
| 45 JS_CALL_IN_ISOLATE, | 46 JS_CALL_IN_ISOLATE, |
| 46 JS_CONST, | 47 JS_CONST, |
| 47 JS_CURRENT_ISOLATE, | 48 JS_CURRENT_ISOLATE, |
| 48 JS_CURRENT_ISOLATE_CONTEXT, | 49 JS_CURRENT_ISOLATE_CONTEXT, |
| 49 JS_EFFECT, | 50 JS_EFFECT, |
| (...skipping 3683 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3733 /// If [asyncBody] is [async_error_codes.SUCCESS]/[async_error_codes.ERROR] it | 3734 /// If [asyncBody] is [async_error_codes.SUCCESS]/[async_error_codes.ERROR] it |
| 3734 /// indicates a return or throw from the async function, and | 3735 /// indicates a return or throw from the async function, and |
| 3735 /// complete/completeError is called on [completer] with [object]. | 3736 /// complete/completeError is called on [completer] with [object]. |
| 3736 /// | 3737 /// |
| 3737 /// Otherwise [asyncBody] is set up to be called when the future is completed | 3738 /// Otherwise [asyncBody] is set up to be called when the future is completed |
| 3738 /// with a code [async_error_codes.SUCCESS]/[async_error_codes.ERROR] depending | 3739 /// with a code [async_error_codes.SUCCESS]/[async_error_codes.ERROR] depending |
| 3739 /// on the success of the future. | 3740 /// on the success of the future. |
| 3740 /// | 3741 /// |
| 3741 /// Returns the future of the completer for convenience of the first call. | 3742 /// Returns the future of the completer for convenience of the first call. |
| 3742 dynamic asyncHelper(dynamic object, | 3743 dynamic asyncHelper(dynamic object, |
| 3743 dynamic /* js function */ bodyFunctionOrErrorCode, | 3744 dynamic /* int | WrappedAsyncBody */ bodyFunctionOrErrorCode, |
| 3744 Completer completer) { | 3745 Completer completer) { |
| 3745 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { | 3746 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |
| 3746 completer.complete(object); | 3747 completer.complete(object); |
| 3747 return; | 3748 return; |
| 3748 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { | 3749 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |
| 3749 // The error is a js-error. | 3750 // The error is a js-error. |
| 3750 completer.completeError(unwrapException(object), | 3751 completer.completeError(unwrapException(object), |
| 3751 getTraceFromException(object)); | 3752 getTraceFromException(object)); |
| 3752 return; | 3753 return; |
| 3753 } | 3754 } |
| 3754 Future future = object is Future ? object : new Future.value(object); | 3755 Future future = object is Future ? object : new Future.value(object); |
| 3755 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3756 future.then( |
| 3756 async_error_codes.SUCCESS), | 3757 (result) => bodyFunctionOrErrorCode(async_error_codes.SUCCESS, result), |
| 3757 onError: (dynamic error, StackTrace stackTrace) { | 3758 onError: (dynamic error, StackTrace stackTrace) { |
| 3758 ExceptionAndStackTrace wrappedException = | 3759 ExceptionAndStackTrace wrappedException = |
| 3759 new ExceptionAndStackTrace(error, stackTrace); | 3760 new ExceptionAndStackTrace(error, stackTrace); |
| 3760 Function wrapped =_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3761 bodyFunctionOrErrorCode(async_error_codes.ERROR, wrappedException); |
| 3761 async_error_codes.ERROR); | |
| 3762 wrapped(wrappedException); | |
| 3763 }); | 3762 }); |
| 3764 return completer.future; | 3763 return completer.future; |
| 3765 } | 3764 } |
| 3766 | 3765 |
| 3767 Function _wrapJsFunctionForAsync(dynamic /* js function */ function, | 3766 typedef void WrappedAsyncBody(int errorCode, dynamic result); |
| 3768 int errorCode) { | 3767 |
| 3768 WrappedAsyncBody _wrapJsFunctionForAsync(dynamic /* js function */ function) { |
| 3769 var protected = JS('', """ | 3769 var protected = JS('', """ |
| 3770 // Invokes [function] with [errorCode] and [result]. | 3770 // Invokes [function] with [errorCode] and [result]. |
| 3771 // | 3771 // |
| 3772 // If (and as long as) the invocation throws, calls [function] again, | 3772 // If (and as long as) the invocation throws, calls [function] again, |
| 3773 // with an error-code. | 3773 // with an error-code. |
| 3774 function(errorCode, result) { | 3774 function(errorCode, result) { |
| 3775 while (true) { | 3775 while (true) { |
| 3776 try { | 3776 try { |
| 3777 #(errorCode, result); | 3777 #(errorCode, result); |
| 3778 break; | 3778 break; |
| 3779 } catch (error) { | 3779 } catch (error) { |
| 3780 result = error; | 3780 result = error; |
| 3781 errorCode = #; | 3781 errorCode = #; |
| 3782 } | 3782 } |
| 3783 } | 3783 } |
| 3784 }""", function, async_error_codes.ERROR); | 3784 }""", function, async_error_codes.ERROR); |
| 3785 return (result) { | 3785 return Zone.current.bindBinaryCallback((int errorCode, dynamic result) { |
| 3786 JS('', '#(#, #)', protected, errorCode, result); | 3786 JS('', '#(#, #)', protected, errorCode, result); |
| 3787 }; | 3787 }); |
| 3788 } | 3788 } |
| 3789 | 3789 |
| 3790 /// Implements the runtime support for async* functions. | 3790 /// Implements the runtime support for async* functions. |
| 3791 /// | 3791 /// |
| 3792 /// Called by the transformed function for each original return, await, yield, | 3792 /// Called by the transformed function for each original return, await, yield, |
| 3793 /// yield* and before starting the function. | 3793 /// yield* and before starting the function. |
| 3794 /// | 3794 /// |
| 3795 /// When the async* function wants to return it calls this function with | 3795 /// When the async* function wants to return it calls this function with |
| 3796 /// [asyncBody] == [async_error_codes.SUCCESS], the asyncStarHelper takes this | 3796 /// [asyncBody] == [async_error_codes.SUCCESS], the asyncStarHelper takes this |
| 3797 /// as signal to close the stream. | 3797 /// as signal to close the stream. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 3817 /// yielded stream, and adds all events and errors to our own controller (taking | 3817 /// yielded stream, and adds all events and errors to our own controller (taking |
| 3818 /// care if the subscription has been paused or canceled) - when the sub-stream | 3818 /// care if the subscription has been paused or canceled) - when the sub-stream |
| 3819 /// is done, schedules [asyncBody] again. | 3819 /// is done, schedules [asyncBody] again. |
| 3820 /// | 3820 /// |
| 3821 /// If the async* function wants to do an await it calls this function with | 3821 /// If the async* function wants to do an await it calls this function with |
| 3822 /// [object] not and [IterationMarker]. | 3822 /// [object] not and [IterationMarker]. |
| 3823 /// | 3823 /// |
| 3824 /// If [object] is not a [Future], it is wrapped in a `Future.value`. | 3824 /// If [object] is not a [Future], it is wrapped in a `Future.value`. |
| 3825 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. | 3825 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. |
| 3826 void asyncStarHelper(dynamic object, | 3826 void asyncStarHelper(dynamic object, |
| 3827 dynamic /* int | js function */ bodyFunctionOrErrorCode, | 3827 dynamic /* int | WrappedAsyncBody */ bodyFunctionOrErrorCode, |
| 3828 AsyncStarStreamController controller) { | 3828 AsyncStarStreamController controller) { |
| 3829 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { | 3829 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |
| 3830 // This happens on return from the async* function. | 3830 // This happens on return from the async* function. |
| 3831 if (controller.isCanceled) { | 3831 if (controller.isCanceled) { |
| 3832 controller.cancelationCompleter.complete(); | 3832 controller.cancelationCompleter.complete(); |
| 3833 } else { | 3833 } else { |
| 3834 controller.close(); | 3834 controller.close(); |
| 3835 } | 3835 } |
| 3836 return; | 3836 return; |
| 3837 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { | 3837 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |
| 3838 // The error is a js-error. | 3838 // The error is a js-error. |
| 3839 if (controller.isCanceled) { | 3839 if (controller.isCanceled) { |
| 3840 controller.cancelationCompleter.completeError( | 3840 controller.cancelationCompleter.completeError( |
| 3841 unwrapException(object), | 3841 unwrapException(object), |
| 3842 getTraceFromException(object)); | 3842 getTraceFromException(object)); |
| 3843 } else { | 3843 } else { |
| 3844 controller.addError(unwrapException(object), | 3844 controller.addError(unwrapException(object), |
| 3845 getTraceFromException(object)); | 3845 getTraceFromException(object)); |
| 3846 controller.close(); | 3846 controller.close(); |
| 3847 } | 3847 } |
| 3848 return; | 3848 return; |
| 3849 } | 3849 } |
| 3850 | 3850 |
| 3851 if (object is IterationMarker) { | 3851 if (object is IterationMarker) { |
| 3852 if (controller.isCanceled) { | 3852 if (controller.isCanceled) { |
| 3853 Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3853 bodyFunctionOrErrorCode(async_error_codes.STREAM_WAS_CANCELED, null); |
| 3854 async_error_codes.STREAM_WAS_CANCELED); | |
| 3855 wrapped(null); | |
| 3856 return; | 3854 return; |
| 3857 } | 3855 } |
| 3858 if (object.state == IterationMarker.YIELD_SINGLE) { | 3856 if (object.state == IterationMarker.YIELD_SINGLE) { |
| 3859 controller.add(object.value); | 3857 controller.add(object.value); |
| 3860 | 3858 |
| 3861 scheduleMicrotask(() { | 3859 scheduleMicrotask(() { |
| 3862 if (controller.isPaused) { | 3860 if (controller.isPaused) { |
| 3863 // We only suspend the thread inside the microtask in order to allow | 3861 // We only suspend the thread inside the microtask in order to allow |
| 3864 // listeners on the output stream to pause in response to the just | 3862 // listeners on the output stream to pause in response to the just |
| 3865 // output value, and have the stream immediately stop producing. | 3863 // output value, and have the stream immediately stop producing. |
| 3866 controller.isSuspended = true; | 3864 controller.isSuspended = true; |
| 3867 return; | 3865 return; |
| 3868 } | 3866 } |
| 3869 Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3867 bodyFunctionOrErrorCode(null, async_error_codes.SUCCESS); |
| 3870 async_error_codes.SUCCESS); | |
| 3871 wrapped(null); | |
| 3872 }); | 3868 }); |
| 3873 return; | 3869 return; |
| 3874 } else if (object.state == IterationMarker.YIELD_STAR) { | 3870 } else if (object.state == IterationMarker.YIELD_STAR) { |
| 3875 Stream stream = object.value; | 3871 Stream stream = object.value; |
| 3876 // Errors of [stream] are passed though to the main stream. (see | 3872 // Errors of [stream] are passed though to the main stream. (see |
| 3877 // [AsyncStreamController.addStream]). | 3873 // [AsyncStreamController.addStream]). |
| 3878 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. | 3874 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. |
| 3879 controller.addStream(stream).then((_) { | 3875 controller.addStream(stream).then((_) { |
| 3880 // No check for isPaused here because the spec 17.16.2 only | 3876 // No check for isPaused here because the spec 17.16.2 only |
| 3881 // demands checks *before* each element in [stream] not after the last | 3877 // demands checks *before* each element in [stream] not after the last |
| 3882 // one. On the other hand we check for isCanceled, as that check happens | 3878 // one. On the other hand we check for isCanceled, as that check happens |
| 3883 // after insertion of each element. | 3879 // after insertion of each element. |
| 3884 int errorCode = controller.isCanceled | 3880 int errorCode = controller.isCanceled |
| 3885 ? async_error_codes.STREAM_WAS_CANCELED | 3881 ? async_error_codes.STREAM_WAS_CANCELED |
| 3886 : async_error_codes.SUCCESS; | 3882 : async_error_codes.SUCCESS; |
| 3887 Function wrapped = _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3883 bodyFunctionOrErrorCode(errorCode, null); |
| 3888 errorCode); | |
| 3889 wrapped(null); | |
| 3890 }); | 3884 }); |
| 3891 return; | 3885 return; |
| 3892 } | 3886 } |
| 3893 } | 3887 } |
| 3894 | 3888 |
| 3895 Future future = object is Future ? object : new Future.value(object); | 3889 Future future = object is Future ? object : new Future.value(object); |
| 3896 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, | 3890 future.then( |
| 3897 async_error_codes.SUCCESS), | 3891 (result) => bodyFunctionOrErrorCode(async_error_codes.SUCCESS, result), |
| 3898 onError: (error, StackTrace stackTrace) { | 3892 onError: (error, StackTrace stackTrace) { |
| 3899 ExceptionAndStackTrace wrappedException = | 3893 ExceptionAndStackTrace wrappedException = |
| 3900 new ExceptionAndStackTrace(error, stackTrace); | 3894 new ExceptionAndStackTrace(error, stackTrace); |
| 3901 Function wrapped = _wrapJsFunctionForAsync( | 3895 bodyFunctionOrErrorCode(async_error_codes.ERROR, wrappedException); |
| 3902 bodyFunctionOrErrorCode, async_error_codes.ERROR); | 3896 }); |
| 3903 return wrapped(wrappedException); | |
| 3904 }); | |
| 3905 } | 3897 } |
| 3906 | 3898 |
| 3907 Stream streamOfController(AsyncStarStreamController controller) { | 3899 Stream streamOfController(AsyncStarStreamController controller) { |
| 3908 return controller.stream; | 3900 return controller.stream; |
| 3909 } | 3901 } |
| 3910 | 3902 |
| 3911 /// A wrapper around a [StreamController] that keeps track of the state of | 3903 /// A wrapper around a [StreamController] that keeps track of the state of |
| 3912 /// the execution of an async* function. | 3904 /// the execution of an async* function. |
| 3913 /// It can be in 1 of 3 states: | 3905 /// It can be in 1 of 3 states: |
| 3914 /// | 3906 /// |
| (...skipping 25 matching lines...) Expand all Loading... |
| 3940 add(event) => controller.add(event); | 3932 add(event) => controller.add(event); |
| 3941 | 3933 |
| 3942 addStream(Stream stream) { | 3934 addStream(Stream stream) { |
| 3943 return controller.addStream(stream, cancelOnError: false); | 3935 return controller.addStream(stream, cancelOnError: false); |
| 3944 } | 3936 } |
| 3945 | 3937 |
| 3946 addError(error, stackTrace) => controller.addError(error, stackTrace); | 3938 addError(error, stackTrace) => controller.addError(error, stackTrace); |
| 3947 | 3939 |
| 3948 close() => controller.close(); | 3940 close() => controller.close(); |
| 3949 | 3941 |
| 3950 AsyncStarStreamController(body) { | 3942 AsyncStarStreamController(WrappedAsyncBody body) { |
| 3951 | 3943 |
| 3952 _resumeBody() { | 3944 _resumeBody() { |
| 3953 scheduleMicrotask(() { | 3945 scheduleMicrotask(() { |
| 3954 Function wrapped = | 3946 body(async_error_codes.SUCCESS, null); |
| 3955 _wrapJsFunctionForAsync(body, async_error_codes.SUCCESS); | |
| 3956 wrapped(null); | |
| 3957 }); | 3947 }); |
| 3958 } | 3948 } |
| 3959 | 3949 |
| 3960 controller = new StreamController( | 3950 controller = new StreamController( |
| 3961 onListen: () { | 3951 onListen: () { |
| 3962 _resumeBody(); | 3952 _resumeBody(); |
| 3963 }, onResume: () { | 3953 }, onResume: () { |
| 3964 // Only schedule again if the async* function actually is suspended. | 3954 // Only schedule again if the async* function actually is suspended. |
| 3965 // Resume directly instead of scheduling, so that the sequence | 3955 // Resume directly instead of scheduling, so that the sequence |
| 3966 // `pause-resume-pause` will result in one extra event produced. | 3956 // `pause-resume-pause` will result in one extra event produced. |
| 3967 if (isSuspended) { | 3957 if (isSuspended) { |
| 3968 isSuspended = false; | 3958 isSuspended = false; |
| 3969 _resumeBody(); | 3959 _resumeBody(); |
| 3970 } | 3960 } |
| 3971 }, onCancel: () { | 3961 }, onCancel: () { |
| 3972 // If the async* is finished we ignore cancel events. | 3962 // If the async* is finished we ignore cancel events. |
| 3973 if (!controller.isClosed) { | 3963 if (!controller.isClosed) { |
| 3974 cancelationCompleter = new Completer(); | 3964 cancelationCompleter = new Completer(); |
| 3975 if (isSuspended) { | 3965 if (isSuspended) { |
| 3976 // Resume the suspended async* function to run finalizers. | 3966 // Resume the suspended async* function to run finalizers. |
| 3977 isSuspended = false; | 3967 isSuspended = false; |
| 3978 scheduleMicrotask(() { | 3968 scheduleMicrotask(() { |
| 3979 Function wrapped =_wrapJsFunctionForAsync(body, | 3969 body(async_error_codes.STREAM_WAS_CANCELED, null); |
| 3980 async_error_codes.STREAM_WAS_CANCELED); | |
| 3981 wrapped(null); | |
| 3982 }); | 3970 }); |
| 3983 } | 3971 } |
| 3984 return cancelationCompleter.future; | 3972 return cancelationCompleter.future; |
| 3985 } | 3973 } |
| 3986 }); | 3974 }); |
| 3987 } | 3975 } |
| 3988 } | 3976 } |
| 3989 | 3977 |
| 3990 makeAsyncStarController(body) { | 3978 makeAsyncStarController(body) { |
| 3991 return new AsyncStarStreamController(body); | 3979 return new AsyncStarStreamController(body); |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4089 // This is a function that will return a helper function that does the | 4077 // This is a function that will return a helper function that does the |
| 4090 // iteration of the sync*. | 4078 // iteration of the sync*. |
| 4091 // | 4079 // |
| 4092 // Each invocation should give a body with fresh state. | 4080 // Each invocation should give a body with fresh state. |
| 4093 final dynamic /* js function */ _outerHelper; | 4081 final dynamic /* js function */ _outerHelper; |
| 4094 | 4082 |
| 4095 SyncStarIterable(this._outerHelper); | 4083 SyncStarIterable(this._outerHelper); |
| 4096 | 4084 |
| 4097 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); | 4085 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |
| 4098 } | 4086 } |
| OLD | NEW |