mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-25 19:11:45 +03:00
Add examples/documentation for closures
This commit is contained in:
parent
176060cc8a
commit
a3e5485b86
@ -32,6 +32,7 @@ members = [
|
||||
"examples/math",
|
||||
"examples/performance",
|
||||
"examples/wasm-in-wasm",
|
||||
"examples/closures",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
44
DESIGN.md
44
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
|
||||
|
77
README.md
77
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<Fn()>);
|
||||
}
|
||||
```
|
||||
|
||||
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<FnMut() -> 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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
4
examples/closures/.gitignore
vendored
Normal file
4
examples/closures/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
package-lock.json
|
||||
closures.js
|
||||
closures_bg.js
|
||||
closures_bg.wasm
|
10
examples/closures/Cargo.toml
Normal file
10
examples/closures/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "closures"
|
||||
version = "0.1.0"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = { path = "../.." }
|
20
examples/closures/README.md
Normal file
20
examples/closures/README.md
Normal file
@ -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
|
12
examples/closures/build.sh
Executable file
12
examples/closures/build.sh
Executable file
@ -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
|
40
examples/closures/index.html
Normal file
40
examples/closures/index.html
Normal file
@ -0,0 +1,40 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
||||
<style>
|
||||
#green-square {
|
||||
background: green;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
text-align: center;
|
||||
line-height: 200px;
|
||||
color: white;
|
||||
}
|
||||
#green-square span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id='loading'>
|
||||
Loading...
|
||||
</div>
|
||||
<div id='script' style='display:none'>
|
||||
<p>
|
||||
The current time is:
|
||||
<span id='current-time'>...</span>
|
||||
</p>
|
||||
|
||||
<div id='green-square'>
|
||||
<span>Click me!</span>
|
||||
</div>
|
||||
<p>
|
||||
You've clicked the green square
|
||||
<span id='num-clicks'>0</span>
|
||||
times
|
||||
</p>
|
||||
</div>
|
||||
<script src='./index.js'></script>
|
||||
</body>
|
||||
</html>
|
4
examples/closures/index.js
Normal file
4
examples/closures/index.js
Normal file
@ -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());
|
10
examples/closures/package.json
Normal file
10
examples/closures/package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
94
examples/closures/src/lib.rs
Normal file
94
examples/closures/src/lib.rs
Normal file
@ -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<FnMut()>, 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<FnMut()>);
|
||||
#[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");
|
||||
}
|
10
examples/closures/webpack.config.js
Normal file
10
examples/closures/webpack.config.js
Normal file
@ -0,0 +1,10 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: "./index.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "index.js",
|
||||
},
|
||||
mode: "development"
|
||||
};
|
226
src/closure.rs
226
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<FnMut()>, time: u32);
|
||||
///
|
||||
/// #[wasm_bindgen(js_namespace = console)]
|
||||
/// fn log(s: &str);
|
||||
/// }
|
||||
///
|
||||
/// #[wasm_bindgen]
|
||||
/// pub struct ClosureHandle(Closure<FnMut()>);
|
||||
///
|
||||
/// #[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<T: WasmShim + ?Sized> {
|
||||
_inner: T::Wrapper,
|
||||
js: ManuallyDrop<JsValue>,
|
||||
}
|
||||
|
||||
impl<T> Closure<T>
|
||||
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<F>(t: F) -> Closure<T>
|
||||
where F: Unsize<T> + '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<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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<T> ToRefWasmBoundary for Closure<T>
|
||||
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<T> Drop for Closure<T>
|
||||
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: U) -> Self::Wrapper where U: Unsize<Self> + 'a;
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + '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<Fn($($var),*) + 'a>;
|
||||
type Wrapper = Box<Fn($($var),*)>;
|
||||
|
||||
fn shim() -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
@ -53,7 +197,7 @@ macro_rules! doit {
|
||||
super::$arity
|
||||
}
|
||||
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + 'a {
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + 'static {
|
||||
Box::new(u) as Box<Self>
|
||||
}
|
||||
|
||||
@ -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<Fn($($var),*) -> R + 'a>;
|
||||
type Wrapper = Box<Fn($($var),*) -> R>;
|
||||
|
||||
fn shim() -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
@ -88,7 +232,7 @@ macro_rules! doit {
|
||||
super::$arity
|
||||
}
|
||||
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + 'a {
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + 'static {
|
||||
Box::new(u) as Box<Self>
|
||||
}
|
||||
|
||||
@ -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<WasmRefCell<FnMut($($var),*) + 'a>>;
|
||||
type Wrapper = Box<WasmRefCell<FnMut($($var),*)>>;
|
||||
|
||||
fn shim() -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
@ -127,7 +271,7 @@ macro_rules! doit {
|
||||
super::$arity
|
||||
}
|
||||
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + 'a {
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + '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<WasmRefCell<FnMut($($var),*) -> R + 'a>>;
|
||||
type Wrapper = Box<WasmRefCell<FnMut($($var),*) -> R>>;
|
||||
|
||||
fn shim() -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
@ -166,7 +310,7 @@ macro_rules! doit {
|
||||
super::$arity
|
||||
}
|
||||
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + 'a {
|
||||
fn wrap<U>(u: U) -> Self::Wrapper where U: Unsize<Self> + '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<JsValue>,
|
||||
_marker: marker::PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, T> Closure<'a, T>
|
||||
where T: WasmShim<'a> + ?Sized,
|
||||
{
|
||||
pub fn new<U>(t: U) -> Closure<'a, T>
|
||||
where U: Unsize<T> + '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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user