guide: Organize docs into "Whirlwind Tour" and "Reference" sections

First part of #616
This commit is contained in:
Nick Fitzgerald
2018-08-03 15:14:06 -07:00
parent 61b3d52dc9
commit 39e576d0be
12 changed files with 30 additions and 9 deletions

View 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.

View 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

View 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.

View 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`?

View 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'));
```

View 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())
}
)
```

View 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 |