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::own(t)
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_maybe_aborting::<true>(Box::new(t))
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_maybe_aborting::<false>(Box::new(t))
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_maybe_aborting::<true>(Box::new(t))
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_maybe_aborting::<true>(data)
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_maybe_aborting::<false>(data)
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_maybe_aborting::<true>(data)
347 }
348
349 fn wrap_maybe_aborting<const UNWIND_SAFE: bool>(
350 data: Box<impl IntoWasmClosure<T> + ?Sized>,
351 ) -> Self {
352 Self {
353 js: crate::__rt::wbg_cast(OwnedClosure::<T, UNWIND_SAFE>(data.unsize())),
354 _marker: PhantomData,
355 _lifetime: PhantomData,
356 }
357 }
358
359 /// Creates a scoped closure by borrowing an immutable `Fn` closure with
360 /// panic unwind support.
361 ///
362 /// This is the recommended way to pass closures to JavaScript for immediate/
363 /// synchronous use. Unlike [`Closure::own`], this does not require the closure
364 /// to be `'static`, allowing you to capture references to local variables.
365 ///
366 /// Use [`borrow_mut`](Self::borrow_mut) when you need to mutate captured state.
367 ///
368 /// The returned `ScopedClosure<'a, _>` has lifetime `'a` from the closure
369 /// reference, which means it cannot outlive the closure or any data the
370 /// closure captures.
371 ///
372 /// Supports unwind via its UnwindSafe bound when building with panic=unwind.
373 /// Wrap with `AssertUnwindSafe` if necessary to achieve this bound, or
374 /// use [`borrow_assert_unwind_safe`](Self::borrow_assert_unwind_safe) or
375 /// [`borrow_aborting`](Self::borrow_aborting) for non-unwind-safe functions.
376 ///
377 /// The resulting closure can be upcasted to `FnMut` using [`upcast_ref`](crate::Upcast::upcast_ref).
378 ///
379 /// # When to use scoped closures
380 ///
381 /// Use `ScopedClosure::borrow` or `ScopedClosure::borrow_mut` when:
382 /// - JavaScript will call the closure immediately and not retain it
383 /// - You need to capture non-`'static` references
384 /// - You want automatic cleanup when the `ScopedClosure` is dropped
385 ///
386 /// # Closure lifetime
387 ///
388 /// The JavaScript function is only valid while the `ScopedClosure` exists.
389 /// Once dropped, the JavaScript function is invalidated. If JavaScript retains
390 /// a reference and calls it later, it will throw: "closure invoked recursively
391 /// or after being dropped".
392 ///
393 /// Rust's borrow checker ensures `ScopedClosure` cannot outlive the closure's
394 /// captured data, preventing use-after-free bugs.
395 ///
396 /// # Example
397 ///
398 /// ```ignore
399 /// use wasm_bindgen::prelude::*;
400 ///
401 /// #[wasm_bindgen]
402 /// extern "C" {
403 /// fn call_with_value(cb: &ScopedClosure<dyn Fn(u32)>, value: u32);
404 /// fn call_fnmut(cb: &ScopedClosure<dyn FnMut(u32)>, value: u32);
405 /// }
406 ///
407 /// let data = vec![1, 2, 3];
408 /// let f = |x| {
409 /// println!("data len: {}, x: {}", data.len(), x);
410 /// };
411 /// let closure = ScopedClosure::borrow(&f);
412 /// call_with_value(&closure, 42);
413 /// // Can also upcast to FnMut
414 /// call_fnmut(closure.upcast_ref(), 42);
415 /// ```
416 pub fn borrow<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
417 where
418 F: IntoWasmClosureRef<T> + MaybeUnwindSafe + ?Sized,
419 {
420 Self::borrow_assert_unwind_safe(t)
421 }
422
423 /// Like [`borrow`](Self::borrow), but does not catch panics.
424 ///
425 /// If the closure panics, the process will abort. This variant does not
426 /// require `UnwindSafe`.
427 ///
428 /// **Note: Not unwind safe. Prefer [`borrow`](Self::borrow) or
429 /// [`borrow_assert_unwind_safe`](Self::borrow_assert_unwind_safe) when possible.**
430 pub fn borrow_aborting<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
431 where
432 F: IntoWasmClosureRef<T> + ?Sized,
433 {
434 Self::wrap_borrow::<_, false>(t)
435 }
436
437 /// Like [`borrow`](Self::borrow), but catches panics without requiring `MaybeUnwindSafe`.
438 ///
439 /// **Safety: Unwind safety is assumed when using this function, like using
440 /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
441 pub fn borrow_assert_unwind_safe<'a, F>(t: &'a F) -> ScopedClosure<'a, T::Static>
442 where
443 F: IntoWasmClosureRef<T> + ?Sized,
444 {
445 Self::wrap_borrow::<_, true>(t)
446 }
447
448 fn wrap_borrow<'a, F, const UNWIND_SAFE: bool>(t: &'a F) -> ScopedClosure<'a, T::Static>
449 where
450 F: IntoWasmClosureRef<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, UNWIND_SAFE> {
456 data: WasmSlice { ptr, len },
457 _marker: PhantomData,
458 }),
459 _marker: PhantomData,
460 _lifetime: PhantomData,
461 }
462 }
463
464 /// Creates a scoped closure by mutably borrowing a `FnMut` closure.
465 ///
466 /// Use this for closures that need to mutate captured state. For closures that
467 /// don't need mutation, prefer [`borrow`](Self::borrow) which creates an immutable
468 /// `Fn` closure that is more likely to satisfy `UnwindSafe` automatically.
469 ///
470 /// Supports unwind via its UnwindSafe bound when building with panic=unwind.
471 /// Wrap with `AssertUnwindSafe` if necessary to achieve this bound, or
472 /// use [`borrow_mut_assert_unwind_safe`](Self::borrow_mut_assert_unwind_safe) or
473 /// [`borrow_mut_aborting`](Self::borrow_mut_aborting) for non-unwind-safe functions.
474 ///
475 /// See [`borrow`](Self::borrow) for full documentation on scoped closures.
476 ///
477 /// # Example
478 ///
479 /// ```ignore
480 /// use wasm_bindgen::prelude::*;
481 ///
482 /// #[wasm_bindgen]
483 /// extern "C" {
484 /// fn call_three_times(cb: &ScopedClosure<dyn FnMut(u32)>);
485 /// }
486 ///
487 /// let mut sum = 0;
488 /// let closure = ScopedClosure::borrow_mut(&mut |x: u32| {
489 /// sum += x;
490 /// });
491 /// call_three_times(&closure);
492 /// // closure dropped, `sum` is accessible again
493 /// assert_eq!(sum, 6); // 1 + 2 + 3
494 /// ```
495 pub fn borrow_mut<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
496 where
497 F: IntoWasmClosureRefMut<T> + MaybeUnwindSafe + ?Sized,
498 {
499 Self::borrow_mut_assert_unwind_safe(t)
500 }
501
502 /// Like [`borrow_mut`](Self::borrow_mut), but does not catch panics.
503 ///
504 /// If the closure panics, the process will abort. This variant does not
505 /// require `UnwindSafe`.
506 ///
507 /// **Note: Not unwind safe. Prefer [`borrow_mut`](Self::borrow_mut) or
508 /// [`borrow_mut_assert_unwind_safe`](Self::borrow_mut_assert_unwind_safe) when possible.**
509 pub fn borrow_mut_aborting<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
510 where
511 F: IntoWasmClosureRefMut<T> + ?Sized,
512 {
513 Self::wrap_borrow_mut::<_, false>(t)
514 }
515
516 /// Like [`borrow_mut`](Self::borrow_mut), but catches panics without requiring `MaybeUnwindSafe`.
517 ///
518 /// **Safety: Unwind safety is assumed when using this function, like using
519 /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
520 pub fn borrow_mut_assert_unwind_safe<'a, F>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
521 where
522 F: IntoWasmClosureRefMut<T> + ?Sized,
523 {
524 Self::wrap_borrow_mut::<_, true>(t)
525 }
526
527 fn wrap_borrow_mut<'a, F, const UNWIND_SAFE: bool>(t: &'a mut F) -> ScopedClosure<'a, T::Static>
528 where
529 F: IntoWasmClosureRefMut<T> + ?Sized,
530 {
531 let t: &mut T = t.unsize_closure_ref();
532 let (ptr, len): (u32, u32) = unsafe { mem::transmute_copy(&t) };
533 ScopedClosure {
534 js: crate::__rt::wbg_cast(BorrowedClosure::<T, UNWIND_SAFE> {
535 data: WasmSlice { ptr, len },
536 _marker: PhantomData,
537 }),
538 _marker: PhantomData,
539 _lifetime: PhantomData,
540 }
541 }
542
543 /// Release memory management of this closure from Rust to the JS GC.
544 ///
545 /// When a `Closure` is dropped it will release the Rust memory and
546 /// invalidate the associated JS closure, but this isn't always desired.
547 /// Some callbacks are alive for the entire duration of the program or for a
548 /// lifetime dynamically managed by the JS GC. This function can be used
549 /// to drop this `Closure` while keeping the associated JS function still
550 /// valid.
551 ///
552 /// If the platform supports weak references, the Rust memory will be
553 /// reclaimed when the JS closure is GC'd. If weak references is not
554 /// supported, this can be dangerous if this function is called many times
555 /// in an application because the memory leak will overwhelm the page
556 /// quickly and crash the wasm.
557 ///
558 /// # Safety Note
559 ///
560 /// This method is only available on `'static` closures. Calling it on a
561 /// borrowed `ScopedClosure` would be unsound because the closure data
562 /// would become invalid when the borrow ends.
563 pub fn into_js_value(self) -> JsValue {
564 let idx = self.js.idx;
565 mem::forget(self);
566 JsValue::_new(idx)
567 }
568
569 /// Same as `mem::forget(self)`.
570 ///
571 /// This can be used to fully relinquish closure ownership to the JS.
572 ///
573 /// # Safety Note
574 ///
575 /// This method is only available on `'static` closures. Calling it on a borrowed
576 /// `ScopedClosure` would be unsound because the closure data would become invalid
577 /// when the borrow ends.
578 pub fn forget(self) {
579 mem::forget(self);
580 }
581
582 /// Create a `Closure` from a function that can only be called once.
583 ///
584 /// Since we have no way of enforcing that JS cannot attempt to call this
585 /// `FnOne(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
586 /// -> R>` that will dynamically throw a JavaScript error if called more
587 /// than once.
588 ///
589 /// # Example
590 ///
591 /// ```rust,no_run
592 /// use wasm_bindgen::prelude::*;
593 ///
594 /// // Create an non-`Copy`, owned `String`.
595 /// let mut s = String::from("Hello");
596 ///
597 /// // Close over `s`. Since `f` returns `s`, it is `FnOnce` and can only be
598 /// // called once. If it was called a second time, it wouldn't have any `s`
599 /// // to work with anymore!
600 /// let f = move || {
601 /// s += ", World!";
602 /// s
603 /// };
604 ///
605 /// // Create a `Closure` from `f`. Note that the `Closure`'s type parameter
606 /// // is `FnMut`, even though `f` is `FnOnce`.
607 /// let closure: Closure<dyn FnMut() -> String> = Closure::once(f);
608 /// ```
609 ///
610 /// Note: the `A` and `R` type parameters are here just for backward compat
611 /// and will be removed in the future.
612 pub fn once<F, A, R>(fn_once: F) -> Self
613 where
614 F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
615 {
616 Closure::wrap_maybe_aborting::<true>(fn_once.into_fn_mut())
617 }
618
619 /// Create a `Closure` from a function that can only be called once.
620 ///
621 /// Unlike `once`, this version does NOT catch panics and does NOT require `UnwindSafe`.
622 /// If the closure panics, the process will abort.
623 ///
624 /// Use this when:
625 /// - Your closure captures types that aren't `UnwindSafe` (like `Rc<Cell<T>>`)
626 /// - You don't need panic catching across the JS boundary
627 /// - You prefer abort-on-panic behavior
628 ///
629 /// Since we have no way of enforcing that JS cannot attempt to call this
630 /// `FnOnce(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
631 /// -> R>` that will dynamically throw a JavaScript error if called more
632 /// than once.
633 ///
634 /// **Note: Not unwind safe. Prefer [`once`](Self::once) or `once` with
635 /// [`AssertUnwindSafe`](core::panic::AssertUnwindSafe) when possible.**
636 ///
637 /// Note: the `A` and `R` type parameters are here just for backward compat
638 /// and will be removed in the future.
639 pub fn once_aborting<F, A, R>(fn_once: F) -> Self
640 where
641 F: WasmClosureFnOnceAbort<T, A, R>,
642 {
643 Closure::wrap_maybe_aborting::<false>(fn_once.into_fn_mut())
644 }
645
646 /// Create a `Closure` from a function that can only be called once,
647 /// with panic unwind support.
648 ///
649 /// **Safety: Unwind safety is assumed when using this function, like using
650 /// `AssertUnwindSafe(...)`, this must be verified explicitly.**
651 ///
652 /// Use this when:
653 /// - Your closure captures types that aren't `UnwindSafe` (like `Rc<Cell<T>>`)
654 /// - You still want panics to be caught and converted to JS exceptions
655 ///
656 /// Since we have no way of enforcing that JS cannot attempt to call this
657 /// `FnOnce(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
658 /// -> R>` that will dynamically throw a JavaScript error if called more
659 /// than once.
660 ///
661 /// Note: the `A` and `R` type parameters are here just for backward compat
662 /// and will be removed in the future.
663 pub fn once_assert_unwind_safe<F, A, R>(fn_once: F) -> Self
664 where
665 F: WasmClosureFnOnceAbort<T, A, R>,
666 {
667 Closure::wrap_maybe_aborting::<true>(fn_once.into_fn_mut())
668 }
669
670 // TODO: Update once closures to be generated on construction as once
671 // closures instead of using wrap(). Then we can share the same into_js()
672 // function between all closures, and deprecate this method.
673 /// Convert a `FnOnce(A...) -> R` into a JavaScript `Function` object.
674 ///
675 /// If the JavaScript function is invoked more than once, it will throw an
676 /// exception.
677 ///
678 /// Unlike `Closure::once`, this does *not* return a `Closure` that can be
679 /// dropped before the function is invoked to deallocate the closure. The
680 /// only way the `FnOnce` is deallocated is by calling the JavaScript
681 /// function. If the JavaScript function is never called then the `FnOnce`
682 /// and everything it closes over will leak.
683 ///
684 /// ```rust,ignore
685 /// use wasm_bindgen::{prelude::*, JsCast};
686 ///
687 /// let f = Closure::once_into_js(move || {
688 /// // ...
689 /// });
690 ///
691 /// assert!(f.is_instance_of::<js_sys::Function>());
692 /// ```
693 ///
694 /// Note: the `A` and `R` type parameters are here just for backward compat
695 /// and will be removed in the future.
696 pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
697 where
698 F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,
699 {
700 fn_once.into_js_function()
701 }
702}
703
704/// A trait for converting an `FnOnce(A...) -> R` into a `FnMut(A...) -> R` that
705/// will throw if ever called more than once.
706#[doc(hidden)]
707pub trait WasmClosureFnOnce<FnMut: ?Sized, A, R>: 'static {
708 fn into_fn_mut(self) -> Box<FnMut>;
709
710 fn into_js_function(self) -> JsValue;
711}
712
713/// A trait for converting an `FnOnce(A...) -> R` into a `FnMut(A...) -> R` that
714/// will throw if ever called more than once. This variant does not require UnwindSafe.
715#[doc(hidden)]
716pub trait WasmClosureFnOnceAbort<FnMut: ?Sized, A, R>: 'static {
717 fn into_fn_mut(self) -> Box<FnMut>;
718
719 fn into_js_function(self) -> JsValue;
720}
721
722impl<T: ?Sized> AsRef<JsValue> for ScopedClosure<'_, T> {
723 fn as_ref(&self) -> &JsValue {
724 &self.js
725 }
726}
727
728/// Internal representation of an owned closure sent to JS.
729/// `UNWIND_SAFE` selects the invoke shim: `true` catches panics, `false` does not.
730#[repr(transparent)]
731struct OwnedClosure<T: ?Sized, const UNWIND_SAFE: bool>(Box<T>);
732
733/// Internal representation of a borrowed closure sent to JS.
734/// `UNWIND_SAFE` selects the invoke shim: `true` catches panics, `false` does not.
735struct BorrowedClosure<T: ?Sized, const UNWIND_SAFE: bool> {
736 data: WasmSlice,
737 _marker: PhantomData<T>,
738}
739
740unsafe extern "C" fn destroy<T: ?Sized>(a: usize, b: usize) {
741 if a == 0 {
742 return;
743 }
744 drop(mem::transmute_copy::<_, Box<T>>(&(a, b)));
745}
746
747impl<T, const UNWIND_SAFE: bool> WasmDescribe for OwnedClosure<T, UNWIND_SAFE>
748where
749 T: WasmClosure + ?Sized,
750{
751 #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
752 fn describe() {
753 inform(CLOSURE);
754 inform(destroy::<T> as *const () as usize as u32);
755 inform(T::IS_MUT as u32);
756 T::describe_invoke::<UNWIND_SAFE>();
757 }
758}
759
760impl<T, const UNWIND_SAFE: bool> WasmDescribe for BorrowedClosure<T, UNWIND_SAFE>
761where
762 T: WasmClosure + ?Sized,
763{
764 #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
765 fn describe() {
766 inform(CLOSURE);
767 inform(0);
768 inform(T::IS_MUT as u32);
769 T::describe_invoke::<UNWIND_SAFE>();
770 }
771}
772
773impl<T, const UNWIND_SAFE: bool> IntoWasmAbi for OwnedClosure<T, UNWIND_SAFE>
774where
775 T: WasmClosure + ?Sized,
776{
777 type Abi = WasmSlice;
778
779 fn into_abi(self) -> WasmSlice {
780 let (a, b): (usize, usize) = unsafe { mem::transmute_copy(&ManuallyDrop::new(self)) };
781 WasmSlice {
782 ptr: a as u32,
783 len: b as u32,
784 }
785 }
786}
787
788impl<T, const UNWIND_SAFE: bool> IntoWasmAbi for BorrowedClosure<T, UNWIND_SAFE>
789where
790 T: WasmClosure + ?Sized,
791{
792 type Abi = WasmSlice;
793 fn into_abi(self) -> WasmSlice {
794 self.data
795 }
796}
797
798impl<T> WasmDescribe for ScopedClosure<'_, T>
799where
800 T: WasmClosure + ?Sized,
801{
802 #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
803 fn describe() {
804 inform(EXTERNREF);
805 }
806}
807
808// `ScopedClosure` can be passed by reference to imports (for any lifetime).
809impl<T> IntoWasmAbi for &ScopedClosure<'_, T>
810where
811 T: WasmClosure + ?Sized,
812{
813 type Abi = u32;
814
815 fn into_abi(self) -> u32 {
816 (&self.js).into_abi()
817 }
818}
819
820impl<T> OptionIntoWasmAbi for &ScopedClosure<'_, T>
821where
822 T: WasmClosure + ?Sized,
823{
824 fn none() -> Self::Abi {
825 0
826 }
827}
828
829/// `'static` closures can be passed by value to JS, transferring ownership.
830///
831/// This is useful for one-shot callbacks where you want JS to own the closure.
832/// The closure will be cleaned up by JS GC (if weak references are supported)
833/// or will leak (if weak references are not supported).
834///
835/// # Example
836///
837/// ```ignore
838/// #[wasm_bindgen]
839/// extern "C" {
840/// fn set_one_shot_callback(cb: Closure<dyn FnMut()>);
841/// }
842///
843/// let cb = Closure::new(|| { /* ... */ });
844/// set_one_shot_callback(cb); // Ownership transferred to JS
845/// // No need to store or forget the closure
846/// ```
847impl<T> IntoWasmAbi for ScopedClosure<'static, T>
848where
849 T: WasmClosure + ?Sized,
850{
851 type Abi = u32;
852
853 fn into_abi(self) -> u32 {
854 let idx = self.js.idx;
855 mem::forget(self);
856 idx
857 }
858}
859
860impl<T> OptionIntoWasmAbi for ScopedClosure<'static, T>
861where
862 T: WasmClosure + ?Sized,
863{
864 fn none() -> Self::Abi {
865 0
866 }
867}
868
869fn _check() {
870 fn _assert<T: IntoWasmAbi>() {}
871 // ScopedClosure by reference (any lifetime)
872 _assert::<&ScopedClosure<dyn Fn()>>();
873 _assert::<&ScopedClosure<dyn Fn(String)>>();
874 _assert::<&ScopedClosure<dyn Fn() -> String>>();
875 _assert::<&ScopedClosure<dyn FnMut()>>();
876 _assert::<&ScopedClosure<dyn FnMut(String)>>();
877 _assert::<&ScopedClosure<dyn FnMut() -> String>>();
878 // ScopedClosure by value (only 'static)
879 _assert::<ScopedClosure<'static, dyn Fn()>>();
880 _assert::<ScopedClosure<'static, dyn FnMut()>>();
881 _assert::<Closure<dyn Fn()>>();
882 _assert::<Closure<dyn FnMut()>>();
883}
884
885impl<T> fmt::Debug for ScopedClosure<'_, T>
886where
887 T: ?Sized,
888{
889 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
890 write!(f, "Closure {{ ... }}")
891 }
892}
893
894/// An internal trait for the `Closure` type.
895///
896/// This trait is not stable and it's not recommended to use this in bounds or
897/// implement yourself.
898#[doc(hidden)]
899pub unsafe trait WasmClosure: WasmDescribe {
900 const IS_MUT: bool;
901 /// The `'static` version of `Self`. For example, if `Self` is `dyn Fn() + 'a`,
902 /// then `Static` is `dyn Fn()` (implicitly `'static`).
903 type Static: ?Sized + WasmClosure;
904 /// The mutable version of this closure type.
905 /// For `dyn Fn(...) -> R` this is `dyn FnMut(...) -> R`.
906 /// For `dyn FnMut(...) -> R` this is itself.
907 type AsMut: ?Sized;
908 /// Emit the FUNCTION descriptor with the invoke shim selected by
909 /// `UNWIND_SAFE`: `true` picks the panic-catching shim, `false`
910 /// picks the non-catching shim.
911 fn describe_invoke<const UNWIND_SAFE: bool>();
912}
913
914unsafe impl<T: WasmClosure> WasmClosure for AssertUnwindSafe<T> {
915 type Static = T::Static;
916 const IS_MUT: bool = T::IS_MUT;
917 type AsMut = T::AsMut;
918 fn describe_invoke<const UNWIND_SAFE: bool>() {
919 T::describe_invoke::<UNWIND_SAFE>();
920 }
921}
922
923/// An internal trait for the `Closure` type.
924///
925/// This trait is not stable and it's not recommended to use this in bounds or
926/// implement yourself.
927#[doc(hidden)]
928pub trait IntoWasmClosure<T: ?Sized> {
929 fn unsize(self: Box<Self>) -> Box<T>;
930}
931
932impl<T: ?Sized + WasmClosure> IntoWasmClosure<T> for T {
933 fn unsize(self: Box<Self>) -> Box<T> {
934 self
935 }
936}
937
938/// Trait for converting a reference to a closure into a trait object reference.
939///
940/// This trait is not stable and it's not recommended to use this in bounds or
941/// implement yourself.
942#[doc(hidden)]
943pub trait IntoWasmClosureRef<T: ?Sized> {
944 fn unsize_closure_ref(&self) -> &T;
945}
946
947/// Trait for converting a mutable reference to a closure into a trait object reference.
948///
949/// This trait is not stable and it's not recommended to use this in bounds or
950/// implement yourself.
951#[doc(hidden)]
952pub trait IntoWasmClosureRefMut<T: ?Sized> {
953 fn unsize_closure_ref(&mut self) -> &mut T;
954}
955
956// Blanket impl for AssertUnwindSafe - delegates to inner type
957impl<T: ?Sized, F> IntoWasmClosureRef<T> for AssertUnwindSafe<F>
958where
959 F: IntoWasmClosureRef<T>,
960{
961 fn unsize_closure_ref(&self) -> &T {
962 self.0.unsize_closure_ref()
963 }
964}
965
966impl<T: ?Sized, F> IntoWasmClosureRefMut<T> for AssertUnwindSafe<F>
967where
968 F: IntoWasmClosureRefMut<T>,
969{
970 fn unsize_closure_ref(&mut self) -> &mut T {
971 self.0.unsize_closure_ref()
972 }
973}
974
975// In ScopedClosure, the Rust closure type is the phantom type that erases.
976unsafe impl<T: ?Sized + WasmClosure> ErasableGeneric for ScopedClosure<'_, T> {
977 type Repr = ScopedClosure<'static, dyn FnMut()>;
978}