Path: blob/main/crates/wiggle/macro/src/lib.rs
1692 views
use proc_macro::TokenStream;1use quote::quote;2use syn::parse_macro_input;34/// This macro expands to a set of `pub` Rust modules:5///6/// * The `types` module contains definitions for each `typename` declared in7/// the witx document. Type names are translated to the Rust-idiomatic8/// CamelCase.9///10/// * For each `module` defined in the witx document, a Rust module is defined11/// containing definitions for that module. Module names are translated to the12/// Rust-idiomatic snake\_case.13///14/// * For each `@interface func` defined in a witx module, an abi-level15/// function is generated which takes ABI-level arguments, along with16/// a ref that impls the module trait, and a `GuestMemory` implementation.17/// Users typically won't use these abi-level functions: Either the18/// `wasmtime_integration` macro or the `lucet-wiggle` crates adapt these19/// to work with a particular WebAssembly engine.20///21/// * A public "module trait" is defined (called the module name, in22/// SnakeCase) which has a `&self` method for each function in the23/// module. These methods takes idiomatic Rust types for each argument24/// and return `Result<($return_types),$error_type>`25///26/// * When the `wiggle` crate is built with the `wasmtime_integration`27/// feature, each module contains an `add_to_linker` function to add it to28/// a `wasmtime::Linker`.29///30/// Arguments are provided using Rust struct value syntax.31///32/// * `witx` takes a list of string literal paths. Paths are relative to the33/// CARGO_MANIFEST_DIR of the crate where the macro is invoked. Alternatively,34/// `witx_literal` takes a string containing a complete witx document.35/// * Optional: `errors` takes a mapping of witx identifiers to types, e.g36/// `{ errno => YourErrnoType }`. This allows you to use the `UserErrorConversion`37/// trait to map these rich errors into the flat witx type, or to terminate38/// WebAssembly execution by trapping.39/// * Instead of requiring the user to define an error type, wiggle can40/// generate an error type for the user which has conversions to/from41/// the base type, and permits trapping, using the syntax42/// `errno => trappable AnErrorType`.43/// * Optional: `async` takes a set of witx modules and functions which are44/// made Rust `async` functions in the module trait.45///46/// ## Example47///48/// ```49/// use wiggle::GuestPtr;50/// wiggle::from_witx!({51/// witx_literal: "52/// (typename $errno53/// (enum (@witx tag u32)54/// $ok55/// $invalid_arg56/// $io57/// $overflow))58/// (typename $alias_to_float f32)59/// (module $example60/// (@interface func (export \"int_float_args\")61/// (param $an_int u32)62/// (param $some_floats (list f32))63/// (result $r (expected (error $errno))))64/// (@interface func (export \"double_int_return_float\")65/// (param $an_int u32)66/// (result $r (expected $alias_to_float (error $errno)))))67/// ",68/// errors: { errno => YourRichError },69/// async: { example::double_int_return_float },70/// });71///72/// /// Witx generates a set of traits, which the user must impl on a73/// /// type they define. We call this the ctx type. It stores any context74/// /// these functions need to execute.75/// pub struct YourCtxType {}76///77/// /// Witx provides a hook to translate "rich" (arbitrary Rust type) errors78/// /// into the flat error enums used at the WebAssembly interface. You will79/// /// need to impl the `types::UserErrorConversion` trait to provide a translation80/// /// from this rich type.81/// #[derive(Debug)]82/// pub enum YourRichError {83/// InvalidArg(String),84/// Io(std::io::Error),85/// Overflow,86/// Trap(String),87/// }88///89/// /// The above witx text contains one module called `$example`. So, we must90/// /// implement this one method trait for our ctx type.91/// #[wiggle::async_trait]92/// /// We specified in the `async_` field that `example::double_int_return_float`93/// /// is an asynchronous method. Therefore, we use the `async_trait` proc macro94/// /// to define this trait, so that `double_int_return_float` can be an `async fn`.95/// /// `wiggle::async_trait` is defined as `#[async_trait::async_trait(?Send)]` -96/// /// in wiggle, async methods do not have the Send constraint.97/// impl example::Example for YourCtxType {98/// /// The arrays module has two methods, shown here.99/// /// Note that the `GuestPtr` type comes from `wiggle`,100/// /// whereas the witx-defined types like `Excuse` and `Errno` come101/// /// from the `pub mod types` emitted by the `wiggle::from_witx!`102/// /// invocation above.103/// fn int_float_args(&mut self, _int: u32, _floats: &GuestPtr<[f32]>)104/// -> Result<(), YourRichError> {105/// unimplemented!()106/// }107/// async fn double_int_return_float(&mut self, int: u32)108/// -> Result<f32, YourRichError> {109/// Ok(int.checked_mul(2).ok_or(YourRichError::Overflow)? as f32)110/// }111/// }112///113/// /// For all types used in the `error` an `expected` in the witx document,114/// /// you must implement `GuestErrorType` which tells wiggle-generated115/// /// code what value to return when the method returns Ok(...).116/// impl wiggle::GuestErrorType for types::Errno {117/// fn success() -> Self {118/// unimplemented!()119/// }120/// }121///122/// /// If you specify a `error` mapping to the macro, you must implement the123/// /// `types::UserErrorConversion` for your ctx type as well. This trait gives124/// /// you an opportunity to store or log your rich error type, while returning125/// /// a basic witx enum to the WebAssembly caller. It also gives you the ability126/// /// to terminate WebAssembly execution by trapping.127///128/// impl types::UserErrorConversion for YourCtxType {129/// fn errno_from_your_rich_error(&mut self, e: YourRichError)130/// -> Result<types::Errno, wiggle::wasmtime_crate::Error>131/// {132/// println!("Rich error: {:?}", e);133/// match e {134/// YourRichError::InvalidArg{..} => Ok(types::Errno::InvalidArg),135/// YourRichError::Io{..} => Ok(types::Errno::Io),136/// YourRichError::Overflow => Ok(types::Errno::Overflow),137/// YourRichError::Trap(s) => Err(wiggle::wasmtime_crate::Error::msg(s)),138/// }139/// }140/// }141///142/// # fn main() { println!("this fools doc tests into compiling the above outside a function body")143/// # }144/// ```145#[proc_macro]146pub fn from_witx(args: TokenStream) -> TokenStream {147let config = parse_macro_input!(args as wiggle_generate::Config);148149let doc = config.load_document();150151let settings = wiggle_generate::CodegenSettings::new(152&config.errors,153&config.async_,154&doc,155config.wasmtime,156&config.tracing,157config.mutable,158)159.expect("validating codegen settings");160161let code = wiggle_generate::generate(&doc, &settings);162let metadata = if cfg!(feature = "wiggle_metadata") {163wiggle_generate::generate_metadata(&doc)164} else {165quote!()166};167168let mut ret = quote! { #code #metadata };169170if std::env::var("WIGGLE_DEBUG_BINDGEN").is_ok() {171use std::path::Path;172use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};173static INVOCATION: AtomicUsize = AtomicUsize::new(0);174let root = Path::new(env!("DEBUG_OUTPUT_DIR"));175let n = INVOCATION.fetch_add(1, Relaxed);176let path = root.join(format!("wiggle{n}.rs"));177178std::fs::write(&path, ret.to_string()).unwrap();179180// optimistically format the code but don't require success181drop(182std::process::Command::new("rustfmt")183.arg(&path)184.arg("--edition=2021")185.output(),186);187188let path = path.to_str().unwrap();189ret = quote!(include!(#path););190}191TokenStream::from(ret)192}193194#[proc_macro_attribute]195pub fn async_trait(attr: TokenStream, item: TokenStream) -> TokenStream {196let _ = parse_macro_input!(attr as syn::parse::Nothing);197let item = proc_macro2::TokenStream::from(item);198TokenStream::from(quote! {199#[wiggle::async_trait_crate::async_trait]200#item201})202}203204/// Define the structs required to integrate a Wiggle implementation with Wasmtime.205///206/// ## Arguments207///208/// Arguments are provided using struct syntax e.g. `{ arg_name: value }`.209///210/// * `target`: The path of the module where the Wiggle implementation is defined.211#[proc_macro]212pub fn wasmtime_integration(args: TokenStream) -> TokenStream {213let config = parse_macro_input!(args as wiggle_generate::WasmtimeConfig);214let doc = config.c.load_document();215216let settings = wiggle_generate::CodegenSettings::new(217&config.c.errors,218&config.c.async_,219&doc,220true,221&config.c.tracing,222config.c.mutable,223)224.expect("validating codegen settings");225226let modules = doc.modules().map(|module| {227wiggle_generate::wasmtime::link_module(&module, Some(&config.target), &settings)228});229quote!( #(#modules)* ).into()230}231232233