wasm_bindgen/convert/
closures.rs1use 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 (@count_one $ty:ty) => (1);
16
17 (@describe ( $($ty:ty),* )) => {
18 const ARG_COUNT: u32 = 0 $(+ closures!(@count_one $ty))*;
21 inform(ARG_COUNT);
22 $(<$ty>::describe();)*
23 };
24
25 (@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 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 #[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 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#[allow(coherence_leak_check)]
191const _: () = {
192 closures!(@impl_for_args (&A) RefFromWasmAbi &*A::ref_from_abi(A) => A a1 a2 a3 a4);
193};