diff --git a/Cargo.lock b/Cargo.lock index e4bea38be3..f3820ae53b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2598,6 +2598,7 @@ dependencies = [ "proc-macro2", "quote", "syn", + "wiggle-generate", "witx", ] diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 12d1d8d89c..ab52d3270c 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -6,7 +6,9 @@ pub use wasi_common::{WasiCtx, WasiCtxBuilder}; // Defines a `struct Wasi` with member fields and appropriate APIs for dealing // with all the various WASI exports. -wasmtime_wiggle::define_struct_for_wiggle!("phases/snapshot/witx/wasi_snapshot_preview1.witx"); +wasmtime_wiggle::define_struct_for_wiggle!({ + witx: ["../wasi-common/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"], +}); pub fn is_wasi_module(name: &str) -> bool { // FIXME: this should be more conservative, but while WASI is in flux and diff --git a/crates/wiggle/wasmtime/macro/Cargo.toml b/crates/wiggle/wasmtime/macro/Cargo.toml index 5dc2c53bb4..30f46f2d9f 100644 --- a/crates/wiggle/wasmtime/macro/Cargo.toml +++ b/crates/wiggle/wasmtime/macro/Cargo.toml @@ -16,6 +16,7 @@ test = false [dependencies] witx = { path = "../../../wasi-common/WASI/tools/witx", version = "0.8.5" } +wiggle-generate = { path = "../../generate", version = "0.18.0" } quote = "1.0" syn = { version = "1.0", features = ["full"] } proc-macro2 = "1.0" diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs new file mode 100644 index 0000000000..5834980293 --- /dev/null +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -0,0 +1,82 @@ +use { + proc_macro2::Span, + syn::{ + braced, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Error, Result, Token, + }, + wiggle_generate::config::WitxConf, +}; + +#[derive(Debug, Clone)] +pub struct Config { + pub witx: WitxConf, +} + +#[derive(Debug, Clone)] +pub enum ConfigField { + Witx(WitxConf), +} + +mod kw { + syn::custom_keyword!(witx); + syn::custom_keyword!(witx_literal); +} + +impl Parse for ConfigField { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(kw::witx) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::Witx(WitxConf::Paths(input.parse()?))) + } else if lookahead.peek(kw::witx_literal) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?))) + } else { + Err(lookahead.error()) + } + } +} + +impl Config { + pub fn build(fields: impl Iterator, err_loc: Span) -> Result { + let mut witx = None; + for f in fields { + match f { + ConfigField::Witx(c) => { + if witx.is_some() { + return Err(Error::new(err_loc, "duplicate `witx` field")); + } + witx = Some(c); + } + } + } + Ok(Config { + witx: witx + .take() + .ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, + }) + } + + /// Load the `witx` document for the configuration. + /// + /// # Panics + /// + /// This method will panic if the paths given in the `witx` field were not valid documents. + pub fn load_document(&self) -> witx::Document { + self.witx.load_document() + } +} + +impl Parse for Config { + fn parse(input: ParseStream) -> Result { + let contents; + let _lbrace = braced!(contents in input); + let fields: Punctuated = + contents.parse_terminated(ConfigField::parse)?; + Ok(Config::build(fields.into_iter(), input.span())?) + } +} diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 466dcc7684..e145853a6d 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -1,12 +1,19 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; +use syn::parse_macro_input; +mod config; mod utils; #[proc_macro] pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { - inner(TokenStream2::from(args)).into() + let mut config = parse_macro_input!(args as config::Config); + config.witx.make_paths_relative_to( + std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env var"), + ); + let doc = config.load_document(); + generate(&doc).into() } enum Abi { @@ -16,15 +23,7 @@ enum Abi { F64, } -fn inner(args: TokenStream2) -> TokenStream2 { - let path = utils::witx_path_from_args(args); - let doc = match witx::load(&[&path]) { - Ok(doc) => doc, - Err(e) => { - panic!("error opening file {}: {}", path.display(), e); - } - }; - +fn generate(doc: &witx::Document) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); let mut ctor_externs = Vec::new(); diff --git a/crates/wiggle/wasmtime/macro/src/utils.rs b/crates/wiggle/wasmtime/macro/src/utils.rs index cc1a9a6e00..d0bd3b334a 100644 --- a/crates/wiggle/wasmtime/macro/src/utils.rs +++ b/crates/wiggle/wasmtime/macro/src/utils.rs @@ -1,51 +1,4 @@ - -use proc_macro2::{Ident, Literal, TokenStream, TokenTree}; -use std::path::PathBuf; - -/// Given the input tokens to a macro invocation, return the path to the -/// witx file to process. -pub(crate) fn witx_path_from_args(args: TokenStream) -> PathBuf { - let mut strings = Vec::new(); - - for arg in args { - if let TokenTree::Literal(literal) = arg { - let parsed = parse_string_literal(literal); - - strings.push(parsed); - } else { - panic!("arguments must be string literals"); - } - } - - if strings.len() != 1 { - panic!("expected one string literals"); - } - let root = PathBuf::from(std::env::var("WASI_ROOT").unwrap()); - return root.join(&strings[0]); -} - -// Convert a `Literal` holding a string literal into the `String`. -// -// FIXME: It feels like there should be an easier way to do this. -fn parse_string_literal(literal: Literal) -> String { - let s = literal.to_string(); - assert!( - s.starts_with('"') && s.ends_with('"'), - "string literal must be enclosed in double-quotes" - ); - - let trimmed = s[1..s.len() - 1].to_owned(); - assert!( - !trimmed.contains('"'), - "string literal must not contain embedded quotes for now" - ); - assert!( - !trimmed.contains('\\'), - "string literal must not contain embedded backslashes for now" - ); - - trimmed -} +use proc_macro2::Ident; pub fn param_name(param: &witx::InterfaceFuncParam) -> Ident { quote::format_ident!(