Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wiggle/macro/src/lib.rs
1692 views
1
use proc_macro::TokenStream;
2
use quote::quote;
3
use syn::parse_macro_input;
4
5
/// This macro expands to a set of `pub` Rust modules:
6
///
7
/// * The `types` module contains definitions for each `typename` declared in
8
/// the witx document. Type names are translated to the Rust-idiomatic
9
/// CamelCase.
10
///
11
/// * For each `module` defined in the witx document, a Rust module is defined
12
/// containing definitions for that module. Module names are translated to the
13
/// Rust-idiomatic snake\_case.
14
///
15
/// * For each `@interface func` defined in a witx module, an abi-level
16
/// function is generated which takes ABI-level arguments, along with
17
/// a ref that impls the module trait, and a `GuestMemory` implementation.
18
/// Users typically won't use these abi-level functions: Either the
19
/// `wasmtime_integration` macro or the `lucet-wiggle` crates adapt these
20
/// to work with a particular WebAssembly engine.
21
///
22
/// * A public "module trait" is defined (called the module name, in
23
/// SnakeCase) which has a `&self` method for each function in the
24
/// module. These methods takes idiomatic Rust types for each argument
25
/// and return `Result<($return_types),$error_type>`
26
///
27
/// * When the `wiggle` crate is built with the `wasmtime_integration`
28
/// feature, each module contains an `add_to_linker` function to add it to
29
/// a `wasmtime::Linker`.
30
///
31
/// Arguments are provided using Rust struct value syntax.
32
///
33
/// * `witx` takes a list of string literal paths. Paths are relative to the
34
/// CARGO_MANIFEST_DIR of the crate where the macro is invoked. Alternatively,
35
/// `witx_literal` takes a string containing a complete witx document.
36
/// * Optional: `errors` takes a mapping of witx identifiers to types, e.g
37
/// `{ errno => YourErrnoType }`. This allows you to use the `UserErrorConversion`
38
/// trait to map these rich errors into the flat witx type, or to terminate
39
/// WebAssembly execution by trapping.
40
/// * Instead of requiring the user to define an error type, wiggle can
41
/// generate an error type for the user which has conversions to/from
42
/// the base type, and permits trapping, using the syntax
43
/// `errno => trappable AnErrorType`.
44
/// * Optional: `async` takes a set of witx modules and functions which are
45
/// made Rust `async` functions in the module trait.
46
///
47
/// ## Example
48
///
49
/// ```
50
/// use wiggle::GuestPtr;
51
/// wiggle::from_witx!({
52
/// witx_literal: "
53
/// (typename $errno
54
/// (enum (@witx tag u32)
55
/// $ok
56
/// $invalid_arg
57
/// $io
58
/// $overflow))
59
/// (typename $alias_to_float f32)
60
/// (module $example
61
/// (@interface func (export \"int_float_args\")
62
/// (param $an_int u32)
63
/// (param $some_floats (list f32))
64
/// (result $r (expected (error $errno))))
65
/// (@interface func (export \"double_int_return_float\")
66
/// (param $an_int u32)
67
/// (result $r (expected $alias_to_float (error $errno)))))
68
/// ",
69
/// errors: { errno => YourRichError },
70
/// async: { example::double_int_return_float },
71
/// });
72
///
73
/// /// Witx generates a set of traits, which the user must impl on a
74
/// /// type they define. We call this the ctx type. It stores any context
75
/// /// these functions need to execute.
76
/// pub struct YourCtxType {}
77
///
78
/// /// Witx provides a hook to translate "rich" (arbitrary Rust type) errors
79
/// /// into the flat error enums used at the WebAssembly interface. You will
80
/// /// need to impl the `types::UserErrorConversion` trait to provide a translation
81
/// /// from this rich type.
82
/// #[derive(Debug)]
83
/// pub enum YourRichError {
84
/// InvalidArg(String),
85
/// Io(std::io::Error),
86
/// Overflow,
87
/// Trap(String),
88
/// }
89
///
90
/// /// The above witx text contains one module called `$example`. So, we must
91
/// /// implement this one method trait for our ctx type.
92
/// #[wiggle::async_trait]
93
/// /// We specified in the `async_` field that `example::double_int_return_float`
94
/// /// is an asynchronous method. Therefore, we use the `async_trait` proc macro
95
/// /// to define this trait, so that `double_int_return_float` can be an `async fn`.
96
/// /// `wiggle::async_trait` is defined as `#[async_trait::async_trait(?Send)]` -
97
/// /// in wiggle, async methods do not have the Send constraint.
98
/// impl example::Example for YourCtxType {
99
/// /// The arrays module has two methods, shown here.
100
/// /// Note that the `GuestPtr` type comes from `wiggle`,
101
/// /// whereas the witx-defined types like `Excuse` and `Errno` come
102
/// /// from the `pub mod types` emitted by the `wiggle::from_witx!`
103
/// /// invocation above.
104
/// fn int_float_args(&mut self, _int: u32, _floats: &GuestPtr<[f32]>)
105
/// -> Result<(), YourRichError> {
106
/// unimplemented!()
107
/// }
108
/// async fn double_int_return_float(&mut self, int: u32)
109
/// -> Result<f32, YourRichError> {
110
/// Ok(int.checked_mul(2).ok_or(YourRichError::Overflow)? as f32)
111
/// }
112
/// }
113
///
114
/// /// For all types used in the `error` an `expected` in the witx document,
115
/// /// you must implement `GuestErrorType` which tells wiggle-generated
116
/// /// code what value to return when the method returns Ok(...).
117
/// impl wiggle::GuestErrorType for types::Errno {
118
/// fn success() -> Self {
119
/// unimplemented!()
120
/// }
121
/// }
122
///
123
/// /// If you specify a `error` mapping to the macro, you must implement the
124
/// /// `types::UserErrorConversion` for your ctx type as well. This trait gives
125
/// /// you an opportunity to store or log your rich error type, while returning
126
/// /// a basic witx enum to the WebAssembly caller. It also gives you the ability
127
/// /// to terminate WebAssembly execution by trapping.
128
///
129
/// impl types::UserErrorConversion for YourCtxType {
130
/// fn errno_from_your_rich_error(&mut self, e: YourRichError)
131
/// -> Result<types::Errno, wiggle::wasmtime_crate::Error>
132
/// {
133
/// println!("Rich error: {:?}", e);
134
/// match e {
135
/// YourRichError::InvalidArg{..} => Ok(types::Errno::InvalidArg),
136
/// YourRichError::Io{..} => Ok(types::Errno::Io),
137
/// YourRichError::Overflow => Ok(types::Errno::Overflow),
138
/// YourRichError::Trap(s) => Err(wiggle::wasmtime_crate::Error::msg(s)),
139
/// }
140
/// }
141
/// }
142
///
143
/// # fn main() { println!("this fools doc tests into compiling the above outside a function body")
144
/// # }
145
/// ```
146
#[proc_macro]
147
pub fn from_witx(args: TokenStream) -> TokenStream {
148
let config = parse_macro_input!(args as wiggle_generate::Config);
149
150
let doc = config.load_document();
151
152
let settings = wiggle_generate::CodegenSettings::new(
153
&config.errors,
154
&config.async_,
155
&doc,
156
config.wasmtime,
157
&config.tracing,
158
config.mutable,
159
)
160
.expect("validating codegen settings");
161
162
let code = wiggle_generate::generate(&doc, &settings);
163
let metadata = if cfg!(feature = "wiggle_metadata") {
164
wiggle_generate::generate_metadata(&doc)
165
} else {
166
quote!()
167
};
168
169
let mut ret = quote! { #code #metadata };
170
171
if std::env::var("WIGGLE_DEBUG_BINDGEN").is_ok() {
172
use std::path::Path;
173
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
174
static INVOCATION: AtomicUsize = AtomicUsize::new(0);
175
let root = Path::new(env!("DEBUG_OUTPUT_DIR"));
176
let n = INVOCATION.fetch_add(1, Relaxed);
177
let path = root.join(format!("wiggle{n}.rs"));
178
179
std::fs::write(&path, ret.to_string()).unwrap();
180
181
// optimistically format the code but don't require success
182
drop(
183
std::process::Command::new("rustfmt")
184
.arg(&path)
185
.arg("--edition=2021")
186
.output(),
187
);
188
189
let path = path.to_str().unwrap();
190
ret = quote!(include!(#path););
191
}
192
TokenStream::from(ret)
193
}
194
195
#[proc_macro_attribute]
196
pub fn async_trait(attr: TokenStream, item: TokenStream) -> TokenStream {
197
let _ = parse_macro_input!(attr as syn::parse::Nothing);
198
let item = proc_macro2::TokenStream::from(item);
199
TokenStream::from(quote! {
200
#[wiggle::async_trait_crate::async_trait]
201
#item
202
})
203
}
204
205
/// Define the structs required to integrate a Wiggle implementation with Wasmtime.
206
///
207
/// ## Arguments
208
///
209
/// Arguments are provided using struct syntax e.g. `{ arg_name: value }`.
210
///
211
/// * `target`: The path of the module where the Wiggle implementation is defined.
212
#[proc_macro]
213
pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
214
let config = parse_macro_input!(args as wiggle_generate::WasmtimeConfig);
215
let doc = config.c.load_document();
216
217
let settings = wiggle_generate::CodegenSettings::new(
218
&config.c.errors,
219
&config.c.async_,
220
&doc,
221
true,
222
&config.c.tracing,
223
config.c.mutable,
224
)
225
.expect("validating codegen settings");
226
227
let modules = doc.modules().map(|module| {
228
wiggle_generate::wasmtime::link_module(&module, Some(&config.target), &settings)
229
});
230
quote!( #(#modules)* ).into()
231
}
232
233