1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
//! [![github]](https://github.com/dtolnay/watt) [![crates-io]](https://crates.io/crates/watt) [![docs-rs]](https://docs.rs/watt)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
//!
//! <br>
//!
//! # Watt
//!
//! Watt is a runtime for executing Rust procedural macros compiled as
//! WebAssembly.
//!
//! <br>
//!
//! # Rationale
//!
//! - **Faster compilation.**&emsp;By compiling macros ahead-of-time to Wasm, we
//!   save all downstream users of the macro from having to compile the macro
//!   logic or its dependencies themselves.
//!
//!   Instead, what they compile is a small self-contained Wasm runtime (~3
//!   seconds, shared by all macros) and a tiny proc macro shim for each macro
//!   crate to hand off Wasm bytecode into the Watt runtime (~0.3 seconds per
//!   proc-macro crate you depend on). This is much less than the 20+ seconds it
//!   can take to compile complex procedural macros and their dependencies.
//!
//! - **Isolation.**&emsp;The Watt runtime is 100% safe code with zero
//!   dependencies. While running in this environment, a macro's *only possible
//!   interaction with the world* is limited to consuming tokens and producing
//!   tokens. This is true regardless of how much unsafe code the macro itself
//!   might contain! Modulo bugs in the Rust compiler or standard library, it is
//!   impossible for a macro to do anything other than shuffle tokens around.
//!
//! - **Determinism.**&emsp;From a build system point of view, a macro backed by
//!   Wasm has the advantage that it can be treated as a purely deterministic
//!   function from input to output. There is no possibility of implicit
//!   dependencies, such as via the filesystem, which aren't visible to or taken
//!   into account by the build system.
//!
//! <br>
//!
//! # 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:
//!
//! ```
//! # const IGNORE: &str = stringify! {
//! 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:
//!
//! ```
//! # const IGNORE: &str = stringify! {
//! 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
//! [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:
//!
//! ```
//! # const IGNORE: &str = stringify! {
//! use proc_macro::TokenStream;
//! use watt::WasmMacro;
//!
//! static MACRO: WasmMacro = WasmMacro::new(WASM);
//! static WASM: &[u8] = include_bytes!("my_macros.wasm");
//!
//! #[proc_macro]
//! pub fn the_macro(input: TokenStream) -> TokenStream {
//!     MACRO.proc_macro("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!
//!
//! <br>
//!
//! # Remaining work
//!
//! - **Performance.**&emsp;Watt compiles pretty fast, but so far I have not put
//!   any effort toward optimizing the runtime. That means macro expansion can
//!   potentially take longer than with a natively compiled proc macro.
//!
//!   Note that the performance overhead of the Wasm environment is partially
//!   offset by the fact that our proc macros are compiled to Wasm in release
//!   mode, so downstream `cargo build` will be running a release-mode macro
//!   when it would have been running debug-mode for a traditional proc macro.
//!
//!   A neat approach would be to provide some kind of `cargo install
//!   watt-runtime` which installs an optimized Wasm runtime locally, which the
//!   Watt crate can detect and hand off code to if available. That way we avoid
//!   running things in a debug-mode runtime altogether. The experimental
//!   beginnings of this can be found under the [jit/] directory.
//!
//! - **Tooling.**&emsp;The getting started section shows there are a lot of
//!   steps to building a macro for Watt, and a pretty hacky patching in of
//!   proc-macro2. Ideally this would all be more straightforward, including
//!   easy tooling for doing reproducible builds of the Wasm artifact for
//!   confirming that it was indeed compiled from the publicly available
//!   sources.
//!
//! - **RFCs.**&emsp;The advantages of fast compile time, isolation, and
//!   determinism may make it worthwhile to build first-class support for Wasm
//!   proc macros into rustc and Cargo. The toolchain could ship its own high
//!   performance Wasm runtime, which is an even better outcome than Watt
//!   because that runtime can be heavily optimized and consumers of macros
//!   don't need to compile it.
//!
//! [jit/]: https://github.com/dtolnay/watt/tree/master/jit
//!
//! <br>
//!
//! # 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.
//!
//! [Rust-WASM]: https://github.com/yblein/rust-wasm
#![deny(rust_2018_idioms)]

extern crate proc_macro;

#[cfg(not(jit))]
#[path = "interpret.rs"]
mod exec;

#[cfg(not(jit))]
#[path = "../runtime/src/lib.rs"]
mod runtime;

#[cfg(jit)]
#[path = "jit.rs"]
mod exec;

#[cfg(jit)]
#[path = "../jit/src/lib.rs"]
mod runtime;

mod data;
mod decode;
mod encode;
mod import;
mod sym;

use proc_macro::TokenStream;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};

