Skip to main content

wry_bindgen/encode/
callbacks.rs

1//! Callback key encoding and closure ABI implementations.
2
3use alloc::boxed::Box;
4use alloc::vec::Vec;
5use core::marker::PhantomData;
6
7use crate::convert::RefFromBinaryDecode;
8use crate::ipc::{DecodeError, DecodedData, EncodedData};
9use crate::object_store::ObjectHandle;
10use crate::value::JsValue;
11use crate::{
12    Closure, IntoWasmClosure, IntoWasmClosureRef, IntoWasmClosureRefMut, WasmClosureFnOnce,
13    WasmClosureFnOnceAbort,
14};
15
16use super::{BinaryDecode, BinaryEncode, EncodeTypeDef, IntoClosure, TypeTag};
17
18/// Wrapper type that encodes a callback registration key with Callback type info.
19/// This tells JS to create a RustFunction wrapper when decoding the value.
20/// The type parameter F should be `dyn FnMut(...) -> R` to capture the callback signature.
21#[derive(Clone, Copy)]
22pub(crate) enum CallbackPolicy {
23    RustOwned = 0,
24    JsOwned = 1,
25    JsOwnedOnce = 2,
26}
27
28pub struct CallbackKey<F: ?Sized>(ObjectHandle, CallbackPolicy, PhantomData<F>);
29
30impl<F: ?Sized> CallbackKey<F> {
31    /// Create a new CallbackKey from an ObjectHandle.
32    pub(crate) fn new(handle: ObjectHandle) -> Self {
33        Self::new_with_policy(handle, CallbackPolicy::JsOwned)
34    }
35
36    pub(crate) fn new_with_policy(handle: ObjectHandle, policy: CallbackPolicy) -> Self {
37        CallbackKey(handle, policy, PhantomData)
38    }
39}
40
41impl<F: ?Sized> BinaryEncode for CallbackKey<F> {
42    fn encode(self, encoder: &mut EncodedData) {
43        self.0.encode(encoder);
44        (self.1 as u32).encode(encoder);
45    }
46}
47
48// Blanket impl: All Closures encode as HeapRef since they're JS heap references
49impl<T: ?Sized> EncodeTypeDef for crate::ScopedClosure<'_, T> {
50    fn encode_type_def(buf: &mut Vec<u8>) {
51        JsValue::encode_type_def(buf);
52    }
53}
54
55/// Helper macro to decode callback arguments and execute a body.
56///
57/// Usage: decode_args!(decoder; [type1, type2, ...] => body)
58/// The body can use the type names as variables containing the decoded arguments.
59macro_rules! decode_args {
60    // Main entry: decode each arg and call body. A decode failure propagates as
61    // `Err` from the enclosing callback closure (which returns
62    // `Result<(), DecodeError>`) instead of panicking via `unwrap`.
63    ($decoder:expr; [$first:ident, $($ty:ident,)*] => $body:expr) => {{
64        #[allow(non_snake_case)]
65        let $first = <$first as BinaryDecode>::decode($decoder)?;
66        decode_args!($decoder; [$($ty,)*] => $body);
67    }};
68    // Nothing left to decode: run the body, then signal success to the closure.
69    ($decoder:expr; [] => $body:expr) => {{
70        $body;
71        return Ok(());
72    }};
73}
74
75/// Emit the body of an `EncodeTypeDef::encode_type_def` for a callback type.
76///
77/// Writes `[Callback tag] [arg count] [arg TypeDefs...] [return TypeDef]`. The
78/// optional `borrow_first` flag (when present) pushes a `BorrowedRef` tag for the
79/// first argument instead of its `EncodeTypeDef`, used by the borrowed-first-arg
80/// closures.
81macro_rules! callback_type_def_body {
82    ($buf:expr; R = $R:ty; $($arg:ty),*) => {{
83        $buf.push(TypeTag::Callback as u8);
84        // Encode arg count
85        let mut count: u8 = 0;
86        $(
87            let _ = PhantomData::<$arg>;
88            count += 1;
89        )*
90        $buf.push(count);
91        // Encode each argument type
92        $(<$arg as EncodeTypeDef>::encode_type_def($buf);)*
93        // Encode return type
94        <$R as EncodeTypeDef>::encode_type_def($buf);
95    }};
96    // Borrowed-first-arg variant: the first argument is a borrowed ref encoded
97    // as a `BorrowedRef` tag, the remaining `$rest` args use their `EncodeTypeDef`.
98    ($buf:expr; R = $R:ty; borrow_first; $($rest:ty),*) => {{
99        $buf.push(TypeTag::Callback as u8);
100        // Encode arg count (starts at 1 for the borrowed first arg)
101        let mut count: u8 = 1;
102        $(
103            let _ = PhantomData::<$rest>;
104            count += 1;
105        )*
106        $buf.push(count);
107        // Encode each argument type
108        $buf.push(TypeTag::BorrowedRef as u8);
109        $(<$rest as EncodeTypeDef>::encode_type_def($buf);)*
110        // Encode return type
111        <$R as EncodeTypeDef>::encode_type_def($buf);
112    }};
113}
114
115macro_rules! impl_fnmut_stub {
116    ($($arg:ident),*) => {
117        // Implement EncodeTypeDef for fn(owned*) -> R
118        impl<R, $($arg,)*> EncodeTypeDef for CallbackKey<fn($($arg),*) -> R>
119            where
120            $($arg: EncodeTypeDef + 'static, )*
121            R: EncodeTypeDef + 'static,
122        {
123            #[allow(unused)]
124            fn encode_type_def(buf: &mut Vec<u8>) {
125                callback_type_def_body!(buf; R = R; $($arg),*);
126            }
127        }
128
129        // Implement WasmClosure trait for dyn FnMut variants
130        impl<R, $($arg,)*> crate::WryWasmClosure<fn($($arg),*) -> R> for dyn FnMut($($arg),*) -> R
131            where
132            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
133            R: BinaryEncode + EncodeTypeDef + 'static,
134        {
135            #[allow(non_snake_case)]
136            #[allow(unused)]
137            fn into_js_closure(mut boxed: Box<Self>) -> crate::Closure<Self> {
138                crate::Closure::wrap_encode_decode_mut::<fn($($arg),*) -> R>(
139                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
140                        // Decode arguments and call the closure
141                        decode_args!(decoder; [$($arg,)*] => {
142                            let result = boxed($($arg),*);
143                            result.encode(encoder);
144                        });
145                    },
146                )
147            }
148        }
149
150        impl<R, $($arg,)*> crate::WasmClosure for dyn FnMut($($arg),*) -> R
151            where
152            $($arg: 'static, )*
153            R: 'static,
154        {
155            type Static = dyn FnMut($($arg),*) -> R;
156            type AsMut = dyn FnMut($($arg),*) -> R;
157        }
158
159        // Implement WasmClosure trait for dyn Fn variants (immutable closures)
160        // These CAN be called reentrantly since Fn only needs &self
161        impl<R, $($arg,)*> crate::WryWasmClosure<fn($($arg),*) -> R> for dyn Fn($($arg),*) -> R
162            where
163            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
164            R: BinaryEncode + EncodeTypeDef + 'static,
165        {
166            #[allow(non_snake_case)]
167            #[allow(unused)]
168            fn into_js_closure(boxed: Box<Self>) -> crate::Closure<Self> {
169                crate::Closure::wrap_encode_decode::<fn($($arg),*) -> R>(
170                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
171                        // Decode arguments and call the closure
172                        decode_args!(decoder; [$($arg,)*] => {
173                            let result = boxed($($arg),*);
174                            result.encode(encoder);
175                        });
176                    }
177                )
178            }
179        }
180
181        impl<R, $($arg,)*> crate::WasmClosure for dyn Fn($($arg),*) -> R
182            where
183            $($arg: 'static, )*
184            R: 'static,
185        {
186            type Static = dyn Fn($($arg),*) -> R;
187            type AsMut = dyn FnMut($($arg),*) -> R;
188        }
189
190        // IntoClosure for F: FnMut -> Closure<dyn FnMut>
191        impl<R, F, $($arg,)*> IntoClosure<fn($($arg),*) -> R, crate::Closure<dyn FnMut($($arg),*) -> R>> for F
192            where F: FnMut($($arg),*) -> R + 'static,
193            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
194            R: BinaryEncode + EncodeTypeDef + 'static,
195        {
196            #[allow(non_snake_case)]
197            #[allow(unused)]
198            fn into_closure(mut self) -> crate::Closure<dyn FnMut($($arg),*) -> R> {
199                crate::Closure::wrap_encode_decode_mut::<fn($($arg),*) -> R>(
200                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
201                        // Decode arguments and call the closure
202                        decode_args!(decoder; [$($arg,)*] => {
203                            let result = self($($arg),*);
204                            result.encode(encoder);
205                        });
206                    },
207                )
208            }
209        }
210
211        // IntoClosure for F: Fn -> Closure<dyn Fn>
212        impl<R, F, $($arg,)*> IntoClosure<fn($($arg),*) -> R, crate::Closure<dyn Fn($($arg),*) -> R>> for F
213            where F: Fn($($arg),*) -> R + 'static,
214            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
215            R: BinaryEncode + EncodeTypeDef + 'static,
216        {
217            #[allow(non_snake_case)]
218            #[allow(unused)]
219            fn into_closure(self) -> crate::Closure<dyn Fn($($arg),*) -> R> {
220                crate::Closure::wrap_encode_decode::<fn($($arg),*) -> R>(
221                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
222                        // Decode arguments and call the closure
223                        decode_args!(decoder; [$($arg,)*] => {
224                            let result = self($($arg),*);
225                            result.encode(encoder);
226                        });
227                    },
228                )
229            }
230        }
231
232        impl<R, F, $($arg,)*> IntoWasmClosure<dyn FnMut($($arg),*) -> R> for F
233            where F: FnMut($($arg),*) -> R + 'static,
234            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
235            R: BinaryEncode + EncodeTypeDef + 'static,
236        {
237            fn into_closure(self) -> crate::Closure<dyn FnMut($($arg),*) -> R> {
238                <F as IntoClosure<fn($($arg),*) -> R, crate::Closure<dyn FnMut($($arg),*) -> R>>>::into_closure(self)
239            }
240
241            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn FnMut($($arg),*) -> R> {
242                <F as IntoWasmClosure<dyn FnMut($($arg),*) -> R>>::into_closure(*self)
243            }
244        }
245
246        impl<R, $($arg,)*> IntoWasmClosure<dyn FnMut($($arg),*) -> R> for dyn FnMut($($arg),*) -> R
247            where
248            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
249            R: BinaryEncode + EncodeTypeDef + 'static,
250        {
251            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn FnMut($($arg),*) -> R> {
252                <Self as crate::WryWasmClosure<fn($($arg),*) -> R>>::into_js_closure(self)
253            }
254        }
255
256        impl<R, F, $($arg,)*> IntoWasmClosure<dyn Fn($($arg),*) -> R> for F
257            where F: Fn($($arg),*) -> R + 'static,
258            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
259            R: BinaryEncode + EncodeTypeDef + 'static,
260        {
261            fn into_closure(self) -> crate::Closure<dyn Fn($($arg),*) -> R> {
262                <F as IntoClosure<fn($($arg),*) -> R, crate::Closure<dyn Fn($($arg),*) -> R>>>::into_closure(self)
263            }
264
265            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn Fn($($arg),*) -> R> {
266                <F as IntoWasmClosure<dyn Fn($($arg),*) -> R>>::into_closure(*self)
267            }
268        }
269
270        impl<R, $($arg,)*> IntoWasmClosure<dyn Fn($($arg),*) -> R> for dyn Fn($($arg),*) -> R
271            where
272            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
273            R: BinaryEncode + EncodeTypeDef + 'static,
274        {
275            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn Fn($($arg),*) -> R> {
276                <Self as crate::WryWasmClosure<fn($($arg),*) -> R>>::into_js_closure(self)
277            }
278        }
279
280        impl<R, F, $($arg,)*> IntoWasmClosureRef<dyn Fn($($arg),*) -> R> for F
281            where F: Fn($($arg),*) -> R,
282            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
283            R: BinaryEncode + EncodeTypeDef + 'static,
284        {
285            #[allow(non_snake_case)]
286            #[allow(unused)]
287            fn into_scoped_closure_ref<'a>(t: &'a Self) -> crate::ScopedClosure<'a, <dyn Fn($($arg),*) -> R as crate::WasmClosure>::Static> {
288                let t: &(dyn Fn($($arg),*) -> R) = t;
289                let ptr = t as *const dyn Fn($($arg),*) -> R;
290                let (data_ptr, vtable_ptr): (usize, usize) = unsafe { core::mem::transmute(ptr) };
291                let callback = crate::function::RustCallback::new_fn(
292                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
293                        let ptr: *const dyn Fn($($arg),*) -> R = unsafe {
294                            core::mem::transmute((data_ptr, vtable_ptr))
295                        };
296                        let f: &dyn Fn($($arg),*) -> R = unsafe { &*ptr };
297                        decode_args!(decoder; [$($arg,)*] => {
298                            let result = f($($arg),*);
299                            result.encode(encoder);
300                        });
301                    },
302                );
303                let handle = crate::object_store::insert_object(callback);
304                let value = crate::__rt::wbg_cast::<CallbackKey<fn($($arg),*) -> R>, crate::JsValue>(
305                    CallbackKey::new_with_policy(handle, CallbackPolicy::RustOwned),
306                );
307                crate::ScopedClosure {
308                    _phantom: PhantomData,
309                    callback: crate::closure::CallbackOwnership::Owned,
310                    value,
311                }
312            }
313        }
314
315        impl<R, $($arg,)*> IntoWasmClosureRef<dyn Fn($($arg),*) -> R> for dyn Fn($($arg),*) -> R
316            where
317            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
318            R: BinaryEncode + EncodeTypeDef + 'static,
319        {
320            #[allow(non_snake_case)]
321            #[allow(unused)]
322            fn into_scoped_closure_ref<'a>(t: &'a Self) -> crate::ScopedClosure<'a, <dyn Fn($($arg),*) -> R as crate::WasmClosure>::Static> {
323                let ptr = t as *const dyn Fn($($arg),*) -> R;
324                let (data_ptr, vtable_ptr): (usize, usize) = unsafe { core::mem::transmute(ptr) };
325                let callback = crate::function::RustCallback::new_fn(
326                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
327                        let ptr: *const dyn Fn($($arg),*) -> R = unsafe {
328                            core::mem::transmute((data_ptr, vtable_ptr))
329                        };
330                        let f: &dyn Fn($($arg),*) -> R = unsafe { &*ptr };
331                        decode_args!(decoder; [$($arg,)*] => {
332                            let result = f($($arg),*);
333                            result.encode(encoder);
334                        });
335                    },
336                );
337                let handle = crate::object_store::insert_object(callback);
338                let value = crate::__rt::wbg_cast::<CallbackKey<fn($($arg),*) -> R>, crate::JsValue>(
339                    CallbackKey::new_with_policy(handle, CallbackPolicy::RustOwned),
340                );
341                crate::ScopedClosure {
342                    _phantom: PhantomData,
343                    callback: crate::closure::CallbackOwnership::Owned,
344                    value,
345                }
346            }
347        }
348
349        impl<R, F, $($arg,)*> IntoWasmClosureRefMut<dyn FnMut($($arg),*) -> R> for F
350            where F: FnMut($($arg),*) -> R,
351            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
352            R: BinaryEncode + EncodeTypeDef + 'static,
353        {
354            #[allow(non_snake_case)]
355            #[allow(unused)]
356            fn into_scoped_closure_ref_mut<'a>(t: &'a mut Self) -> crate::ScopedClosure<'a, <dyn FnMut($($arg),*) -> R as crate::WasmClosure>::Static> {
357                let t: &mut dyn FnMut($($arg),*) -> R = t;
358                let ptr = t as *mut dyn FnMut($($arg),*) -> R;
359                let (data_ptr, vtable_ptr): (usize, usize) = unsafe { core::mem::transmute(ptr) };
360                let callback = crate::function::RustCallback::new_fn_mut(
361                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
362                        let ptr: *mut dyn FnMut($($arg),*) -> R = unsafe {
363                            core::mem::transmute((data_ptr, vtable_ptr))
364                        };
365                        let f: &mut dyn FnMut($($arg),*) -> R = unsafe { &mut *ptr };
366                        decode_args!(decoder; [$($arg,)*] => {
367                            let result = f($($arg),*);
368                            result.encode(encoder);
369                        });
370                    },
371                );
372                let handle = crate::object_store::insert_object(callback);
373                let value = crate::__rt::wbg_cast::<CallbackKey<fn($($arg),*) -> R>, crate::JsValue>(
374                    CallbackKey::new_with_policy(handle, CallbackPolicy::RustOwned),
375                );
376                crate::ScopedClosure {
377                    _phantom: PhantomData,
378                    callback: crate::closure::CallbackOwnership::Owned,
379                    value,
380                }
381            }
382        }
383
384        impl<R, $($arg,)*> IntoWasmClosureRefMut<dyn FnMut($($arg),*) -> R> for dyn FnMut($($arg),*) -> R
385            where
386            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
387            R: BinaryEncode + EncodeTypeDef + 'static,
388        {
389            #[allow(non_snake_case)]
390            #[allow(unused)]
391            fn into_scoped_closure_ref_mut<'a>(t: &'a mut Self) -> crate::ScopedClosure<'a, <dyn FnMut($($arg),*) -> R as crate::WasmClosure>::Static> {
392                let ptr = t as *mut dyn FnMut($($arg),*) -> R;
393                let (data_ptr, vtable_ptr): (usize, usize) = unsafe { core::mem::transmute(ptr) };
394                let callback = crate::function::RustCallback::new_fn_mut(
395                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
396                        let ptr: *mut dyn FnMut($($arg),*) -> R = unsafe {
397                            core::mem::transmute((data_ptr, vtable_ptr))
398                        };
399                        let f: &mut dyn FnMut($($arg),*) -> R = unsafe { &mut *ptr };
400                        decode_args!(decoder; [$($arg,)*] => {
401                            let result = f($($arg),*);
402                            result.encode(encoder);
403                        });
404                    },
405                );
406                let handle = crate::object_store::insert_object(callback);
407                let value = crate::__rt::wbg_cast::<CallbackKey<fn($($arg),*) -> R>, crate::JsValue>(
408                    CallbackKey::new_with_policy(handle, CallbackPolicy::RustOwned),
409                );
410                crate::ScopedClosure {
411                    _phantom: PhantomData,
412                    callback: crate::closure::CallbackOwnership::Owned,
413                    value,
414                }
415            }
416        }
417    };
418}
419
420/// Emit a `BinaryEncode` impl for a closure-reference type.
421///
422/// The closure reference is decomposed into a raw fat pointer (data + vtable) to
423/// erase its lifetime, registered as a `RustCallback`, and shipped to JS as a
424/// `CallbackKey`. SAFETY across all variants: the closure reference must remain
425/// valid for the duration of the JS call, which holds because `mark_needs_flush`
426/// forces synchronous invocation before this function returns.
427///
428/// Variants differ only in the closure trait (`Fn`/`FnMut`), the pointer
429/// mutability used to reconstruct it, and the `RustCallback` constructor.
430macro_rules! impl_closure_ref_binary_encode {
431    (
432        impl ($($self_ty:tt)*) via *mut dyn FnMut, $ctor:ident;
433        $($arg:ident),*
434    ) => {
435        impl<R, $($arg,)*> BinaryEncode for $($self_ty)*
436            where
437            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
438            R: BinaryEncode + EncodeTypeDef + 'static,
439        {
440            #[allow(non_snake_case)]
441            #[allow(unused)]
442            fn encode(self, encoder: &mut EncodedData) {
443                encoder.mark_needs_flush();
444
445                let ptr = self as *mut dyn FnMut($($arg),*) -> R;
446                let (data_ptr, vtable_ptr): (usize, usize) = unsafe { core::mem::transmute(ptr) };
447
448                let callback = crate::function::RustCallback::$ctor(
449                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
450                        let ptr: *mut dyn FnMut($($arg),*) -> R = unsafe {
451                            core::mem::transmute((data_ptr, vtable_ptr))
452                        };
453                        let f: &mut dyn FnMut($($arg),*) -> R = unsafe { &mut *ptr };
454                        $(let $arg = <$arg as BinaryDecode>::decode(decoder)?;)*
455                        let result = f($($arg),*);
456                        result.encode(encoder);
457                        Ok(())
458                    },
459                );
460                let handle = crate::object_store::insert_object(callback);
461                let key: CallbackKey<fn($($arg),*) -> R> =
462                    CallbackKey::new_with_policy(handle, CallbackPolicy::RustOwned);
463                key.encode(encoder);
464                crate::batch::queue_rust_object_drop(handle);
465            }
466        }
467    };
468    (
469        impl ($($self_ty:tt)*) via *const dyn Fn, $ctor:ident;
470        $($arg:ident),*
471    ) => {
472        impl<R, $($arg,)*> BinaryEncode for $($self_ty)*
473            where
474            $($arg: BinaryDecode + EncodeTypeDef + 'static, )*
475            R: BinaryEncode + EncodeTypeDef + 'static,
476        {
477            #[allow(non_snake_case)]
478            #[allow(unused)]
479            fn encode(self, encoder: &mut EncodedData) {
480                encoder.mark_needs_flush();
481
482                let ptr = self as *const dyn Fn($($arg),*) -> R;
483                let (data_ptr, vtable_ptr): (usize, usize) = unsafe { core::mem::transmute(ptr) };
484
485                let callback = crate::function::RustCallback::$ctor(
486                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
487                        let ptr: *const dyn Fn($($arg),*) -> R = unsafe {
488                            core::mem::transmute((data_ptr, vtable_ptr))
489                        };
490                        let f: &dyn Fn($($arg),*) -> R = unsafe { &*ptr };
491                        $(let $arg = <$arg as BinaryDecode>::decode(decoder)?;)*
492                        let result = f($($arg),*);
493                        result.encode(encoder);
494                        Ok(())
495                    },
496                );
497                let handle = crate::object_store::insert_object(callback);
498                let key: CallbackKey<fn($($arg),*) -> R> =
499                    CallbackKey::new_with_policy(handle, CallbackPolicy::RustOwned);
500                key.encode(encoder);
501                crate::batch::queue_rust_object_drop(handle);
502            }
503        }
504    };
505}
506
507/// Macro to implement EncodeTypeDef and BinaryEncode for closure reference types.
508/// These are used by js-sys bindings like `&mut dyn FnMut(JsValue, u32, Array) -> bool`.
509/// Unlike the WasmClosure impls above, these use simple BinaryDecode arguments without markers.
510macro_rules! impl_closure_ref_encode {
511    ($($arg:ident),*) => {
512        // Implement EncodeTypeDef for &mut dyn FnMut(...) -> R
513        impl<R, $($arg,)*> EncodeTypeDef for &mut dyn FnMut($($arg),*) -> R
514            where
515            $($arg: EncodeTypeDef + 'static, )*
516            R: EncodeTypeDef + 'static,
517        {
518            #[allow(unused)]
519            fn encode_type_def(buf: &mut Vec<u8>) {
520                callback_type_def_body!(buf; R = R; $($arg),*);
521            }
522        }
523
524        // Implement BinaryEncode for &mut dyn FnMut(...) -> R
525        impl_closure_ref_binary_encode!(
526            impl (&mut dyn FnMut($($arg),*) -> R) via *mut dyn FnMut, new_fn_mut;
527            $($arg),*
528        );
529
530        // Implement EncodeTypeDef for &dyn Fn(...) -> R
531        impl<R, $($arg,)*> EncodeTypeDef for &dyn Fn($($arg),*) -> R
532            where
533            $($arg: EncodeTypeDef + 'static, )*
534            R: EncodeTypeDef + 'static,
535        {
536            #[allow(unused)]
537            fn encode_type_def(buf: &mut Vec<u8>) {
538                callback_type_def_body!(buf; R = R; $($arg),*);
539            }
540        }
541
542        // Implement BinaryEncode for &dyn Fn(...) -> R (supports reentrant calls)
543        impl_closure_ref_binary_encode!(
544            impl (&dyn Fn($($arg),*) -> R) via *const dyn Fn, new_fn;
545            $($arg),*
546        );
547
548        // Implement EncodeTypeDef for &mut dyn Fn(...) -> R
549        impl<R, $($arg,)*> EncodeTypeDef for &mut dyn Fn($($arg),*) -> R
550            where
551            $($arg: EncodeTypeDef + 'static, )*
552            R: EncodeTypeDef + 'static,
553        {
554            #[allow(unused)]
555            fn encode_type_def(buf: &mut Vec<u8>) {
556                callback_type_def_body!(buf; R = R; $($arg),*);
557            }
558        }
559
560        // Implement BinaryEncode for &mut dyn Fn(...) -> R (supports reentrant calls)
561        // Uses *const because Fn only requires & to call.
562        impl_closure_ref_binary_encode!(
563            impl (&mut dyn Fn($($arg),*) -> R) via *const dyn Fn, new_fn;
564            $($arg),*
565        );
566    };
567}
568
569impl_closure_ref_encode!();
570impl_closure_ref_encode!(A1);
571impl_closure_ref_encode!(A1, A2);
572impl_closure_ref_encode!(A1, A2, A3);
573impl_closure_ref_encode!(A1, A2, A3, A4);
574impl_closure_ref_encode!(A1, A2, A3, A4, A5);
575impl_closure_ref_encode!(A1, A2, A3, A4, A5, A6);
576impl_closure_ref_encode!(A1, A2, A3, A4, A5, A6, A7);
577
578impl_fnmut_stub!();
579impl_fnmut_stub!(A1);
580impl_fnmut_stub!(A1, A2);
581impl_fnmut_stub!(A1, A2, A3);
582impl_fnmut_stub!(A1, A2, A3, A4);
583impl_fnmut_stub!(A1, A2, A3, A4, A5);
584impl_fnmut_stub!(A1, A2, A3, A4, A5, A6);
585impl_fnmut_stub!(A1, A2, A3, A4, A5, A6, A7);
586impl_fnmut_stub!(A1, A2, A3, A4, A5, A6, A7, A8);
587
588/// Marker type for closures that borrow the first argument.
589pub struct BorrowedFirstArg;
590
591/// Macro to implement WasmClosure and IntoClosure for closures that borrow the first argument.
592/// This uses RefFromBinaryDecode for the first arg and BinaryDecode for the rest.
593macro_rules! impl_fnmut_stub_ref {
594    ($first:ident $(, $rest:ident)*) => {
595        // Implement EncodeTypeDef for fn(borrowed, owned*) -> R
596        #[allow(coherence_leak_check)]
597        impl<R, $first, $($rest,)*> EncodeTypeDef for CallbackKey<fn(&$first, $($rest),*) -> R>
598            where
599            $first: EncodeTypeDef + 'static,
600            $($rest: EncodeTypeDef + 'static, )*
601            R: EncodeTypeDef + 'static,
602        {
603            #[allow(unused)]
604            fn encode_type_def(buf: &mut Vec<u8>) {
605                callback_type_def_body!(buf; R = R; borrow_first; $($rest),*);
606            }
607        }
608
609        // WasmClosure for dyn FnMut(&First, ...) -> R
610        impl<R, $first, $($rest,)*> crate::WryWasmClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R)> for dyn FnMut(&$first, $($rest),*) -> R
611            where
612            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
613            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
614            R: BinaryEncode + EncodeTypeDef + 'static,
615        {
616            #[allow(non_snake_case)]
617            #[allow(unused)]
618            fn into_js_closure(mut boxed: Box<Self>) -> crate::Closure<Self> {
619                crate::Closure::wrap_encode_decode_mut::<fn(&$first, $($rest),*) -> R>(
620                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
621                        let anchor = <$first as RefFromBinaryDecode>::ref_decode(decoder)?;
622                        $(let $rest = <$rest as BinaryDecode>::decode(decoder)?;)*
623                        let result = boxed(&*anchor, $($rest),*);
624                        result.encode(encoder);
625                        Ok(())
626                    },
627                )
628            }
629        }
630
631        // Trait objects like `dyn FnMut(&Event)` are commonly inferred as
632        // higher-ranked over the borrowed argument lifetime.
633        #[allow(coherence_leak_check)]
634        impl<R, $first, $($rest,)*> crate::WasmClosure for dyn FnMut(&$first, $($rest),*) -> R
635            where
636            $first: 'static,
637            $($rest: 'static,)*
638            R: 'static,
639        {
640            type Static = dyn FnMut(&$first, $($rest),*) -> R;
641            type AsMut = dyn FnMut(&$first, $($rest),*) -> R;
642        }
643
644        // WasmClosure for dyn Fn(&First, ...) -> R (supports reentrant calls)
645        impl<R, $first, $($rest,)*> crate::WryWasmClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R)> for dyn Fn(&$first, $($rest),*) -> R
646            where
647            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
648            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
649            R: BinaryEncode + EncodeTypeDef + 'static,
650        {
651            #[allow(non_snake_case)]
652            #[allow(unused)]
653            fn into_js_closure(boxed: Box<Self>) -> crate::Closure<Self> {
654                crate::Closure::wrap_encode_decode::<fn(&$first, $($rest),*) -> R>(
655                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
656                        let anchor = <$first as RefFromBinaryDecode>::ref_decode(decoder)?;
657                        $(let $rest = <$rest as BinaryDecode>::decode(decoder)?;)*
658                        let result = boxed(&*anchor, $($rest),*);
659                        result.encode(encoder);
660                        Ok(())
661                    },
662                )
663            }
664        }
665
666        #[allow(coherence_leak_check)]
667        impl<R, $first, $($rest,)*> crate::WasmClosure for dyn Fn(&$first, $($rest),*) -> R
668            where
669            $first: 'static,
670            $($rest: 'static,)*
671            R: 'static,
672        {
673            type Static = dyn Fn(&$first, $($rest),*) -> R;
674            type AsMut = dyn FnMut(&$first, $($rest),*) -> R;
675        }
676
677        // IntoClosure for F: FnMut(&First, ...) -> R -> Closure<dyn FnMut(&First, ...) -> R>
678        impl<R, F, $first, $($rest,)*> IntoClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R), crate::Closure<dyn FnMut(&$first, $($rest),*) -> R>> for F
679            where F: FnMut(&$first, $($rest),*) -> R + 'static,
680            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
681            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
682            R: BinaryEncode + EncodeTypeDef + 'static,
683        {
684            #[allow(non_snake_case)]
685            #[allow(unused)]
686            fn into_closure(mut self) -> crate::Closure<dyn FnMut(&$first, $($rest),*) -> R> {
687                crate::Closure::wrap_encode_decode_mut::<fn(&$first, $($rest),*) -> R>(
688                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
689                        let anchor = <$first as RefFromBinaryDecode>::ref_decode(decoder)?;
690                        $(let $rest = <$rest as BinaryDecode>::decode(decoder)?;)*
691                        let result = self(&*anchor, $($rest),*);
692                        result.encode(encoder);
693                        Ok(())
694                    },
695                )
696            }
697        }
698
699        // IntoClosure for F: Fn(&First, ...) -> R -> Closure<dyn Fn(&First, ...) -> R>
700        impl<R, F, $first, $($rest,)*> IntoClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R), crate::Closure<dyn Fn(&$first, $($rest),*) -> R>> for F
701            where F: Fn(&$first, $($rest),*) -> R + 'static,
702            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
703            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
704            R: BinaryEncode + EncodeTypeDef + 'static,
705        {
706            #[allow(non_snake_case)]
707            #[allow(unused)]
708            fn into_closure(self) -> crate::Closure<dyn Fn(&$first, $($rest),*) -> R> {
709                crate::Closure::wrap_encode_decode::<fn(&$first, $($rest),*) -> R>(
710                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
711                        let anchor = <$first as RefFromBinaryDecode>::ref_decode(decoder)?;
712                        $(let $rest = <$rest as BinaryDecode>::decode(decoder)?;)*
713                        let result = self(&*anchor, $($rest),*);
714                        result.encode(encoder);
715                        Ok(())
716                    },
717                )
718            }
719        }
720
721        #[allow(coherence_leak_check)]
722        impl<R, F, $first, $($rest,)*> IntoWasmClosure<dyn FnMut(&$first, $($rest),*) -> R> for F
723            where F: FnMut(&$first, $($rest),*) -> R + 'static,
724            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
725            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
726            R: BinaryEncode + EncodeTypeDef + 'static,
727        {
728            fn into_closure(self) -> crate::Closure<dyn FnMut(&$first, $($rest),*) -> R> {
729                <F as IntoClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R), crate::Closure<dyn FnMut(&$first, $($rest),*) -> R>>>::into_closure(self)
730            }
731
732            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn FnMut(&$first, $($rest),*) -> R> {
733                <F as IntoWasmClosure<dyn FnMut(&$first, $($rest),*) -> R>>::into_closure(*self)
734            }
735        }
736
737        #[allow(coherence_leak_check)]
738        impl<R, $first, $($rest,)*> IntoWasmClosure<dyn FnMut(&$first, $($rest),*) -> R> for dyn FnMut(&$first, $($rest),*) -> R
739            where
740            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
741            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
742            R: BinaryEncode + EncodeTypeDef + 'static,
743        {
744            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn FnMut(&$first, $($rest),*) -> R> {
745                <Self as crate::WryWasmClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R)>>::into_js_closure(self)
746            }
747        }
748
749        #[allow(coherence_leak_check)]
750        impl<R, F, $first, $($rest,)*> IntoWasmClosure<dyn Fn(&$first, $($rest),*) -> R> for F
751            where F: Fn(&$first, $($rest),*) -> R + 'static,
752            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
753            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
754            R: BinaryEncode + EncodeTypeDef + 'static,
755        {
756            fn into_closure(self) -> crate::Closure<dyn Fn(&$first, $($rest),*) -> R> {
757                <F as IntoClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R), crate::Closure<dyn Fn(&$first, $($rest),*) -> R>>>::into_closure(self)
758            }
759
760            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn Fn(&$first, $($rest),*) -> R> {
761                <F as IntoWasmClosure<dyn Fn(&$first, $($rest),*) -> R>>::into_closure(*self)
762            }
763        }
764
765        #[allow(coherence_leak_check)]
766        impl<R, $first, $($rest,)*> IntoWasmClosure<dyn Fn(&$first, $($rest),*) -> R> for dyn Fn(&$first, $($rest),*) -> R
767            where
768            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
769            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
770            R: BinaryEncode + EncodeTypeDef + 'static,
771        {
772            fn into_closure_box(self: Box<Self>) -> crate::Closure<dyn Fn(&$first, $($rest),*) -> R> {
773                <Self as crate::WryWasmClosure<(BorrowedFirstArg, fn(&$first, $($rest),*) -> R)>>::into_js_closure(self)
774            }
775        }
776    };
777}
778
779impl_fnmut_stub_ref!(A1);
780impl_fnmut_stub_ref!(A1, A2);
781impl_fnmut_stub_ref!(A1, A2, A3);
782impl_fnmut_stub_ref!(A1, A2, A3, A4);
783impl_fnmut_stub_ref!(A1, A2, A3, A4, A5);
784impl_fnmut_stub_ref!(A1, A2, A3, A4, A5, A6);
785impl_fnmut_stub_ref!(A1, A2, A3, A4, A5, A6, A7);
786impl_fnmut_stub_ref!(A1, A2, A3, A4, A5, A6, A7, A8);
787
788/// Macro to implement WasmClosureFnOnce for FnOnce closures of various arities.
789/// This wraps an FnOnce in an FnMut that panics if called more than once.
790macro_rules! impl_fn_once {
791    ($($arg:ident),*) => {
792        impl<R, F, $($arg,)*> WasmClosureFnOnce<dyn FnMut($($arg),*) -> R, fn($($arg),*) -> R, R> for F
793        where
794            F: FnOnce($($arg),*) -> R + 'static,
795            $($arg: BinaryDecode + EncodeTypeDef + 'static,)*
796            R: BinaryEncode + EncodeTypeDef + 'static,
797        {
798            #[allow(non_snake_case)]
799            #[allow(unused_variables)]
800            fn into_closure(self) -> Closure<dyn FnMut($($arg),*) -> R> {
801                // Use Option to allow taking the FnOnce
802                let mut me = Some(self);
803                // Register the callback using the same pattern as impl_fnmut_stub
804                crate::Closure::wrap_once_encode_decode_mut::<fn($($arg),*) -> R>(
805                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
806                        let f = me.take().expect("FnOnce closure called more than once");
807                        decode_args!(decoder; [$($arg,)*] => {
808                            let result = f($($arg),*);
809                            result.encode(encoder);
810                        });
811                    },
812                )
813            }
814        }
815
816        impl<R, F, $($arg,)*> WasmClosureFnOnceAbort<dyn FnMut($($arg),*) -> R, fn($($arg),*) -> R, R> for F
817        where
818            F: FnOnce($($arg),*) -> R + 'static,
819            $($arg: BinaryDecode + EncodeTypeDef + 'static,)*
820            R: BinaryEncode + EncodeTypeDef + 'static,
821        {
822            #[allow(non_snake_case)]
823            #[allow(unused_variables)]
824            fn into_closure(self) -> Closure<dyn FnMut($($arg),*) -> R> {
825                <F as WasmClosureFnOnce<dyn FnMut($($arg),*) -> R, fn($($arg),*) -> R, R>>::into_closure(self)
826            }
827        }
828    };
829}
830
831impl_fn_once!();
832impl_fn_once!(A1);
833impl_fn_once!(A1, A2);
834impl_fn_once!(A1, A2, A3);
835impl_fn_once!(A1, A2, A3, A4);
836impl_fn_once!(A1, A2, A3, A4, A5);
837impl_fn_once!(A1, A2, A3, A4, A5, A6);
838impl_fn_once!(A1, A2, A3, A4, A5, A6, A7);
839impl_fn_once!(A1, A2, A3, A4, A5, A6, A7, A8);
840
841/// Macro to implement WasmClosureFnOnce for FnOnce closures that borrow the first argument.
842/// This uses RefFromBinaryDecode for the first arg and BinaryDecode for the rest.
843macro_rules! impl_fn_once_ref {
844    ($first:ident $(, $rest:ident)*) => {
845        impl<R, F, $first, $($rest,)*> WasmClosureFnOnce<dyn FnMut(&$first, $($rest),*) -> R, (BorrowedFirstArg, fn(&$first, $($rest),*) -> R), R> for F
846        where
847            F: FnOnce(&$first, $($rest),*) -> R + 'static,
848            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
849            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
850            R: BinaryEncode + EncodeTypeDef + 'static,
851        {
852            #[allow(non_snake_case)]
853            #[allow(unused_variables)]
854            fn into_closure(self) -> Closure<dyn FnMut(&$first, $($rest),*) -> R> {
855                let mut me = Some(self);
856                crate::Closure::wrap_once_encode_decode_mut::<fn(&$first, $($rest),*) -> R>(
857                    move |decoder: &mut DecodedData, encoder: &mut EncodedData| {
858                        let f = me.take().expect("FnOnce closure called more than once");
859                        let anchor = <$first as RefFromBinaryDecode>::ref_decode(decoder)?;
860                        $(let $rest = <$rest as BinaryDecode>::decode(decoder)?;)*
861                        let result = f(&*anchor, $($rest),*);
862                        result.encode(encoder);
863                        Ok(())
864                    },
865                )
866            }
867        }
868
869        impl<R, F, $first, $($rest,)*> WasmClosureFnOnceAbort<dyn FnMut(&$first, $($rest),*) -> R, (BorrowedFirstArg, fn(&$first, $($rest),*) -> R), R> for F
870        where
871            F: FnOnce(&$first, $($rest),*) -> R + 'static,
872            $first: RefFromBinaryDecode + EncodeTypeDef + 'static,
873            $($rest: BinaryDecode + EncodeTypeDef + 'static,)*
874            R: BinaryEncode + EncodeTypeDef + 'static,
875        {
876            #[allow(non_snake_case)]
877            #[allow(unused_variables)]
878            fn into_closure(self) -> Closure<dyn FnMut(&$first, $($rest),*) -> R> {
879                <F as WasmClosureFnOnce<dyn FnMut(&$first, $($rest),*) -> R, (BorrowedFirstArg, fn(&$first, $($rest),*) -> R), R>>::into_closure(self)
880            }
881        }
882    };
883}
884
885impl_fn_once_ref!(A1);
886impl_fn_once_ref!(A1, A2);
887impl_fn_once_ref!(A1, A2, A3);
888impl_fn_once_ref!(A1, A2, A3, A4);
889impl_fn_once_ref!(A1, A2, A3, A4, A5);
890impl_fn_once_ref!(A1, A2, A3, A4, A5, A6);
891impl_fn_once_ref!(A1, A2, A3, A4, A5, A6, A7);
892impl_fn_once_ref!(A1, A2, A3, A4, A5, A6, A7, A8);
893
894impl<F: ?Sized> BinaryDecode for crate::Closure<F> {
895    fn decode(decoder: &mut DecodedData) -> Result<Self, DecodeError> {
896        // Decode the JsValue wrapping the closure
897        let value = <crate::JsValue as BinaryDecode>::decode(decoder)?;
898        Ok(Self {
899            _phantom: PhantomData,
900            callback: crate::closure::CallbackOwnership::None,
901            value,
902        })
903    }
904}
905
906impl<F: ?Sized> BinaryEncode for crate::Closure<F> {
907    fn encode(mut self, encoder: &mut EncodedData) {
908        if self.callback.needs_flush() {
909            encoder.mark_needs_flush();
910        }
911        // Hand the closure off to JS: ScopedClosure::drop must not dispose.
912        // JsValue::drop still queues the heap-ref release.
913        self.callback.detach();
914        (&self.value).encode(encoder);
915    }
916}
917
918impl<F: ?Sized> BinaryEncode for &crate::ScopedClosure<'_, F> {
919    fn encode(self, encoder: &mut EncodedData) {
920        if self.callback.needs_flush() {
921            encoder.mark_needs_flush();
922        }
923        // Encode the JsValue
924        (&self.value).encode(encoder);
925    }
926}