mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-24 02:01:35 +00:00
guide: Organize docs into "Whirlwind Tour" and "Reference" sections
First part of #616
This commit is contained in:
30
guide/src/reference/cli-reference.md
Normal file
30
guide/src/reference/cli-reference.md
Normal file
@ -0,0 +1,30 @@
|
||||
# CLI Reference
|
||||
|
||||
The `wasm-bindgen` tool has a number of options available to it to tweak the JS
|
||||
that is generated. By default the generated JS uses ES modules and is compatible
|
||||
with both Node and browsers (but will likely require a bundler for both use
|
||||
cases).
|
||||
|
||||
Supported flags of the CLI tool can be learned via `wasm-bindgen --help`, but
|
||||
some notable options are:
|
||||
|
||||
* `--nodejs`: this flag will tailor output for Node instead of browsers,
|
||||
allowing for native usage of `require` of the generated JS and internally
|
||||
using `require` instead of ES modules. When using this flag no further
|
||||
postprocessing (aka a bundler) should be necessary to work with the wasm.
|
||||
|
||||
* `--browser`: this flag will tailor the output specifically for browsers,
|
||||
making it incompatible with Node. This will basically make the generated JS a
|
||||
tiny bit smaller as runtime checks for Node won't be necessary.
|
||||
|
||||
* `--no-modules`: the default output of `wasm-bindgen` uses ES modules but this
|
||||
option indicates that ES modules should not be used and output should be
|
||||
tailored for a web browser. More information on this flag, and
|
||||
`--no-modules-global`, can be found in the [no ES modules
|
||||
documentation](./no-esm.html).
|
||||
|
||||
* `--no-typescript`: by default a `*.d.ts` file is generated for the generated
|
||||
JS file, but this flag will disable generating this TypeScript file.
|
||||
|
||||
* `--debug`: generates a bit more JS and wasm in "debug mode" to help catch
|
||||
programmer errors, but this output isn't intended to be shipped to production.
|
65
guide/src/reference/closures.md
Normal file
65
guide/src/reference/closures.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Closures
|
||||
|
||||
The `#[wasm_bindgen]` attribute supports some Rust closures being passed to JS.
|
||||
Examples of what you can do are:
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
fn foo(a: &Fn()); // could also be `&mut 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 like exports do, for example:
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
type Foo;
|
||||
|
||||
fn bar(a: &Fn(u32, String) -> Foo);
|
||||
}
|
||||
```
|
||||
|
||||
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 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.
|
||||
|
||||
Like stack closures a `Closure` also supports `FnMut`:
|
||||
|
||||
```rust
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
fn another(a: &Closure<FnMut() -> u32>);
|
||||
}
|
||||
```
|
||||
|
||||
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
|
51
guide/src/reference/feature-reference.md
Normal file
51
guide/src/reference/feature-reference.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Feature Reference
|
||||
|
||||
Here this section will attempt to be a reference for the various features
|
||||
implemented in this project. This is likely not exhaustive but the [tests]
|
||||
should also be a great place to look for examples.
|
||||
|
||||
[tests]: https://github.com/rustwasm/wasm-bindgen/tree/master/tests
|
||||
|
||||
The `#[wasm_bindgen]` attribute can be attached to functions, structs,
|
||||
impls, and foreign modules. Impls can only contain functions, and the attribute
|
||||
cannot be attached to functions in an impl block or functions in a foreign
|
||||
module. No lifetime parameters or type parameters are allowed on any of these
|
||||
types. Foreign modules must have the `"C"` abi (or none listed). Free functions
|
||||
with `#[wasm_bindgen]` might not have the `"C"` abi or none listed, and it's also not
|
||||
necessary to annotate with the `#[no_mangle]` attribute.
|
||||
|
||||
All structs referenced through arguments to functions should be defined in the
|
||||
macro itself. Arguments allowed implement the `WasmBoundary` trait, and examples
|
||||
are:
|
||||
|
||||
* Integers (u64/i64 require `BigInt` support)
|
||||
* Floats
|
||||
* Borrowed strings (`&str`)
|
||||
* Owned strings (`String`)
|
||||
* Exported structs (`Foo`, annotated with `#[wasm_bindgen]`)
|
||||
* Exported C-like enums (`Foo`, annotated with `#[wasm_bindgen]`)
|
||||
* Imported types in a foreign module annotated with `#[wasm_bindgen]`
|
||||
* Borrowed exported structs (`&Foo` or `&mut Bar`)
|
||||
* The `JsValue` type and `&JsValue` (not mutable references)
|
||||
* Vectors and slices of supported integer types and of the `JsValue` type.
|
||||
* Optional vectors/slices
|
||||
|
||||
All of the above can also be returned except borrowed references. Passing
|
||||
`Vec<JsValue>` as an argument to a function is not currently supported. Strings are
|
||||
implemented with shim functions to copy data in/out of the Rust heap. That is, a
|
||||
string passed to Rust from JS is copied to the Rust heap (using a generated shim
|
||||
to malloc some space) and then will be freed appropriately.
|
||||
|
||||
Owned values are implemented through boxes. When you return a `Foo` it's
|
||||
actually turned into `Box<RefCell<Foo>>` under the hood and returned to JS as a
|
||||
pointer. The pointer is to have a defined ABI, and the `RefCell` is to ensure
|
||||
safety with reentrancy and aliasing in JS. In general you shouldn't see
|
||||
`RefCell` panics with normal usage.
|
||||
|
||||
JS-values-in-Rust are implemented through indexes that index a table generated
|
||||
as part of the JS bindings. This table is managed via the ownership specified in
|
||||
Rust and through the bindings that we're returning. More information about this
|
||||
can be found in the [design doc].
|
||||
|
||||
All of these constructs currently create relatively straightforward code on the
|
||||
JS side of things, mostly having a 1:1 match in Rust with JS.
|
10
guide/src/reference/index.md
Normal file
10
guide/src/reference/index.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Reference
|
||||
|
||||
This section contains reference material for using `wasm-bindgen`. It is not
|
||||
intended to be read start to finish. Instead, it aims to quickly answer
|
||||
questions like:
|
||||
|
||||
* Is type X supported as a parameter in a Rust function exported to JavaScript?
|
||||
|
||||
* What was that CLI flag to disable ECMAScript modules output, and instead
|
||||
attach the JavaScript bindings directly to `window`?
|
79
guide/src/reference/no-esm.md
Normal file
79
guide/src/reference/no-esm.md
Normal file
@ -0,0 +1,79 @@
|
||||
# No ES Modules
|
||||
|
||||
Explained a bit more in the [internal design](design.html) section one of the
|
||||
key foundational principles of `wasm-bindgen` is ES modules. It supports working
|
||||
without ES modules, however! Not all JS tooling and browsers are ready for ES
|
||||
modules by default, so it can sometimes be helpful to quickly get up and running
|
||||
without them to kick the tires and see how `wasm-bindgen` works.
|
||||
|
||||
Let's start out with our hello-world example from previous chapters, and you can
|
||||
also [follow along in the repository][repo].
|
||||
|
||||
[repo]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/no_modules
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
fn alert(msg: &str);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn greet(name: &str) -> String {
|
||||
alert(&format!("Hello, {}!", name);
|
||||
}
|
||||
```
|
||||
|
||||
Like usual, we first compile this to wasm:
|
||||
|
||||
```
|
||||
$ cargo build --target wasm32-unknown-unknown
|
||||
```
|
||||
|
||||
Next, to avoid using ES modules, pass the `--no-modules` option to the
|
||||
`wasm-bindgen` command:
|
||||
|
||||
```
|
||||
$ wasm-bindgen target/wasm32-unknown-unknown/debug/hello.wasm --no-modules --out-dir .
|
||||
```
|
||||
|
||||
Next up we need to write some HTML to interact with the wasm:
|
||||
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
<script src='./hello.js'></script>
|
||||
<script>
|
||||
wasm_bindgen('./hello_bg.wasm')
|
||||
.then(() => wasm_bindgen.greet('World'));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
and that's it! If you open up that web page in a browser (needs to be over HTTP)
|
||||
then you should see an alert for "Hello, World!".
|
||||
|
||||
The `--no-modules` output will not instantiate or compile the wasm module when
|
||||
included on a web page, instead it just parses and configures the JS bindings
|
||||
for the wasm-module-to-be. The page is configured with one exported global, in
|
||||
this case `wasm_bindgen`. The name of this global can be configured with the
|
||||
`--no-modules-global` option.
|
||||
|
||||
The global `wasm_bindgen` is a function that takes one argument, the path to the
|
||||
wasm file. When invoked `wasm_bindgen` will return a promise for when the wasm
|
||||
file is ready-to-go. After that all exported functionality on
|
||||
`wasm_bindgen` will be functional.
|
||||
|
||||
In the example above, after calling `wasm_bindgen('./hello_bg.wasm')` we wait
|
||||
for the wasm module to be compiled, and afterwards we're invoking our `greet`
|
||||
export.
|
||||
|
||||
Note that exports are available for binding before the wasm module has been
|
||||
instantiated, for example this would have also worked:
|
||||
|
||||
```js
|
||||
const { greet } = wasm_bindgen;
|
||||
|
||||
wasm_bindgen('./hello_bg.wasm')
|
||||
.then(() => greet('World'));
|
||||
```
|
72
guide/src/reference/passing-data.md
Normal file
72
guide/src/reference/passing-data.md
Normal file
@ -0,0 +1,72 @@
|
||||
# Passing arbitrary data to JS
|
||||
|
||||
It's possible to pass data from Rust to JS not explicitly supported
|
||||
in the [Feature Reference](./feature-reference.md) by serializing via [Serde](https://github.com/serde-rs/serde).
|
||||
|
||||
`wasm-bindgen` includes the `JsValue` type, which streamlines serializing and deserializing.
|
||||
|
||||
In order accomplish this, we must include the serde and serde_derive
|
||||
crates in `Cargo.toml`, and configure `wasm-bindgen` to work with this feature:
|
||||
|
||||
Cargo.toml
|
||||
```toml
|
||||
[dependencies]
|
||||
serde = "^1.0.59"
|
||||
serde_derive = "^1.0.59"
|
||||
|
||||
[dependencies.wasm-bindgen]
|
||||
version = "^0.2"
|
||||
features = ["serde-serialize"]
|
||||
```
|
||||
|
||||
In our top-level Rust file (eg `lib.rs` or `main.rs`), we enable the `Serialize`
|
||||
macro:
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
```
|
||||
|
||||
The data we pass at all nesting levels must be supported by serde, or be a `struct` or `enum` that
|
||||
derives the Serialize trait. For example, let's say we'd like to pass this
|
||||
struct to JS; doing so is not possible in bindgen directly due to the use
|
||||
of public fields, `HashMap`s, arrays, and nested `Vec`s. Note that we do not
|
||||
need to use the `#[wasm_bindgen]` macro.
|
||||
|
||||
```rust
|
||||
#[derive(Serialize)]
|
||||
pub struct Example {
|
||||
pub field1: HashMap<u32, String>,
|
||||
pub field2: Vec<Vec<f32>>,
|
||||
pub field3: [f32; 4],
|
||||
}
|
||||
```
|
||||
|
||||
Here's a function that will pass an instance of this `struct` to JS:
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
pub fn pass_example() -> JsValue {
|
||||
let mut field1 = HashMap::new();
|
||||
field1.insert(0, String::from("ex"));
|
||||
let example = Example {
|
||||
field1,
|
||||
field2: vec![vec![1., 2.], vec![3., 4.]],
|
||||
field3: [1., 2., 3., 4.]
|
||||
};
|
||||
|
||||
JsValue::from_serde(&example).unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
When calling this function from JS, its output will automatically be deserialized.
|
||||
In this example, `fied1` will be a JS `object` (Not a JS `Map`), `field2` will be a
|
||||
2d JS `array`, and `field3` will be a 1d JS `array`. Example calling code:
|
||||
|
||||
```typescript
|
||||
const rust = import("./from_rust");
|
||||
|
||||
rust.then(
|
||||
r => {
|
||||
console.log(r.pass_example())
|
||||
}
|
||||
)
|
||||
```
|
17
guide/src/reference/reference.md
Normal file
17
guide/src/reference/reference.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Reference
|
||||
|
||||
The table below provides an overview of all the types that wasm-bindgen can send/receive across the wasm ABI boundary.
|
||||
|
||||
| Type | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| `str` | No | Yes | No | Yes | Yes | No |
|
||||
| `char` | Yes | No | No | Yes | No | No |
|
||||
| `bool` | Yes | No | No | Yes | No | No |
|
||||
| `JsValue` | Yes | Yes | Yes | Yes | No | No |
|
||||
| `Box<[JsValue]>` | Yes | No | No | Yes | Yes | yes |
|
||||
| `*const T` | Yes | No | No | Yes | No | No |
|
||||
| `*mut T` | Yes | No | No | Yes | No | No |
|
||||
| `u8` `i8` `u16` `i16` `u64` `i64` `isize` `size` | Yes | No | No | Yes | No | No |
|
||||
| `u32` `i32` `f32` `f64` | Yes | Yes | Yes | Yes | No | No |
|
||||
| `Box<[u8]>` `Box<[i8]>` `Box<[u16]>` `Box<[i16]>` `Box<[u32]>` `Box<[i32]>` `Box<[u64]>` `Box<[i64]>` `Box<[f32]>` `Box<[f64]`> | Yes | No | No | Yes | Yes | Yes |
|
||||
| `[u8]` `[i8]` `[u16]` `[i16]` `[u32]` `[i32]` `[u64]` `[i64]` `[f32]` `[f64]` | No | Yes | Yes | No | Yes | No |
|
Reference in New Issue
Block a user