/// An instantiation of a WebAssembly module used to invoke procedural macro
/// methods on the wasm module.
///
///
/// # Examples
///
/// ```
/// # const IGNORE: &str = stringify! {
/// static MACRO: WasmMacro = WasmMacro::new(WASM);
/// static WASM: &[u8] = include_bytes!("my_macros.wasm");
/// # };
/// ```
pub struct WasmMacro<'bytecode> {
    wasm: &'bytecode [u8],
    id: AtomicUsize,
}

impl<'bytecode> WasmMacro<'bytecode> {
    /// Creates a new `WasmMacro` from the statically included blob of wasm bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # const IGNORE: &str = stringify! {
    /// static MACRO: WasmMacro = WasmMacro::new(WASM);
    /// static WASM: &[u8] = include_bytes!("my_macros.wasm");
    /// # };
    /// ```
    pub const fn new(wasm: &'bytecode [u8]) -> Self {
        WasmMacro {
            wasm,
            id: AtomicUsize::new(0),
        }
    }

    /// A #\[proc_macro\] implemented in wasm!
    ///
    /// # Canonical macro implementation:
    ///
    /// ```
    /// # const IGNORE: &str = stringify! {
    /// use proc_macro2::TokenStream;
    ///
    /// #[no_mangle]
    /// pub extern "C" fn the_macro(input: TokenStream) -> TokenStream {
    ///     ...
    /// }
    /// # };
    /// ```
    ///
    /// # Canonical entry point:
    ///
    /// ```
    /// # const IGNORE: &str = stringify! {
    /// use proc_macro::TokenStream;
    /// use watt::WasmMacro;
    ///
    /// static MACRO: WasmMacro = WasmMacro::new(WASM);
    /// static WASM: &[u8] = include_bytes!("my_macros.wasm");
    ///
    /// #[proc_macro]
    /// pub fn the_macro(input: TokenStream) -> TokenStream {
    ///     MACRO.proc_macro("the_macro", input)
    /// }
    /// # };
    /// ```
    pub fn proc_macro(&self, fun: &str, input: TokenStream) -> TokenStream {
        exec::proc_macro(fun, vec![input], self)
    }

    /// A #\[proc_macro_derive\] implemented in wasm!
    ///
    /// # Canonical macro implementation:
    ///
    /// ```
    /// # const IGNORE: &str = stringify! {
    /// use proc_macro2::TokenStream;
    ///
    /// #[no_mangle]
    /// pub extern "C" fn the_macro(input: TokenStream) -> TokenStream {
    ///     ...
    /// }
    /// # };
    /// ```
    ///
    /// # Canonical entry point:
    ///
    /// ```
    /// # const IGNORE: &str = stringify! {
    /// use proc_macro::TokenStream;
    /// use watt::WasmMacro;
    ///
    /// static MACRO: WasmMacro = WasmMacro::new(WASM);
    /// static WASM: &[u8] = include_bytes!("my_macros.wasm");
    ///
    /// #[proc_macro_derive(MyDerive)]
    /// pub fn the_macro(input: TokenStream) -> TokenStream {
    ///     MACRO.proc_macro_derive("the_macro", input)
    /// }
    /// # };
    /// ```
    pub fn proc_macro_derive(&self, fun: &str, input: TokenStream) -> TokenStream {
        exec::proc_macro(fun, vec![input], self)
    }

    /// A #\[proc_macro_attribute\] implemented in wasm!
    ///
    /// # Canonical macro implementation:
    ///
    /// ```
    /// # const IGNORE: &str = stringify! {
    /// use proc_macro2::TokenStream;
    ///
    /// #[no_mangle]
    /// pub extern "C" fn the_macro(args: TokenStream, input: TokenStream) -> TokenStream {
    ///     ...
    /// }
    /// # };
    /// ```
    ///
    /// # Canonical entry point:
    ///
    /// ```
    /// # const IGNORE: &str = stringify! {
    /// use proc_macro::TokenStream;
    /// use watt::WasmMacro;
    ///
    /// static MACRO: WasmMacro = WasmMacro::new(WASM);
    /// static WASM: &[u8] = include_bytes!("my_macros.wasm");
    ///
    /// #[proc_macro_attribute]
    /// pub fn the_macro(args: TokenStream, input: TokenStream) -> TokenStream {
    ///     MACRO.proc_macro_attribute("the_macro", args, input)
    /// }
    /// # };
    /// ```
    pub fn proc_macro_attribute(
        &self,
        fun: &str,
        args: TokenStream,
        input: TokenStream,
    ) -> TokenStream {
        exec::proc_macro(fun, vec![args, input], self)
    }

    fn id(&self) -> usize {
        static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
        match self.id.load(SeqCst) {
            0 => {}
            n => return n,
        }
        let id = NEXT_ID.fetch_add(1, SeqCst);
        self.id
            .compare_exchange(0, id, SeqCst, SeqCst)
            .unwrap_or_else(|id| id)
    }
}