Watt

github crates.io docs.rs

Watt is a runtime for executing Rust procedural macros compiled as WebAssembly.

toml [dependencies] watt = "0.4"

Compiler support: requires rustc 1.42+


Rationale


Getting started

Start by implementing and testing your proc macro as you normally would, using whatever dependencies you want (syn, quote, etc). You will end up with something that looks like:

```rust use proc_macro::TokenStream;

[proc_macro]

pub fn the_macro(input: TokenStream) -> TokenStream { /* ... */ } ```

#[proc_macro_derive] and #[proc_macro_attribute] are supported as well; everything is analogous to what will be shown here for #[proc_macro].

When your macro is ready, there are just a few changes we need to make to the signature and the Cargo.toml. In your lib.rs, change each of your macro entry points to a no_mangle extern "C" function, and change the TokenStream in the signature from proc_macro to proc_macro2.

It will look like:

```rust use proc_macro2::TokenStream;

[no_mangle]

pub extern "C" fn the_macro(input: TokenStream) -> TokenStream { /* same as before */ } ```

Now in your macro's Cargo.toml which used to contain this:

```toml

my_macros/Cargo.toml

[lib] proc-macro = true ```

change it instead to say:

```toml [lib] crate-type = ["cdylib"]

[patch.crates-io] proc-macro2 = { git = "https://github.com/dtolnay/watt" } ```

This crate will be the binary that we compile to Wasm. Compile it by running:

console $ cargo build --release --target wasm32-unknown-unknown

Next we need to make a small proc-macro shim crate to hand off the compiled Wasm bytes into the Watt runtime. It's fine to give this the same crate name as the previous crate, since the other one won't be getting published to crates.io. In a new Cargo.toml, put:

```toml [lib] proc-macro = true

[dependencies] watt = "0.4" ```

And in its src/lib.rs, define real proc macros corresponding to each of the ones previously defined as no_mangle extern "C" functions in the other crate:

```rust use proc_macro::TokenStream; use watt::WasmMacro;

static MACRO: WasmMacro = WasmMacro::new(WASM); static WASM: &[u8] = includebytes!("mymacros.wasm");

[proc_macro]

pub fn themacro(input: TokenStream) -> TokenStream { MACRO.procmacro("the_macro", input) } ```

Finally, copy the compiled Wasm binary from target/wasm32-unknown-unknown/release/my_macros.wasm under your implementation crate, to the src directory of your shim crate, and it's ready to publish!


Remaining work


This can't be real

To assist in convincing you that this is real, here is serde_derive compiled to Wasm. It was compiled from the commit serde-rs/serde@1afae183. Feel free to try it out as:

```rust // [dependencies] // serde = "1.0" // serde_json = "1.0" // wa-serde-derive = "0.1"

use waserdederive::Serialize;

[derive(Serialize)]

struct Watt { msg: &'static str, }

fn main() { let w = Watt { msg: "hello from wasm!" }; println!("{}", serdejson::tostring(&w).unwrap()); } ```


Acknowledgements

The current underlying Wasm runtime is a fork of the [Rust-WASM] project by Yoann Blein and Hugo Guiroux, a simple and spec-compliant WebAssembly interpreter.


License

Everything outside of the runtime directory is licensed under either of Apache License, Version 2.0 or MIT license at your option. The runtime directory is licensed under the ISC license.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.