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