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 |