Skip to main content

wasm_bindgen/convert/
closures.rs

1use alloc::boxed::Box;
2use core::mem;
3use core::panic::AssertUnwindSafe;
4
5use crate::__rt::marker::ErasableGeneric;
6use crate::__rt::maybe_catch_unwind;
7use crate::closure::{
8    Closure, IntoWasmClosure, IntoWasmClosureRef, IntoWasmClosureRefMut, ScopedClosure,
9    WasmClosure, WasmClosureFnOnce, WasmClosureFnOnceAbort,
10};
11use crate::convert::slices::WasmSlice;
12use crate::convert::traits::UpcastFrom;
13use crate::convert::RefFromWasmAbi;
14use crate::convert::{FromWasmAbi, IntoWasmAbi, ReturnWasmAbi, WasmAbi, WasmRet};
15use crate::describe::{inform, WasmDescribe, FUNCTION};
16use crate::sys::Undefined;
17use crate::throw_str;
18use crate::JsValue;
19use crate::UnwrapThrowExt;
20
21macro_rules! closures {
22    // Unwind safe passing
23    ([$($maybe_unwind_safe:tt)*] $($rest:tt)*) => {
24        closures!(@process [$($maybe_unwind_safe)*] $($rest)*);
25    };
26
27    // One-arity recurse
28    (@process [$($unwind_safe:tt)*] ($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) $($rest:tt)*) => {
29        closures!(@impl_for_args ($($var),*) FromWasmAbi [$($unwind_safe)*] $($var::from_abi($var) => $var $arg1 $arg2 $arg3 $arg4)*);
30        closures!(@process [$($unwind_safe)*] $($rest)*);
31    };
32
33    // Base case
34    (@process [$($unwind_safe:tt)*]) => {};
35
36    // A counter helper to count number of arguments.
37    (@count_one $ty:ty) => (1);
38
39    (@describe ( $($ty:ty),* )) => {
40        // Needs to be a constant so that interpreter doesn't crash on
41        // unsupported operations in debug mode.
42        const ARG_COUNT: u32 = 0 $(+ closures!(@count_one $ty))*;
43        inform(ARG_COUNT);
44        $(<$ty>::describe();)*
45    };
46
47    // This silly helper is because by default Rust infers `|var_with_ref_type| ...` closure
48    // as `impl Fn(&'outer_lifetime A)` instead of `impl for<'temp_lifetime> Fn(&'temp_lifetime A)`
49    // while `|var_with_ref_type: &A|` makes it use the higher-order generic as expected.
50    (@closure ($($ty:ty),*) $($var:ident)* $body:block) => (move |$($var: $ty),*| $body);
51
52    (@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 _: () = {
53        impl<$($var,)* R> IntoWasmAbi for &'_ $($mut)? (dyn $Fn $FnArgs -> R + '_)
54        where
55            Self: WasmDescribe,
56        {
57            type Abi = WasmSlice;
58
59            fn into_abi(self) -> WasmSlice {
60                unsafe {
61                    let (a, b): (usize, usize) = mem::transmute(self);
62                    WasmSlice { ptr: a as u32, len: b as u32 }
63                }
64            }
65        }
66
67        unsafe impl<'a, $($var,)* R> ErasableGeneric for &'a $($mut)? (dyn $Fn $FnArgs -> R + 'a)
68        where
69            $($var: ErasableGeneric,)*
70            R: ErasableGeneric
71        {
72            type Repr = &'static (dyn $Fn ($(<$var as ErasableGeneric>::Repr,)*) -> <R as ErasableGeneric>::Repr + 'static);
73        }
74
75        // Invoke shim for closures. The const generic `UNWIND_SAFE` controls
76        // whether panics are caught and converted to JS exceptions (`true`) or
77        // left to unwind/abort (`false`). When `panic=unwind` is not available,
78        // `UNWIND_SAFE` has no effect — panics always abort.
79        #[allow(non_snake_case)]
80        unsafe extern "C-unwind" fn invoke<$($var: $FromWasmAbi,)* R: ReturnWasmAbi, const UNWIND_SAFE: bool>(
81            a: usize,
82            b: usize,
83            $(
84            $arg1: <$var::Abi as WasmAbi>::Prim1,
85            $arg2: <$var::Abi as WasmAbi>::Prim2,
86            $arg3: <$var::Abi as WasmAbi>::Prim3,
87            $arg4: <$var::Abi as WasmAbi>::Prim4,
88            )*
89        ) -> WasmRet<R::Abi> {
90            if a == 0 {
91                throw_str("closure invoked recursively or after being dropped");
92            }
93            let ret = {
94                let f: & $($mut)? dyn $Fn $FnArgs -> R = mem::transmute((a, b));
95                $(
96                    let $var = $var::Abi::join($arg1, $arg2, $arg3, $arg4);
97                )*
98                if UNWIND_SAFE {
99                    maybe_catch_unwind(AssertUnwindSafe(|| f($($var_expr),*)))
100                } else {
101                    f($($var_expr),*)
102                }
103            };
104            ret.return_abi().into()
105        }
106
107        #[allow(clippy::fn_to_numeric_cast)]
108        impl<$($var,)* R> WasmDescribe for dyn $Fn $FnArgs -> R + '_
109        where
110            $($var: $FromWasmAbi,)*
111            R: ReturnWasmAbi,
112        {
113            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
114            fn describe() {
115                // Raw &dyn Fn/&dyn FnMut passed as arguments use the catching
116                // invoke shim by default, matching the previous runtime behavior.
117                <Self as WasmClosure>::describe_invoke::<true>();
118            }
119        }
120
121        unsafe impl<'__closure, $($var,)* R> WasmClosure for dyn $Fn $FnArgs -> R + '__closure
122        where
123            $($var: $FromWasmAbi,)*
124            R: ReturnWasmAbi,
125        {
126            const IS_MUT: bool = $is_mut;
127            type Static = dyn $Fn $FnArgs -> R;
128            type AsMut = dyn FnMut $FnArgs -> R + '__closure;
129            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
130            fn describe_invoke<const UNWIND_SAFE: bool>() {
131                inform(FUNCTION);
132                inform(invoke::<$($var,)* R, UNWIND_SAFE> as *const () as usize as u32);
133                closures!(@describe $FnArgs);
134                R::describe();
135                R::describe();
136            }
137        }
138
139        impl<T, $($var,)* R> IntoWasmClosure<dyn $Fn $FnArgs -> R> for T
140        where
141            T: 'static + $Fn $FnArgs -> R,
142        {
143            fn unsize(self: Box<Self>) -> Box<dyn $Fn $FnArgs -> R> { self }
144        }
145    };);
146
147    // IntoWasmClosureRef is only implemented for Fn, not FnMut.
148    // IntoWasmClosureRefMut is implemented for FnMut.
149    // Since Fn: FnMut, any Fn closure can be used as FnMut, so this covers all cases.
150    (@impl_unsize_closure_ref $FnArgs:tt $FromWasmAbi:ident $($var_expr:expr => $var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) => (
151        impl<'a, T: 'a, $($var: 'a + $FromWasmAbi,)* R: 'a + ReturnWasmAbi> IntoWasmClosureRef<dyn Fn $FnArgs -> R + 'a> for T
152        where
153            T: Fn $FnArgs -> R,
154        {
155            fn unsize_closure_ref(&self) -> &(dyn Fn $FnArgs -> R + 'a) { self }
156        }
157
158        impl<'a, T: 'a, $($var: 'a + $FromWasmAbi,)* R: 'a + ReturnWasmAbi> IntoWasmClosureRefMut<dyn FnMut $FnArgs -> R + 'a> for T
159        where
160            T: FnMut $FnArgs -> R,
161        {
162            fn unsize_closure_ref(&mut self) -> &mut (dyn FnMut $FnArgs -> R + 'a) { self }
163        }
164    );
165
166    (@impl_for_args $FnArgs:tt $FromWasmAbi:ident [$($maybe_unwind_safe:tt)*] $($var_expr:expr => $var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) => {
167        closures!(@impl_for_fn false [] Fn $FnArgs $FromWasmAbi $($var_expr => $var $arg1 $arg2 $arg3 $arg4)*);
168        closures!(@impl_for_fn true [mut] FnMut $FnArgs $FromWasmAbi $($var_expr => $var $arg1 $arg2 $arg3 $arg4)*);
169        closures!(@impl_unsize_closure_ref $FnArgs $FromWasmAbi $($var_expr => $var $arg1 $arg2 $arg3 $arg4)*);
170
171        // The memory safety here in these implementations below is a bit tricky. We
172        // want to be able to drop the `Closure` object from within the invocation of a
173        // `Closure` for cases like promises. That means that while it's running we
174        // might drop the `Closure`, but that shouldn't invalidate the environment yet.
175        //
176        // Instead what we do is to wrap closures in `Rc` variables. The main `Closure`
177        // has a strong reference count which keeps the trait object alive. Each
178        // invocation of a closure then *also* clones this and gets a new reference
179        // count. When the closure returns it will release the reference count.
180        //
181        // This means that if the main `Closure` is dropped while it's being invoked
182        // then destruction is deferred until execution returns. Otherwise it'll
183        // deallocate data immediately.
184
185        #[allow(non_snake_case, unused_parens)]
186        impl<T, $($var,)* R> WasmClosureFnOnce<dyn FnMut $FnArgs -> R, $FnArgs, R> for T
187        where
188            T: 'static + (FnOnce $FnArgs -> R),
189            $($var: $FromWasmAbi + 'static,)*
190            R: ReturnWasmAbi + 'static,
191            $($maybe_unwind_safe)*
192        {
193            fn into_fn_mut(self) -> Box<dyn FnMut $FnArgs -> R> {
194                let mut me = Some(self);
195                Box::new(move |$($var),*| {
196                    let me = me.take().expect_throw("FnOnce called more than once");
197                    me($($var),*)
198                })
199            }
200
201            fn into_js_function(self) -> JsValue {
202                use alloc::rc::Rc;
203                use crate::__rt::WasmRefCell;
204
205                let rc1 = Rc::new(WasmRefCell::new(None));
206                let rc2 = rc1.clone();
207
208                let closure = Closure::once(closures!(@closure $FnArgs $($var)* {
209                    let result = self($($var),*);
210
211                    // And then drop the `Rc` holding this function's `Closure`
212                    // alive.
213                    debug_assert_eq!(Rc::strong_count(&rc2), 1);
214                    let option_closure = rc2.borrow_mut().take();
215                    debug_assert!(option_closure.is_some());
216                    drop(option_closure);
217
218                    result
219                }));
220
221                let js_val = closure.as_ref().clone();
222
223                *rc1.borrow_mut() = Some(closure);
224                debug_assert_eq!(Rc::strong_count(&rc1), 2);
225                drop(rc1);
226
227                js_val
228            }
229        }
230
231        #[allow(non_snake_case, unused_parens)]
232        impl<T, $($var,)* R> WasmClosureFnOnceAbort<dyn FnMut $FnArgs -> R, $FnArgs, R> for T
233        where
234            T: 'static + (FnOnce $FnArgs -> R),
235            $($var: $FromWasmAbi + 'static,)*
236            R: ReturnWasmAbi + 'static,
237        {
238            fn into_fn_mut(self) -> Box<dyn FnMut $FnArgs -> R> {
239                let mut me = Some(self);
240                Box::new(move |$($var),*| {
241                    let me = me.take().expect_throw("FnOnce called more than once");
242                    me($($var),*)
243                })
244            }
245
246            fn into_js_function(self) -> JsValue {
247                use alloc::rc::Rc;
248                use crate::__rt::WasmRefCell;
249
250                let rc1 = Rc::new(WasmRefCell::new(None));
251                let rc2 = rc1.clone();
252
253                // TODO: Unwind safety for FnOnce
254                let closure = Closure::once_aborting(closures!(@closure $FnArgs $($var)* {
255                    let result = self($($var),*);
256
257                    // And then drop the `Rc` holding this function's `Closure`
258                    // alive.
259                    debug_assert_eq!(Rc::strong_count(&rc2), 1);
260                    let option_closure = rc2.borrow_mut().take();
261                    debug_assert!(option_closure.is_some());
262                    drop(option_closure);
263
264                    result
265                }));
266
267                let js_val = closure.as_ref().clone();
268
269                *rc1.borrow_mut() = Some(closure);
270                debug_assert_eq!(Rc::strong_count(&rc1), 2);
271                drop(rc1);
272
273                js_val
274            }
275        }
276    };
277
278    ([$($unwind_safe:tt)*] $( ($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) )*) => ($(
279        closures!(@impl_for_args ($($var),*) FromWasmAbi [$($maybe_unwind_safe)*] $($var::from_abi($var) => $var $arg1 $arg2 $arg3 $arg4)*);
280    )*);
281}
282
283#[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
284closures! {
285    [T: core::panic::UnwindSafe,]
286    ()
287    (A a1 a2 a3 a4)
288    (A a1 a2 a3 a4 B b1 b2 b3 b4)
289    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
290    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
291    (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)
292    (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)
293    (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)
294    (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)
295}
296
297#[cfg(not(all(feature = "std", target_arch = "wasm32", panic = "unwind")))]
298closures! {
299    []
300    ()
301    (A a1 a2 a3 a4)
302    (A a1 a2 a3 a4 B b1 b2 b3 b4)
303    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
304    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
305    (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)
306    (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)
307    (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)
308    (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)
309}
310
311// Comprehensive type-safe cross-function covariant and contravariant casting rules
312macro_rules! impl_fn_upcasts {
313    () => {
314        impl_fn_upcasts!(@arities
315            [0 []]
316            [1 [A1 B1] O1]
317            [2 [A1 B1 A2 B2] O2]
318            [3 [A1 B1 A2 B2 A3 B3] O3]
319            [4 [A1 B1 A2 B2 A3 B3 A4 B4] O4]
320            [5 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5] O5]
321            [6 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A6 B6] O6]
322            [7 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A6 B6 A7 B7] O7]
323            [8 [A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A6 B6 A7 B7 A8 B8] O8]
324        );
325    };
326
327    (@arities) => {};
328
329    (@arities [$n:tt $args:tt $($opt:ident)?] $([$rest_n:tt $rest_args:tt $($rest_opt:ident)?])*) => {
330        impl_fn_upcasts!(@same $args);
331        impl_fn_upcasts!(@cross_all $args [] $([$rest_n $rest_args $($rest_opt)?])*);
332        impl_fn_upcasts!(@arities $([$rest_n $rest_args $($rest_opt)?])*);
333    };
334
335    (@same []) => {
336        impl<R1, R2> UpcastFrom<fn() -> R1> for fn() -> R2
337        where
338            R2: UpcastFrom<R1>
339        {}
340
341        impl<'a, R1, R2> UpcastFrom<dyn Fn() -> R1 + 'a> for dyn Fn() -> R2 + 'a
342        where
343            R2: UpcastFrom<R1>
344        {}
345
346        impl<'a, R1, R2> UpcastFrom<dyn FnMut() -> R1 + 'a> for dyn FnMut() -> R2 + 'a
347        where
348            R2: UpcastFrom<R1>
349        {}
350    };
351
352    // Arguments implemented with contravariance
353    (@same [$($A1:ident $A2:ident)+]) => {
354        impl<R1, R2, $($A1, $A2),+> UpcastFrom<fn($($A1),+) -> R1> for fn($($A2),+) -> R2
355        where
356            R2: UpcastFrom<R1>,
357            $($A1: UpcastFrom<$A2>,)+
358        {}
359
360        impl<'a, R1, R2, $($A1, $A2),+> UpcastFrom<dyn Fn($($A1),+) -> R1 + 'a> for dyn Fn($($A2),+) -> R2 + 'a
361        where
362            R2: UpcastFrom<R1>,
363            $($A1: UpcastFrom<$A2>,)+
364        {}
365
366        impl<'a, R1, R2, $($A1, $A2),+> UpcastFrom<dyn FnMut($($A1),+) -> R1 + 'a> for dyn FnMut($($A2),+) -> R2 + 'a
367        where
368            R2: UpcastFrom<R1>,
369            $($A1: UpcastFrom<$A2>,)+
370        {}
371    };
372
373    // Cross-all: done
374    (@cross_all $args:tt $opts:tt) => {};
375
376    // Cross-all: process next
377    (@cross_all $args:tt [$($opts:ident)*] [$next_n:tt $next_args:tt $next_opt:ident] $([$rest_n:tt $rest_args:tt $($rest_opt:ident)?])*) => {
378        impl_fn_upcasts!(@extend $args [$($opts)* $next_opt]);
379        impl_fn_upcasts!(@shrink $args [$($opts)* $next_opt]);
380        impl_fn_upcasts!(@cross_all $args [$($opts)* $next_opt] $([$rest_n $rest_args $($rest_opt)?])*);
381    };
382
383    // Extend: 0 -> N
384    (@extend [] [$($O:ident)+]) => {
385        impl<R1, R2, $($O),+> UpcastFrom<fn() -> R1> for fn($($O),+) -> R2
386        where
387            R2: UpcastFrom<R1>,
388            $($O: UpcastFrom<Undefined>,)+
389        {}
390
391        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn Fn() -> R1 + 'a> for dyn Fn($($O),+) -> R2 + 'a
392        where
393            R2: UpcastFrom<R1>,
394            $($O: UpcastFrom<Undefined>,)+
395        {}
396
397        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn FnMut() -> R1 + 'a> for dyn FnMut($($O),+) -> R2 + 'a
398        where
399            R2: UpcastFrom<R1>,
400            $($O: UpcastFrom<Undefined>,)+
401        {}
402    };
403
404    // Extend: N -> M
405    (@extend [$($A1:ident $A2:ident)+] [$($O:ident)+]) => {
406        impl<R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<fn($($A1),+) -> R1> for fn($($A2,)+ $($O),+) -> R2
407        where
408            R2: UpcastFrom<R1>,
409            $($A1: UpcastFrom<$A2>,)+  // Contravariant
410            $($O: UpcastFrom<Undefined>,)+
411        {}
412
413        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn Fn($($A1),+) -> R1 + 'a> for dyn Fn($($A2,)+ $($O),+) -> R2 + 'a
414        where
415            R2: UpcastFrom<R1>,
416            $($A1: UpcastFrom<$A2>,)+  // Contravariant
417            $($O: UpcastFrom<Undefined>,)+
418        {}
419
420        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn FnMut($($A1),+) -> R1 + 'a> for dyn FnMut($($A2,)+ $($O),+) -> R2 + 'a
421        where
422            R2: UpcastFrom<R1>,
423            $($A1: UpcastFrom<$A2>,)+  // Contravariant
424            $($O: UpcastFrom<Undefined>,)+
425        {}
426    };
427
428    // Shrink: N -> 0
429    (@shrink [] [$($O:ident)+]) => {
430        impl<R1, R2, $($O),+> UpcastFrom<fn($($O),+) -> R1> for fn() -> R2
431        where
432            R2: UpcastFrom<R1>,
433            $($O: UpcastFrom<Undefined>,)+
434        {}
435
436        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn Fn($($O),+) -> R1 + 'a> for dyn Fn() -> R2 + 'a
437        where
438            R2: UpcastFrom<R1>,
439            $($O: UpcastFrom<Undefined>,)+
440        {}
441
442        impl<'a, R1, R2, $($O),+> UpcastFrom<dyn FnMut($($O),+) -> R1 + 'a> for dyn FnMut() -> R2 + 'a
443        where
444            R2: UpcastFrom<R1>,
445            $($O: UpcastFrom<Undefined>,)+
446        {}
447    };
448
449    // Shrink: M -> N
450    (@shrink [$($A1:ident $A2:ident)+] [$($O:ident)+]) => {
451        impl<R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<fn($($A1,)+ $($O),+) -> R1> for fn($($A2),+) -> R2
452        where
453            R2: UpcastFrom<R1>,
454            $($A1: UpcastFrom<$A2>,)+  // Contravariant
455            $($O: UpcastFrom<Undefined>,)+
456        {}
457
458        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn Fn($($A1,)+ $($O),+) -> R1 + 'a> for dyn Fn($($A2),+) -> R2 + 'a
459        where
460            R2: UpcastFrom<R1>,
461            $($A1: UpcastFrom<$A2>,)+  // Contravariant
462            $($O: UpcastFrom<Undefined>,)+
463        {}
464
465        impl<'a, R1, R2, $($A1, $A2,)+ $($O),+> UpcastFrom<dyn FnMut($($A1,)+ $($O),+) -> R1 + 'a> for dyn FnMut($($A2),+) -> R2 + 'a
466        where
467            R2: UpcastFrom<R1>,
468            $($A1: UpcastFrom<$A2>,)+  // Contravariant
469            $($O: UpcastFrom<Undefined>,)+
470        {}
471    };
472}
473
474impl_fn_upcasts!();
475
476// Copy the above impls down here for where there's only one argument and it's a
477// reference. We could add more impls for more kinds of references, but it
478// becomes a combinatorial explosion quickly. Let's see how far we can get with
479// just this one! Maybe someone else can figure out voodoo so we don't have to
480// duplicate.
481
482// 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.
483#[allow(coherence_leak_check)]
484const _: () = {
485    #[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
486    closures!(@impl_for_args (&A) RefFromWasmAbi [T: core::panic::UnwindSafe,] &*A::ref_from_abi(A) => A a1 a2 a3 a4);
487
488    #[cfg(not(all(feature = "std", target_arch = "wasm32", panic = "unwind")))]
489    closures!(@impl_for_args (&A) RefFromWasmAbi [] &*A::ref_from_abi(A) => A a1 a2 a3 a4);
490};
491
492// UpcastFrom impl for ScopedClosure.
493// ScopedClosure<T1> upcasts to ScopedClosure<T2> when the underlying closure type T1 upcasts to T2.
494// The dyn Fn/FnMut UpcastFrom impls above encode correct variance (covariant return, contravariant args).
495//
496// The 'a: 'b bound is critical for soundness: it ensures the target lifetime 'b does not
497// exceed the source lifetime 'a. Without it, upcast_into could fabricate a
498// ScopedClosure<'static, _> from a short-lived ScopedClosure, enabling use-after-free.
499impl<'a: 'b, 'b, T1, T2> UpcastFrom<ScopedClosure<'a, T1>> for ScopedClosure<'b, T2>
500where
501    T1: ?Sized + WasmClosure,
502    T2: ?Sized + WasmClosure + UpcastFrom<T1>,
503{
504}