wasm_bindgen/
closure.rs

1//! Support for long-lived closures in `wasm-bindgen`
2//!
3//! This module defines the `Closure` type which is used to pass "owned
4//! closures" from Rust to JS. Some more details can be found on the `Closure`
5//! type itself.
6
7#![allow(clippy::fn_to_numeric_cast)]
8
9use alloc::boxed::Box;
10use alloc::string::String;
11use core::fmt;
12use core::mem::{self, ManuallyDrop};
13
14use crate::convert::*;
15use crate::describe::*;
16use crate::throw_str;
17use crate::JsValue;
18use crate::UnwrapThrowExt;
19
20/// A handle to both a closure in Rust as well as JS closure which will invoke
21/// the Rust closure.
22///
23/// A `Closure` is the primary way that a `'static` lifetime closure is
24/// transferred from Rust to JS. `Closure` currently requires that the closures
25/// it's created with have the `'static` lifetime in Rust for soundness reasons.
26///
27/// This type is a "handle" in the sense that whenever it is dropped it will
28/// invalidate the JS closure that it refers to. Any usage of the closure in JS
29/// after the `Closure` has been dropped will raise an exception. It's then up
30/// to you to arrange for `Closure` to be properly deallocate at an appropriate
31/// location in your program.
32///
33/// The type parameter on `Closure` is the type of closure that this represents.
34/// Currently this can only be the `Fn` and `FnMut` traits with up to 7
35/// arguments (and an optional return value).
36///
37/// # Examples
38///
39/// Here are a number of examples of using `Closure`.
40///
41/// ## Using the `setInterval` API
42///
43/// Sample usage of `Closure` to invoke the `setInterval` API.
44///
45/// ```rust,no_run
46/// use wasm_bindgen::prelude::*;
47///
48/// #[wasm_bindgen]
49/// extern "C" {
50///     fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
51///     fn clearInterval(id: i32);
52///
53///     #[wasm_bindgen(js_namespace = console)]
54///     fn log(s: &str);
55/// }
56///
57/// #[wasm_bindgen]
58/// pub struct IntervalHandle {
59///     interval_id: i32,
60///     _closure: Closure<dyn FnMut()>,
61/// }
62///
63/// impl Drop for IntervalHandle {
64///     fn drop(&mut self) {
65///         clearInterval(self.interval_id);
66///     }
67/// }
68///
69/// #[wasm_bindgen]
70/// pub fn run() -> IntervalHandle {
71///     // First up we use `Closure::new` to wrap up a Rust closure and create
72///     // a JS closure.
73///     let cb = Closure::new(|| {
74///         log("interval elapsed!");
75///     });
76///
77///     // Next we pass this via reference to the `setInterval` function, and
78///     // `setInterval` gets a handle to the corresponding JS closure.
79///     let interval_id = setInterval(&cb, 1_000);
80///
81///     // If we were to drop `cb` here it would cause an exception to be raised
82///     // whenever the interval elapses. Instead we *return* our handle back to JS
83///     // so JS can decide when to cancel the interval and deallocate the closure.
84///     IntervalHandle {
85///         interval_id,
86///         _closure: cb,
87///     }
88/// }
89/// ```
90///
91/// ## Casting a `Closure` to a `js_sys::Function`
92///
93/// This is the same `setInterval` example as above, except it is using
94/// `web_sys` (which uses `js_sys::Function` for callbacks) instead of manually
95/// writing bindings to `setInterval` and other Web APIs.
96///
97/// ```rust,ignore
98/// use wasm_bindgen::JsCast;
99///
100/// #[wasm_bindgen]
101/// pub struct IntervalHandle {
102///     interval_id: i32,
103///     _closure: Closure<dyn FnMut()>,
104/// }
105///
106/// impl Drop for IntervalHandle {
107///     fn drop(&mut self) {
108///         let window = web_sys::window().unwrap();
109///         window.clear_interval_with_handle(self.interval_id);
110///     }
111/// }
112///
113/// #[wasm_bindgen]
114/// pub fn run() -> Result<IntervalHandle, JsValue> {
115///     let cb = Closure::new(|| {
116///         web_sys::console::log_1(&"interval elapsed!".into());
117///     });
118///
119///     let window = web_sys::window().unwrap();
120///     let interval_id = window.set_interval_with_callback_and_timeout_and_arguments_0(
121///         // Note this method call, which uses `as_ref()` to get a `JsValue`
122///         // from our `Closure` which is then converted to a `&Function`
123///         // using the `JsCast::unchecked_ref` function.
124///         cb.as_ref().unchecked_ref(),
125///         1_000,
126///     )?;
127///
128///     // Same as above.
129///     Ok(IntervalHandle {
130///         interval_id,
131///         _closure: cb,
132///     })
133/// }
134/// ```
135///
136/// ## Using `FnOnce` and `Closure::once` with `requestAnimationFrame`
137///
138/// Because `requestAnimationFrame` only calls its callback once, we can use
139/// `FnOnce` and `Closure::once` with it.
140///
141/// ```rust,no_run
142/// use wasm_bindgen::prelude::*;
143///
144/// #[wasm_bindgen]
145/// extern "C" {
146///     fn requestAnimationFrame(closure: &Closure<dyn FnMut()>) -> u32;
147///     fn cancelAnimationFrame(id: u32);
148///
149///     #[wasm_bindgen(js_namespace = console)]
150///     fn log(s: &str);
151/// }
152///
153/// #[wasm_bindgen]
154/// pub struct AnimationFrameHandle {
155///     animation_id: u32,
156///     _closure: Closure<dyn FnMut()>,
157/// }
158///
159/// impl Drop for AnimationFrameHandle {
160///     fn drop(&mut self) {
161///         cancelAnimationFrame(self.animation_id);
162///     }
163/// }
164///
165/// // A type that will log a message when it is dropped.
166/// struct LogOnDrop(&'static str);
167/// impl Drop for LogOnDrop {
168///     fn drop(&mut self) {
169///         log(self.0);
170///     }
171/// }
172///
173/// #[wasm_bindgen]
174/// pub fn run() -> AnimationFrameHandle {
175///     // We are using `Closure::once` which takes a `FnOnce`, so the function
176///     // can drop and/or move things that it closes over.
177///     let fired = LogOnDrop("animation frame fired or canceled");
178///     let cb = Closure::once(move || drop(fired));
179///
180///     // Schedule the animation frame!
181///     let animation_id = requestAnimationFrame(&cb);
182///
183///     // Again, return a handle to JS, so that the closure is not dropped
184///     // immediately and JS can decide whether to cancel the animation frame.
185///     AnimationFrameHandle {
186///         animation_id,
187///         _closure: cb,
188///     }
189/// }
190/// ```
191///
192/// ## Converting `FnOnce`s directly into JavaScript Functions with `Closure::once_into_js`
193///
194/// If we don't want to allow a `FnOnce` to be eagerly dropped (maybe because we
195/// just want it to drop after it is called and don't care about cancellation)
196/// then we can use the `Closure::once_into_js` function.
197///
198/// This is the same `requestAnimationFrame` example as above, but without
199/// supporting early cancellation.
200///
201/// ```
202/// use wasm_bindgen::prelude::*;
203///
204/// #[wasm_bindgen]
205/// extern "C" {
206///     // We modify the binding to take an untyped `JsValue` since that is what
207///     // is returned by `Closure::once_into_js`.
208///     //
209///     // If we were using the `web_sys` binding for `requestAnimationFrame`,
210///     // then the call sites would cast the `JsValue` into a `&js_sys::Function`
211///     // using `f.unchecked_ref::<js_sys::Function>()`. See the `web_sys`
212///     // example above for details.
213///     fn requestAnimationFrame(callback: JsValue);
214///
215///     #[wasm_bindgen(js_namespace = console)]
216///     fn log(s: &str);
217/// }
218///
219/// // A type that will log a message when it is dropped.
220/// struct LogOnDrop(&'static str);
221/// impl Drop for LogOnDrop {
222///     fn drop(&mut self) {
223///         log(self.0);
224///     }
225/// }
226///
227/// #[wasm_bindgen]
228/// pub fn run() {
229///     // We are using `Closure::once_into_js` which takes a `FnOnce` and
230///     // converts it into a JavaScript function, which is returned as a
231///     // `JsValue`.
232///     let fired = LogOnDrop("animation frame fired");
233///     let cb = Closure::once_into_js(move || drop(fired));
234///
235///     // Schedule the animation frame!
236///     requestAnimationFrame(cb);
237///
238///     // No need to worry about whether or not we drop a `Closure`
239///     // here or return some sort of handle to JS!
240/// }
241/// ```
242pub struct Closure<T: ?Sized> {
243    js: ManuallyDrop<JsValue>,
244    data: ManuallyDrop<Box<T>>,
245}
246
247union FatPtr<T: ?Sized> {
248    ptr: *mut T,
249    fields: (usize, usize),
250}
251
252impl<T> Closure<T>
253where
254    T: ?Sized + WasmClosure,
255{
256    /// Creates a new instance of `Closure` from the provided Rust function.
257    ///
258    /// Note that the closure provided here, `F`, has a few requirements
259    /// associated with it:
260    ///
261    /// * It must implement `Fn` or `FnMut` (for `FnOnce` functions see
262    ///   `Closure::once` and `Closure::once_into_js`).
263    ///
264    /// * It must be `'static`, aka no stack references (use the `move`
265    ///   keyword).
266    ///
267    /// * It can have at most 7 arguments.
268    ///
269    /// * Its arguments and return values are all types that can be shared with
270    ///   JS (i.e. have `#[wasm_bindgen]` annotations or are simple numbers,
271    ///   etc.)
272    pub fn new<F>(t: F) -> Closure<T>
273    where
274        F: IntoWasmClosure<T> + 'static,
275    {
276        Closure::wrap(Box::new(t).unsize())
277    }
278
279    /// A more direct version of `Closure::new` which creates a `Closure` from
280    /// a `Box<dyn Fn>`/`Box<dyn FnMut>`, which is how it's kept internally.
281    pub fn wrap(mut data: Box<T>) -> Closure<T> {
282        assert_eq!(mem::size_of::<*const T>(), mem::size_of::<FatPtr<T>>());
283        let (a, b) = unsafe {
284            FatPtr {
285                ptr: &mut *data as *mut T,
286            }
287            .fields
288        };
289
290        // Here we need to create a `JsValue` with the data and `T::invoke()`
291        // function pointer. To do that we... take a few unconventional turns.
292        // In essence what happens here is this:
293        //
294        // 1. First up, below we call a function, `breaks_if_inlined`. This
295        //    function, as the name implies, does not work if it's inlined.
296        //    More on that in a moment.
297        // 2. This function internally calls a special import recognized by the
298        //    `wasm-bindgen` CLI tool, `__wbindgen_describe_closure`. This
299        //    imported symbol is similar to `__wbindgen_describe` in that it's
300        //    not intended to show up in the final binary but it's an
301        //    intermediate state for a `wasm-bindgen` binary.
302        // 3. The `__wbindgen_describe_closure` import is namely passed a
303        //    descriptor function, monomorphized for each invocation.
304        //
305        // Most of this doesn't actually make sense to happen at runtime! The
306        // real magic happens when `wasm-bindgen` comes along and updates our
307        // generated code. When `wasm-bindgen` runs it performs a few tasks:
308        //
309        // * First, it finds all functions that call
310        //   `__wbindgen_describe_closure`. These are all `breaks_if_inlined`
311        //   defined below as the symbol isn't called anywhere else.
312        // * Next, `wasm-bindgen` executes the `breaks_if_inlined`
313        //   monomorphized functions, passing it dummy arguments. This will
314        //   execute the function just enough to invoke the special import,
315        //   namely telling us about the function pointer that is the describe
316        //   shim.
317        // * This knowledge is then used to actually find the descriptor in the
318        //   function table which is then executed to figure out the signature
319        //   of the closure.
320        // * Finally, and probably most heinously, the call to
321        //   `breaks_if_inlined` is rewritten to call an otherwise globally
322        //   imported function. This globally imported function will generate
323        //   the `JsValue` for this closure specialized for the signature in
324        //   question.
325        //
326        // Later on `wasm-gc` will clean up all the dead code and ensure that
327        // we don't actually call `__wbindgen_describe_closure` at runtime. This
328        // means we will end up not actually calling `breaks_if_inlined` in the
329        // final binary, all calls to that function should be pruned.
330        //
331        // See crates/cli-support/src/js/closures.rs for a more information
332        // about what's going on here.
333
334        #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
335        extern "C" fn describe<T: WasmClosure + ?Sized>() {
336            inform(CLOSURE);
337            T::describe()
338        }
339
340        #[inline(never)]
341        #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
342        unsafe fn breaks_if_inlined<T: WasmClosure + ?Sized>(a: usize, b: usize) -> u32 {
343            super::__wbindgen_describe_closure(a as u32, b as u32, describe::<T> as usize as u32)
344        }
345
346        let idx = unsafe { breaks_if_inlined::<T>(a, b) };
347
348        Closure {
349            js: ManuallyDrop::new(JsValue::_new(idx)),
350            data: ManuallyDrop::new(data),
351        }
352    }
353
354    /// Release memory management of this closure from Rust to the JS GC.
355    ///
356    /// When a `Closure` is dropped it will release the Rust memory and
357    /// invalidate the associated JS closure, but this isn't always desired.
358    /// Some callbacks are alive for the entire duration of the program or for a
359    /// lifetime dynamically managed by the JS GC. This function can be used
360    /// to drop this `Closure` while keeping the associated JS function still
361    /// valid.
362    ///
363    /// If the platform supports weak references, the Rust memory will be
364    /// reclaimed when the JS closure is GC'd. If weak references is not
365    /// supported, this can be dangerous if this function is called many times
366    /// in an application because the memory leak will overwhelm the page
367    /// quickly and crash the wasm.
368    pub fn into_js_value(self) -> JsValue {
369        let idx = self.js.idx;
370        mem::forget(self);
371        JsValue::_new(idx)
372    }
373
374    /// Same as `into_js_value`, but doesn't return a value.
375    pub fn forget(self) {
376        drop(self.into_js_value());
377    }
378}
379
380// NB: we use a specific `T` for this `Closure<T>` impl block to avoid every
381// call site having to provide an explicit, turbo-fished type like
382// `Closure::<dyn FnOnce()>::once(...)`.
383impl Closure<dyn FnOnce()> {
384    /// Create a `Closure` from a function that can only be called once.
385    ///
386    /// Since we have no way of enforcing that JS cannot attempt to call this
387    /// `FnOne(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
388    /// -> R>` that will dynamically throw a JavaScript error if called more
389    /// than once.
390    ///
391    /// # Example
392    ///
393    /// ```rust,no_run
394    /// use wasm_bindgen::prelude::*;
395    ///
396    /// // Create an non-`Copy`, owned `String`.
397    /// let mut s = String::from("Hello");
398    ///
399    /// // Close over `s`. Since `f` returns `s`, it is `FnOnce` and can only be
400    /// // called once. If it was called a second time, it wouldn't have any `s`
401    /// // to work with anymore!
402    /// let f = move || {
403    ///     s += ", World!";
404    ///     s
405    /// };
406    ///
407    /// // Create a `Closure` from `f`. Note that the `Closure`'s type parameter
408    /// // is `FnMut`, even though `f` is `FnOnce`.
409    /// let closure: Closure<dyn FnMut() -> String> = Closure::once(f);
410    /// ```
411    pub fn once<F, A, R>(fn_once: F) -> Closure<F::FnMut>
412    where
413        F: 'static + WasmClosureFnOnce<A, R>,
414    {
415        Closure::wrap(fn_once.into_fn_mut())
416    }
417
418    /// Convert a `FnOnce(A...) -> R` into a JavaScript `Function` object.
419    ///
420    /// If the JavaScript function is invoked more than once, it will throw an
421    /// exception.
422    ///
423    /// Unlike `Closure::once`, this does *not* return a `Closure` that can be
424    /// dropped before the function is invoked to deallocate the closure. The
425    /// only way the `FnOnce` is deallocated is by calling the JavaScript
426    /// function. If the JavaScript function is never called then the `FnOnce`
427    /// and everything it closes over will leak.
428    ///
429    /// ```rust,ignore
430    /// use wasm_bindgen::{prelude::*, JsCast};
431    ///
432    /// let f = Closure::once_into_js(move || {
433    ///     // ...
434    /// });
435    ///
436    /// assert!(f.is_instance_of::<js_sys::Function>());
437    /// ```
438    pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
439    where
440        F: 'static + WasmClosureFnOnce<A, R>,
441    {
442        fn_once.into_js_function()
443    }
444}
445
446/// A trait for converting an `FnOnce(A...) -> R` into a `FnMut(A...) -> R` that
447/// will throw if ever called more than once.
448#[doc(hidden)]
449pub trait WasmClosureFnOnce<A, R>: 'static {
450    type FnMut: ?Sized + 'static + WasmClosure;
451
452    fn into_fn_mut(self) -> Box<Self::FnMut>;
453
454    fn into_js_function(self) -> JsValue;
455}
456
457impl<T: ?Sized> AsRef<JsValue> for Closure<T> {
458    fn as_ref(&self) -> &JsValue {
459        &self.js
460    }
461}
462
463impl<T> WasmDescribe for Closure<T>
464where
465    T: WasmClosure + ?Sized,
466{
467    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
468    fn describe() {
469        inform(EXTERNREF);
470    }
471}
472
473// `Closure` can only be passed by reference to imports.
474impl<T> IntoWasmAbi for &Closure<T>
475where
476    T: WasmClosure + ?Sized,
477{
478    type Abi = u32;
479
480    fn into_abi(self) -> u32 {
481        (&*self.js).into_abi()
482    }
483}
484
485impl<T> OptionIntoWasmAbi for &Closure<T>
486where
487    T: WasmClosure + ?Sized,
488{
489    fn none() -> Self::Abi {
490        0
491    }
492}
493
494fn _check() {
495    fn _assert<T: IntoWasmAbi>() {}
496    _assert::<&Closure<dyn Fn()>>();
497    _assert::<&Closure<dyn Fn(String)>>();
498    _assert::<&Closure<dyn Fn() -> String>>();
499    _assert::<&Closure<dyn FnMut()>>();
500    _assert::<&Closure<dyn FnMut(String)>>();
501    _assert::<&Closure<dyn FnMut() -> String>>();
502}
503
504impl<T> fmt::Debug for Closure<T>
505where
506    T: ?Sized,
507{
508    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509        write!(f, "Closure {{ ... }}")
510    }
511}
512
513impl<T> Drop for Closure<T>
514where
515    T: ?Sized,
516{
517    fn drop(&mut self) {
518        unsafe {
519            // this will implicitly drop our strong reference in addition to
520            // invalidating all future invocations of the closure
521            if super::__wbindgen_cb_drop(self.js.idx) != 0 {
522                ManuallyDrop::drop(&mut self.data);
523            }
524        }
525    }
526}
527
528/// An internal trait for the `Closure` type.
529///
530/// This trait is not stable and it's not recommended to use this in bounds or
531/// implement yourself.
532#[doc(hidden)]
533pub unsafe trait WasmClosure {
534    fn describe();
535}
536
537/// An internal trait for the `Closure` type.
538///
539/// This trait is not stable and it's not recommended to use this in bounds or
540/// implement yourself.
541#[doc(hidden)]
542pub trait IntoWasmClosure<T: ?Sized> {
543    fn unsize(self: Box<Self>) -> Box<T>;
544}
545
546// The memory safety here in these implementations below is a bit tricky. We
547// want to be able to drop the `Closure` object from within the invocation of a
548// `Closure` for cases like promises. That means that while it's running we
549// might drop the `Closure`, but that shouldn't invalidate the environment yet.
550//
551// Instead what we do is to wrap closures in `Rc` variables. The main `Closure`
552// has a strong reference count which keeps the trait object alive. Each
553// invocation of a closure then *also* clones this and gets a new reference
554// count. When the closure returns it will release the reference count.
555//
556// This means that if the main `Closure` is dropped while it's being invoked
557// then destruction is deferred until execution returns. Otherwise it'll
558// deallocate data immediately.
559
560macro_rules! doit {
561    ($(
562        ($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*)
563    )*) => ($(
564        #[allow(coherence_leak_check)]
565        unsafe impl<$($var,)* R> WasmClosure for dyn Fn($($var),*) -> R + 'static
566            where $($var: FromWasmAbi + 'static,)*
567                  R: ReturnWasmAbi + 'static,
568        {
569            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
570            fn describe() {
571                #[allow(non_snake_case)]
572                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
573                unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
574                    a: usize,
575                    b: usize,
576                    $(
577                    $arg1: <$var::Abi as WasmAbi>::Prim1,
578                    $arg2: <$var::Abi as WasmAbi>::Prim2,
579                    $arg3: <$var::Abi as WasmAbi>::Prim3,
580                    $arg4: <$var::Abi as WasmAbi>::Prim4,
581                    )*
582                ) -> WasmRet<R::Abi> {
583                    if a == 0 {
584                        throw_str("closure invoked after being dropped");
585                    }
586                    // Make sure all stack variables are converted before we
587                    // convert `ret` as it may throw (for `Result`, for
588                    // example)
589                    let ret = {
590                        let f: *const dyn Fn($($var),*) -> R =
591                            FatPtr { fields: (a, b) }.ptr;
592                        $(
593                            let $var = <$var as FromWasmAbi>::from_abi($var::Abi::join($arg1, $arg2, $arg3, $arg4));
594                        )*
595                        (*f)($($var),*)
596                    };
597                    ret.return_abi().into()
598                }
599
600                inform(invoke::<$($var,)* R> as usize as u32);
601
602                unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
603                    a: usize,
604                    b: usize,
605                ) {
606                    // This can be called by the JS glue in erroneous situations
607                    // such as when the closure has already been destroyed. If
608                    // that's the case let's not make things worse by
609                    // segfaulting and/or asserting, so just ignore null
610                    // pointers.
611                    if a == 0 {
612                        return;
613                    }
614                    drop(Box::from_raw(FatPtr::<dyn Fn($($var,)*) -> R> {
615                        fields: (a, b)
616                    }.ptr));
617                }
618                inform(destroy::<$($var,)* R> as usize as u32);
619
620                <&Self>::describe();
621            }
622        }
623
624        #[allow(coherence_leak_check)]
625        unsafe impl<$($var,)* R> WasmClosure for dyn FnMut($($var),*) -> R + 'static
626            where $($var: FromWasmAbi + 'static,)*
627                  R: ReturnWasmAbi + 'static,
628        {
629            #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
630            fn describe() {
631                #[allow(non_snake_case)]
632                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
633                unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
634                    a: usize,
635                    b: usize,
636                    $(
637                    $arg1: <$var::Abi as WasmAbi>::Prim1,
638                    $arg2: <$var::Abi as WasmAbi>::Prim2,
639                    $arg3: <$var::Abi as WasmAbi>::Prim3,
640                    $arg4: <$var::Abi as WasmAbi>::Prim4,
641                    )*
642                ) -> WasmRet<R::Abi> {
643                    if a == 0 {
644                        throw_str("closure invoked recursively or after being dropped");
645                    }
646                    // Make sure all stack variables are converted before we
647                    // convert `ret` as it may throw (for `Result`, for
648                    // example)
649                    let ret = {
650                        let f: *const dyn FnMut($($var),*) -> R =
651                            FatPtr { fields: (a, b) }.ptr;
652                        let f = f as *mut dyn FnMut($($var),*) -> R;
653                        $(
654                            let $var = <$var as FromWasmAbi>::from_abi($var::Abi::join($arg1, $arg2, $arg3, $arg4));
655                        )*
656                        (*f)($($var),*)
657                    };
658                    ret.return_abi().into()
659                }
660
661                inform(invoke::<$($var,)* R> as usize as u32);
662
663                unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
664                    a: usize,
665                    b: usize,
666                ) {
667                    // See `Fn()` above for why we simply return
668                    if a == 0 {
669                        return;
670                    }
671                    drop(Box::from_raw(FatPtr::<dyn FnMut($($var,)*) -> R> {
672                        fields: (a, b)
673                    }.ptr));
674                }
675                inform(destroy::<$($var,)* R> as usize as u32);
676
677                <&mut Self>::describe();
678            }
679        }
680
681        #[allow(non_snake_case, unused_parens)]
682        impl<T, $($var,)* R> WasmClosureFnOnce<($($var),*), R> for T
683            where T: 'static + FnOnce($($var),*) -> R,
684                  $($var: FromWasmAbi + 'static,)*
685                  R: ReturnWasmAbi + 'static
686        {
687            type FnMut = dyn FnMut($($var),*) -> R;
688
689            fn into_fn_mut(self) -> Box<Self::FnMut> {
690                let mut me = Some(self);
691                Box::new(move |$($var),*| {
692                    let me = me.take().expect_throw("FnOnce called more than once");
693                    me($($var),*)
694                })
695            }
696
697            fn into_js_function(self) -> JsValue {
698                use alloc::rc::Rc;
699                use crate::__rt::WasmRefCell;
700
701                let mut me = Some(self);
702
703                let rc1 = Rc::new(WasmRefCell::new(None));
704                let rc2 = rc1.clone();
705
706                let closure = Closure::wrap(Box::new(move |$($var),*| {
707                    // Invoke ourself and get the result.
708                    let me = me.take().expect_throw("FnOnce called more than once");
709                    let result = me($($var),*);
710
711                    // And then drop the `Rc` holding this function's `Closure`
712                    // alive.
713                    debug_assert_eq!(Rc::strong_count(&rc2), 1);
714                    let option_closure = rc2.borrow_mut().take();
715                    debug_assert!(option_closure.is_some());
716                    drop(option_closure);
717
718                    result
719                }) as Box<dyn FnMut($($var),*) -> R>);
720
721                let js_val = closure.as_ref().clone();
722
723                *rc1.borrow_mut() = Some(closure);
724                debug_assert_eq!(Rc::strong_count(&rc1), 2);
725                drop(rc1);
726
727                js_val
728            }
729        }
730
731        impl<T, $($var,)* R> IntoWasmClosure<dyn FnMut($($var),*) -> R> for T
732            where T: 'static + FnMut($($var),*) -> R,
733                  $($var: FromWasmAbi + 'static,)*
734                  R: ReturnWasmAbi + 'static,
735        {
736            fn unsize(self: Box<Self>) -> Box<dyn FnMut($($var),*) -> R> { self }
737        }
738
739        impl<T, $($var,)* R> IntoWasmClosure<dyn Fn($($var),*) -> R> for T
740            where T: 'static + Fn($($var),*) -> R,
741                  $($var: FromWasmAbi + 'static,)*
742                  R: ReturnWasmAbi + 'static,
743        {
744            fn unsize(self: Box<Self>) -> Box<dyn Fn($($var),*) -> R> { self }
745        }
746    )*)
747}
748
749doit! {
750    ()
751    (A a1 a2 a3 a4)
752    (A a1 a2 a3 a4 B b1 b2 b3 b4)
753    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
754    (A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
755    (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)
756    (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)
757    (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)
758    (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)
759}
760
761// Copy the above impls down here for where there's only one argument and it's a
762// reference. We could add more impls for more kinds of references, but it
763// becomes a combinatorial explosion quickly. Let's see how far we can get with
764// just this one! Maybe someone else can figure out voodoo so we don't have to
765// duplicate.
766
767unsafe impl<A, R> WasmClosure for dyn Fn(&A) -> R
768where
769    A: RefFromWasmAbi,
770    R: ReturnWasmAbi + 'static,
771{
772    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
773    fn describe() {
774        #[allow(non_snake_case)]
775        #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
776        unsafe extern "C" fn invoke<A: RefFromWasmAbi, R: ReturnWasmAbi>(
777            a: usize,
778            b: usize,
779            arg1: <A::Abi as WasmAbi>::Prim1,
780            arg2: <A::Abi as WasmAbi>::Prim2,
781            arg3: <A::Abi as WasmAbi>::Prim3,
782            arg4: <A::Abi as WasmAbi>::Prim4,
783        ) -> WasmRet<R::Abi> {
784            if a == 0 {
785                throw_str("closure invoked after being dropped");
786            }
787            // Make sure all stack variables are converted before we
788            // convert `ret` as it may throw (for `Result`, for
789            // example)
790            let ret = {
791                let f: *const dyn Fn(&A) -> R = FatPtr { fields: (a, b) }.ptr;
792                let arg = <A as RefFromWasmAbi>::ref_from_abi(A::Abi::join(arg1, arg2, arg3, arg4));
793                (*f)(&*arg)
794            };
795            ret.return_abi().into()
796        }
797
798        inform(invoke::<A, R> as usize as u32);
799
800        #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
801        unsafe extern "C" fn destroy<A: RefFromWasmAbi, R: ReturnWasmAbi>(a: usize, b: usize) {
802            // See `Fn()` above for why we simply return
803            if a == 0 {
804                return;
805            }
806            drop(Box::from_raw(
807                FatPtr::<dyn Fn(&A) -> R> { fields: (a, b) }.ptr,
808            ));
809        }
810        inform(destroy::<A, R> as usize as u32);
811
812        <&Self>::describe();
813    }
814}
815
816unsafe impl<A, R> WasmClosure for dyn FnMut(&A) -> R
817where
818    A: RefFromWasmAbi,
819    R: ReturnWasmAbi + 'static,
820{
821    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
822    fn describe() {
823        #[allow(non_snake_case)]
824        #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
825        unsafe extern "C" fn invoke<A: RefFromWasmAbi, R: ReturnWasmAbi>(
826            a: usize,
827            b: usize,
828            arg1: <A::Abi as WasmAbi>::Prim1,
829            arg2: <A::Abi as WasmAbi>::Prim2,
830            arg3: <A::Abi as WasmAbi>::Prim3,
831            arg4: <A::Abi as WasmAbi>::Prim4,
832        ) -> WasmRet<R::Abi> {
833            if a == 0 {
834                throw_str("closure invoked recursively or after being dropped");
835            }
836            // Make sure all stack variables are converted before we
837            // convert `ret` as it may throw (for `Result`, for
838            // example)
839            let ret = {
840                let f: *const dyn FnMut(&A) -> R = FatPtr { fields: (a, b) }.ptr;
841                let f = f as *mut dyn FnMut(&A) -> R;
842                let arg = <A as RefFromWasmAbi>::ref_from_abi(A::Abi::join(arg1, arg2, arg3, arg4));
843                (*f)(&*arg)
844            };
845            ret.return_abi().into()
846        }
847
848        inform(invoke::<A, R> as usize as u32);
849
850        #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
851        unsafe extern "C" fn destroy<A: RefFromWasmAbi, R: ReturnWasmAbi>(a: usize, b: usize) {
852            // See `Fn()` above for why we simply return
853            if a == 0 {
854                return;
855            }
856            drop(Box::from_raw(
857                FatPtr::<dyn FnMut(&A) -> R> { fields: (a, b) }.ptr,
858            ));
859        }
860        inform(destroy::<A, R> as usize as u32);
861
862        <&mut Self>::describe();
863    }
864}
865
866#[allow(non_snake_case)]
867impl<T, A, R> WasmClosureFnOnce<(&A,), R> for T
868where
869    T: 'static + FnOnce(&A) -> R,
870    A: RefFromWasmAbi + 'static,
871    R: ReturnWasmAbi + 'static,
872{
873    type FnMut = dyn FnMut(&A) -> R;
874
875    fn into_fn_mut(self) -> Box<Self::FnMut> {
876        let mut me = Some(self);
877        Box::new(move |arg| {
878            let me = me.take().expect_throw("FnOnce called more than once");
879            me(arg)
880        })
881    }
882
883    fn into_js_function(self) -> JsValue {
884        use crate::__rt::WasmRefCell;
885        use alloc::rc::Rc;
886
887        let mut me = Some(self);
888
889        let rc1 = Rc::new(WasmRefCell::new(None));
890        let rc2 = rc1.clone();
891
892        let closure = Closure::wrap(Box::new(move |arg: &A| {
893            // Invoke ourself and get the result.
894            let me = me.take().expect_throw("FnOnce called more than once");
895            let result = me(arg);
896
897            // And then drop the `Rc` holding this function's `Closure`
898            // alive.
899            debug_assert_eq!(Rc::strong_count(&rc2), 1);
900            let option_closure = rc2.borrow_mut().take();
901            debug_assert!(option_closure.is_some());
902            drop(option_closure);
903
904            result
905        }) as Box<dyn FnMut(&A) -> R>);
906
907        let js_val = closure.as_ref().clone();
908
909        *rc1.borrow_mut() = Some(closure);
910        debug_assert_eq!(Rc::strong_count(&rc1), 2);
911        drop(rc1);
912
913        js_val
914    }
915}