wasm_bindgen/convert/
closures.rs

1#![allow(clippy::fn_to_numeric_cast)]
2
3use core::mem;
4
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;
10
11macro_rules! stack_closures {
12    ($( ($cnt:tt $invoke:ident $invoke_mut:ident $($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) )*) => ($(
13        impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (dyn Fn($($var),*) -> R + 'b)
14            where $($var: FromWasmAbi,)*
15                  R: ReturnWasmAbi
16        {
17            type Abi = WasmSlice;
18
19            fn into_abi(self) -> WasmSlice {
20                unsafe {
21                    let (a, b): (usize, usize) = mem::transmute(self);
22                    WasmSlice { ptr: a as u32, len: b as u32 }
23                }
24            }
25        }
26
27        #[allow(non_snake_case)]
28        unsafe extern "C" fn $invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
29            a: usize,
30            b: usize,
31            $(
32            $arg1: <$var::Abi as WasmAbi>::Prim1,
33            $arg2: <$var::Abi as WasmAbi>::Prim2,
34            $arg3: <$var::Abi as WasmAbi>::Prim3,
35            $arg4: <$var::Abi as WasmAbi>::Prim4,
36            )*
37        ) -> WasmRet<R::Abi> {
38            if a == 0 {
39                throw_str("closure invoked after being dropped");
40            }
41            // Scope all local variables before we call `return_abi` to
42            // ensure they're all destroyed as `return_abi` may throw
43            let ret = {
44                let f: &dyn Fn($($var),*) -> R = mem::transmute((a, b));
45                $(
46                    let $var = <$var as FromWasmAbi>::from_abi($var::Abi::join($arg1, $arg2, $arg3, $arg4));
47                )*
48                f($($var),*)
49            };
50            ret.return_abi().into()
51        }
52
53        impl<'a, $($var,)* R> WasmDescribe for dyn Fn($($var),*) -> R + 'a
54            where $($var: FromWasmAbi,)*
55                  R: ReturnWasmAbi
56        {
57            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
58            fn describe() {
59                inform(FUNCTION);
60                inform($invoke::<$($var,)* R> as usize as u32);
61                inform($cnt);
62                $(<$var as WasmDescribe>::describe();)*
63                <R as WasmDescribe>::describe();
64                <R as WasmDescribe>::describe();
65            }
66        }
67
68        impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a mut (dyn FnMut($($var),*) -> R + 'b)
69            where $($var: FromWasmAbi,)*
70                  R: ReturnWasmAbi
71        {
72            type Abi = WasmSlice;
73
74            fn into_abi(self) -> WasmSlice {
75                unsafe {
76                    let (a, b): (usize, usize) = mem::transmute(self);
77                    WasmSlice { ptr: a as u32, len: b as u32 }
78                }
79            }
80        }
81
82        #[allow(non_snake_case)]
83        unsafe extern "C" fn $invoke_mut<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
84            a: usize,
85            b: usize,
86            $(
87            $arg1: <$var::Abi as WasmAbi>::Prim1,
88            $arg2: <$var::Abi as WasmAbi>::Prim2,
89            $arg3: <$var::Abi as WasmAbi>::Prim3,
90            $arg4: <$var::Abi as WasmAbi>::Prim4,
91            )*
92        ) -> WasmRet<R::Abi> {
93            if a == 0 {
94                throw_str("closure invoked recursively or after being dropped");
95            }
96            // Scope all local variables before we call `return_abi` to
97            // ensure they're all destroyed as `return_abi` may throw
98            let ret = {
99                let f: &mut dyn FnMut($($var),*) -> R = mem::transmute((a, b));
100                $(
101                    let $var = <$var as FromWasmAbi>::from_abi($var::Abi::join($arg1, $arg2, $arg3, $arg4));
102                )*
103                f($($var),*)
104            };
105            ret.return_abi().into()
106        }
107
108        impl<'a, $($var,)* R> WasmDescribe for dyn FnMut($($var),*) -> R + 'a
109            where $($var: FromWasmAbi,)*
110                  R: ReturnWasmAbi
111        {
112            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
113            fn describe() {
114                inform(FUNCTION);
115                inform($invoke_mut::<$($var,)* R> as usize as u32);
116                inform($cnt);
117                $(<$var as WasmDescribe>::describe();)*
118                <R as WasmDescribe>::describe();
119                <R as WasmDescribe>::describe();
120            }
121        }
122    )*)
123}
124
125stack_closures! {
126    (0 invoke0 invoke0_mut)
127    (1 invoke1 invoke1_mut A a1 a2 a3 a4)
128    (2 invoke2 invoke2_mut A a1 a2 a3 a4 B b1 b2 b3 b4)
129    (3 invoke3 invoke3_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
130    (4 invoke4 invoke4_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
131    (5 invoke5 invoke5_mut 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)
132    (6 invoke6 invoke6_mut 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)
133    (7 invoke7 invoke7_mut 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)
134    (8 invoke8 invoke8_mut 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)
135}
136
137impl<A, R> IntoWasmAbi for &(dyn Fn(&A) -> R + '_)
138where
139    A: RefFromWasmAbi,
140    R: ReturnWasmAbi,
141{
142    type Abi = WasmSlice;
143
144    fn into_abi(self) -> WasmSlice {
145        unsafe {
146            let (a, b): (usize, usize) = mem::transmute(self);
147            WasmSlice {
148                ptr: a as u32,
149                len: b as u32,
150            }
151        }
152    }
153}
154
155#[allow(non_snake_case)]
156#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
157unsafe extern "C" fn invoke1_ref<A: RefFromWasmAbi, R: ReturnWasmAbi>(
158    a: usize,
159    b: usize,
160    arg1: <A::Abi as WasmAbi>::Prim1,
161    arg2: <A::Abi as WasmAbi>::Prim2,
162    arg3: <A::Abi as WasmAbi>::Prim3,
163    arg4: <A::Abi as WasmAbi>::Prim4,
164) -> WasmRet<R::Abi> {
165    if a == 0 {
166        throw_str("closure invoked after being dropped");
167    }
168    // Scope all local variables before we call `return_abi` to
169    // ensure they're all destroyed as `return_abi` may throw
170    let ret = {
171        let f: &dyn Fn(&A) -> R = mem::transmute((a, b));
172        let arg = <A as RefFromWasmAbi>::ref_from_abi(A::Abi::join(arg1, arg2, arg3, arg4));
173        f(&*arg)
174    };
175    ret.return_abi().into()
176}
177
178impl<A, R> WasmDescribe for dyn Fn(&A) -> R + '_
179where
180    A: RefFromWasmAbi,
181    R: ReturnWasmAbi,
182{
183    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
184    fn describe() {
185        inform(FUNCTION);
186        inform(invoke1_ref::<A, R> as usize as u32);
187        inform(1);
188        <&A as WasmDescribe>::describe();
189        <R as WasmDescribe>::describe();
190        <R as WasmDescribe>::describe();
191    }
192}
193
194impl<A, R> IntoWasmAbi for &mut (dyn FnMut(&A) -> R + '_)
195where
196    A: RefFromWasmAbi,
197    R: ReturnWasmAbi,
198{
199    type Abi = WasmSlice;
200
201    fn into_abi(self) -> WasmSlice {
202        unsafe {
203            let (a, b): (usize, usize) = mem::transmute(self);
204            WasmSlice {
205                ptr: a as u32,
206                len: b as u32,
207            }
208        }
209    }
210}
211
212#[allow(non_snake_case)]
213#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
214unsafe extern "C" fn invoke1_mut_ref<A: RefFromWasmAbi, R: ReturnWasmAbi>(
215    a: usize,
216    b: usize,
217    arg1: <A::Abi as WasmAbi>::Prim1,
218    arg2: <A::Abi as WasmAbi>::Prim2,
219    arg3: <A::Abi as WasmAbi>::Prim3,
220    arg4: <A::Abi as WasmAbi>::Prim4,
221) -> WasmRet<R::Abi> {
222    if a == 0 {
223        throw_str("closure invoked recursively or after being dropped");
224    }
225    // Scope all local variables before we call `return_abi` to
226    // ensure they're all destroyed as `return_abi` may throw
227    let ret = {
228        let f: &mut dyn FnMut(&A) -> R = mem::transmute((a, b));
229        let arg = <A as RefFromWasmAbi>::ref_from_abi(A::Abi::join(arg1, arg2, arg3, arg4));
230        f(&*arg)
231    };
232    ret.return_abi().into()
233}
234
235impl<A, R> WasmDescribe for dyn FnMut(&A) -> R + '_
236where
237    A: RefFromWasmAbi,
238    R: ReturnWasmAbi,
239{
240    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
241    fn describe() {
242        inform(FUNCTION);
243        inform(invoke1_mut_ref::<A, R> as usize as u32);
244        inform(1);
245        <&A as WasmDescribe>::describe();
246        <R as WasmDescribe>::describe();
247        <R as WasmDescribe>::describe();
248    }
249}