From a3e5485b86c578beee37963a35bb3b3b0aadade5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Apr 2018 08:49:21 -0700 Subject: [PATCH] Add examples/documentation for closures --- Cargo.toml | 1 + DESIGN.md | 44 ++++++ README.md | 77 +++++++++- crates/cli-support/src/js.rs | 6 + examples/README.md | 2 + examples/closures/.gitignore | 4 + examples/closures/Cargo.toml | 10 ++ examples/closures/README.md | 20 +++ examples/closures/build.sh | 12 ++ examples/closures/index.html | 40 +++++ examples/closures/index.js | 4 + examples/closures/package.json | 10 ++ examples/closures/src/lib.rs | 94 ++++++++++++ examples/closures/webpack.config.js | 10 ++ src/closure.rs | 226 ++++++++++++++++++++-------- src/lib.rs | 1 + 16 files changed, 493 insertions(+), 68 deletions(-) create mode 100644 examples/closures/.gitignore create mode 100644 examples/closures/Cargo.toml create mode 100644 examples/closures/README.md create mode 100755 examples/closures/build.sh create mode 100644 examples/closures/index.html create mode 100644 examples/closures/index.js create mode 100644 examples/closures/package.json create mode 100644 examples/closures/src/lib.rs create mode 100644 examples/closures/webpack.config.js diff --git a/Cargo.toml b/Cargo.toml index b78940d74..f1422b48a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ "examples/math", "examples/performance", "examples/wasm-in-wasm", + "examples/closures", ] [profile.release] diff --git a/DESIGN.md b/DESIGN.md index 63395b2c0..0db86779d 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -872,6 +872,7 @@ Under the hood this generates shims that do a bunch of translation, but it suffices to say that a call in wasm to `foo` should always return appropriately. + ## Customizing import behavior The `#[wasm_bindgen]` macro supports a good amount of configuration for @@ -1037,6 +1038,49 @@ possibilities! All of these functions will call `console.log` in Rust, but each identifier will have only one signature in Rust. +## Closures + +Closures are a particularly tricky topic in wasm-bindgen right now. They use +somewhat advanced language features to currently be implemented and *still* the +amount of functionality you can use is quite limiting. + +Most of the implementation details of closures can be found in `src/convert.rs` +and `src/closure.rs`, effectively the `ToRefWasmBoundary` implementations for +closure types. Stack closures are pretty straightforward in that they pass +a function pointer and a data pointer to JS. This function pointer is accessed +via the exported `WebAssembly.Table` in JS, and the data pointer is passed along +eventually when the JS closure is invoked. + +Stack closures currently only support `Fn` because there's no great location to +insert a `RefCell` for types like `FnMut`. This restriction may be lift-able +though in the future... + +Long-lived closures are a bit more complicated. The general idea there is: + +* First you create a `Closure`. This manufactures a JS callback and "passes it" + to Rust so Rust can store it. +* Next you later pass it as `&Closure<...>` to JS. This extracts the callback + from Rust and passes it to JS. +* Finally you eventually drop the Rust `Closure` which invalidates the JS + closure. + +Creation of the initial JS function is done with a bunch of +`__wbindgen_cb_arityN` functions. These functions create a JS closure with the +given arity (number of arguments). This isn't really that scalable unfortunately +and also means that it's very difficult to support richer types one day. Unsure +how to solve this. + +The `ToRefWasmBoundary` is quite straightforward for `Closure` as it just plucks +out the JS closure and passes it along. The real meat comes down to the +`WasmShim` internal trait. This is implemented for all the *unsized* closure +types to avoid running afoul with coherence. Each trait impl defines a shim +function to be invokeable from JS as well as the ability to wrap up the sized +verion (aka transition from `F: FnMut()` to `FnMut()`). Impls for `FnMut` also +embed the `RefCell` internally. + +The `WasmShim` design is basically the first thing that got working today. It's +not great and will likely change in the future to hopefully be more flexible! + ## Wrapping up That's currently at least what `wasm-bindgen` has to offer! If you've got more diff --git a/README.md b/README.md index d7d838cde..a7401831e 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Notable features of this project includes: * Importing JS functionality in to Rust such as [DOM manipulation][dom-ex], [console logging][console-log], or [performance monitoring][perf-ex]. * [Exporting Rust functionality][smorg-ex] to JS such as classes, functions, etc. -* Working with rich types like strings, numbers, classes, and objects rather - than simply `u32` and floats. +* Working with rich types like strings, numbers, classes, closures, and objects + rather than simply `u32` and floats. This project is still relatively new but feedback is of course always welcome! If you're curious about the design plus even more information about @@ -397,6 +397,79 @@ export class Awesome { booted.then(main); ``` +## Closures + +The `#[wasm_bindgen]` attribute supports a limited subset of Rust closures being +passed to JS at this time. There are plans to expand this support currently but +it's not clear how to proceed unfortunately. In any case some examples of what +you can do are: + +```rust +#[wasm_bindgen] +extern { + fn foo(a: &Fn()); // must be `Fn`, not `FnMut` +} +``` + +Here a function `foo` is imported from JS where the first argument is a *stack +closure*. You can call this function with a `&Fn()` argument and JS will receive +a JS function. When the `foo` function returns, however, the JS function will be +invalidated and any future usage of it will raise an exception. + +Closures also support arguments and return values native to the wasm type +system, aka f32/u32: + +```rust +#[wasm_bindgen] +extern { + fn bar(a: &Fn(u32, f32) -> f64); +} +``` + +At this time [types like strings aren't supported][cbstr] unfortunately. + +[cbstr]: https://github.com/rustwasm/wasm-bindgen/issues/104 + +Sometimes the stack behavior of these closures is not desired. For example you'd +like to schedule a closure to be run on the next turn of the event loop in JS +through `setTimeout`. For this you want the imported function to return but the +JS closure still needs to be valid! + +To support this use case you can also do: + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern { + fn baz(a: &Closure); +} +``` + +The `Closure` type is defined in the `wasm_bindgen` crate and represents a "long +lived" closure. The JS closure passed to `baz` is still valid after `baz` +returns, and the validity of the JS closure is tied to the lifetime of the +`Closure` in Rust. Once `Closure` is dropped it will deallocate its internal +memory and invalidate the corresponding JS function. + +Unlike stack closures a `Closure` supports `FnMut`: + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern { + fn another(a: &Closure u32>); +} +``` + +Like stack closures, however, only wasm types like u32/f32 are supported today. + +At this time you cannot [pass a JS closure to Rust][cbjs], you can only pass a +Rust closure to JS in limited circumstances. + +[cbjs]: https://github.com/rustwasm/wasm-bindgen/issues/103 + ## Feature reference Here this section will attempt to be a reference for the various features diff --git a/crates/cli-support/src/js.rs b/crates/cli-support/src/js.rs index ee5290339..fbc820cd3 100644 --- a/crates/cli-support/src/js.rs +++ b/crates/cli-support/src/js.rs @@ -255,6 +255,12 @@ impl<'a> Context<'a> { dropRef(i); }") }); + bind("__wbindgen_cb_forget", &|me| { + me.expose_drop_ref(); + String::from("function(i) { + dropRef(i); + }") + }); } self.rewrite_imports(module_name); diff --git a/examples/README.md b/examples/README.md index 2834b24e5..da64b936a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -22,3 +22,5 @@ The examples here are: operations in Rust * `wasm-in-wasm` - how to interact with namespaced APIs like `WebAssembly.Module` and shows off creation of a WebAssembly module from Rust +* `closures` - an example of how to invoke functions like `setInterval` or use + the `onclick` property in conjunction with closures. diff --git a/examples/closures/.gitignore b/examples/closures/.gitignore new file mode 100644 index 000000000..3e0dcd948 --- /dev/null +++ b/examples/closures/.gitignore @@ -0,0 +1,4 @@ +package-lock.json +closures.js +closures_bg.js +closures_bg.wasm diff --git a/examples/closures/Cargo.toml b/examples/closures/Cargo.toml new file mode 100644 index 000000000..3fafb3481 --- /dev/null +++ b/examples/closures/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "closures" +version = "0.1.0" +authors = ["Alex Crichton "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = { path = "../.." } diff --git a/examples/closures/README.md b/examples/closures/README.md new file mode 100644 index 000000000..b3ee97283 --- /dev/null +++ b/examples/closures/README.md @@ -0,0 +1,20 @@ +# Closure examples + +This directory is an example of using the `#[wasm_bindgen]` macro with closures +to interact with the DOM. + +You can build the example with: + +``` +$ ./build.sh +``` + +(or running the commands on Windows manually) + +and then opening up `index.html` in a web browser should show a hello message on +the web page generated by the wasm. + +For more information about this example be sure to check out +[`hello_world`][hello] which also has more comments about caveats and such. + +[hello]: https://github.com/alexcrichton/wasm-bindgen/tree/master/examples/hello_world diff --git a/examples/closures/build.sh b/examples/closures/build.sh new file mode 100755 index 000000000..438e8331a --- /dev/null +++ b/examples/closures/build.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# For more coments about what's going on here, see the `hello_world` example + +set -ex + +cargo +nightly build --target wasm32-unknown-unknown +cargo +nightly run --manifest-path ../../crates/cli/Cargo.toml \ + --bin wasm-bindgen -- \ + ../../target/wasm32-unknown-unknown/debug/closures.wasm --out-dir . +#npm install +npm run serve diff --git a/examples/closures/index.html b/examples/closures/index.html new file mode 100644 index 000000000..e0b1d36a6 --- /dev/null +++ b/examples/closures/index.html @@ -0,0 +1,40 @@ + + + + + + +
+ Loading... +
+ + + + diff --git a/examples/closures/index.js b/examples/closures/index.js new file mode 100644 index 000000000..3035ac7ab --- /dev/null +++ b/examples/closures/index.js @@ -0,0 +1,4 @@ +// For more comments about what's going on here, check out the `hello_world` +// example +const rust = import("./closures"); +rust.then(m => m.run()); diff --git a/examples/closures/package.json b/examples/closures/package.json new file mode 100644 index 000000000..408b462e6 --- /dev/null +++ b/examples/closures/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "serve": "webpack-dev-server" + }, + "devDependencies": { + "webpack": "^4.0.1", + "webpack-cli": "^2.0.10", + "webpack-dev-server": "^3.1.0" + } +} diff --git a/examples/closures/src/lib.rs b/examples/closures/src/lib.rs new file mode 100644 index 000000000..b49178451 --- /dev/null +++ b/examples/closures/src/lib.rs @@ -0,0 +1,94 @@ +#![feature(proc_macro, wasm_custom_section, wasm_import_module)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern { + // Binding for the `setInverval` method in JS. This function takes a "long + // lived" closure as the first argument so we use `Closure` instead of + // a bare `&Fn()` which only surives for that one stack frame. + // + // The second argument is then the interval and the return value is how we + // clear this interval. We're not going to clear our interval in this + // example though so the return value is ignored. + #[wasm_bindgen(js_name = setInterval)] + fn set_interval(cb: &Closure, delay: u32) -> f64; + + // Bindings for JS `Date` so we can update our local timer + type Date; + #[wasm_bindgen(constructor)] + fn new() -> Date; + #[wasm_bindgen(method, js_name = toLocaleString)] + fn to_locale_string(this: &Date) -> String; + + // Bindings for `document` and various methods of updating HTML elements. + // Like with the `dom` example these'll ideally be upstream in a generated + // crate one day but for now we manually define them. + type HTMLDocument; + static document: HTMLDocument; + #[wasm_bindgen(method, js_name = getElementById)] + fn get_element_by_id(this: &HTMLDocument, id: &str) -> Element; + #[wasm_bindgen(method, js_name = getElementById)] + fn get_html_element_by_id(this: &HTMLDocument, id: &str) -> HTMLElement; + + type Element; + #[wasm_bindgen(method, setter = innerHTML)] + fn set_inner_html(this: &Element, html: &str); + + type HTMLElement; + #[wasm_bindgen(method, setter)] + fn set_onclick(this: &HTMLElement, cb: &Closure); + #[wasm_bindgen(method, getter)] + fn style(this: &HTMLElement) -> CSS2Properties; + + type CSS2Properties; + #[wasm_bindgen(method, setter)] + fn set_display(this: &CSS2Properties, display: &str); +} + +#[wasm_bindgen] +pub fn run() { + // Set up a clock on our page and update it each second to ensure it's got + // an accurate date. + let a = Closure::new(update_time); + set_interval(&a, 1000); + update_time(); + fn update_time() { + document.get_element_by_id("current-time") + .set_inner_html(&Date::new().to_locale_string()); + } + + // We also want to count the number of times that our green square has been + // clicked. Our callback will update the `#num-clicks` div + let square = document.get_html_element_by_id("green-square"); + let mut clicks = 0; + let b = Closure::new(move || { + clicks += 1; + document.get_element_by_id("num-clicks") + .set_inner_html(&clicks.to_string()); + }); + square.set_onclick(&b); + + // The instances of `Closure` that we created will invalidate their + // corresponding JS callback whenever they're dropped, so if we were to + // normally return from `run` then both of our registered closures will + // raise exceptions when invoked. + // + // Normally we'd store these handles to later get dropped at an appropriate + // time but for now we want these to be global handlers so we use the + // `forget` method to drop them without invalidating the closure. Note that + // this is leaking memory in Rust, so this should be done judiciously! + a.forget(); + b.forget(); + + // And finally now that our demo is ready to go let's switch things up so + // everything is displayed and our loading prompt is hidden. + document.get_html_element_by_id("loading") + .style() + .set_display("none"); + document.get_html_element_by_id("script") + .style() + .set_display("block"); +} diff --git a/examples/closures/webpack.config.js b/examples/closures/webpack.config.js new file mode 100644 index 000000000..5910e2ae1 --- /dev/null +++ b/examples/closures/webpack.config.js @@ -0,0 +1,10 @@ +const path = require('path'); + +module.exports = { + entry: "./index.js", + output: { + path: path.resolve(__dirname, "dist"), + filename: "index.js", + }, + mode: "development" +}; diff --git a/src/closure.rs b/src/closure.rs index 83825cb36..65a464c90 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -1,11 +1,155 @@ -use std::mem::ManuallyDrop; -use std::marker::{self, Unsize}; +//! Support for long-lived closures in `wasm-bindgen` +//! +//! This module defines the `Closure` type which is used to pass "owned +//! closures" from Rust to JS. Some more details can be found on the `Closure` +//! type itself. + +use std::mem::{self, ManuallyDrop}; +use std::marker::Unsize; use {throw, JsValue}; use convert::*; use __rt::WasmRefCell; -pub unsafe trait WasmShim<'a> { +/// A handle to both a closure in Rust as well as JS closure which will invoke +/// the Rust closure. +/// +/// A `Closure` is the primary way that a `'static` lifetime closure is +/// transferred from Rust to JS. `Closure` currently requires that the closures +/// it's created with have the `'static` lifetime in Rust for soundness reasons. +/// +/// This type is a "handle" in the sense that whenever it is dropped it will +/// invalidate the JS closure that it refers to. Any usage of the closure in JS +/// after the `Closure` has been dropped will raise an exception. It's then up +/// to you to arrange for `Closure` to be properly deallocate at an appropriate +/// location in your program. +/// +/// The type parameter on `Closure` is the type of closure that this represents. +/// Currently this can only be the `Fn` and `FnMut` traits with up to 7 +/// arguments (and an optional return value). The arguments/return value of the +/// trait must be numbers like `u32` for now, although this restriction may be +/// lifted in the future! +/// +/// # Example +/// +/// ```rust,no_run +/// #[wasm_bindgen] +/// extern { +/// fn setTimeout(closure: &Closure, time: u32); +/// +/// #[wasm_bindgen(js_namespace = console)] +/// fn log(s: &str); +/// } +/// +/// #[wasm_bindgen] +/// pub struct ClosureHandle(Closure); +/// +/// #[wasm_bindgen] +/// pub fn run() -> ClosureHandle { +/// // First up we use `Closure::new` to wrap up a Rust closure and create +/// a JS closure. +/// let cb = Closure::new(|| { +/// log("timeout elapsed!"); +/// }); +/// +/// // Next we pass this via reference to the `setTimeout` function, and +/// // `setTimeout` gets a handle to the corresponding JS closure. +/// setTimeout(&cb, 1_000); +/// +/// // If we were to drop `cb` here it would cause an exception to be raised +/// // when the timeout elapses. Instead we *return* our handle back to JS +/// // so JS can tell us later when it would like to deallocate this handle. +/// ClosureHandle(cb) +/// } +/// ``` +pub struct Closure { + _inner: T::Wrapper, + js: ManuallyDrop, +} + +impl Closure + where T: WasmShim + ?Sized, +{ + /// Creates a new instance of `Closure` from the provided Rust closure. + /// + /// Note that the closure provided here, `F`, has a few requirements + /// associated with it: + /// + /// * It must implement `Fn` or `FnMut` + /// * It must be `'static`, aka no stack references (use the `move` keyword) + /// * It can have at most 7 arguments + /// * Its arguments and return values are all wasm types like u32/f64. + /// + /// This is unfortunately pretty restrictive for now but hopefully some of + /// these restrictions can be lifted in the future! + pub fn new(t: F) -> Closure + where F: Unsize + 'static + { + Closure::wrap(T::wrap(t)) + } + + /// A mostly internal function to wrap a boxed closure inside a `Closure` + /// type. + /// + /// This is the function where the JS closure is manufactured. + pub fn wrap(t: T::Wrapper) -> Closure { + unsafe { + let data = T::data(&t); + let js = T::factory()(T::shim(), data[0], data[1]); + Closure { + _inner: t, + js: ManuallyDrop::new(JsValue::from_abi(js, &mut GlobalStack::new())), + } + } + } + + /// Leaks this `Closure` to ensure it remains valid for the duration of the + /// entire program. + /// + /// > **Note**: this function will leak memory. It should be used sparingly + /// > to ensure the memory leak doesn't affect the program too much. + /// + /// When a `Closure` is dropped it will invalidate the associated JS + /// closure, but this isn't always desired. Some callbacks are alive for + /// the entire duration of the program, so this can be used to conveniently + /// leak this instance of `Closure` while performing as much internal + /// cleanup as it can. + pub fn forget(self) { + unsafe { + super::__wbindgen_cb_forget(self.js.to_abi_ref(&mut GlobalStack::new())); + mem::forget(self); + } + } +} + +// `Closure` can only be passed by reference to imports. +impl ToRefWasmBoundary for Closure + where T: WasmShim + ?Sized, +{ + type Abi = u32; + const DESCRIPTOR: Descriptor = T::DESCRIPTOR; + + fn to_abi_ref(&self, extra: &mut Stack) -> u32 { + self.js.to_abi_ref(extra) + } +} + +impl Drop for Closure + where T: WasmShim + ?Sized, +{ + fn drop(&mut self) { + unsafe { + let idx = self.js.to_abi_ref(&mut GlobalStack::new()); + super::__wbindgen_cb_drop(idx); + } + } +} + +/// An internal trait for the `Closure` type. +/// +/// This trait is not stable and it's not recommended to use this in bounds or +/// implement yourself. +pub unsafe trait WasmShim { #[doc(hidden)] const DESCRIPTOR: Descriptor; #[doc(hidden)] @@ -15,7 +159,7 @@ pub unsafe trait WasmShim<'a> { #[doc(hidden)] fn factory() -> unsafe extern fn(u32, u32, u32) -> u32; #[doc(hidden)] - fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'a; + fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'static; #[doc(hidden)] fn data(t: &Self::Wrapper) -> [u32; 2]; } @@ -30,9 +174,9 @@ macro_rules! doit { ($($var:ident)*) => $arity:ident )*) => ($( // Fn with no return - unsafe impl<'a, $($var: WasmAbi),*> WasmShim<'a> for Fn($($var),*) + 'a { + unsafe impl<$($var: WasmAbi),*> WasmShim for Fn($($var),*) { const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC; - type Wrapper = Box; + type Wrapper = Box; fn shim() -> u32 { #[allow(non_snake_case)] @@ -53,7 +197,7 @@ macro_rules! doit { super::$arity } - fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'a { + fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'static { Box::new(u) as Box } @@ -65,9 +209,9 @@ macro_rules! doit { } // Fn with a return - unsafe impl<'a, $($var: WasmAbi,)* R: WasmAbi> WasmShim<'a> for Fn($($var),*) -> R + 'a { + unsafe impl<$($var: WasmAbi,)* R: WasmAbi> WasmShim for Fn($($var),*) -> R { const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC; - type Wrapper = Box R + 'a>; + type Wrapper = Box R>; fn shim() -> u32 { #[allow(non_snake_case)] @@ -88,7 +232,7 @@ macro_rules! doit { super::$arity } - fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'a { + fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'static { Box::new(u) as Box } @@ -100,9 +244,9 @@ macro_rules! doit { } // FnMut with no return - unsafe impl<'a, $($var: WasmAbi),*> WasmShim<'a> for FnMut($($var),*) + 'a { + unsafe impl<$($var: WasmAbi),*> WasmShim for FnMut($($var),*) { const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC; - type Wrapper = Box>; + type Wrapper = Box>; fn shim() -> u32 { #[allow(non_snake_case)] @@ -127,7 +271,7 @@ macro_rules! doit { super::$arity } - fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'a { + fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'static { Box::new(WasmRefCell::new(u)) as Box<_> } @@ -139,9 +283,9 @@ macro_rules! doit { } // FnMut with a return - unsafe impl<'a, $($var: WasmAbi,)* R: WasmAbi> WasmShim<'a> for FnMut($($var),*) -> R + 'a { + unsafe impl<$($var: WasmAbi,)* R: WasmAbi> WasmShim for FnMut($($var),*) -> R { const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC; - type Wrapper = Box R + 'a>>; + type Wrapper = Box R>>; fn shim() -> u32 { #[allow(non_snake_case)] @@ -166,7 +310,7 @@ macro_rules! doit { super::$arity } - fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'a { + fn wrap(u: U) -> Self::Wrapper where U: Unsize + 'static { Box::new(WasmRefCell::new(u)) as Box<_> } @@ -189,53 +333,3 @@ doit! { (A B C D E F) => __wbindgen_cb_arity6 (A B C D E F G) => __wbindgen_cb_arity7 } - -pub struct Closure<'a, T: WasmShim<'a> + ?Sized + 'a> { - _inner: T::Wrapper, - js: ManuallyDrop, - _marker: marker::PhantomData<&'a ()>, -} - -impl<'a, T> Closure<'a, T> - where T: WasmShim<'a> + ?Sized, -{ - pub fn new(t: U) -> Closure<'a, T> - where U: Unsize + 'a - { - Closure::wrap(T::wrap(t)) - } - - pub fn wrap(t: T::Wrapper) -> Closure<'a, T> { - unsafe { - let data = T::data(&t); - let js = T::factory()(T::shim(), data[0], data[1]); - Closure { - _inner: t, - js: ManuallyDrop::new(JsValue::from_abi(js, &mut GlobalStack::new())), - _marker: marker::PhantomData, - } - } - } -} - -impl<'a, T> ToRefWasmBoundary for Closure<'a, T> - where T: WasmShim<'a> + ?Sized, -{ - type Abi = u32; - const DESCRIPTOR: Descriptor = T::DESCRIPTOR; - - fn to_abi_ref(&self, extra: &mut Stack) -> u32 { - self.js.to_abi_ref(extra) - } -} - -impl<'a, T> Drop for Closure<'a, T> - where T: WasmShim<'a> + ?Sized, -{ - fn drop(&mut self) { - unsafe { - let idx = self.js.to_abi_ref(&mut GlobalStack::new()); - super::__wbindgen_cb_drop(idx); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 00ad89479..1773dcbfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,6 +242,7 @@ extern { fn __wbindgen_cb_arity6(a: u32, b: u32, c: u32) -> u32; fn __wbindgen_cb_arity7(a: u32, b: u32, c: u32) -> u32; fn __wbindgen_cb_drop(idx: u32); + fn __wbindgen_cb_forget(idx: u32); } impl Clone for JsValue {