Skip to main content

wasm_bindgen/
closure.rs

1//! Support for closures in `wasm-bindgen`
2//!
3//! This module defines the [`ScopedClosure`] type which is used to pass Rust closures
4//! to JavaScript. All closures are unwind safe by default: panics are caught and converted to
5//! JavaScript exceptions when built with `panic=unwind`.
6//!
7//! # Immutable by Default
8//!
9//! The closure API defaults to **immutable** (`Fn`) closures because they are more
10//! likely to satisfy `UnwindSafe` automatically:
11//!
12//! - `&T` where `T: RefUnwindSafe` is `UnwindSafe`
13//! - `&mut T` is **never** `UnwindSafe` regardless of `T`
14//!
15//! This means:
16//! - [`ScopedClosure::borrow`] creates an immutable `Fn` closure
17//! - [`ScopedClosure::borrow_mut`] creates a mutable `FnMut` closure
18//!
19//! Immutable closures can be upcasted to mutable closures using [`upcast_ref`](crate::Upcast::upcast_ref).
20//!
21//! # Type Aliases
22//!
23//! - [`ScopedClosure<'a, T>`] — The unified closure type with a lifetime parameter
24//! - [`Closure<T>`] — Alias for `ScopedClosure<'static, T>` (for backwards compatibility)
25//!
26//! # Unwind Safety
27//!
28//! For immediate/synchronous callbacks, use `&dyn FnMut` / `&dyn Fn`, when you are
29//! **absolutely sure** the code will support unwind safety.
30//!
31//! For [`ScopedClosure`], the default constructors (`borrow`, `borrow_mut`, `own`) catch
32//! panics, while the `_aborting` variants (`borrow_aborting`, `borrow_mut_aborting`, etc.) do not.
33//!
34//! # Ownership Model
35//!
36//! `ScopedClosure` follows the same ownership model as other wasm-bindgen types:
37//! the JavaScript reference remains valid until the Rust value is dropped. When
38//! dropped, the closure is invalidated and any subsequent calls from JavaScript
39//! will throw an exception.
40//!
41//! For borrowed closures created with `borrow`/`borrow_mut`, Rust's borrow checker
42//! ensures the `ScopedClosure` cannot outlive the closure's captured data.
43//!
44//! See the [`ScopedClosure`] type documentation for detailed examples.
45
46#![allow(clippy::fn_to_numeric_cast)]
47use alloc::boxed::Box;
48use alloc::string::String;
49use core::fmt;
50use core::mem::{self, ManuallyDrop};
51
52use crate::__rt::marker::ErasableGeneric;
53use crate::__rt::marker::MaybeUnwindSafe;
54use crate::describe::*;
55use crate::JsValue;
56use crate::{convert::*, JsCast};
57use core::marker::PhantomData;
58use core::panic::AssertUnwindSafe;
59
60#[wasm_bindgen_macro::wasm_bindgen(wasm_bindgen = crate)]
61extern "C" {
62    type JsClosure;
63
64    #[wasm_bindgen(method)]
65    fn _wbg_cb_unref(js: &JsClosure);
66}
67
68/// A closure with a lifetime parameter that represents a Rust closure passed to JavaScript.
69///
70/// `ScopedClosure<'a, T>` is the unified closure type. The lifetime `'a` indicates
71/// how long the closure is valid:
72///
73/// - **`ScopedClosure<'static, T>`** - An owned closure with heap-allocated data. Requires
74///   `'static` captures. Use for long-lived closures like event listeners and timers.
75///   Created with [`Closure::new`] or [`ScopedClosure::own`]. May transfer ownership to the
76///   JS GC using finalizers.
77///
78/// - **`ScopedClosure<'a, T>`** (non-`'static`) - A borrowed closure referencing stack data.
79///   Allows non-`'static` captures. Use for immediate/synchronous callbacks. Created with
80///   [`ScopedClosure::borrow`] (for `FnMut`) or [`ScopedClosure::borrow_immutable`] (for `Fn`).
81///   Cannot transfer ownership to JS GC.
82///
83/// [`Closure<T>`] is currently a type alias for `ScopedClosure<'static, T>`. In
84/// a future release, a lifetime argument will be added to this alias.
85///
86/// # Ownership Model
87///
88/// `ScopedClosure` follows the same ownership model as other wasm-bindgen types:
89/// the JavaScript reference remains valid until the Rust value is dropped. When
90/// dropped, the closure is invalidated and any subsequent calls from JavaScript
91/// will throw: "closure invoked recursively or after being dropped".
92///
93/// For `'static` closures, you can also:
94/// - Pass by value to transfer ownership to JS (implements [`IntoWasmAbi`])
95/// - Call [`forget()`](Self::forget) to leak the closure (JS can use it indefinitely)
96/// - Call [`into_js_value()`](Self::into_js_value) to transfer to JS GC management
97///
98/// # Lifetime Safety
99///
100/// For borrowed closures, Rust's borrow checker ensures that `ScopedClosure` cannot
101/// be held longer than the closure's captured data:
102///
103/// ```ignore
104/// let mut sum = 0;
105/// let mut f = |x: u32| { sum += x; };  // f borrows sum
106/// let closure = ScopedClosure::borrow(&mut f);  // closure borrows f
107/// // closure cannot outlive f, and f cannot outlive sum
108/// ```
109///
110/// # Examples
111///
112/// ## Borrowed closures with `ScopedClosure::borrow`
113///
114/// Use for immediate/synchronous callbacks where JS calls the closure right away:
115///
116/// ```ignore
117/// use wasm_bindgen::prelude::*;
118///
119/// #[wasm_bindgen]
120/// extern "C" {
121///     fn call_immediately(cb: &ScopedClosure<dyn FnMut(u32)>);
122/// }
123///
124/// let mut sum = 0;
125/// {
126///     let mut f = |x: u32| { sum += x; };
127///     let closure = ScopedClosure::borrow(&mut f);
128///     call_immediately(&closure);
129/// }  // closure dropped here, JS function invalidated
130/// assert_eq!(sum, 42);
131/// ```
132///
133/// ## Owned closures with `Closure::new`
134///
135/// Use for long-lived callbacks like event listeners and timers:
136///
137/// ```ignore
138/// use wasm_bindgen::prelude::*;
139///
140/// #[wasm_bindgen]
141/// extern "C" {
142///     fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
143/// }
144///
145/// // Closure::new requires 'static, so use `move` to capture by value
146/// let cb = Closure::new(move || {
147///     // ...
148/// });
149/// setInterval(&cb, 1000);
150/// // Must keep `cb` alive or call `cb.forget()` to transfer to JS
151/// ```
152///
153/// ## Transferring ownership to JS
154///
155/// Pass a `ScopedClosure<'static, T>` by value to transfer ownership:
156///
157/// ```ignore
158/// use wasm_bindgen::prelude::*;
159///
160/// #[wasm_bindgen]
161/// extern "C" {
162///     fn set_one_shot_callback(cb: ScopedClosure<dyn FnMut()>);
163/// }
164///
165/// let cb = ScopedClosure::own(|| { /* ... */ });
166/// set_one_shot_callback(cb);  // Ownership transferred, no need to store
167/// ```
168pub struct ScopedClosure<'a, T: ?Sized> {
169    js: JsClosure,
170    // careful: must be Box<T> not just T because unsized PhantomData
171    // seems to have weird interaction with Pin<>
172    _marker: PhantomData<Box<T>>,
173    _lifetime: PhantomData<&'a ()>,
174}
175
176/// Alias for [`ScopedClosure<'static, T>`] for backwards compatibility.
177///
178/// In a future major version, `Closure` will become `ScopedClosure<'a, T>` with a
179/// lifetime parameter.
180pub type Closure<T> = ScopedClosure<'static, T>;
181
182// ScopedClosure is Unpin because it only contains a JsValue (which is just a u32)
183// and PhantomData markers. The closure data is either on the heap (owned) or
184// referenced through a raw pointer (borrowed), neither of which is stored inline.
185impl<T: ?Sized> Unpin for ScopedClosure<'_, T> {}
186
187fn _assert_compiles<T>(pin: core::pin::Pin<&mut ScopedClosure<'static, T>>) {
188    let _ = &mut *pin.get_mut();
189}
190
191impl<T: ?Sized> Drop for ScopedClosure<'_, T> {
192    fn drop(&mut self) {
193        // Invalidate the closure on the JS side.
194        //
195        // The JS bindings distinguish owned vs borrowed closures via the `dtor_idx`
196        // encoded in `WasmDescribe`: owned closures pass a non-zero destructor
197        // function pointer, borrowed closures pass `0`.
198        //
199        // For owned closures (`Closure::new`/`ScopedClosure::own`), this decreases
200        // the refcount and frees the Rust heap data when the count reaches zero.
201        //
202        // For borrowed closures (`ScopedClosure::borrow`/`borrow_mut`), this sets
203        // state.a = state.b = 0 to prevent any further calls to the closure.
204        self.js._wbg_cb_unref();
205    }
206}
207
208impl<'a, T> ScopedClosure<'a, T>
209where
210    T: ?Sized + WasmClosure,
211{
212    /// Obtain a `&JsValue` reference for this closure
213    pub fn as_js_value(&self) -> &JsValue {
214        self.js.unchecked_ref()
215    }
216}
217
218/// Methods for creating and managing `'static` closures.
219///
220/// These methods are only available on `ScopedClosure<'static, T>`
221/// not on borrowed `ScopedClosure<'a, T>` where `'a` is not `'static`.
222impl<T> ScopedClosure<'static, T>
223where
224    T: ?Sized + WasmClosure,
225{
226    /// Alias for [`Closure::own`].
227    ///
228    /// Creates a new static owned `ScopedClosure<'static, T>` from the provided
229    /// Rust function, with panic unwind support.
230    ///
231    /// The type parameter `T` determines whether the closure is `Fn` or `FnMut`.
232    ///
233    /// Supports unwind via its UnwindSafe bound when building with panic=unwind.
234    /// Alternatively, pass `Closure::own(AssertUnwindSafe(...))` to assert unwind
235    /// safety, or use [`own_assert_unwind_safe`](Self::own_assert_unwind_safe) or
236    /// [`own_aborting`](Self::own_aborting).
237    ///
238    /// When provided to a JS function as an own value, to be managed by the JS GC.
239    ///
240    /// See [`borrow`](Self::borrow) for creating a borrowed `ScopedClosure` with
241    /// an associated lifetime (defaults to immutable `Fn`).
242    pub fn new<F>(t: F) -> Self
243    where
244        F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
245    {
246        Self::_wrap(Box::new(t).unsize(), true)
247    }
248
249    /// Creates a new static owned `ScopedClosure<'static, T>` from the provided
250    /// Rust function, with panic unwind support.
251    ///
252    /// The type parameter `T` determines whether the closure is `Fn` or `FnMut`.
253    ///
254    /// Supports unwind via its UnwindSafe bound when building with panic=unwind.
255    /// Alternatively, pass `Closure::own(AssertUnwindSafe(...))` to assert unwind
256    /// safety, or use [`own_assert_unwind_safe`](Self::own_assert_unwind_safe) or
257    /// [`own_aborting`](Self::own_aborting).
258    ///
259    /// When provided to a JS function as an own value, to be managed by the JS GC.
260    ///
261    /// See [`borrow`](Self::borrow) for creating a borrowed `ScopedClosure` with
262    /// an associated lifetime (defaults to immutable `Fn`).
263    pub fn own<F>(t: F) -> Self
264    where
265        F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,
266    {
267        Self::_wrap(Box::new(t).unsize(), true)
268    }
269
270    /// Creates a new owned `'static` closure that aborts on panic.
271    ///
272    /// Unlike [`own`](Self::own), this version does NOT catch panics and does NOT
273    /// require `UnwindSafe`. If the closure panics, the process will abort.
274    ///
275    /// The type parameter `T` determines whether the closure is `Fn` or `FnMut`.
276    ///
277    /// When provided to a JS function as an own value, to be managed by the JS GC.
278    ///
279    /// See [`borrow_aborting`](Self::borrow_aborting) for creating a borrowed
280    /// `ScopedClosure` with an associated lifetime.
281    ///
282    /// **Note: Not unwind safe. Prefer [`own`](Self::own) or `own` with
283    /// [`AssertUnwindSafe`](core::panic::AssertUnwindSafe) when possible.**
284    pub fn own_aborting<F>(t: F) -> Self
285    where
286        F: IntoWasmClosure<T> + 'static,
287    {
288        Self::_wrap(Box::new(t).unsize(), false)
289    }
290
291    /// Creates a new static owned `ScopedClosure<'static, T>` from the provided
292    /// Rust function, with panic unwind support.
293    ///
294    /// The type parameter `T` determines whether the closure is `Fn` or `FnMut`.
295    ///
296    /// When provided to a JS function as an own value, to be managed by the JS GC.
297    ///
298    /// **Safety: Unwind safety is assumed when using this function, like using
299    /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
300    ///
301    /// See [`borrow_aborting`](Self::borrow_aborting) for creating a borrowed
302    /// `ScopedClosure` with an associated lifetime.
303    pub fn own_assert_unwind_safe<F>(t: F) -> Self
304    where
305        F: IntoWasmClosure<T> + 'static,
306    {
307        Self::_wrap(Box::new(t).unsize(), true)
308    }
309
310    /// A more direct version of `Closure::new` which creates a `Closure` from
311    /// a `Box<dyn Fn>`/`Box<dyn FnMut>`, which is how it's kept internally.
312    ///
313    /// Supports unwind via its UnwindSafe bound when building with panic=unwind.
314    /// Alternatively, use [`wrap_assert_unwind_safe`](Self::wrap_assert_unwind_safe)
315    /// to assert unwind safety, or use [`wrap_aborting`](Self::wrap_aborting).
316    ///
317    pub fn wrap<F>(data: Box<F>) -> Self
318    where
319        F: IntoWasmClosure<T> + ?Sized + MaybeUnwindSafe,
320    {
321        Self::_wrap(data.unsize(), true)
322    }
323
324    /// A more direct version of `Closure::new` which creates a `Closure` from
325    /// a `Box<dyn Fn>`/`Box<dyn FnMut>`, which is how it's kept internally.
326    ///
327    /// This version aborts on panics.
328    pub fn wrap_aborting<F>(data: Box<F>) -> Self
329    where
330        F: IntoWasmClosure<T> + ?Sized,
331    {
332        Self::_wrap(data.unsize(), false)
333    }
334
335    /// A more direct version of `Closure::new` which creates a `Closure` from
336    /// a `Box<dyn Fn>`/`Box<dyn FnMut>`, which is how it's kept internally.
337    ///
338    /// **Safety: Unwind safety is assumed when using this function, like using
339    /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
340    ///
341    /// This version catches panics when unwinding is available.
342    pub fn wrap_assert_unwind_safe<F>(data: Box<F>) -> Self
343    where
344        F: IntoWasmClosure<T> + ?Sized,
345    {
346        Self::_wrap(data.unsize(), true)
347    }
348
349    #[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
350    fn _wrap(data: Box<T>, unwind_safe: bool) -> Self {
351        Self {
352            js: crate::__rt::wbg_cast(OwnedClosureUnwind { data, unwind_safe }),
353            _marker: PhantomData,
354            _lifetime: PhantomData,
355        }
356    }
357
358    #[cfg(not(all(feature = "std", target_arch = "wasm32", panic = "unwind")))]
359    fn _wrap(data: Box<T>, _unwind_safe: bool) -> Self {
360        Self {
361            js: crate::__rt::wbg_cast(OwnedClosure(data)),
362            _marker: PhantomData,
363            _lifetime: PhantomData,
364        }
365    }
366
367    /// Creates a scoped closure by borrowing an immutable `Fn` closure with
368    /// panic unwind support.
369    ///
370    /// This is the recommended way to pass closures to JavaScript for immediate/
371    /// synchronous use. Unlike [`Closure::own`], this does not require the closure
372    /// to be `'static`, allowing you to capture references to local variables.
373    ///
374    /// Use [`borrow_mut`](Self::borrow_mut) when you need to mutate captured state.
375    ///
376    /// The returned `ScopedClosure<'a, _>` has lifetime `'a` from the closure
377    /// reference, which means it cannot outlive the closure or any data the
378    /// closure captures.
379    ///
380    /// Supports unwind via its UnwindSafe bound when building with panic=unwind.
381    /// Wrap with `AssertUnwindSafe` if necessary to achieve this bound, or
382    /// use [`borrow_assert_unwind_safe`](Self::borrow_assert_unwind_safe) or
383    /// [`borrow_aborting`](Self::borrow_aborting) for non-unwind-safe functions.
384    ///
385    /// The resulting closure can be upcasted to `FnMut` using [`upcast_ref`](crate::Upcast::upcast_ref).
386    ///
387    /// # When to use scoped closures
388    ///
389    /// Use `ScopedClosure::borrow` or `ScopedClosure::borrow_mut` when:
390    /// - JavaScript will call the closure immediately and not retain it
391    /// - You need to capture non-`'static` references
392    /// - You want automatic cleanup when the `ScopedClosure` is dropped
393    ///
394    /// # Closure lifetime
395    ///
396    /// The JavaScript function is only valid while the `ScopedClosure` exists.
397    /// Once dropped, the JavaScript function is invalidated. If JavaScript retains
398    /// a reference and calls it later, it will throw: "closure invoked recursively
399    /// or after being dropped".
400    ///
401    /// Rust's borrow checker ensures `ScopedClosure` cannot outlive the closure's
402    /// captured data, preventing use-after-free bugs.
403    ///
404    /// # Example
405    ///
406    /// ```ignore
407    /// use wasm_bindgen::prelude::*;
408    ///
409    /// #[wasm_bindgen]
410    /// extern "C" {
411    ///     fn call_with_value(cb: &ScopedClosure<dyn Fn(u32)>, value: u32);
412    ///     fn call_fnmut(cb: &ScopedClosure<dyn FnMut(u32)>, value: u32);
413    /// }
414    ///
415    /// let data = vec![1, 2, 3];
416    /// let f = |x| {
417    ///     println!("data len: {}, x: {}", data.len(), x);
418    /// };
419    /// let closure = ScopedClosure::borrow(&f);
420    /// call_with_value(&closure, 42);
421    /// // Can also upcast to FnMut
422    /// call_fnmut(closure.upcast_ref(), 42);
423    /// ```
424    pub fn borrow<'a, F>(t: &'a F) -> ScopedClosure<'a, F::Static>
425    where
426        F: IntoWasmClosureRef<'a, T> + MaybeUnwindSafe + ?Sized,
427    {
428        let t: &T = t.unsize_closure_ref();
429        let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&t) };
430        ScopedClosure {
431            js: crate::__rt::wbg_cast(BorrowedClosure::<T> {
432                data: WasmSlice { ptr, len },
433                unwind_safe: true,
434                _marker: PhantomData,
435            }),
436            _marker: PhantomData,
437            _lifetime: PhantomData,
438        }
439    }
440
441    /// Like [`borrow`](Self::borrow), but does not catch panics.
442    ///
443    /// If the closure panics, the process will abort. This variant does not
444    /// require `UnwindSafe`.
445    ///
446    /// **Note: Not unwind safe. Prefer [`borrow`](Self::borrow) or
447    /// [`borrow_assert_unwind_safe`](Self::borrow_assert_unwind_safe) when possible.**
448    pub fn borrow_aborting<'a, F>(t: &'a F) -> ScopedClosure<'a, F::Static>
449    where
450        F: IntoWasmClosureRef<'a, T> + ?Sized,
451    {
452        let t: &T = t.unsize_closure_ref();
453        let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&t) };
454        ScopedClosure {
455            js: crate::__rt::wbg_cast(BorrowedClosure::<T> {
456                data: WasmSlice { ptr, len },
457                unwind_safe: false,
458                _marker: PhantomData,
459            }),
460            _marker: PhantomData,
461            _lifetime: PhantomData,
462        }
463    }
464
465    /// Like [`borrow`](Self::borrow), but catches panics without requiring `MaybeUnwindSafe`.
466    ///
467    /// **Safety: Unwind safety is assumed when using this function, like using
468    /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
469    pub fn borrow_assert_unwind_safe<'a, F>(t: &'a F) -> ScopedClosure<'a, F::Static>
470    where
471        F: IntoWasmClosureRef<'a, T> + ?Sized,
472    {
473        let t: &T = t.unsize_closure_ref();
474        let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&t) };
475        ScopedClosure {
476            js: crate::__rt::wbg_cast(BorrowedClosure::<T> {
477                data: WasmSlice { ptr, len },
478                unwind_safe: true,
479                _marker: PhantomData,
480            }),
481            _marker: PhantomData,
482            _lifetime: PhantomData,
483        }
484    }
485
486    /// Creates a scoped closure by mutably borrowing a `FnMut` closure.
487    ///
488    /// Use this for closures that need to mutate captured state. For closures that
489    /// don't need mutation, prefer [`borrow`](Self::borrow) which creates an immutable
490    /// `Fn` closure that is more likely to satisfy `UnwindSafe` automatically.
491    ///
492    /// Supports unwind via its UnwindSafe bound when building with panic=unwind.
493    /// Wrap with `AssertUnwindSafe` if necessary to achieve this bound, or
494    /// use [`borrow_mut_assert_unwind_safe`](Self::borrow_mut_assert_unwind_safe) or
495    /// [`borrow_mut_aborting`](Self::borrow_mut_aborting) for non-unwind-safe functions.
496    ///
497    /// See [`borrow`](Self::borrow) for full documentation on scoped closures.
498    ///
499    /// # Example
500    ///
501    /// ```ignore
502    /// use wasm_bindgen::prelude::*;
503    ///
504    /// #[wasm_bindgen]
505    /// extern "C" {
506    ///     fn call_three_times(cb: &ScopedClosure<dyn FnMut(u32)>);
507    /// }
508    ///
509    /// let mut sum = 0;
510    /// let closure = ScopedClosure::borrow_mut(&mut |x: u32| {
511    ///     sum += x;
512    /// });
513    /// call_three_times(&closure);
514    /// // closure dropped, `sum` is accessible again
515    /// assert_eq!(sum, 6); // 1 + 2 + 3
516    /// ```
517    pub fn borrow_mut<'a, F>(t: &'a mut F) -> ScopedClosure<'a, F::Static>
518    where
519        F: IntoWasmClosureRefMut<'a, T> + MaybeUnwindSafe + ?Sized,
520    {
521        let t: &mut T = t.unsize_closure_ref();
522        let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&t) };
523        ScopedClosure {
524            js: crate::__rt::wbg_cast(BorrowedClosure::<T> {
525                data: WasmSlice { ptr, len },
526                unwind_safe: true,
527                _marker: PhantomData,
528            }),
529            _marker: PhantomData,
530            _lifetime: PhantomData,
531        }
532    }
533
534    /// Like [`borrow_mut`](Self::borrow_mut), but does not catch panics.
535    ///
536    /// If the closure panics, the process will abort. This variant does not
537    /// require `UnwindSafe`.
538    ///
539    /// **Note: Not unwind safe. Prefer [`borrow_mut`](Self::borrow_mut) or
540    /// [`borrow_mut_assert_unwind_safe`](Self::borrow_mut_assert_unwind_safe) when possible.**
541    pub fn borrow_mut_aborting<'a, F>(t: &'a mut F) -> ScopedClosure<'a, F::Static>
542    where
543        F: IntoWasmClosureRefMut<'a, T> + ?Sized,
544    {
545        let t: &mut T = t.unsize_closure_ref();
546        let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&t) };
547        ScopedClosure {
548            js: crate::__rt::wbg_cast(BorrowedClosure::<T> {
549                data: WasmSlice { ptr, len },
550                unwind_safe: false,
551                _marker: PhantomData,
552            }),
553            _marker: PhantomData,
554            _lifetime: PhantomData,
555        }
556    }
557
558    /// Like [`borrow_mut`](Self::borrow_mut), but catches panics without requiring `MaybeUnwindSafe`.
559    ///
560    /// **Safety: Unwind safety is assumed when using this function, like using
561    /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
562    pub fn borrow_mut_assert_unwind_safe<'a, F>(t: &'a mut F) -> ScopedClosure<'a, F::Static>
563    where
564        F: IntoWasmClosureRefMut<'a, T> + ?Sized,
565    {
566        let t: &mut T = t.unsize_closure_ref();
567        let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&t) };
568        ScopedClosure {
569            js: crate::__rt::wbg_cast(BorrowedClosure::<T> {
570                data: WasmSlice { ptr, len },
571                unwind_safe: true,
572                _marker: PhantomData,
573            }),
574            _marker: PhantomData,
575            _lifetime: PhantomData,
576        }
577    }
578
579    /// Release memory management of this closure from Rust to the JS GC.
580    ///
581    /// When a `Closure` is dropped it will release the Rust memory and
582    /// invalidate the associated JS closure, but this isn't always desired.
583    /// Some callbacks are alive for the entire duration of the program or for a
584    /// lifetime dynamically managed by the JS GC. This function can be used
585    /// to drop this `Closure` while keeping the associated JS function still
586    /// valid.
587    ///
588    /// If the platform supports weak references, the Rust memory will be
589    /// reclaimed when the JS closure is GC'd. If weak references is not
590    /// supported, this can be dangerous if this function is called many times
591    /// in an application because the memory leak will overwhelm the page
592    /// quickly and crash the wasm.
593    ///
594    /// # Safety Note
595    ///
596    /// This method is only available on `'static` closures. Calling it on a
597    /// borrowed `ScopedClosure` would be unsound because the closure data
598    /// would become invalid when the borrow ends.
599    pub fn into_js_value(self) -> JsValue {
600        let idx = self.js.idx;
601        mem::forget(self);
602        JsValue::_new(idx)
603    }
604
605    /// Same as `mem::forget(self)`.
606    ///
607    /// This can be used to fully relinquish closure ownership to the JS.
608    ///
609    /// # Safety Note
610    ///
611    /// This method is only available on `'static` closures. Calling it on a borrowed
612    /// `ScopedClosure` would be unsound because the closure data would become invalid
613    /// when the borrow ends.
614    pub fn forget(self) {
615        mem::forget(self);
616    }
617
618    /// Create a `Closure` from a function that can only be called once.
619    ///
620    /// Since we have no way of enforcing that JS cannot attempt to call this
621    /// `FnOne(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
622    /// -> R>` that will dynamically throw a JavaScript error if called more
623    /// than once.
624    ///
625    /// # Example
626    ///
627    /// ```rust,no_run
628    /// use wasm_bindgen::prelude::*;
629    ///
630    /// // Create an non-`Copy`, owned `String`.
631    /// let mut s = String::from("Hello");
632    ///
633    /// // Close over `s`. Since `f` returns `s`, it is `FnOnce` and can only be
634    /// // called once. If it was called a second time, it wouldn't have any `s`
635    /// // to work with anymore!
636    /// let f = move || {
637    ///     s += ", World!";
638    ///     s
639    /// };
640    ///
641    /// // Create a `Closure` from `f`. Note that the `Closure`'s type parameter
642    /// // is `FnMut`, even though `f` is `FnOnce`.
643    /// let closure: Closure<dyn FnMut() -> String> = Closure::once(f);
644    /// ```
645    ///
646    /// Note: the `A` and `R` type parameters are here just for backward compat
647    /// and will be removed in the future.
648    pub fn once<F, A, R>(fn_once: F) -> Self
649    where
650        F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
651    {
652        Closure::_wrap(fn_once.into_fn_mut(), true)
653    }
654
655    /// Create a `Closure` from a function that can only be called once.
656    ///
657    /// Unlike `once`, this version does NOT catch panics and does NOT require `UnwindSafe`.
658    /// If the closure panics, the process will abort.
659    ///
660    /// Use this when:
661    /// - Your closure captures types that aren't `UnwindSafe` (like `Rc<Cell<T>>`)
662    /// - You don't need panic catching across the JS boundary
663    /// - You prefer abort-on-panic behavior
664    ///
665    /// Since we have no way of enforcing that JS cannot attempt to call this
666    /// `FnOnce(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
667    /// -> R>` that will dynamically throw a JavaScript error if called more
668    /// than once.
669    ///
670    /// **Note: Not unwind safe. Prefer [`once`](Self::once) or `once` with
671    /// [`AssertUnwindSafe`](core::panic::AssertUnwindSafe) when possible.**
672    ///
673    /// Note: the `A` and `R` type parameters are here just for backward compat
674    /// and will be removed in the future.
675    pub fn once_aborting<F, A, R>(fn_once: F) -> Self
676    where
677        F: WasmClosureFnOnceAbort<T, A, R>,
678    {
679        Closure::_wrap(fn_once.into_fn_mut(), false)
680    }
681
682    /// Create a `Closure` from a function that can only be called once,
683    /// with panic unwind support.
684    ///
685    /// **Safety: Unwind safety is assumed when using this function, like using
686    /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
687    ///
688    /// Use this when:
689    /// - Your closure captures types that aren't `UnwindSafe` (like `Rc<Cell<T>>`)
690    /// - You still want panics to be caught and converted to JS exceptions
691    ///
692    /// Since we have no way of enforcing that JS cannot attempt to call this
693    /// `FnOnce(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
694    /// -> R>` that will dynamically throw a JavaScript error if called more
695    /// than once.
696    ///
697    /// Note: the `A` and `R` type parameters are here just for backward compat
698    /// and will be removed in the future.
699    pub fn once_assert_unwind_safe<F, A, R>(fn_once: F) -> Self
700    where
701        F: WasmClosureFnOnceAbort<T, A, R>,
702    {
703        Closure::_wrap(fn_once.into_fn_mut(), true)
704    }
705
706    // TODO: Update once closures to be generated on construction as once
707    // closures instead of using wrap(). Then we can share the same into_js()
708    // function between all closures, and deprecate this method.
709    /// Convert a `FnOnce(A...) -> R` into a JavaScript `Function` object.
710    ///
711    /// If the JavaScript function is invoked more than once, it will throw an
712    /// exception.
713    ///
714    /// Unlike `Closure::once`, this does *not* return a `Closure` that can be
715    /// dropped before the function is invoked to deallocate the closure. The
716    /// only way the `FnOnce` is deallocated is by calling the JavaScript
717    /// function. If the JavaScript function is never called then the `FnOnce`
718    /// and everything it closes over will leak.
719    ///
720    /// ```rust,ignore
721    /// use wasm_bindgen::{prelude::*, JsCast};
722    ///
723    /// let f = Closure::once_into_js(move || {
724    ///     // ...
725    /// });
726    ///
727    /// assert!(f.is_instance_of::<js_sys::Function>());
728    /// ```
729    ///
730    /// Note: the `A` and `R` type parameters are here just for backward compat
731    /// and will be removed in the future.
732    pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
733    where
734        F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
735    {
736        fn_once.into_js_function()
737    }
738}
739
740/// A trait for converting an `FnOnce(A...) -> R` into a `FnMut(A...) -> R` that
741/// will throw if ever called more than once.
742#[doc(hidden)]
743pub trait WasmClosureFnOnce<FnMut: ?Sized, A, R>: 'static {
744    fn into_fn_mut(self) -> Box<FnMut>;
745
746    fn into_js_function(self) -> JsValue;
747}
748
749/// A trait for converting an `FnOnce(A...) -> R` into a `FnMut(A...) -> R` that
750/// will throw if ever called more than once. This variant does not require UnwindSafe.
751#[doc(hidden)]
752pub trait WasmClosureFnOnceAbort<FnMut: ?Sized, A, R>: 'static {
753    fn into_fn_mut(self) -> Box<FnMut>;
754
755    fn into_js_function(self) -> JsValue;
756}
757
758impl<T: ?Sized> AsRef<JsValue> for ScopedClosure<'_, T> {
759    fn as_ref(&self) -> &JsValue {
760        &self.js
761    }
762}
763
764/// Internal representation of an owned closure that we send to JS.
765/// This is used when panic=abort or when panic=unwind but without the unwind_safe flag.
766#[repr(transparent)]
767struct OwnedClosure<T: ?Sized>(Box<T>);
768
769/// Internal representation of an owned closure with unwind safety flag. Used
770/// when panic=unwind to pass both the closure and the unwind_safe flag to JS.
771#[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
772struct OwnedClosureUnwind<T: ?Sized> {
773    data: Box<T>,
774    unwind_safe: bool,
775}
776
777struct BorrowedClosure<T: ?Sized> {
778    data: WasmSlice,
779    unwind_safe: bool,
780    _marker: PhantomData<T>,
781}
782
783unsafe extern "C" fn destroy<T: ?Sized>(a: usize, mut b: usize) {
784    if a == 0 {
785        return;
786    }
787    // Mask out unwind_safe flag
788    b &= !0x80000000;
789    drop(mem::transmute_copy::<_, Box<T>>(&(a, b)));
790}
791
792impl<T> WasmDescribe for OwnedClosure<T>
793where
794    T: WasmClosure + ?Sized,
795{
796    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
797    fn describe() {
798        inform(CLOSURE);
799        inform(destroy::<T> as *const () as usize as u32);
800        inform(T::IS_MUT as u32);
801        T::describe();
802    }
803}
804
805impl<T> WasmDescribe for BorrowedClosure<T>
806where
807    T: WasmClosure + ?Sized,
808{
809    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
810    fn describe() {
811        inform(CLOSURE);
812        inform(0);
813        inform(T::IS_MUT as u32);
814        T::describe();
815    }
816}
817
818impl<T> IntoWasmAbi for OwnedClosure<T>
819where
820    T: WasmClosure + ?Sized,
821{
822    type Abi = WasmSlice;
823
824    fn into_abi(self) -> WasmSlice {
825        let (a, b): (usize, usize) = unsafe { mem::transmute_copy(&ManuallyDrop::new(self)) };
826        WasmSlice {
827            ptr: a as u32,
828            len: b as u32,
829        }
830    }
831}
832
833impl<T> IntoWasmAbi for BorrowedClosure<T>
834where
835    T: WasmClosure + ?Sized,
836{
837    type Abi = WasmSlice;
838    fn into_abi(self) -> WasmSlice {
839        let WasmSlice { ptr, mut len } = self.data;
840        if self.unwind_safe {
841            len |= 0x80000000;
842        }
843        WasmSlice { ptr, len }
844    }
845}
846
847#[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
848impl<T> WasmDescribe for OwnedClosureUnwind<T>
849where
850    T: WasmClosure + ?Sized,
851{
852    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
853    fn describe() {
854        // Delegate to the inner closure's descriptor - type info is the same
855        OwnedClosure::<T>::describe();
856    }
857}
858
859#[cfg(all(feature = "std", target_arch = "wasm32", panic = "unwind"))]
860impl<T> IntoWasmAbi for OwnedClosureUnwind<T>
861where
862    T: WasmClosure + ?Sized,
863{
864    type Abi = WasmSlice;
865
866    fn into_abi(self) -> WasmSlice {
867        let (a, b): (usize, usize) = unsafe { mem::transmute_copy(&ManuallyDrop::new(self.data)) };
868        // Pack unwind_safe into most significant bit (bit 31) of vtable
869        let b_with_flag = if self.unwind_safe {
870            (b as u32) | 0x80000000
871        } else {
872            b as u32
873        };
874        WasmSlice {
875            ptr: a as u32,
876            len: b_with_flag,
877        }
878    }
879}
880
881impl<T> WasmDescribe for ScopedClosure<'_, T>
882where
883    T: WasmClosure + ?Sized,
884{
885    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
886    fn describe() {
887        inform(EXTERNREF);
888    }
889}
890
891// `ScopedClosure` can be passed by reference to imports (for any lifetime).
892impl<T> IntoWasmAbi for &ScopedClosure<'_, T>
893where
894    T: WasmClosure + ?Sized,
895{
896    type Abi = u32;
897
898    fn into_abi(self) -> u32 {
899        (&self.js).into_abi()
900    }
901}
902
903impl<T> OptionIntoWasmAbi for &ScopedClosure<'_, T>
904where
905    T: WasmClosure + ?Sized,
906{
907    fn none() -> Self::Abi {
908        0
909    }
910}
911
912/// `'static` closures can be passed by value to JS, transferring ownership.
913///
914/// This is useful for one-shot callbacks where you want JS to own the closure.
915/// The closure will be cleaned up by JS GC (if weak references are supported)
916/// or will leak (if weak references are not supported).
917///
918/// # Example
919///
920/// ```ignore
921/// #[wasm_bindgen]
922/// extern "C" {
923///     fn set_one_shot_callback(cb: Closure<dyn FnMut()>);
924/// }
925///
926/// let cb = Closure::new(|| { /* ... */ });
927/// set_one_shot_callback(cb);  // Ownership transferred to JS
928/// // No need to store or forget the closure
929/// ```
930impl<T> IntoWasmAbi for ScopedClosure<'static, T>
931where
932    T: WasmClosure + ?Sized,
933{
934    type Abi = u32;
935
936    fn into_abi(self) -> u32 {
937        let idx = self.js.idx;
938        mem::forget(self);
939        idx
940    }
941}
942
943impl<T> OptionIntoWasmAbi for ScopedClosure<'static, T>
944where
945    T: WasmClosure + ?Sized,
946{
947    fn none() -> Self::Abi {
948        0
949    }
950}
951
952fn _check() {
953    fn _assert<T: IntoWasmAbi>() {}
954    // ScopedClosure by reference (any lifetime)
955    _assert::<&ScopedClosure<dyn Fn()>>();
956    _assert::<&ScopedClosure<dyn Fn(String)>>();
957    _assert::<&ScopedClosure<dyn Fn() -> String>>();
958    _assert::<&ScopedClosure<dyn FnMut()>>();
959    _assert::<&ScopedClosure<dyn FnMut(String)>>();
960    _assert::<&ScopedClosure<dyn FnMut() -> String>>();
961    // ScopedClosure by value (only 'static)
962    _assert::<ScopedClosure<'static, dyn Fn()>>();
963    _assert::<ScopedClosure<'static, dyn FnMut()>>();
964    _assert::<Closure<dyn Fn()>>();
965    _assert::<Closure<dyn FnMut()>>();
966}
967
968impl<T> fmt::Debug for ScopedClosure<'_, T>
969where
970    T: ?Sized,
971{
972    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
973        write!(f, "Closure {{ ... }}")
974    }
975}
976
977/// An internal trait for the `Closure` type.
978///
979/// This trait is not stable and it's not recommended to use this in bounds or
980/// implement yourself.
981#[doc(hidden)]
982pub unsafe trait WasmClosure: WasmDescribe {
983    const IS_MUT: bool;
984    /// The mutable version of this closure type.
985    /// For `dyn Fn(...) -> R` this is `dyn FnMut(...) -> R`.
986    /// For `dyn FnMut(...) -> R` this is itself.
987    type AsMut: ?Sized;
988    fn to_wasm_slice(r: &Self) -> WasmSlice;
989}
990
991unsafe impl<T: WasmClosure> WasmClosure for AssertUnwindSafe<T> {
992    const IS_MUT: bool = T::IS_MUT;
993    type AsMut = T::AsMut;
994    fn to_wasm_slice(r: &Self) -> WasmSlice {
995        let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&r) };
996        WasmSlice { ptr, len }
997    }
998}
999
1000/// An internal trait for the `Closure` type.
1001///
1002/// This trait is not stable and it's not recommended to use this in bounds or
1003/// implement yourself.
1004#[doc(hidden)]
1005pub trait IntoWasmClosure<T: ?Sized> {
1006    fn unsize(self: Box<Self>) -> Box<T>;
1007}
1008
1009impl<T: ?Sized + WasmClosure> IntoWasmClosure<T> for T {
1010    fn unsize(self: Box<Self>) -> Box<T> {
1011        self
1012    }
1013}
1014
1015/// Trait for converting a reference to a closure into a trait object reference.
1016///
1017/// This trait is not stable and it's not recommended to use this in bounds or
1018/// implement yourself.
1019#[doc(hidden)]
1020pub trait IntoWasmClosureRef<'a, T: ?Sized> {
1021    /// The `'static` version of `T`. For example, if `T` is `dyn Fn() + 'a`,
1022    /// then `Static` is `dyn Fn()` (implicitly `'static`).
1023    type Static: ?Sized + WasmClosure;
1024    type WithLifetime: ?Sized + WasmClosure + 'a;
1025    fn unsize_closure_ref(&self) -> &T;
1026}
1027
1028/// Trait for converting a mutable reference to a closure into a trait object reference.
1029///
1030/// This trait is not stable and it's not recommended to use this in bounds or
1031/// implement yourself.
1032#[doc(hidden)]
1033pub trait IntoWasmClosureRefMut<'a, T: ?Sized> {
1034    /// The `'static` version of `T`. For example, if `T` is `dyn FnMut() + 'a`,
1035    /// then `Static` is `dyn FnMut()` (implicitly `'static`).
1036    type Static: ?Sized + WasmClosure;
1037    type WithLifetime: ?Sized + WasmClosure + 'a;
1038    fn unsize_closure_ref(&mut self) -> &mut T;
1039}
1040
1041// Blanket impl for AssertUnwindSafe - delegates to inner type
1042impl<'a, T: ?Sized, F> IntoWasmClosureRef<'a, T> for AssertUnwindSafe<F>
1043where
1044    F: IntoWasmClosureRef<'a, T>,
1045{
1046    type Static = F::Static;
1047    type WithLifetime = F::WithLifetime;
1048    fn unsize_closure_ref(&self) -> &T {
1049        self.0.unsize_closure_ref()
1050    }
1051}
1052
1053impl<'a, T: ?Sized, F> IntoWasmClosureRefMut<'a, T> for AssertUnwindSafe<F>
1054where
1055    F: IntoWasmClosureRefMut<'a, T>,
1056{
1057    type Static = F::Static;
1058    type WithLifetime = F::WithLifetime;
1059    fn unsize_closure_ref(&mut self) -> &mut T {
1060        self.0.unsize_closure_ref()
1061    }
1062}
1063
1064// In ScopedClosure, the Rust closure type is the phantom type that erases.
1065unsafe impl<T: ?Sized + WasmClosure> ErasableGeneric for ScopedClosure<'_, T> {
1066    type Repr = ScopedClosure<'static, dyn FnMut()>;
1067}