# wasm-bindgen A project for facilitating high-level interactions between wasm modules and JS. [host]: https://github.com/WebAssembly/host-bindings [![Build Status](https://travis-ci.org/rustwasm/wasm-bindgen.svg?branch=master)](https://travis-ci.org/rustwasm/wasm-bindgen) [![Build status](https://ci.appveyor.com/api/projects/status/559c0lj5oh271u4c?svg=true)](https://ci.appveyor.com/project/alexcrichton/wasm-bindgen) This project is sort of half polyfill for features like the [host bindings proposal][host] and half features for empowering high-level interactions between JS and wasm-compiled code (currently mostly from Rust). More specifically this project allows JS/wasm to communicate with strings, JS objects, classes, etc, as opposed to purely integers and floats. Using `wasm-bindgen` for example you can define a JS class in Rust or take a string from JS or return one. The functionality is growing as well! Currently this tool is Rust-focused but the underlying foundation is language-independent, and it's hoping that over time as this tool stabilizes that it can be used for languages like C/C++! 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, 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 what this crate can do, check out the [design doc]. [design doc]: https://github.com/rustwasm/wasm-bindgen/blob/master/DESIGN.md [dom-ex]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/dom [console-log]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/console_log [perf-ex]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/performance [smorg-ex]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/smorgasboard ## Basic usage Let's implement the equivalent of "Hello, world!" for this crate. > **Note:** Currently this projects uses *nightly Rust* which you can acquire > through [rustup] and configure with `rustup default nightly` [rustup]: https://rustup.rs First up, let's install the tools we need ``` $ rustup target add wasm32-unknown-unknown $ cargo install wasm-bindgen-cli ``` The first command here installs the wasm target so you can compile to it, and the latter will install the `wasm-bindgen` CLI tool we'll be using later. Next up let's make our project ``` $ cargo new js-hello-world --lib ``` Now let's add a dependency on this project inside `Cargo.toml` as well as configuring our build output: ```toml [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2" ``` Next up our actual code! We'll write this in `src/lib.rs`: ```rust #![feature(proc_macro, wasm_custom_section, wasm_import_module)] extern crate wasm_bindgen; use wasm_bindgen::prelude::*; #[wasm_bindgen] extern { fn alert(s: &str); } #[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); } ``` And that's it! If we were to write the `greet` function naively without the `#[wasm_bindgen]` attribute then JS wouldn't be able to communicate with the types like `str`, so slapping a `#[wasm_bindgen]` on the function and the import of `alert` ensures that the right shims are generated. Next up let's build our project: ``` $ cargo build --target wasm32-unknown-unknown ``` After this you'll have a wasm file at `target/wasm32-unknown-unknown/debug/js_hello_world.wasm`. Don't be alarmed at the size, this is an unoptimized program! Now that we've generated the wasm module it's time to run the bindgen tool itself! This tool will postprocess the wasm file rustc generated, generating a new wasm file and a set of JS bindings as well. Let's invoke it! ``` $ wasm-bindgen target/wasm32-unknown-unknown/debug/js_hello_world.wasm \ --out-dir . ``` This is the main point where the magic happens. The `js_hello_world.wasm` file emitted by rustc contains *descriptors* of how to communicate via richer types than wasm currently supports. The `wasm-bindgen` tool will interpret this information, emitting a **replacement module** for the wasm file. The previous `js_hello_world.wasm` file is interpreted as if it were an ES6 module. The `js_hello_world.js` file emitted by `wasm-bindgen` should have the intended interface of the wasm file, notably with rich types like strings, classes, etc. The `wasm-bindgen` tool also emits a few other files needed to implement this module. For example `js_hello_world_bg.wasm` is the original wasm file but postprocessed a bit. It's intended that the `js_hello_world_bg.wasm` file, like before, acts like an ES6 module. At this point you'll probably plug these files into a larger build system. Files emitted by `wasm-bindgen` act like normal ES6 modules (one just happens to be wasm). As of the time of this writing there's unfortunately not a lot of tools that natively do this, but Webpack's 4.0 beta release has native wasm support!. Let's take a look at that and see how it works. First create an `index.js` file: ```js const js = import("./js_hello_world"); js.then(js => { js.greet("World!"); }); ``` Note that we're using `import(..)` here because Webpack [doesn't support][webpack-issue] synchronously importing modules from the main chunk just yet. [webpack-issue]: https://github.com/webpack/webpack/issues/6615 Next our JS dependencies by creating a `package.json`: ```json { "scripts": { "serve": "webpack-dev-server" }, "devDependencies": { "webpack": "^4.0.1", "webpack-cli": "^2.0.10", "webpack-dev-server": "^3.1.0" } } ``` and our webpack configuration ```js // webpack.config.js const path = require('path'); module.exports = { entry: "./index.js", output: { path: path.resolve(__dirname, "dist"), filename: "index.js", }, mode: "development" }; ``` Our corresponding `index.html`: ```html
``` And finally: ``` $ npm run serve ``` If you open https://localhost:8080 in a browser you should see a `Hello, world!` dialog pop up! This works in Firefox out of the box but not in Chrome due to a webpack issue. See [the hello_world README][hello-readme] for a workaround. If that was all a bit much, no worries! You can [follow along online][hello-tree] to see all the files necessary as well as a script to set it all up. [hello-tree]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/hello_world [hello-readme]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/hello_world/README.md ## What just happened? Phew! That was a lot of words and a lot ended up happening along the way. There were two main pieces of magic happening: the `#[wasm_bindgen]` attribute and the `wasm-bindgen` CLI tool. **The `#[wasm_bindgen]` attribute** This attribute, exported from the `wasm-bindgen` crate, is the entrypoint to exposing Rust functions to JS. This is a procedural macro (hence requiring the nightly Rust toolchain) which will generate the appropriate shims in Rust to translate from your type signature to one that JS can interface with. Finally the attribute also serializes some information to the output artifact which `wasm-bindgen`-the-tool will discard after it parses. There's a more thorough explanation below of the various bits and pieces of the attribute, but it suffices for now to say that you can attach it to free functions, structs, impl blocks for those structs and `extern { ... }` blocks. Some Rust features like generics, lifetime parameters, etc, aren't supported on functions tagged with `#[wasm_bindgen]` right now. **The `wasm-bindgen` CLI tool** The next half of what happened here was all in the `wasm-bindgen` tool. This tool opened up the wasm module that rustc generated and found an encoded description of what was passed to the `#[wasm_bindgen]` attribute. You can think of this as the `#[wasm_bindgen]` attribute created a special section of the output module which `wasm-bindgen` strips and processes. This information gave `wasm-bindgen` all it needed to know to generate the JS file that we then imported. The JS file wraps instantiating the underlying wasm module (aka calling `WebAssembly.instantiate`) and then provides wrappers for classes/functions within. ## What else can we do? Much more! Here's a taste of various features you can use in this project: ```rust // src/lib.rs #![feature(proc_macro, wasm_custom_section, wasm_import_module)] extern crate wasm_bindgen; use wasm_bindgen::prelude::*; // Strings can both be passed in and received #[wasm_bindgen] pub fn concat(a: &str, b: &str) -> String { let mut a = a.to_string(); a.push_str(b); return a } // A struct will show up as a class on the JS side of things #[wasm_bindgen] pub struct Foo { contents: u32, } #[wasm_bindgen] impl Foo { pub fn new() -> Foo { Foo { contents: 0 } } // Methods can be defined with `&mut self` or `&self`, and arguments you // can pass to a normal free function also all work in methods. pub fn add(&mut self, amt: u32) -> u32 { self.contents += amt; return self.contents } // You can also take a limited set of references to other types as well. pub fn add_other(&mut self, bar: &Bar) { self.contents += bar.contents; } // Ownership can work too! pub fn consume_other(&mut self, bar: Bar) { self.contents += bar.contents; } } #[wasm_bindgen] pub struct Bar { contents: u32, opaque: JsValue, // defined in `wasm_bindgen`, imported via prelude } #[wasm_bindgen(module = "./index")] // what ES6 module to import from extern { fn bar_on_reset(to: &str, opaque: &JsValue); // We can import classes and annotate functionality on those classes as well type Awesome; #[wasm_bindgen(constructor)] fn new() -> Awesome; #[wasm_bindgen(method)] fn get_internal(this: &Awesome) -> u32; } #[wasm_bindgen] impl Bar { pub fn from_str(s: &str, opaque: JsValue) -> Bar { let contents = s.parse().unwrap_or_else(|_| { Awesome::new().get_internal() }); Bar { contents, opaque } } pub fn reset(&mut self, s: &str) { if let Ok(n) = s.parse() { bar_on_reset(s, &self.opaque); self.contents = n; } } } ``` The generated JS bindings for this invocation of the macro [look like this][bindings]. You can view them in action like so: [bindings]: https://gist.github.com/alexcrichton/3d85c505e785fb8ff32e2c1cf9618367 and our corresponding `index.js`: ```js import { Foo, Bar, concat } from "./js_hello_world"; import { booted } from "./js_hello_world_wasm"; export function bar_on_reset(s, token) { console.log(token); console.log(`this instance of bar was reset to ${s}`); } function assertEq(a, b) { if (a !== b) throw new Error(`${a} != ${b}`); console.log(`found ${a} === ${b}`); } function main() { assertEq(concat('a', 'b'), 'ab'); // Note the `new Foo()` syntax cannot be used, static function // constructors must be used instead. Additionally objects allocated // corresponding to Rust structs will need to be deallocated on the // Rust side of things with an explicit call to `free`. let foo = Foo.new(); assertEq(foo.add(10), 10); foo.free(); // Pass objects to one another let foo1 = Foo.new(); let bar = Bar.from_str("22", { opaque: 'object' }); foo1.add_other(bar); // We also don't have to `free` the `bar` variable as this function is // transferring ownership to `foo1` bar.reset('34'); foo1.consume_other(bar); assertEq(foo1.add(2), 22 + 34 + 2); foo1.free(); alert('all passed!') } export class Awesome { constructor() { this.internal = 32; } get_internal() { return this.internal; } } 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