guide: add exhuastive reference docs for #[wasm_bindgen] attributes

This commit is contained in:
Nick Fitzgerald
2018-08-06 16:35:28 -07:00
parent b6a6dee7f1
commit 33520d4828
19 changed files with 477 additions and 280 deletions

View File

@ -0,0 +1,6 @@
# `#[wasm_bindgen]` Attributes
The `#[wasm_bindgen]` macro supports a good amount of configuration for
controlling precisely how exports are exported, how imports are imported, and
what the generated JavaScript glue ends up looking like. This section is an
exhaustive reference of the possibilities!

View File

@ -0,0 +1,29 @@
# `catch`
The `catch` attribute allows catching a JavaScript exception. This can be
attached to any imported function or method, and the function must return a
`Result` where the `Err` payload is a `JsValue`:
```rust
#[wasm_bindgen]
extern {
// `catch` on a standalone function.
#[wasm_bindgen(catch)]
fn foo() -> Result<(), JsValue>;
// `catch` on a method.
type Zoidberg;
#[wasm_bindgen(catch, method)]
fn woop_woop_woop(this: &Zoidberg) -> Result<u32, JsValue>;
}
```
If calling the imported function throws an exception, then `Err` will be
returned with the exception that was raised. Otherwise, `Ok` is returned with
the result of the function.
> By default `wasm-bindgen` will take no action when wasm calls a JS function
> which ends up throwing an exception. The wasm spec right now doesn't support
> stack unwinding and as a result Rust code **will not execute destructors**.
> This can unfortunately cause memory leaks in Rust right now, but as soon as
> wasm implements catching exceptions we'll be sure to add support as well!

View File

@ -0,0 +1,24 @@
# `constructor`
The `constructor` attribute is used to indicate that the function being bound
should actually translate to calling the `new` operator in JavaScript. The final
argument must be a type that's imported from JavaScript, and it's what will get
used in the generated glue:
```rust
#[wasm_bindgen]
extern {
type Shoes;
#[wasm_bindgen(constructor)]
fn new() -> Shoes;
}
```
This will attach a `new` static method to the `Shoes` type, and in JavaScript
when this method is called, it will be equivalent to `new Shoes()`.
```rust
// Become a cobbler; construct `new Shoes()`
let shoes = Shoes::new();
```

View File

@ -0,0 +1,78 @@
# `getter` and `setter`
These two attributes can be combined with `method` to indicate that this is a
getter or setter method. A `getter`-tagged function by default accesses the
JavaScript property with the same name as the getter function. A `setter`'s
function name is currently required to start with `set_` and the property it
accesses is the suffix after `set\_`.
Consider the following JavaScript class that has a getter and setter for the
`white_russians` property:
```js
class TheDude {
get white_russians() {
...
}
set white_russians(val) {
...
}
}
```
We would import this with the following `#[wasm_bindgen]` attributes:
```rust
#[wasm_bindgen]
extern {
type TheDude;
#[wasm_bindgen(method, getter)]
fn white_russians(this: &TheDude) -> u32;
#[wasm_bindgen(method, setter)]
fn set_white_russians(this: &TheDude, val: u32);
}
```
Here we're importing the `TheDude` type and defining the ability to access each
object's `white_russians` property. The first function here is a getter and will
be available in Rust as `the_dude.white_russians()`, and the latter is the
setter which is accessible as `the_dude.set_white_russians(2)`. Note that both
functions have a `this` argument as they're tagged with `method`.
Finally, you can also pass an argument to the `getter` and `setter`
properties to configure what property is accessed. When the property is
explicitly specified then there is no restriction on the method name. For
example the below is equivalent to the above:
```rust
#[wasm_bindgen]
extern {
type TheDude;
#[wasm_bindgen(method, getter = white_russians)]
fn my_custom_getter_name(this: &TheDude) -> u32;
#[wasm_bindgen(method, setter = white_russians)]
fn my_custom_setter_name(this: &TheDude, val: u32);
}
```
Heads up! `getter` and `setter` functions are found on the constructor's
prototype chain once at load time, cached, and then the cached accessor is
invoked on each access. If you need to dynamically walk the prototype chain on
every access, add the `structural` attribute!
```js
// This is the default function Rust will invoke on `the_dude.white_russians()`:
const white_russians = Object.getOwnPropertyDescriptor(
TheDude.prototype,
"white_russians"
).get;
// This is what you get by adding `structural`:
const white_russians = function(the_dude) {
return the_dude.white_russians;
};
```

