wasm_bindgen/convert/
closures.rs

1use alloc::boxed::Box;
2use core::mem;
3
4use crate::closure::{Closure, IntoWasmClosure, WasmClosure, WasmClosureFnOnce};
5use crate::convert::slices::WasmSlice;
6use crate::convert::RefFromWasmAbi;
7use crate::convert::{FromWasmAbi, IntoWasmAbi, ReturnWasmAbi, WasmAbi, WasmRet};
8use crate::describe::{inform, WasmDescribe, FUNCTION};
9use crate::throw_str;
10use crate::JsValue;
11use crate::UnwrapThrowExt;
12
13macro_rules! closures {
14    // A counter helper to count number of arguments.
15    (@count_one $ty:ty) => (1);
16
17    (@describe ( $($ty:ty),* )) => {
18        // Needs to be a constant so that interpreter doesn't crash on
19        // unsupported operations in debug mode.
20        const ARG_COUNT: u32 = 0 $(+ closures!(@count_one $ty))*;
21        inform(ARG_COUNT);
22        $(<$ty>::describe();)*
23    };
24
25    // This silly helper is because by default Rust infers `|var_with_ref_type| ...` closure
26    // as `impl Fn(&'outer_lifetime A)` instead of `impl for<'temp_lifetime> Fn(&'temp_lifetime A)`
27    // while `|var_with_ref_type: &A|` makes it use the higher-order generic as expected.
28    (@closure ($($ty:ty),*) $($var:ident)* $body:block) => (move |$($var: $ty),*| $body);
29
30    (@impl_for_fn $is_mut:literal [$($mut:ident)?] $Fn:ident $FnArgs:tt $FromWasmAbi:ident $($var_expr:expr => $var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) => (const _: () = {
31        impl<$($var,)* R> IntoWasmAbi for &'_ $($mut)? (dyn $Fn $FnArgs -> R + '_)
32        where
33            Self: WasmDescribe,
34        {
35            type Abi = WasmSlice;
36
37            fn into_abi(self) -> WasmSlice {
38                unsafe {
39                    let (a, b): (usize, usize) = mem::transmute(self);
40                    WasmSlice { ptr: a as u32, len: b as u32 }
41                }
42            }
43        }
44
45        #[allow(non_snake_case)]
46        unsafe extern "C" fn invoke<$($var: $FromWasmAbi,)* R: ReturnWasmAbi>(
47            a: usize,
48            b: usize,
49            $(
50            $arg1: <$var::Abi as WasmAbi>::Prim1,
51            $arg2: <$var::Abi as WasmAbi>::Prim2,
52            $arg3: <$var::Abi as WasmAbi>::Prim3,
53            $arg4: <$var::Abi as WasmAbi>::Prim4,
54            )*
55        ) -> WasmRet<R::Abi> {
56            if a == 0 {
57                throw_str("closure invoked recursively or after being dropped");
58            }
59            // Scope all local variables before we call `return_abi` to
60            // ensure they're all destroyed as `return_abi` may throw
61            let ret = {
62                let f: & $($mut)? dyn $Fn $FnArgs -> R = mem::transmute((a, b));
63                $(
64                    let $var = $var::Abi::join($arg1, $arg2, $arg3, $arg4);
65                )*
66                f($($var_expr),*)
67            };
68            ret.return_abi().into()
69        }
70
71        #[allow(clippy::fn_to_numeric_cast)]
72        impl<$($var,)* R> WasmDescribe for dyn $Fn $FnArgs -> R + '_
73        where
74            $($var: $FromWasmAbi,)*
75            R: ReturnWasmAbi,
76        {
77            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
78            fn describe() {
79                inform(FUNCTION);
80                inform(invoke::<$($var,)* R> as usize as u32);
81                closures!(@describe $FnArgs);
82                R::describe();
83                R::describe();
84            }
85        }
86
87        unsafe impl<$($var,)* R> WasmClosure for dyn $Fn $FnArgs -> R + '_
88        where
89            Self: WasmDescribe,
90        {
91            const IS_MUT: bool = $is_mut;
92        }
93
94        impl<T, $($var,)* R> IntoWasmClosure<dyn $Fn $FnArgs -> R> for T
95        where
96            T: 'static + $Fn $FnArgs -> R,
97        {
98            fn unsize(self: Box<Self>) -> Box<dyn $Fn $FnArgs -> R> { self }
99        }
100    };);
101
102    (@impl_for_args $FnArgs:tt $FromWasmAbi:ident $($var_expr:expr => $var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) => {
103        closures!(@impl_for_fn false [] Fn $FnArgs $FromWasmAbi $($var_expr => $var $arg1 $arg2 $arg3 $arg4)*);
104        closures!(@impl_for_fn true [mut] FnMut $FnArgs $FromWasmAbi $($var_expr => $var $arg1 $arg2 $arg3 $arg4)*);
105
106        // The memory safety here in these implementations below is a bit tricky. We
107        // want to be able to drop the `Closure` object from within the invocation of a
108        // `Closure` for cases like promises. That means that while it's running we
109        // might drop the `Closure`, but that shouldn't invalidate the environment yet.
110        //
111        // Instead what we do is to wrap closures in `Rc` variables. The main `Closure`
112        // has a strong reference count which keeps the trait object alive. Each
113        // invocation of a closure then *also* clones this and gets a new reference
114        // count. When the closure returns it will release the reference count.
115        //
116        // This means that if the main `Closure` is dropped while it's being invoked
117        // then destruction is deferred until execution returns. Otherwise it'll
118        // deallocate data immediately.
119
120        #[allow(non_snake_case, unused_parens)]
121        impl<T, $($var,)* R> WasmClosureFnOnce<dyn FnMut $FnArgs -> R, $FnArgs, R> for T
122        where
123            T: 'static + FnOnce $FnArgs -> R,
124            $($var: $FromWasmAbi + 'static,)*
125            R: ReturnWasmAbi + 'static,
126        {
127            fn into_fn_mut(self) -> Box<dyn FnMut $FnArgs -> R> {
128                let mut me = Some(self);
129                Box::new(move |$($var),*| {
130                    let me = me.take().expect_throw("FnOnce called more than once");
131                    me($($var),*)
132                })
133            }
134
135            fn into_js_function(self) -> JsValue {
136                use alloc::rc::Rc;
137                use crate::__rt::WasmRefCell;
138
139                let rc1 = Rc::new(WasmRefCell::new(None));
140                let rc2 = rc1.clone();
141
142                let closure = Closure::once(closures!(@closure $FnArgs $($var)* {
143                    let result = self($($var),*);
144
145                    // And then drop the `Rc` holding this function's `Closure`
146                    // alive.
147                    debug_assert_eq!(Rc::strong_count(&rc2), 1);
148                    let option_closure = rc2.borrow_mut().take();
149                    debug_assert!(option_closure.is_some());
150                    drop(option_closure);
151
152                    result
153                }));
154
155                let js_val = closure.as_ref().clone();
156
157                *rc1.borrow_mut() = Some(closure);
158                debug_assert_eq!(Rc::strong_count(&rc1), 2);
159                drop(rc1);
160
161                js_val
162            }
163        }
164    };
165
166    ($( ($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) )*) => ($(
167        closures!(@impl_for_args ($($var),*) FromWasmAbi $($var::from_abi($var) => $var $arg1 $arg2 $arg3 $arg4)*);
168    )*);
169}
170
171closures! {
172    ()
173    (A a1 a2 a3 a4)
174    (A a1 a2 a3 a4 B b1 b2 b3 b4)
175    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
176    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
177    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4)
178    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4)
179    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4)
180    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4 H h1 h2 h3 h4)
181}
182
183// Copy the above impls down here for where there's only one argument and it's a
184// reference. We could add more impls for more kinds of references, but it
185// becomes a combinatorial explosion quickly. Let's see how far we can get with
186// just this one! Maybe someone else can figure out voodoo so we don't have to
187// duplicate.
188
189// We need to allow coherence leak check just for these traits because we're providing separate implementation for `Fn(&A)` variants when `Fn(A)` one already exists.
190#[allow(coherence_leak_check)]
191const _: () = {
192    closures!(@impl_for_args (&A) RefFromWasmAbi &*A::ref_from_abi(A) => A a1 a2 a3 a4);
193};