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}