View File

@ -0,0 +1,5 @@
# `#[wasm_bindgen]` on JavaScript Imports
This section enumerates the attributes available for customizing bindings for
JavaScript functions and classes imported into Rust within an `extern { ... }`
block.

View File

@ -0,0 +1,20 @@
# `js_class = "Blah"`
The `js_class` attribute can be used in conjunction with the `method` attribute
to bind methods of imported JavaScript classes that have been renamed on the
Rust side.
```rust
#[wasm_bindgen]
extern {
// We don't want to import JS strings as `String`, since Rust already has a
// `String` type in its prelude, so rename it as `JsString`.
#[wasm_bindgen(js_name = String)]
type JsString;
// This is a method on the JavaScript "String" class, so specify that with
// the `js_class` attribute.
#[wasm_bindgen(method, js_class = "String", js_name = charAt)]
fn char_at(this: &JsString, index: u32) -> JsString;
}
```

View File

@ -0,0 +1,46 @@
# `js_name = blah`
The `js_name` attribute can be used to bind to a different function in
JavaScript than the identifier that's defined in Rust.
Most often, this is used to convert a camel-cased JavaScript identifier into a
snake-cased Rust identifier:
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_name = jsOftenUsesCamelCase)]
fn js_often_uses_camel_case() -> u32;
}
```
Sometimes, it is used to bind to JavaScript identifiers that are not valid Rust
identifiers, in which case `js_name = "some string"` is used instead of `js_name
= ident`:
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_name = "$$$")]
fn cash_money() -> u32;
}
```
However, you can also use `js_name` to define multiple signatures for
polymorphic JavaScript functions:
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn console_log_str(s: &str);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn console_log_u32(n: u32);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn console_log_many(a: u32, b: &JsValue);
}
```
All of these functions will call `console.log` in JavaScript, but each
identifier will have only one signature in Rust.

View File

@ -0,0 +1,21 @@
# `js_namespace = blah`
This attribute indicates that the JavaScript type is accessed through the given
namespace. For example, the `WebAssembly.Module` APIs are all accessed through
the `WebAssembly` namespace. `js_namespace` can be applied to any import
(function or type) and whenever the generated JavaScript attempts to reference a
name (like a class or function name) it'll be accessed through this namespace.
```rust
#[wasm_bindgen]
extern {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
log("hello, console!");
```
This is an example of how to bind `console.log` in Rust. The `log` function will
be available in the Rust module and will be invoked as `console.log` in
JavaScript.

View File

@ -0,0 +1,26 @@
# `method`
The `method` attribute allows you to describe methods of imported JavaScript
objects. It is applied on a function that has `this` as its first parameter,
which is a shared reference to an imported JavaScript type.
```rust
#[wasm_bindgen]
extern {
type Set;
#[wasm_bindgen(method)]
fn has(this: &Set, element: &JsValue) -> bool;
}
```
This generates a `has` method on `Set` in Rust, which invokes the
`Set.prototype.has` method in JavaScript.
```rust
let set: Set = ...;
let elem: JsValue = ...;
if set.has(&elem) {
...
}
```

View File

@ -0,0 +1,33 @@
# `module = "blah"`
The `module` attributes configures the module from which items are imported. For
example,
```rust
#[wasm_bindgen(module = "wu/tang/clan")]
extern {
type ThirtySixChambers;
}
```
generates JavaScript import glue like:
```js
import { ThirtySixChambers } from "wu/tang/clan";
```
If a `module` attribute is not present, then the global scope is used
instead. For example,
```rust
#[wasm_bindgen]
extern {
fn illmatic() -> u32;
}
```
generates JavaScript import glue like:
```js
let illmatic = this.illmatic;
```

View File

@ -0,0 +1,25 @@
# `static_method_of = Blah`
The `static_method_of` attribute allows one to specify that an imported function
is a static method of the given imported JavaScript class. For example, to bind
to JavaScript's `Date.now()` static method, one would use this attribute:
```rust
#[wasm_bindgen]
extern {
type Date;
#[wasm_bindgen(static_method_of = Date)]
pub fn now() -> f64;
}
```
The `now` function becomes a static method of the imported type in the Rust
bindings as well:
```rust
let instant = Date::now();
```
This is similar to the `js_namespace` attribute, but the usage from within Rust
is different since the method also becomes a static method of the imported type.

View File

@ -0,0 +1,50 @@
# `structural`
The `structural` flag can be added to `method` annotations, indicating that the
method being accessed (or property with getters/setters) should be accessed in a
structural, duck-type-y fashion. Rather than walking the constructor's prototype
chain once at load time and caching the property result, the prototype chain is
dynamically walked on every access.
```rust
#[wasm_bindgen]
extern {
type Duck;
#[wasm_bindgen(method, structural)]
fn quack(this: &Duck);
#[wasm_bindgen(method, getter, structural)]
fn is_swimming(this: &Duck) -> bool;
}
```
The constructor for the type here, `Duck`, is not required to exist in
JavaScript (it's not referenced). Instead `wasm-bindgen` will generate shims
that will access the passed in JavaScript value's `quack` method or its
`is_swimming` property.
```js
// Without `structural`, get the method directly off the prototype at load time:
const Duck_prototype_quack = Duck.prototype.quack;
function quack(duck) {
Duck_prototype_quack.call(duck);
}
// With `structural`, walk the prototype chain on every access:
function quack(duck) {
duck.quack();
}
```
## Why don't we always use the `structural` behavior?
In theory, it is faster since the prototype chain doesn't need to be traversed
every time the method or property is accessed, but today's optimizing JIT
compilers are really good about eliminating that cost. The real reason is to be
future compatible with the ["host bindings" proposal][host-bindings], which
requires that there be no JavaScript shim between the caller and the native host
function. In this scenario, the properties and methods *must* be resolved before
the wasm is instantiated.
[host-bindings]: https://github.com/WebAssembly/host-bindings/blob/master/proposals/host-bindings/Overview.md

View File

@ -0,0 +1,34 @@
# `constructor`
When attached to a Rust "constructor" it will make the generated JavaScript
bindings callable as `new Foo()`.
For example, consider this exported Rust type and `constructor` annotation:
```rust
#[wasm_bindgen]
pub struct Foo {
contents: u32,
}
#[wasm_bindgen]
impl Foo {
#[wasm_bindgen(constructor)]
pub fn new() -> Foo {
Foo { contents: 0 }
}
pub fn get_contents(&self) -> u32 {
self.contents
}
}
```
This can be used in JavaScript as:
```js
import { Foo } from './my_module';
const f = new Foo();
console.log(f.get_contents());
```

View File

@ -0,0 +1 @@
# `#[wasm_bindgen]` on Rust Exports

View File

@ -0,0 +1,24 @@
# `js_name = Blah`
The `js_name` attribute can be used to export a different name in JS than what
something is named in Rust. It can be applied to both exported Rust functions
and types.
For example, this is often used to convert between Rust's snake-cased
identifiers into JavaScript's camel-cased identifiers:
```rust
#[wasm_bindgen(js_name = doTheThing)]
pub fn do_the_thing() -> u32 {
42
}
```
This can be used in JavaScript as:
```js
import { doTheThing } from './my_module';
const x = doTheThing();
console.log(x);
```

View File

@ -0,0 +1,39 @@
# `readonly`
When attached to a `pub` struct field this indicates that it's read-only from
JavaScript, and a setter will not be generated and exported to JavaScript.
```rust
#[wasm_bindgen]
pub fn make_foo() -> Foo {
Foo {
first: 10,
second: 20,
}
}
#[wasm_bindgen]
pub struct Foo {
pub first: u32,
#[wasm_bindgen(readonly)]
pub second: u32,
}
```
Here the `first` field will be both readable and writable from JS, but the
`second` field will be a `readonly` field in JS where the setter isn't
implemented and attempting to set it will throw an exception.
```js
import { make_foo } from "./my_module";
const foo = make_foo();
// Can both get and set `first`.
foo.first = 99;
console.log(foo.first);
// Can only get `second`.
console.log(foo.second);
```