From 712990191a1e7c4a2145c50bc91fc2a2717de716 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 17:16:39 -0700 Subject: [PATCH 01/17] create wasmtime-wiggle crate, by copying implementation of `wig` --- Cargo.lock | 20 ++ Cargo.toml | 1 + crates/wiggle/wasmtime/Cargo.toml | 35 +++ crates/wiggle/wasmtime/macro/Cargo.toml | 25 ++ crates/wiggle/wasmtime/macro/src/lib.rs | 279 ++++++++++++++++++++++ crates/wiggle/wasmtime/macro/src/utils.rs | 58 +++++ crates/wiggle/wasmtime/src/lib.rs | 24 ++ 7 files changed, 442 insertions(+) create mode 100644 crates/wiggle/wasmtime/Cargo.toml create mode 100644 crates/wiggle/wasmtime/macro/Cargo.toml create mode 100644 crates/wiggle/wasmtime/macro/src/lib.rs create mode 100644 crates/wiggle/wasmtime/macro/src/utils.rs create mode 100644 crates/wiggle/wasmtime/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7b8c50b2f2..a81dd32301 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2580,6 +2580,26 @@ dependencies = [ "wast 17.0.0", ] +[[package]] +name = "wasmtime-wiggle" +version = "0.18.0" +dependencies = [ + "wasmtime", + "wasmtime-wiggle-macro", + "wiggle", + "witx", +] + +[[package]] +name = "wasmtime-wiggle-macro" +version = "0.18.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "witx", +] + [[package]] name = "wast" version = "11.0.0" diff --git a/Cargo.toml b/Cargo.toml index 147c2d6c5b..b9255545e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ members = [ "crates/misc/run-examples", "crates/misc/rust", "crates/wiggle", + "crates/wiggle/wasmtime", "examples/fib-debug/wasm", "examples/wasi/wasm", "fuzz", diff --git a/crates/wiggle/wasmtime/Cargo.toml b/crates/wiggle/wasmtime/Cargo.toml new file mode 100644 index 0000000000..5983576994 --- /dev/null +++ b/crates/wiggle/wasmtime/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "wasmtime-wiggle" +version = "0.18.0" +authors = ["Pat Hickey ", "Jakub Konka ", "Alex Crichton "] +edition = "2018" +license = "Apache-2.0 WITH LLVM-exception" +description = "Integrate Wiggle code generator with Wasmtime" +categories = ["wasm"] +keywords = ["webassembly", "wasm"] +repository = "https://github.com/bytecodealliance/wasmtime" +include = ["src/**/*", "LICENSE"] + +[dependencies] +wasmtime = { path = "../../wasmtime", version = "0.18.0" } +wasmtime-wiggle-macro = { path = "./macro", version = "0.18.0" } +witx = { path = "../../wasi-common/WASI/tools/witx", version = "0.8.5", optional = true } +wiggle = { path = "..", version = "0.18.0" } + +[badges] +maintenance = { status = "actively-developed" } + +[features] +# The wiggle proc-macro emits some code (inside `pub mod metadata`) guarded +# by the `wiggle_metadata` feature flag. We use this feature flag so that +# users of wiggle are not forced to take a direct dependency on the `witx` +# crate unless they want it. +wiggle_metadata = ['witx', "wiggle/wiggle_metadata"] + +# The `tracing` crate can use the `log` ecosystem of backends with this +# non-default feature. We don't need to provide this by default, but its +# useful for users that don't want to use `tracing-subscriber` to get +# the logs out of wiggle-generated libraries. +tracing_log = [ "wiggle/tracing_log" ] + +default = ["wiggle_metadata" ] diff --git a/crates/wiggle/wasmtime/macro/Cargo.toml b/crates/wiggle/wasmtime/macro/Cargo.toml new file mode 100644 index 0000000000..5dc2c53bb4 --- /dev/null +++ b/crates/wiggle/wasmtime/macro/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "wasmtime-wiggle-macro" +version = "0.18.0" +authors = ["Pat Hickey ", "Jakub Konka ", "Alex Crichton "] +edition = "2018" +license = "Apache-2.0 WITH LLVM-exception" +description = "Macro for integrating Wiggle code generator with Wasmtime" +categories = ["wasm"] +keywords = ["webassembly", "wasm"] +repository = "https://github.com/bytecodealliance/wasmtime" +include = ["src/**/*", "LICENSE"] + +[lib] +proc-macro = true +test = false + +[dependencies] +witx = { path = "../../../wasi-common/WASI/tools/witx", version = "0.8.5" } +quote = "1.0" +syn = { version = "1.0", features = ["full"] } +proc-macro2 = "1.0" + +[badges] +maintenance = { status = "actively-developed" } + diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs new file mode 100644 index 0000000000..466dcc7684 --- /dev/null +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -0,0 +1,279 @@ +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; +use quote::{format_ident, quote}; + +mod utils; + +#[proc_macro] +pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { + inner(TokenStream2::from(args)).into() +} + +enum Abi { + I32, + I64, + F32, + 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); + } + }; + + let mut fields = Vec::new(); + let mut get_exports = Vec::new(); + let mut ctor_externs = Vec::new(); + let mut ctor_fields = Vec::new(); + let mut linker_add = Vec::new(); + + for module in doc.modules() { + let module_name = module.name.as_str(); + let module_id = Ident::new(module_name, Span::call_site()); + for func in module.funcs() { + let name = func.name.as_str(); + let name_ident = Ident::new(name, Span::call_site()); + fields.push(quote! { pub #name_ident: wasmtime::Func }); + get_exports.push(quote! { #name => Some(&self.#name_ident) }); + ctor_fields.push(name_ident.clone()); + linker_add.push(quote! { + linker.define(#module_name, #name, self.#name_ident.clone())?; + }); + // `proc_exit` is special; it's essentially an unwinding primitive, + // so we implement it in the runtime rather than use the implementation + // in wasi-common. + if name == "proc_exit" { + ctor_externs.push(quote! { + let #name_ident = wasmtime::Func::wrap(store, crate::wasi_proc_exit); + }); + continue; + } + + let mut shim_arg_decls = Vec::new(); + let mut params = Vec::new(); + let mut formats = Vec::new(); + let mut format_args = Vec::new(); + let mut hostcall_args = Vec::new(); + + for param in func.params.iter() { + let name = utils::param_name(param); + + // Registers a new parameter to the shim we're making with the + // given `name`, the `abi_ty` wasm type and `hex` defines + // whether it's debug-printed in a hex format or not. + // + // This will register a whole bunch of things: + // + // * The cranelift type for the parameter + // * Syntax to specify the actual function parameter + // * How to log the parameter value in a call to `trace!` + // * How to actually pass this argument to the host + // implementation, converting as necessary. + let mut add_param = |name: &Ident, abi_ty: Abi, hex: bool| { + match abi_ty { + Abi::I32 => { + params.push(quote! { types::I32 }); + shim_arg_decls.push(quote! { #name: i32 }); + } + Abi::I64 => { + params.push(quote! { types::I64 }); + shim_arg_decls.push(quote! { #name: i64 }); + } + Abi::F32 => { + params.push(quote! { types::F32 }); + shim_arg_decls.push(quote! { #name: f32 }); + } + Abi::F64 => { + params.push(quote! { types::F64 }); + shim_arg_decls.push(quote! { #name: f64 }); + } + } + formats.push(format!("{}={}", name, if hex { "{:#x}" } else { "{}" },)); + format_args.push(name.clone()); + hostcall_args.push(quote! { #name as _ }); + }; + + match &*param.tref.type_() { + witx::Type::Int(e) => match e.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64, false), + _ => add_param(&name, Abi::I32, false), + }, + + witx::Type::Enum(e) => match e.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64, false), + _ => add_param(&name, Abi::I32, false), + }, + + witx::Type::Flags(f) => match f.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64, true), + _ => add_param(&name, Abi::I32, true), + }, + + witx::Type::Builtin(witx::BuiltinType::Char8) + | witx::Type::Builtin(witx::BuiltinType::S8) + | witx::Type::Builtin(witx::BuiltinType::U8) + | witx::Type::Builtin(witx::BuiltinType::S16) + | witx::Type::Builtin(witx::BuiltinType::U16) + | witx::Type::Builtin(witx::BuiltinType::S32) + | witx::Type::Builtin(witx::BuiltinType::U32) + | witx::Type::Builtin(witx::BuiltinType::USize) => { + add_param(&name, Abi::I32, false); + } + + witx::Type::Builtin(witx::BuiltinType::S64) + | witx::Type::Builtin(witx::BuiltinType::U64) => { + add_param(&name, Abi::I64, false); + } + + witx::Type::Builtin(witx::BuiltinType::F32) => { + add_param(&name, Abi::F32, false); + } + + witx::Type::Builtin(witx::BuiltinType::F64) => { + add_param(&name, Abi::F64, false); + } + + // strings/arrays have an extra ABI parameter for the length + // of the array passed. + witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { + add_param(&name, Abi::I32, true); + let len = format_ident!("{}_len", name); + add_param(&len, Abi::I32, false); + } + + witx::Type::ConstPointer(_) + | witx::Type::Handle(_) + | witx::Type::Pointer(_) => { + add_param(&name, Abi::I32, true); + } + + witx::Type::Struct(_) | witx::Type::Union(_) => { + panic!("unsupported argument type") + } + } + } + + let mut results = func.results.iter(); + let mut ret_ty = quote! { () }; + let mut cvt_ret = quote! {}; + let mut returns = Vec::new(); + let mut handle_early_error = quote! { panic!("error: {:?}", e) }; + + // The first result is returned bare right now... + if let Some(ret) = results.next() { + handle_early_error = quote! { return e.into() }; + match &*ret.tref.type_() { + // Eventually we'll want to add support for more returned + // types, but for now let's just conform to what `*.witx` + // definitions currently use. + witx::Type::Enum(e) => match e.repr { + witx::IntRepr::U16 => { + returns.push(quote! { types::I32 }); + ret_ty = quote! { i32 }; + cvt_ret = quote! { .into() } + } + other => panic!("unsupported ret enum repr {:?}", other), + }, + other => panic!("unsupported first return {:?}", other), + } + } + + // ... and all remaining results are returned via out-poiners + for result in results { + let name = format_ident!("{}", result.name.as_str()); + params.push(quote! { types::I32 }); + shim_arg_decls.push(quote! { #name: i32 }); + formats.push(format!("{}={{:#x}}", name)); + format_args.push(name.clone()); + hostcall_args.push(quote! { #name }); + } + + let format_str = format!("{}({})", name, formats.join(", ")); + ctor_externs.push(quote! { + let my_cx = cx.clone(); + let #name_ident = wasmtime::Func::wrap( + store, + move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { + log::trace!( + #format_str, + #(#format_args),* + ); + unsafe { + let mem = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + log::warn!("callee does not export a memory as \"memory\""); + let e = wasi_common::wasi::Errno::Inval; + #handle_early_error + } + }; + // Wiggle does not expose any methods for + // functions to re-enter the WebAssembly module, + // or expose the memory via non-wiggle mechanisms. + // Therefore, creating a new BorrowChecker at the + // root of each function invocation is correct. + let bc = wasmtime_wiggle::BorrowChecker::new(); + let mem = wasmtime_wiggle::WasmtimeGuestMemory::new( mem, bc ); + wasi_common::wasi::#module_id::#name_ident( + &mut my_cx.borrow_mut(), + &mem, + #(#hostcall_args),* + ) #cvt_ret + } + } + ); + }); + } + } + + quote! { + /// An instantiated instance of the wasi exports. + /// + /// This represents a wasi module which can be used to instantiate other + /// wasm modules. This structure exports all that various fields of the + /// wasi instance as fields which can be used to implement your own + /// instantiation logic, if necessary. Additionally [`Wasi::get_export`] + /// can be used to do name-based resolution. + pub struct Wasi { + #(#fields,)* + } + + impl Wasi { + /// Creates a new [`Wasi`] instance. + /// + /// External values are allocated into the `store` provided and + /// configuration of the wasi instance itself should be all + /// contained in the `cx` parameter. + pub fn new(store: &wasmtime::Store, cx: WasiCtx) -> Wasi { + let cx = std::rc::Rc::new(std::cell::RefCell::new(cx)); + #(#ctor_externs)* + + Wasi { + #(#ctor_fields,)* + } + } + + /// Looks up a field called `name` in this structure, returning it + /// if found. + /// + /// This is often useful when instantiating a `wasmtime` instance + /// where name resolution often happens with strings. + pub fn get_export(&self, name: &str) -> Option<&wasmtime::Func> { + match name { + #(#get_exports,)* + _ => None, + } + } + + /// Adds all wasi items to the specified `Linker`. + pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> { + #(#linker_add)* + Ok(()) + } + } + } +} diff --git a/crates/wiggle/wasmtime/macro/src/utils.rs b/crates/wiggle/wasmtime/macro/src/utils.rs new file mode 100644 index 0000000000..cc1a9a6e00 --- /dev/null +++ b/crates/wiggle/wasmtime/macro/src/utils.rs @@ -0,0 +1,58 @@ + +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 +} + +pub fn param_name(param: &witx::InterfaceFuncParam) -> Ident { + quote::format_ident!( + "{}", + match param.name.as_str() { + "in" | "type" => format!("r#{}", param.name.as_str()), + s => s.to_string(), + } + ) +} diff --git a/crates/wiggle/wasmtime/src/lib.rs b/crates/wiggle/wasmtime/src/lib.rs new file mode 100644 index 0000000000..bd4d303ed6 --- /dev/null +++ b/crates/wiggle/wasmtime/src/lib.rs @@ -0,0 +1,24 @@ +pub use wasmtime_wiggle_macro::*; +pub use wiggle::*; + +/// Lightweight `wasmtime::Memory` wrapper so we can implement the +/// `wiggle::GuestMemory` trait on it. +pub struct WasmtimeGuestMemory { + mem: wasmtime::Memory, + bc: BorrowChecker, +} + +impl WasmtimeGuestMemory { + pub fn new(mem: wasmtime::Memory, bc: BorrowChecker) -> Self { + Self { mem, bc } + } +} + +unsafe impl GuestMemory for WasmtimeGuestMemory { + fn base(&self) -> (*mut u8, u32) { + (self.mem.data_ptr(), self.mem.data_size() as _) + } + fn borrow_checker(&self) -> &BorrowChecker { + &self.bc + } +} From 303f7172a146c79c83153b7c4800a990a11dee47 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 17:23:11 -0700 Subject: [PATCH 02/17] wasi: switch to use wasmtime-wiggle for wiggle integration --- Cargo.lock | 1 + crates/wasi/Cargo.toml | 1 + crates/wasi/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a81dd32301..e4bea38be3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2567,6 +2567,7 @@ dependencies = [ "wasi-common", "wasmtime", "wasmtime-runtime", + "wasmtime-wiggle", "wig", "wiggle", ] diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 6616a6ef12..229816ebb7 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -19,6 +19,7 @@ wasmtime = { path = "../wasmtime", version = "0.18.0", default-features = false wasmtime-runtime = { path = "../runtime", version = "0.18.0" } wig = { path = "../wasi-common/wig", version = "0.18.0" } wiggle = { path = "../wiggle", version = "0.18.0" } +wasmtime-wiggle = { path = "../wiggle/wasmtime", version = "0.18.0" } [badges] maintenance = { status = "actively-developed" } diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index e92b1d21d9..12d1d8d89c 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -6,7 +6,7 @@ pub use wasi_common::{WasiCtx, WasiCtxBuilder}; // Defines a `struct Wasi` with member fields and appropriate APIs for dealing // with all the various WASI exports. -wig::define_wasi_struct_for_wiggle!("phases/snapshot/witx/wasi_snapshot_preview1.witx"); +wasmtime_wiggle::define_struct_for_wiggle!("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 From 7d3d16aef0f2344a67585e1305d5b9fa8831c4a5 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 17:24:43 -0700 Subject: [PATCH 03/17] wig: delete wiggle integration functionality --- crates/wasi-common/wig/src/lib.rs | 5 - crates/wasi-common/wig/src/wasi.rs | 279 ----------------------------- 2 files changed, 284 deletions(-) diff --git a/crates/wasi-common/wig/src/lib.rs b/crates/wasi-common/wig/src/lib.rs index 1a6688621b..d0fd3f41e0 100644 --- a/crates/wasi-common/wig/src/lib.rs +++ b/crates/wasi-common/wig/src/lib.rs @@ -38,11 +38,6 @@ pub fn define_wasi_struct(args: TokenStream) -> TokenStream { wasi::define_struct(args.into()).into() } -#[proc_macro] -pub fn define_wasi_struct_for_wiggle(args: TokenStream) -> TokenStream { - wasi::define_struct_for_wiggle(args.into()).into() -} - #[proc_macro] pub fn define_hostcalls(args: TokenStream) -> TokenStream { hostcalls::define(args.into()).into() diff --git a/crates/wasi-common/wig/src/wasi.rs b/crates/wasi-common/wig/src/wasi.rs index 479cb2d867..9e31879659 100644 --- a/crates/wasi-common/wig/src/wasi.rs +++ b/crates/wasi-common/wig/src/wasi.rs @@ -273,282 +273,3 @@ pub fn define_struct(args: TokenStream) -> TokenStream { } } } - -pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { - 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); - } - }; - - let mut fields = Vec::new(); - let mut get_exports = Vec::new(); - let mut ctor_externs = Vec::new(); - let mut ctor_fields = Vec::new(); - let mut linker_add = Vec::new(); - - for module in doc.modules() { - let module_name = module.name.as_str(); - let module_id = Ident::new(module_name, Span::call_site()); - for func in module.funcs() { - let name = func.name.as_str(); - let name_ident = Ident::new(name, Span::call_site()); - fields.push(quote! { pub #name_ident: wasmtime::Func }); - get_exports.push(quote! { #name => Some(&self.#name_ident) }); - ctor_fields.push(name_ident.clone()); - linker_add.push(quote! { - linker.define(#module_name, #name, self.#name_ident.clone())?; - }); - // `proc_exit` is special; it's essentially an unwinding primitive, - // so we implement it in the runtime rather than use the implementation - // in wasi-common. - if name == "proc_exit" { - ctor_externs.push(quote! { - let #name_ident = wasmtime::Func::wrap(store, crate::wasi_proc_exit); - }); - continue; - } - - let mut shim_arg_decls = Vec::new(); - let mut params = Vec::new(); - let mut formats = Vec::new(); - let mut format_args = Vec::new(); - let mut hostcall_args = Vec::new(); - - for param in func.params.iter() { - let name = utils::param_name(param); - - // Registers a new parameter to the shim we're making with the - // given `name`, the `abi_ty` wasm type and `hex` defines - // whether it's debug-printed in a hex format or not. - // - // This will register a whole bunch of things: - // - // * The cranelift type for the parameter - // * Syntax to specify the actual function parameter - // * How to log the parameter value in a call to `trace!` - // * How to actually pass this argument to the host - // implementation, converting as necessary. - let mut add_param = |name: &Ident, abi_ty: Abi, hex: bool| { - match abi_ty { - Abi::I32 => { - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - } - Abi::I64 => { - params.push(quote! { types::I64 }); - shim_arg_decls.push(quote! { #name: i64 }); - } - Abi::F32 => { - params.push(quote! { types::F32 }); - shim_arg_decls.push(quote! { #name: f32 }); - } - Abi::F64 => { - params.push(quote! { types::F64 }); - shim_arg_decls.push(quote! { #name: f64 }); - } - } - formats.push(format!("{}={}", name, if hex { "{:#x}" } else { "{}" },)); - format_args.push(name.clone()); - hostcall_args.push(quote! { #name as _ }); - }; - - match &*param.tref.type_() { - witx::Type::Int(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64, false), - _ => add_param(&name, Abi::I32, false), - }, - - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64, false), - _ => add_param(&name, Abi::I32, false), - }, - - witx::Type::Flags(f) => match f.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64, true), - _ => add_param(&name, Abi::I32, true), - }, - - witx::Type::Builtin(witx::BuiltinType::Char8) - | witx::Type::Builtin(witx::BuiltinType::S8) - | witx::Type::Builtin(witx::BuiltinType::U8) - | witx::Type::Builtin(witx::BuiltinType::S16) - | witx::Type::Builtin(witx::BuiltinType::U16) - | witx::Type::Builtin(witx::BuiltinType::S32) - | witx::Type::Builtin(witx::BuiltinType::U32) - | witx::Type::Builtin(witx::BuiltinType::USize) => { - add_param(&name, Abi::I32, false); - } - - witx::Type::Builtin(witx::BuiltinType::S64) - | witx::Type::Builtin(witx::BuiltinType::U64) => { - add_param(&name, Abi::I64, false); - } - - witx::Type::Builtin(witx::BuiltinType::F32) => { - add_param(&name, Abi::F32, false); - } - - witx::Type::Builtin(witx::BuiltinType::F64) => { - add_param(&name, Abi::F64, false); - } - - // strings/arrays have an extra ABI parameter for the length - // of the array passed. - witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { - add_param(&name, Abi::I32, true); - let len = format_ident!("{}_len", name); - add_param(&len, Abi::I32, false); - } - - witx::Type::ConstPointer(_) - | witx::Type::Handle(_) - | witx::Type::Pointer(_) => { - add_param(&name, Abi::I32, true); - } - - witx::Type::Struct(_) | witx::Type::Union(_) => { - panic!("unsupported argument type") - } - } - } - - let mut results = func.results.iter(); - let mut ret_ty = quote! { () }; - let mut cvt_ret = quote! {}; - let mut returns = Vec::new(); - let mut handle_early_error = quote! { panic!("error: {:?}", e) }; - - // The first result is returned bare right now... - if let Some(ret) = results.next() { - handle_early_error = quote! { return e.into() }; - match &*ret.tref.type_() { - // Eventually we'll want to add support for more returned - // types, but for now let's just conform to what `*.witx` - // definitions currently use. - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U16 => { - returns.push(quote! { types::I32 }); - ret_ty = quote! { i32 }; - cvt_ret = quote! { .into() } - } - other => panic!("unsupported ret enum repr {:?}", other), - }, - other => panic!("unsupported first return {:?}", other), - } - } - - // ... and all remaining results are returned via out-poiners - for result in results { - let name = format_ident!("{}", result.name.as_str()); - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - formats.push(format!("{}={{:#x}}", name)); - format_args.push(name.clone()); - hostcall_args.push(quote! { #name }); - } - - let format_str = format!("{}({})", name, formats.join(", ")); - ctor_externs.push(quote! { - let my_cx = cx.clone(); - let #name_ident = wasmtime::Func::wrap( - store, - move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { - log::trace!( - #format_str, - #(#format_args),* - ); - unsafe { - let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, - _ => { - log::warn!("callee does not export a memory as \"memory\""); - let e = wasi_common::wasi::Errno::Inval; - #handle_early_error - } - }; - // Wiggle does not expose any methods for - // functions to re-enter the WebAssembly module, - // or expose the memory via non-wiggle mechanisms. - // Therefore, creating a new BorrowChecker at the - // root of each function invocation is correct. - let bc = wiggle::BorrowChecker::new(); - let mem = WasiMemory { mem, bc }; - wasi_common::wasi::#module_id::#name_ident( - &mut my_cx.borrow_mut(), - &mem, - #(#hostcall_args),* - ) #cvt_ret - } - } - ); - }); - } - } - - quote! { - /// Lightweight `wasmtime::Memory` wrapper so that we can - /// implement `wiggle::GuestMemory` trait on it which is - /// now required to interface with `wasi-common`. - struct WasiMemory { - mem: wasmtime::Memory, - bc: wiggle::BorrowChecker, - } - - unsafe impl wiggle::GuestMemory for WasiMemory { - fn base(&self) -> (*mut u8, u32) { - (self.mem.data_ptr(), self.mem.data_size() as _) - } - fn borrow_checker(&self) -> &wiggle::BorrowChecker { - &self.bc - } - } - - /// An instantiated instance of the wasi exports. - /// - /// This represents a wasi module which can be used to instantiate other - /// wasm modules. This structure exports all that various fields of the - /// wasi instance as fields which can be used to implement your own - /// instantiation logic, if necessary. Additionally [`Wasi::get_export`] - /// can be used to do name-based resolution. - pub struct Wasi { - #(#fields,)* - } - - impl Wasi { - /// Creates a new [`Wasi`] instance. - /// - /// External values are allocated into the `store` provided and - /// configuration of the wasi instance itself should be all - /// contained in the `cx` parameter. - pub fn new(store: &wasmtime::Store, cx: WasiCtx) -> Wasi { - let cx = std::rc::Rc::new(std::cell::RefCell::new(cx)); - #(#ctor_externs)* - - Wasi { - #(#ctor_fields,)* - } - } - - /// Looks up a field called `name` in this structure, returning it - /// if found. - /// - /// This is often useful when instantiating a `wasmtime` instance - /// where name resolution often happens with strings. - pub fn get_export(&self, name: &str) -> Option<&wasmtime::Func> { - match name { - #(#get_exports,)* - _ => None, - } - } - - /// Adds all wasi items to the specified `Linker`. - pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> { - #(#linker_add)* - Ok(()) - } - } - } -} From 185701df1bc387fcd1eb9cc817fe57d0051c1c49 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 18:39:33 -0700 Subject: [PATCH 04/17] wasmtime-wiggle-macro: adopt config system from wiggle-generate --- Cargo.lock | 1 + crates/wasi/src/lib.rs | 4 +- crates/wiggle/wasmtime/macro/Cargo.toml | 1 + crates/wiggle/wasmtime/macro/src/config.rs | 82 ++++++++++++++++++++++ crates/wiggle/wasmtime/macro/src/lib.rs | 19 +++-- crates/wiggle/wasmtime/macro/src/utils.rs | 49 +------------ 6 files changed, 97 insertions(+), 59 deletions(-) create mode 100644 crates/wiggle/wasmtime/macro/src/config.rs 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!( From 1050c6d99cc8c5a06f0ca53c5b70b7a49810d2f4 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 18:47:21 -0700 Subject: [PATCH 05/17] wasmtime-wiggle-macro: re-use Names functionality rather than try to duplicate it in utils --- crates/wasi/src/lib.rs | 1 + crates/wiggle/wasmtime/macro/src/config.rs | 19 ++++++++++++++++++- crates/wiggle/wasmtime/macro/src/lib.rs | 10 ++++++---- crates/wiggle/wasmtime/macro/src/utils.rs | 11 ----------- 4 files changed, 25 insertions(+), 16 deletions(-) delete mode 100644 crates/wiggle/wasmtime/macro/src/utils.rs diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index ab52d3270c..ff04a257d6 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -8,6 +8,7 @@ pub use wasi_common::{WasiCtx, WasiCtxBuilder}; // with all the various WASI exports. wasmtime_wiggle::define_struct_for_wiggle!({ witx: ["../wasi-common/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"], + ctx: WasiCtx, }); pub fn is_wasi_module(name: &str) -> bool { diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index 5834980293..82839dd360 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -6,22 +6,25 @@ use { punctuated::Punctuated, Error, Result, Token, }, - wiggle_generate::config::WitxConf, + wiggle_generate::config::{CtxConf, WitxConf}, }; #[derive(Debug, Clone)] pub struct Config { pub witx: WitxConf, + pub ctx: CtxConf, } #[derive(Debug, Clone)] pub enum ConfigField { Witx(WitxConf), + Ctx(CtxConf), } mod kw { syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); + syn::custom_keyword!(ctx); } impl Parse for ConfigField { @@ -35,6 +38,10 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?))) + } else if lookahead.peek(kw::ctx) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::Ctx(input.parse()?)) } else { Err(lookahead.error()) } @@ -44,6 +51,7 @@ impl Parse for ConfigField { impl Config { pub fn build(fields: impl Iterator, err_loc: Span) -> Result { let mut witx = None; + let mut ctx = None; for f in fields { match f { ConfigField::Witx(c) => { @@ -52,12 +60,21 @@ impl Config { } witx = Some(c); } + ConfigField::Ctx(c) => { + if ctx.is_some() { + return Err(Error::new(err_loc, "duplicate `ctx` field")); + } + ctx = Some(c); + } } } Ok(Config { witx: witx .take() .ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, + ctx: ctx + .take() + .ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, }) } diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index e145853a6d..2ff7e08d06 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -2,9 +2,9 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::parse_macro_input; +use wiggle_generate::Names; mod config; -mod utils; #[proc_macro] pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { @@ -13,7 +13,9 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env var"), ); let doc = config.load_document(); - generate(&doc).into() + let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle)); + + generate(&doc, &names).into() } enum Abi { @@ -23,7 +25,7 @@ enum Abi { F64, } -fn generate(doc: &witx::Document) -> TokenStream2 { +fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); let mut ctor_externs = Vec::new(); @@ -59,7 +61,7 @@ fn generate(doc: &witx::Document) -> TokenStream2 { let mut hostcall_args = Vec::new(); for param in func.params.iter() { - let name = utils::param_name(param); + let name = names.func_param(¶m.name); // Registers a new parameter to the shim we're making with the // given `name`, the `abi_ty` wasm type and `hex` defines diff --git a/crates/wiggle/wasmtime/macro/src/utils.rs b/crates/wiggle/wasmtime/macro/src/utils.rs deleted file mode 100644 index d0bd3b334a..0000000000 --- a/crates/wiggle/wasmtime/macro/src/utils.rs +++ /dev/null @@ -1,11 +0,0 @@ -use proc_macro2::Ident; - -pub fn param_name(param: &witx::InterfaceFuncParam) -> Ident { - quote::format_ident!( - "{}", - match param.name.as_str() { - "in" | "type" => format!("r#{}", param.name.as_str()), - s => s.to_string(), - } - ) -} From 09971bc09051f5385c22f862fddddff30a6f86a5 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 18:54:07 -0700 Subject: [PATCH 06/17] replace more custom idents with Names --- crates/wiggle/wasmtime/macro/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 2ff7e08d06..f709eb177c 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; +use proc_macro2::{Ident, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::parse_macro_input; use wiggle_generate::Names; @@ -32,12 +32,14 @@ fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { let mut ctor_fields = Vec::new(); let mut linker_add = Vec::new(); + let runtime = names.runtime_mod(); + for module in doc.modules() { let module_name = module.name.as_str(); - let module_id = Ident::new(module_name, Span::call_site()); + let module_id = names.module(&module.name); for func in module.funcs() { let name = func.name.as_str(); - let name_ident = Ident::new(name, Span::call_site()); + let name_ident = names.func(&func.name); fields.push(quote! { pub #name_ident: wasmtime::Func }); get_exports.push(quote! { #name => Some(&self.#name_ident) }); ctor_fields.push(name_ident.clone()); @@ -217,8 +219,8 @@ fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { // or expose the memory via non-wiggle mechanisms. // Therefore, creating a new BorrowChecker at the // root of each function invocation is correct. - let bc = wasmtime_wiggle::BorrowChecker::new(); - let mem = wasmtime_wiggle::WasmtimeGuestMemory::new( mem, bc ); + let bc = #runtime::BorrowChecker::new(); + let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); wasi_common::wasi::#module_id::#name_ident( &mut my_cx.borrow_mut(), &mem, @@ -231,6 +233,8 @@ fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { } } + let ctx_type = names.ctx_type(); + quote! { /// An instantiated instance of the wasi exports. /// @@ -249,7 +253,7 @@ fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { /// External values are allocated into the `store` provided and /// configuration of the wasi instance itself should be all /// contained in the `cx` parameter. - pub fn new(store: &wasmtime::Store, cx: WasiCtx) -> Wasi { + pub fn new(store: &wasmtime::Store, cx: #ctx_type) -> Wasi { let cx = std::rc::Rc::new(std::cell::RefCell::new(cx)); #(#ctor_externs)* From 6adbae3007aa69d76b279758808ffd770a6c8107 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 19:01:29 -0700 Subject: [PATCH 07/17] paramaterize the instance type name as well --- crates/wasi/src/lib.rs | 1 + crates/wiggle/wasmtime/macro/src/config.rs | 32 +++++++++++++++++++++- crates/wiggle/wasmtime/macro/src/lib.rs | 23 ++++++++++------ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index ff04a257d6..73e5481fbc 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -9,6 +9,7 @@ pub use wasi_common::{WasiCtx, WasiCtxBuilder}; wasmtime_wiggle::define_struct_for_wiggle!({ witx: ["../wasi-common/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"], ctx: WasiCtx, + instance: Wasi, }); pub fn is_wasi_module(name: &str) -> bool { diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index 82839dd360..c9783925f2 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -4,7 +4,7 @@ use { braced, parse::{Parse, ParseStream}, punctuated::Punctuated, - Error, Result, Token, + Error, Ident, Result, Token, }, wiggle_generate::config::{CtxConf, WitxConf}, }; @@ -13,18 +13,21 @@ use { pub struct Config { pub witx: WitxConf, pub ctx: CtxConf, + pub instance_typename: InstanceTypenameConf, } #[derive(Debug, Clone)] pub enum ConfigField { Witx(WitxConf), Ctx(CtxConf), + InstanceTypename(InstanceTypenameConf), } mod kw { syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); syn::custom_keyword!(ctx); + syn::custom_keyword!(instance); } impl Parse for ConfigField { @@ -42,6 +45,10 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::Ctx(input.parse()?)) + } else if lookahead.peek(kw::instance) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::InstanceTypename(input.parse()?)) } else { Err(lookahead.error()) } @@ -52,6 +59,7 @@ impl Config { pub fn build(fields: impl Iterator, err_loc: Span) -> Result { let mut witx = None; let mut ctx = None; + let mut instance_typename = None; for f in fields { match f { ConfigField::Witx(c) => { @@ -66,6 +74,12 @@ impl Config { } ctx = Some(c); } + ConfigField::InstanceTypename(c) => { + if instance_typename.is_some() { + return Err(Error::new(err_loc, "duplicate `instance` field")); + } + instance_typename = Some(c); + } } } Ok(Config { @@ -75,6 +89,9 @@ impl Config { ctx: ctx .take() .ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, + instance_typename: instance_typename + .take() + .ok_or_else(|| Error::new(err_loc, "`instance` field required"))?, }) } @@ -97,3 +114,16 @@ impl Parse for Config { Ok(Config::build(fields.into_iter(), input.span())?) } } + +#[derive(Debug, Clone)] +pub struct InstanceTypenameConf { + pub name: Ident, +} + +impl Parse for InstanceTypenameConf { + fn parse(input: ParseStream) -> Result { + Ok(InstanceTypenameConf { + name: input.parse()?, + }) + } +} diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index f709eb177c..d80626f4b6 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,6 +6,8 @@ use wiggle_generate::Names; mod config; +use config::InstanceTypenameConf; + #[proc_macro] pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { let mut config = parse_macro_input!(args as config::Config); @@ -15,7 +17,7 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle)); - generate(&doc, &names).into() + generate(&doc, &names, &config.instance_typename).into() } enum Abi { @@ -25,7 +27,11 @@ enum Abi { F64, } -fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { +fn generate( + doc: &witx::Document, + names: &Names, + instance_typename: &InstanceTypenameConf, +) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); let mut ctor_externs = Vec::new(); @@ -233,6 +239,7 @@ fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { } } + let inst_type = instance_typename.name.clone(); let ctx_type = names.ctx_type(); quote! { @@ -241,23 +248,23 @@ fn generate(doc: &witx::Document, names: &Names) -> TokenStream2 { /// This represents a wasi module which can be used to instantiate other /// wasm modules. This structure exports all that various fields of the /// wasi instance as fields which can be used to implement your own - /// instantiation logic, if necessary. Additionally [`Wasi::get_export`] + /// instantiation logic, if necessary. Additionally [`#inst_type::get_export`] /// can be used to do name-based resolution. - pub struct Wasi { + pub struct #inst_type { #(#fields,)* } - impl Wasi { - /// Creates a new [`Wasi`] instance. + impl #inst_type { + /// Creates a new [`#inst_type`] instance. /// /// External values are allocated into the `store` provided and /// configuration of the wasi instance itself should be all /// contained in the `cx` parameter. - pub fn new(store: &wasmtime::Store, cx: #ctx_type) -> Wasi { + pub fn new(store: &wasmtime::Store, cx: #ctx_type) -> Self { let cx = std::rc::Rc::new(std::cell::RefCell::new(cx)); #(#ctor_externs)* - Wasi { + Self { #(#ctor_fields,)* } } From cde32070fc7c041ce3142aba7e008cce9905aa32 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 22 Jun 2020 19:27:31 -0700 Subject: [PATCH 08/17] factor the docs out as well --- crates/wasi/src/lib.rs | 11 ++- crates/wiggle/wasmtime/macro/src/config.rs | 85 ++++++++++++++++++---- crates/wiggle/wasmtime/macro/src/lib.rs | 42 +++++------ 3 files changed, 102 insertions(+), 36 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 73e5481fbc..b79e56e484 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -9,7 +9,16 @@ pub use wasi_common::{WasiCtx, WasiCtxBuilder}; wasmtime_wiggle::define_struct_for_wiggle!({ witx: ["../wasi-common/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"], ctx: WasiCtx, - instance: Wasi, + instance: { + name: Wasi, + docs: "An instantiated instance of the wasi exports. + +This represents a wasi module which can be used to instantiate other +wasm modules. This structure exports all that various fields of the +wasi instance as fields which can be used to implement your own +instantiation logic, if necessary. Additionally [`Wasi::get_export`] +can be used to do name-based resolution." + } }); pub fn is_wasi_module(name: &str) -> bool { diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index c9783925f2..6a414ff11b 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -13,14 +13,14 @@ use { pub struct Config { pub witx: WitxConf, pub ctx: CtxConf, - pub instance_typename: InstanceTypenameConf, + pub instance: InstanceConf, } #[derive(Debug, Clone)] pub enum ConfigField { Witx(WitxConf), Ctx(CtxConf), - InstanceTypename(InstanceTypenameConf), + Instance(InstanceConf), } mod kw { @@ -28,6 +28,8 @@ mod kw { syn::custom_keyword!(witx_literal); syn::custom_keyword!(ctx); syn::custom_keyword!(instance); + syn::custom_keyword!(name); + syn::custom_keyword!(docs); } impl Parse for ConfigField { @@ -48,7 +50,7 @@ impl Parse for ConfigField { } else if lookahead.peek(kw::instance) { input.parse::()?; input.parse::()?; - Ok(ConfigField::InstanceTypename(input.parse()?)) + Ok(ConfigField::Instance(input.parse()?)) } else { Err(lookahead.error()) } @@ -59,7 +61,7 @@ impl Config { pub fn build(fields: impl Iterator, err_loc: Span) -> Result { let mut witx = None; let mut ctx = None; - let mut instance_typename = None; + let mut instance = None; for f in fields { match f { ConfigField::Witx(c) => { @@ -74,11 +76,11 @@ impl Config { } ctx = Some(c); } - ConfigField::InstanceTypename(c) => { - if instance_typename.is_some() { + ConfigField::Instance(c) => { + if instance.is_some() { return Err(Error::new(err_loc, "duplicate `instance` field")); } - instance_typename = Some(c); + instance = Some(c); } } } @@ -89,7 +91,7 @@ impl Config { ctx: ctx .take() .ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, - instance_typename: instance_typename + instance: instance .take() .ok_or_else(|| Error::new(err_loc, "`instance` field required"))?, }) @@ -115,15 +117,70 @@ impl Parse for Config { } } -#[derive(Debug, Clone)] -pub struct InstanceTypenameConf { - pub name: Ident, +enum InstanceConfField { + Name(Ident), + Docs(String), } -impl Parse for InstanceTypenameConf { +impl Parse for InstanceConfField { fn parse(input: ParseStream) -> Result { - Ok(InstanceTypenameConf { - name: input.parse()?, + let lookahead = input.lookahead1(); + if lookahead.peek(kw::name) { + input.parse::()?; + input.parse::()?; + Ok(InstanceConfField::Name(input.parse()?)) + } else if lookahead.peek(kw::docs) { + input.parse::()?; + input.parse::()?; + let docs: syn::LitStr = input.parse()?; + Ok(InstanceConfField::Docs(docs.value())) + } else { + Err(lookahead.error()) + } + } +} + +#[derive(Debug, Clone)] +pub struct InstanceConf { + pub name: Ident, + pub docs: Option, +} + +impl InstanceConf { + fn build(fields: impl Iterator, err_loc: Span) -> Result { + let mut name = None; + let mut docs = None; + for f in fields { + match f { + InstanceConfField::Name(c) => { + if name.is_some() { + return Err(Error::new(err_loc, "duplicate `name` field")); + } + name = Some(c); + } + InstanceConfField::Docs(c) => { + if docs.is_some() { + return Err(Error::new(err_loc, "duplicate `docs` field")); + } + docs = Some(c); + } + } + } + Ok(InstanceConf { + name: name + .take() + .ok_or_else(|| Error::new(err_loc, "`name` field required"))?, + docs, }) } } + +impl Parse for InstanceConf { + fn parse(input: ParseStream) -> Result { + let contents; + let _lbrace = braced!(contents in input); + let fields: Punctuated = + contents.parse_terminated(InstanceConfField::parse)?; + Ok(InstanceConf::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 d80626f4b6..35a083e0eb 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,7 +6,7 @@ use wiggle_generate::Names; mod config; -use config::InstanceTypenameConf; +use config::InstanceConf; #[proc_macro] pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { @@ -17,7 +17,7 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle)); - generate(&doc, &names, &config.instance_typename).into() + generate(&doc, &names, &config.instance).into() } enum Abi { @@ -27,11 +27,7 @@ enum Abi { F64, } -fn generate( - doc: &witx::Document, - names: &Names, - instance_typename: &InstanceTypenameConf, -) -> TokenStream2 { +fn generate(doc: &witx::Document, names: &Names, instance_conf: &InstanceConf) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); let mut ctor_externs = Vec::new(); @@ -239,27 +235,31 @@ fn generate( } } - let inst_type = instance_typename.name.clone(); + let inst_type = instance_conf.name.clone(); + let inst_docs = if let Some(ref docs) = instance_conf.docs { + quote!( #[doc = #docs] ) + } else { + quote!() + }; + let constructor_docs = format!( + "Creates a new [`{}`] instance. + +External values are allocated into the `store` provided and +configuration of the wasi instance itself should be all +contained in the `cx` parameter.", + instance_conf.name.to_string() + ); + let ctx_type = names.ctx_type(); quote! { - /// An instantiated instance of the wasi exports. - /// - /// This represents a wasi module which can be used to instantiate other - /// wasm modules. This structure exports all that various fields of the - /// wasi instance as fields which can be used to implement your own - /// instantiation logic, if necessary. Additionally [`#inst_type::get_export`] - /// can be used to do name-based resolution. + #inst_docs pub struct #inst_type { #(#fields,)* } impl #inst_type { - /// Creates a new [`#inst_type`] instance. - /// - /// External values are allocated into the `store` provided and - /// configuration of the wasi instance itself should be all - /// contained in the `cx` parameter. + #[doc = #constructor_docs] pub fn new(store: &wasmtime::Store, cx: #ctx_type) -> Self { let cx = std::rc::Rc::new(std::cell::RefCell::new(cx)); #(#ctor_externs)* @@ -281,7 +281,7 @@ fn generate( } } - /// Adds all wasi items to the specified `Linker`. + /// Adds all instance items to the specified `Linker`. pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> { #(#linker_add)* Ok(()) From abc3982234abae7e8acf6ae275661ea0deb12ee1 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 23 Jun 2020 14:13:27 -0700 Subject: [PATCH 09/17] add target to config --- crates/wasi/src/lib.rs | 1 + crates/wiggle/wasmtime/macro/src/config.rs | 34 ++++++++++++++++++++-- crates/wiggle/wasmtime/macro/src/lib.rs | 14 ++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index b79e56e484..d4a6e3a0d1 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -7,6 +7,7 @@ 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!({ + target: wasi_common::wasi, witx: ["../wasi-common/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"], ctx: WasiCtx, instance: { diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index 6a414ff11b..f42f4121a0 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -4,13 +4,14 @@ use { braced, parse::{Parse, ParseStream}, punctuated::Punctuated, - Error, Ident, Result, Token, + Error, Ident, Path, Result, Token, }, wiggle_generate::config::{CtxConf, WitxConf}, }; #[derive(Debug, Clone)] pub struct Config { + pub target: TargetConf, pub witx: WitxConf, pub ctx: CtxConf, pub instance: InstanceConf, @@ -18,12 +19,14 @@ pub struct Config { #[derive(Debug, Clone)] pub enum ConfigField { + Target(TargetConf), Witx(WitxConf), Ctx(CtxConf), Instance(InstanceConf), } mod kw { + syn::custom_keyword!(target); syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); syn::custom_keyword!(ctx); @@ -35,7 +38,11 @@ mod kw { impl Parse for ConfigField { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); - if lookahead.peek(kw::witx) { + if lookahead.peek(kw::target) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::Target(input.parse()?)) + } else if lookahead.peek(kw::witx) { input.parse::()?; input.parse::()?; Ok(ConfigField::Witx(WitxConf::Paths(input.parse()?))) @@ -59,11 +66,18 @@ impl Parse for ConfigField { impl Config { pub fn build(fields: impl Iterator, err_loc: Span) -> Result { + let mut target = None; let mut witx = None; let mut ctx = None; let mut instance = None; for f in fields { match f { + ConfigField::Target(c) => { + if target.is_some() { + return Err(Error::new(err_loc, "duplicate `target` field")); + } + target = Some(c); + } ConfigField::Witx(c) => { if witx.is_some() { return Err(Error::new(err_loc, "duplicate `witx` field")); @@ -85,6 +99,9 @@ impl Config { } } Ok(Config { + target: target + .take() + .ok_or_else(|| Error::new(err_loc, "`target` field required"))?, witx: witx .take() .ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, @@ -117,6 +134,19 @@ impl Parse for Config { } } +#[derive(Debug, Clone)] +pub struct TargetConf { + pub path: Path, +} + +impl Parse for TargetConf { + fn parse(input: ParseStream) -> Result { + Ok(TargetConf { + path: input.parse()?, + }) + } +} + enum InstanceConfField { Name(Ident), Docs(String), diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 35a083e0eb..7b520836dc 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,7 +6,7 @@ use wiggle_generate::Names; mod config; -use config::InstanceConf; +use config::{InstanceConf, TargetConf}; #[proc_macro] pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { @@ -17,7 +17,7 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle)); - generate(&doc, &names, &config.instance).into() + generate(&doc, &names, &config.target, &config.instance).into() } enum Abi { @@ -27,7 +27,12 @@ enum Abi { F64, } -fn generate(doc: &witx::Document, names: &Names, instance_conf: &InstanceConf) -> TokenStream2 { +fn generate( + doc: &witx::Document, + names: &Names, + target_conf: &TargetConf, + instance_conf: &InstanceConf, +) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); let mut ctor_externs = Vec::new(); @@ -35,6 +40,7 @@ fn generate(doc: &witx::Document, names: &Names, instance_conf: &InstanceConf) - let mut linker_add = Vec::new(); let runtime = names.runtime_mod(); + let target_path = &target_conf.path; for module in doc.modules() { let module_name = module.name.as_str(); @@ -223,7 +229,7 @@ fn generate(doc: &witx::Document, names: &Names, instance_conf: &InstanceConf) - // root of each function invocation is correct. let bc = #runtime::BorrowChecker::new(); let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); - wasi_common::wasi::#module_id::#name_ident( + #target_path::#module_id::#name_ident( &mut my_cx.borrow_mut(), &mem, #(#hostcall_args),* From bb339aaba0275c255e66d58896450b03a14723cf Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 23 Jun 2020 14:16:53 -0700 Subject: [PATCH 10/17] rename macro. add comments to invocation. --- crates/wasi/src/lib.rs | 7 ++++++- crates/wiggle/wasmtime/macro/src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index d4a6e3a0d1..f281b84e2a 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -6,10 +6,15 @@ 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!({ +wasmtime_wiggle::define_wasmtime_integration!({ + // The wiggle code to integrate with lives here: target: wasi_common::wasi, + // This must be the same witx document as used above: witx: ["../wasi-common/WASI/phases/snapshot/witx/wasi_snapshot_preview1.witx"], + // This must be the same ctx type as used for the target: ctx: WasiCtx, + // This macro will emit a struct to represent the instance, + // with this name and docs: instance: { name: Wasi, docs: "An instantiated instance of the wasi exports. diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 7b520836dc..ebfc0bb98c 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -9,7 +9,7 @@ mod config; use config::{InstanceConf, TargetConf}; #[proc_macro] -pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream { +pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { 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"), From 62237de7ced87ed804dcaa2d72175037621f1e2f Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 23 Jun 2020 14:49:17 -0700 Subject: [PATCH 11/17] remove logging: wiggle now provides this functionality --- crates/wiggle/wasmtime/macro/src/lib.rs | 43 +++++++++---------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index ebfc0bb98c..9aa3c300f5 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -66,25 +66,21 @@ fn generate( let mut shim_arg_decls = Vec::new(); let mut params = Vec::new(); - let mut formats = Vec::new(); - let mut format_args = Vec::new(); let mut hostcall_args = Vec::new(); for param in func.params.iter() { let name = names.func_param(¶m.name); // Registers a new parameter to the shim we're making with the - // given `name`, the `abi_ty` wasm type and `hex` defines - // whether it's debug-printed in a hex format or not. + // given `name`, the `abi_ty` wasm type // // This will register a whole bunch of things: // // * The cranelift type for the parameter // * Syntax to specify the actual function parameter - // * How to log the parameter value in a call to `trace!` // * How to actually pass this argument to the host // implementation, converting as necessary. - let mut add_param = |name: &Ident, abi_ty: Abi, hex: bool| { + let mut add_param = |name: &Ident, abi_ty: Abi| { match abi_ty { Abi::I32 => { params.push(quote! { types::I32 }); @@ -103,25 +99,23 @@ fn generate( shim_arg_decls.push(quote! { #name: f64 }); } } - formats.push(format!("{}={}", name, if hex { "{:#x}" } else { "{}" },)); - format_args.push(name.clone()); hostcall_args.push(quote! { #name as _ }); }; match &*param.tref.type_() { witx::Type::Int(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64, false), - _ => add_param(&name, Abi::I32, false), + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), }, witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64, false), - _ => add_param(&name, Abi::I32, false), + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), }, witx::Type::Flags(f) => match f.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64, true), - _ => add_param(&name, Abi::I32, true), + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), }, witx::Type::Builtin(witx::BuiltinType::Char8) @@ -132,34 +126,34 @@ fn generate( | witx::Type::Builtin(witx::BuiltinType::S32) | witx::Type::Builtin(witx::BuiltinType::U32) | witx::Type::Builtin(witx::BuiltinType::USize) => { - add_param(&name, Abi::I32, false); + add_param(&name, Abi::I32); } witx::Type::Builtin(witx::BuiltinType::S64) | witx::Type::Builtin(witx::BuiltinType::U64) => { - add_param(&name, Abi::I64, false); + add_param(&name, Abi::I64); } witx::Type::Builtin(witx::BuiltinType::F32) => { - add_param(&name, Abi::F32, false); + add_param(&name, Abi::F32); } witx::Type::Builtin(witx::BuiltinType::F64) => { - add_param(&name, Abi::F64, false); + add_param(&name, Abi::F64); } // strings/arrays have an extra ABI parameter for the length // of the array passed. witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { - add_param(&name, Abi::I32, true); + add_param(&name, Abi::I32); let len = format_ident!("{}_len", name); - add_param(&len, Abi::I32, false); + add_param(&len, Abi::I32); } witx::Type::ConstPointer(_) | witx::Type::Handle(_) | witx::Type::Pointer(_) => { - add_param(&name, Abi::I32, true); + add_param(&name, Abi::I32); } witx::Type::Struct(_) | witx::Type::Union(_) => { @@ -198,21 +192,14 @@ fn generate( let name = format_ident!("{}", result.name.as_str()); params.push(quote! { types::I32 }); shim_arg_decls.push(quote! { #name: i32 }); - formats.push(format!("{}={{:#x}}", name)); - format_args.push(name.clone()); hostcall_args.push(quote! { #name }); } - let format_str = format!("{}({})", name, formats.join(", ")); ctor_externs.push(quote! { let my_cx = cx.clone(); let #name_ident = wasmtime::Func::wrap( store, move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { - log::trace!( - #format_str, - #(#format_args),* - ); unsafe { let mem = match caller.get_export("memory") { Some(wasmtime::Extern::Memory(m)) => m, From 49c62ee828fc052aabd9b7fc41c893dfa5aa9c58 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 23 Jun 2020 15:26:17 -0700 Subject: [PATCH 12/17] make the missing memory error value configurable --- crates/wasi/src/lib.rs | 4 ++- crates/wiggle/wasmtime/macro/src/config.rs | 33 +++++++++++++++++++++- crates/wiggle/wasmtime/macro/src/lib.rs | 15 ++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index f281b84e2a..5d29cce913 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -24,7 +24,9 @@ wasm modules. This structure exports all that various fields of the wasi instance as fields which can be used to implement your own instantiation logic, if necessary. Additionally [`Wasi::get_export`] can be used to do name-based resolution." - } + }, + // Error to return when caller module is missing memory export: + missing_memory: { wasi_common::wasi::Errno::Inval }, }); pub fn is_wasi_module(name: &str) -> bool { diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index f42f4121a0..b3d44d4492 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -1,5 +1,5 @@ use { - proc_macro2::Span, + proc_macro2::{Span, TokenStream}, syn::{ braced, parse::{Parse, ParseStream}, @@ -15,6 +15,7 @@ pub struct Config { pub witx: WitxConf, pub ctx: CtxConf, pub instance: InstanceConf, + pub missing_memory: MissingMemoryConf, } #[derive(Debug, Clone)] @@ -23,6 +24,7 @@ pub enum ConfigField { Witx(WitxConf), Ctx(CtxConf), Instance(InstanceConf), + MissingMemory(MissingMemoryConf), } mod kw { @@ -33,6 +35,7 @@ mod kw { syn::custom_keyword!(instance); syn::custom_keyword!(name); syn::custom_keyword!(docs); + syn::custom_keyword!(missing_memory); } impl Parse for ConfigField { @@ -58,6 +61,10 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::Instance(input.parse()?)) + } else if lookahead.peek(kw::missing_memory) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::MissingMemory(input.parse()?)) } else { Err(lookahead.error()) } @@ -70,6 +77,7 @@ impl Config { let mut witx = None; let mut ctx = None; let mut instance = None; + let mut missing_memory = None; for f in fields { match f { ConfigField::Target(c) => { @@ -96,6 +104,12 @@ impl Config { } instance = Some(c); } + ConfigField::MissingMemory(c) => { + if missing_memory.is_some() { + return Err(Error::new(err_loc, "duplicate `missing_memory` field")); + } + missing_memory = Some(c); + } } } Ok(Config { @@ -111,6 +125,9 @@ impl Config { instance: instance .take() .ok_or_else(|| Error::new(err_loc, "`instance` field required"))?, + missing_memory: missing_memory + .take() + .ok_or_else(|| Error::new(err_loc, "`missing_memory` field required"))?, }) } @@ -214,3 +231,17 @@ impl Parse for InstanceConf { Ok(InstanceConf::build(fields.into_iter(), input.span())?) } } + +#[derive(Debug, Clone)] +pub struct MissingMemoryConf { + pub err: TokenStream, +} +impl Parse for MissingMemoryConf { + fn parse(input: ParseStream) -> Result { + let contents; + let _lbrace = braced!(contents in input); + Ok(MissingMemoryConf { + err: contents.parse()?, + }) + } +} diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 9aa3c300f5..543d8bbe18 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,7 +6,7 @@ use wiggle_generate::Names; mod config; -use config::{InstanceConf, TargetConf}; +use config::{InstanceConf, MissingMemoryConf, TargetConf}; #[proc_macro] pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { @@ -17,7 +17,14 @@ pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle)); - generate(&doc, &names, &config.target, &config.instance).into() + generate( + &doc, + &names, + &config.target, + &config.instance, + &config.missing_memory, + ) + .into() } enum Abi { @@ -32,6 +39,7 @@ fn generate( names: &Names, target_conf: &TargetConf, instance_conf: &InstanceConf, + missing_mem_conf: &MissingMemoryConf, ) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); @@ -41,6 +49,7 @@ fn generate( let runtime = names.runtime_mod(); let target_path = &target_conf.path; + let missing_mem_err = &missing_mem_conf.err; for module in doc.modules() { let module_name = module.name.as_str(); @@ -205,7 +214,7 @@ fn generate( Some(wasmtime::Extern::Memory(m)) => m, _ => { log::warn!("callee does not export a memory as \"memory\""); - let e = wasi_common::wasi::Errno::Inval; + let e = { #missing_mem_err }; #handle_early_error } }; From 69f81397a8c9ed0d101a8289579a266d25d8392f Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 23 Jun 2020 16:29:11 -0700 Subject: [PATCH 13/17] add func overrides, to get rid of proc exit special case --- crates/wasi/src/lib.rs | 5 ++ crates/wiggle/wasmtime/macro/src/config.rs | 61 ++++++++++++++++++++++ crates/wiggle/wasmtime/macro/src/lib.rs | 12 ++--- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 5d29cce913..beb7b753ff 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -27,6 +27,11 @@ can be used to do name-based resolution." }, // Error to return when caller module is missing memory export: missing_memory: { wasi_common::wasi::Errno::Inval }, + // Don't use the wiggle generated code to implement proc_exit, we need to hook directly into + // the runtime there: + function_override: { + wasi_snapshot_preview1:proc_exit => wasi_proc_exit + } }); pub fn is_wasi_module(name: &str) -> bool { diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index b3d44d4492..837a7ba4d9 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -16,6 +16,7 @@ pub struct Config { pub ctx: CtxConf, pub instance: InstanceConf, pub missing_memory: MissingMemoryConf, + pub function_override: FunctionOverrideConf, } #[derive(Debug, Clone)] @@ -25,6 +26,7 @@ pub enum ConfigField { Ctx(CtxConf), Instance(InstanceConf), MissingMemory(MissingMemoryConf), + FunctionOverride(FunctionOverrideConf), } mod kw { @@ -36,6 +38,7 @@ mod kw { syn::custom_keyword!(name); syn::custom_keyword!(docs); syn::custom_keyword!(missing_memory); + syn::custom_keyword!(function_override); } impl Parse for ConfigField { @@ -65,6 +68,10 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::MissingMemory(input.parse()?)) + } else if lookahead.peek(kw::function_override) { + input.parse::()?; + input.parse::()?; + Ok(ConfigField::FunctionOverride(input.parse()?)) } else { Err(lookahead.error()) } @@ -78,6 +85,7 @@ impl Config { let mut ctx = None; let mut instance = None; let mut missing_memory = None; + let mut function_override = None; for f in fields { match f { ConfigField::Target(c) => { @@ -110,6 +118,12 @@ impl Config { } missing_memory = Some(c); } + ConfigField::FunctionOverride(c) => { + if function_override.is_some() { + return Err(Error::new(err_loc, "duplicate `function_override` field")); + } + function_override = Some(c); + } } } Ok(Config { @@ -128,6 +142,7 @@ impl Config { missing_memory: missing_memory .take() .ok_or_else(|| Error::new(err_loc, "`missing_memory` field required"))?, + function_override: function_override.take().unwrap_or_default(), }) } @@ -245,3 +260,49 @@ impl Parse for MissingMemoryConf { }) } } + +#[derive(Debug, Clone, Default)] +pub struct FunctionOverrideConf { + pub funcs: Vec, +} +impl FunctionOverrideConf { + pub fn find(&self, module: &str, field: &str) -> Option<&Ident> { + self.funcs + .iter() + .find(|f| f.module == module && f.field == field) + .map(|f| &f.replacement) + } +} + +impl Parse for FunctionOverrideConf { + fn parse(input: ParseStream) -> Result { + let contents; + let _lbrace = braced!(contents in input); + let fields: Punctuated = + contents.parse_terminated(FunctionOverrideField::parse)?; + Ok(FunctionOverrideConf { + funcs: fields.into_iter().collect(), + }) + } +} + +#[derive(Debug, Clone)] +pub struct FunctionOverrideField { + pub module: String, + pub field: String, + pub replacement: Ident, +} +impl Parse for FunctionOverrideField { + fn parse(input: ParseStream) -> Result { + let module = input.parse::()?.to_string(); + input.parse::()?; + let field = input.parse::()?.to_string(); + input.parse::]>()?; + let replacement = input.parse::()?; + Ok(FunctionOverrideField { + module, + field, + replacement, + }) + } +} diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 543d8bbe18..542e166497 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,7 +6,7 @@ use wiggle_generate::Names; mod config; -use config::{InstanceConf, MissingMemoryConf, TargetConf}; +use config::{FunctionOverrideConf, InstanceConf, MissingMemoryConf, TargetConf}; #[proc_macro] pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { @@ -23,6 +23,7 @@ pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { &config.target, &config.instance, &config.missing_memory, + &config.function_override, ) .into() } @@ -40,6 +41,7 @@ fn generate( target_conf: &TargetConf, instance_conf: &InstanceConf, missing_mem_conf: &MissingMemoryConf, + func_override_conf: &FunctionOverrideConf, ) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); @@ -63,12 +65,10 @@ fn generate( linker_add.push(quote! { linker.define(#module_name, #name, self.#name_ident.clone())?; }); - // `proc_exit` is special; it's essentially an unwinding primitive, - // so we implement it in the runtime rather than use the implementation - // in wasi-common. - if name == "proc_exit" { + + if let Some(func_override) = func_override_conf.find(module_name, name) { ctor_externs.push(quote! { - let #name_ident = wasmtime::Func::wrap(store, crate::wasi_proc_exit); + let #name_ident = wasmtime::Func::wrap(store, #func_override); }); continue; } From f66c1fbde94f03bfa54dc69d1d9f3b1e5c2ae97f Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 23 Jun 2020 17:42:04 -0700 Subject: [PATCH 14/17] reorganize configuration into modules --- crates/wasi/src/lib.rs | 27 +- crates/wiggle/wasmtime/macro/src/config.rs | 145 ++++---- crates/wiggle/wasmtime/macro/src/lib.rs | 401 ++++++++++----------- 3 files changed, 291 insertions(+), 282 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index beb7b753ff..bfaab7b540 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -15,23 +15,24 @@ wasmtime_wiggle::define_wasmtime_integration!({ ctx: WasiCtx, // This macro will emit a struct to represent the instance, // with this name and docs: - instance: { - name: Wasi, - docs: "An instantiated instance of the wasi exports. + modules: { wasi_snapshot_preview1 => + { name: Wasi, + docs: "An instantiated instance of the wasi exports. -This represents a wasi module which can be used to instantiate other -wasm modules. This structure exports all that various fields of the -wasi instance as fields which can be used to implement your own -instantiation logic, if necessary. Additionally [`Wasi::get_export`] -can be used to do name-based resolution." +This represents a wasi module which can be used to instantiate other wasm +modules. This structure exports all that various fields of the wasi instance +as fields which can be used to implement your own instantiation logic, if +necessary. Additionally [`Wasi::get_export`] can be used to do name-based +resolution.", + // Don't use the wiggle generated code to implement proc_exit, we need + // to hook directly into the runtime there: + function_override: { + proc_exit => wasi_proc_exit + } + }, }, // Error to return when caller module is missing memory export: missing_memory: { wasi_common::wasi::Errno::Inval }, - // Don't use the wiggle generated code to implement proc_exit, we need to hook directly into - // the runtime there: - function_override: { - wasi_snapshot_preview1:proc_exit => wasi_proc_exit - } }); pub fn is_wasi_module(name: &str) -> bool { diff --git a/crates/wiggle/wasmtime/macro/src/config.rs b/crates/wiggle/wasmtime/macro/src/config.rs index 837a7ba4d9..8f2d9bbc0f 100644 --- a/crates/wiggle/wasmtime/macro/src/config.rs +++ b/crates/wiggle/wasmtime/macro/src/config.rs @@ -1,5 +1,6 @@ use { proc_macro2::{Span, TokenStream}, + std::collections::HashMap, syn::{ braced, parse::{Parse, ParseStream}, @@ -14,9 +15,8 @@ pub struct Config { pub target: TargetConf, pub witx: WitxConf, pub ctx: CtxConf, - pub instance: InstanceConf, + pub modules: ModulesConf, pub missing_memory: MissingMemoryConf, - pub function_override: FunctionOverrideConf, } #[derive(Debug, Clone)] @@ -24,9 +24,8 @@ pub enum ConfigField { Target(TargetConf), Witx(WitxConf), Ctx(CtxConf), - Instance(InstanceConf), + Modules(ModulesConf), MissingMemory(MissingMemoryConf), - FunctionOverride(FunctionOverrideConf), } mod kw { @@ -34,7 +33,7 @@ mod kw { syn::custom_keyword!(witx); syn::custom_keyword!(witx_literal); syn::custom_keyword!(ctx); - syn::custom_keyword!(instance); + syn::custom_keyword!(modules); syn::custom_keyword!(name); syn::custom_keyword!(docs); syn::custom_keyword!(missing_memory); @@ -60,18 +59,14 @@ impl Parse for ConfigField { input.parse::()?; input.parse::()?; Ok(ConfigField::Ctx(input.parse()?)) - } else if lookahead.peek(kw::instance) { - input.parse::()?; + } else if lookahead.peek(kw::modules) { + input.parse::()?; input.parse::()?; - Ok(ConfigField::Instance(input.parse()?)) + Ok(ConfigField::Modules(input.parse()?)) } else if lookahead.peek(kw::missing_memory) { input.parse::()?; input.parse::()?; Ok(ConfigField::MissingMemory(input.parse()?)) - } else if lookahead.peek(kw::function_override) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::FunctionOverride(input.parse()?)) } else { Err(lookahead.error()) } @@ -83,9 +78,8 @@ impl Config { let mut target = None; let mut witx = None; let mut ctx = None; - let mut instance = None; + let mut modules = None; let mut missing_memory = None; - let mut function_override = None; for f in fields { match f { ConfigField::Target(c) => { @@ -106,11 +100,11 @@ impl Config { } ctx = Some(c); } - ConfigField::Instance(c) => { - if instance.is_some() { - return Err(Error::new(err_loc, "duplicate `instance` field")); + ConfigField::Modules(c) => { + if modules.is_some() { + return Err(Error::new(err_loc, "duplicate `modules` field")); } - instance = Some(c); + modules = Some(c); } ConfigField::MissingMemory(c) => { if missing_memory.is_some() { @@ -118,31 +112,15 @@ impl Config { } missing_memory = Some(c); } - ConfigField::FunctionOverride(c) => { - if function_override.is_some() { - return Err(Error::new(err_loc, "duplicate `function_override` field")); - } - function_override = Some(c); - } } } Ok(Config { - target: target - .take() - .ok_or_else(|| Error::new(err_loc, "`target` field required"))?, - witx: witx - .take() - .ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, - ctx: ctx - .take() - .ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, - instance: instance - .take() - .ok_or_else(|| Error::new(err_loc, "`instance` field required"))?, + target: target.ok_or_else(|| Error::new(err_loc, "`target` field required"))?, + witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?, + ctx: ctx.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?, + modules: modules.ok_or_else(|| Error::new(err_loc, "`modules` field required"))?, missing_memory: missing_memory - .take() .ok_or_else(|| Error::new(err_loc, "`missing_memory` field required"))?, - function_override: function_override.take().unwrap_or_default(), }) } @@ -179,23 +157,28 @@ impl Parse for TargetConf { } } -enum InstanceConfField { +enum ModuleConfField { Name(Ident), Docs(String), + FunctionOverride(FunctionOverrideConf), } -impl Parse for InstanceConfField { +impl Parse for ModuleConfField { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(kw::name) { input.parse::()?; input.parse::()?; - Ok(InstanceConfField::Name(input.parse()?)) + Ok(ModuleConfField::Name(input.parse()?)) } else if lookahead.peek(kw::docs) { input.parse::()?; input.parse::()?; let docs: syn::LitStr = input.parse()?; - Ok(InstanceConfField::Docs(docs.value())) + Ok(ModuleConfField::Docs(docs.value())) + } else if lookahead.peek(kw::function_override) { + input.parse::()?; + input.parse::()?; + Ok(ModuleConfField::FunctionOverride(input.parse()?)) } else { Err(lookahead.error()) } @@ -203,47 +186,82 @@ impl Parse for InstanceConfField { } #[derive(Debug, Clone)] -pub struct InstanceConf { +pub struct ModuleConf { pub name: Ident, pub docs: Option, + pub function_override: FunctionOverrideConf, } -impl InstanceConf { - fn build(fields: impl Iterator, err_loc: Span) -> Result { +impl ModuleConf { + fn build(fields: impl Iterator, err_loc: Span) -> Result { let mut name = None; let mut docs = None; + let mut function_override = None; for f in fields { match f { - InstanceConfField::Name(c) => { + ModuleConfField::Name(c) => { if name.is_some() { return Err(Error::new(err_loc, "duplicate `name` field")); } name = Some(c); } - InstanceConfField::Docs(c) => { + ModuleConfField::Docs(c) => { if docs.is_some() { return Err(Error::new(err_loc, "duplicate `docs` field")); } docs = Some(c); } + ModuleConfField::FunctionOverride(c) => { + if function_override.is_some() { + return Err(Error::new(err_loc, "duplicate `function_override` field")); + } + function_override = Some(c); + } } } - Ok(InstanceConf { - name: name - .take() - .ok_or_else(|| Error::new(err_loc, "`name` field required"))?, + Ok(ModuleConf { + name: name.ok_or_else(|| Error::new(err_loc, "`name` field required"))?, docs, + function_override: function_override.unwrap_or_default(), }) } } -impl Parse for InstanceConf { +impl Parse for ModuleConf { fn parse(input: ParseStream) -> Result { let contents; let _lbrace = braced!(contents in input); - let fields: Punctuated = - contents.parse_terminated(InstanceConfField::parse)?; - Ok(InstanceConf::build(fields.into_iter(), input.span())?) + let fields: Punctuated = + contents.parse_terminated(ModuleConfField::parse)?; + Ok(ModuleConf::build(fields.into_iter(), input.span())?) + } +} + +#[derive(Debug, Clone)] +pub struct ModulesConf { + pub mods: HashMap, +} + +impl ModulesConf { + pub fn iter(&self) -> impl Iterator { + self.mods.iter() + } +} + +impl Parse for ModulesConf { + fn parse(input: ParseStream) -> Result { + let contents; + let _lbrace = braced!(contents in input); + let fields: Punctuated<(String, ModuleConf), Token![,]> = + contents.parse_terminated(|i| { + let name = i.parse::()?.to_string(); + i.parse::]>()?; + let val = i.parse()?; + Ok((name, val)) + })?; + Ok(ModulesConf { + mods: fields.into_iter().collect(), + }) } } @@ -266,10 +284,10 @@ pub struct FunctionOverrideConf { pub funcs: Vec, } impl FunctionOverrideConf { - pub fn find(&self, module: &str, field: &str) -> Option<&Ident> { + pub fn find(&self, name: &str) -> Option<&Ident> { self.funcs .iter() - .find(|f| f.module == module && f.field == field) + .find(|f| f.name == name) .map(|f| &f.replacement) } } @@ -288,21 +306,14 @@ impl Parse for FunctionOverrideConf { #[derive(Debug, Clone)] pub struct FunctionOverrideField { - pub module: String, - pub field: String, + pub name: String, pub replacement: Ident, } impl Parse for FunctionOverrideField { fn parse(input: ParseStream) -> Result { - let module = input.parse::()?.to_string(); - input.parse::()?; - let field = input.parse::()?.to_string(); + let name = input.parse::()?.to_string(); input.parse::]>()?; let replacement = input.parse::()?; - Ok(FunctionOverrideField { - module, - field, - replacement, - }) + Ok(FunctionOverrideField { name, replacement }) } } diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 542e166497..ddc97a8b0f 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -6,7 +6,7 @@ use wiggle_generate::Names; mod config; -use config::{FunctionOverrideConf, InstanceConf, MissingMemoryConf, TargetConf}; +use config::{MissingMemoryConf, ModuleConf, TargetConf}; #[proc_macro] pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { @@ -17,15 +17,19 @@ pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { let doc = config.load_document(); let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle)); - generate( - &doc, - &names, - &config.target, - &config.instance, - &config.missing_memory, - &config.function_override, - ) - .into() + let modules = config.modules.iter().map(|(name, module_conf)| { + let module = doc + .module(&witx::Id::new(name)) + .unwrap_or_else(|| panic!("witx document did not contain module named '{}'", name)); + generate_module( + &module, + &module_conf, + &names, + &config.target, + &config.missing_memory, + ) + }); + quote!( #(#modules)* ).into() } enum Abi { @@ -35,13 +39,12 @@ enum Abi { F64, } -fn generate( - doc: &witx::Document, +fn generate_module( + module: &witx::Module, + module_conf: &ModuleConf, names: &Names, target_conf: &TargetConf, - instance_conf: &InstanceConf, missing_mem_conf: &MissingMemoryConf, - func_override_conf: &FunctionOverrideConf, ) -> TokenStream2 { let mut fields = Vec::new(); let mut get_exports = Vec::new(); @@ -53,214 +56,208 @@ fn generate( let target_path = &target_conf.path; let missing_mem_err = &missing_mem_conf.err; - for module in doc.modules() { - let module_name = module.name.as_str(); - let module_id = names.module(&module.name); - for func in module.funcs() { - let name = func.name.as_str(); - let name_ident = names.func(&func.name); - fields.push(quote! { pub #name_ident: wasmtime::Func }); - get_exports.push(quote! { #name => Some(&self.#name_ident) }); - ctor_fields.push(name_ident.clone()); - linker_add.push(quote! { - linker.define(#module_name, #name, self.#name_ident.clone())?; - }); - - if let Some(func_override) = func_override_conf.find(module_name, name) { - ctor_externs.push(quote! { - let #name_ident = wasmtime::Func::wrap(store, #func_override); - }); - continue; - } - - let mut shim_arg_decls = Vec::new(); - let mut params = Vec::new(); - let mut hostcall_args = Vec::new(); - - for param in func.params.iter() { - let name = names.func_param(¶m.name); - - // Registers a new parameter to the shim we're making with the - // given `name`, the `abi_ty` wasm type - // - // This will register a whole bunch of things: - // - // * The cranelift type for the parameter - // * Syntax to specify the actual function parameter - // * How to actually pass this argument to the host - // implementation, converting as necessary. - let mut add_param = |name: &Ident, abi_ty: Abi| { - match abi_ty { - Abi::I32 => { - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - } - Abi::I64 => { - params.push(quote! { types::I64 }); - shim_arg_decls.push(quote! { #name: i64 }); - } - Abi::F32 => { - params.push(quote! { types::F32 }); - shim_arg_decls.push(quote! { #name: f32 }); - } - Abi::F64 => { - params.push(quote! { types::F64 }); - shim_arg_decls.push(quote! { #name: f64 }); - } - } - hostcall_args.push(quote! { #name as _ }); - }; - - match &*param.tref.type_() { - witx::Type::Int(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Flags(f) => match f.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Builtin(witx::BuiltinType::Char8) - | witx::Type::Builtin(witx::BuiltinType::S8) - | witx::Type::Builtin(witx::BuiltinType::U8) - | witx::Type::Builtin(witx::BuiltinType::S16) - | witx::Type::Builtin(witx::BuiltinType::U16) - | witx::Type::Builtin(witx::BuiltinType::S32) - | witx::Type::Builtin(witx::BuiltinType::U32) - | witx::Type::Builtin(witx::BuiltinType::USize) => { - add_param(&name, Abi::I32); - } - - witx::Type::Builtin(witx::BuiltinType::S64) - | witx::Type::Builtin(witx::BuiltinType::U64) => { - add_param(&name, Abi::I64); - } - - witx::Type::Builtin(witx::BuiltinType::F32) => { - add_param(&name, Abi::F32); - } - - witx::Type::Builtin(witx::BuiltinType::F64) => { - add_param(&name, Abi::F64); - } - - // strings/arrays have an extra ABI parameter for the length - // of the array passed. - witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { - add_param(&name, Abi::I32); - let len = format_ident!("{}_len", name); - add_param(&len, Abi::I32); - } - - witx::Type::ConstPointer(_) - | witx::Type::Handle(_) - | witx::Type::Pointer(_) => { - add_param(&name, Abi::I32); - } - - witx::Type::Struct(_) | witx::Type::Union(_) => { - panic!("unsupported argument type") - } - } - } - - let mut results = func.results.iter(); - let mut ret_ty = quote! { () }; - let mut cvt_ret = quote! {}; - let mut returns = Vec::new(); - let mut handle_early_error = quote! { panic!("error: {:?}", e) }; - - // The first result is returned bare right now... - if let Some(ret) = results.next() { - handle_early_error = quote! { return e.into() }; - match &*ret.tref.type_() { - // Eventually we'll want to add support for more returned - // types, but for now let's just conform to what `*.witx` - // definitions currently use. - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U16 => { - returns.push(quote! { types::I32 }); - ret_ty = quote! { i32 }; - cvt_ret = quote! { .into() } - } - other => panic!("unsupported ret enum repr {:?}", other), - }, - other => panic!("unsupported first return {:?}", other), - } - } - - // ... and all remaining results are returned via out-poiners - for result in results { - let name = format_ident!("{}", result.name.as_str()); - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - hostcall_args.push(quote! { #name }); - } + let module_name = module.name.as_str(); + let module_id = names.module(&module.name); + for func in module.funcs() { + let func_name = func.name.as_str(); + let name_ident = names.func(&func.name); + fields.push(quote! { pub #name_ident: wasmtime::Func }); + get_exports.push(quote! { #func_name => Some(&self.#name_ident) }); + ctor_fields.push(name_ident.clone()); + linker_add.push(quote! { + linker.define(#module_name, #func_name, self.#name_ident.clone())?; + }); + if let Some(func_override) = module_conf.function_override.find(func_name) { ctor_externs.push(quote! { - let my_cx = cx.clone(); - let #name_ident = wasmtime::Func::wrap( - store, - move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { - unsafe { - let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, - _ => { - log::warn!("callee does not export a memory as \"memory\""); - let e = { #missing_mem_err }; - #handle_early_error - } - }; - // Wiggle does not expose any methods for - // functions to re-enter the WebAssembly module, - // or expose the memory via non-wiggle mechanisms. - // Therefore, creating a new BorrowChecker at the - // root of each function invocation is correct. - let bc = #runtime::BorrowChecker::new(); - let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); - #target_path::#module_id::#name_ident( - &mut my_cx.borrow_mut(), - &mem, - #(#hostcall_args),* - ) #cvt_ret - } - } - ); + let #name_ident = wasmtime::Func::wrap(store, #func_override); }); + continue; } + + let mut shim_arg_decls = Vec::new(); + let mut params = Vec::new(); + let mut hostcall_args = Vec::new(); + + for param in func.params.iter() { + let name = names.func_param(¶m.name); + + // Registers a new parameter to the shim we're making with the + // given `name`, the `abi_ty` wasm type + // + // This will register a whole bunch of things: + // + // * The cranelift type for the parameter + // * Syntax to specify the actual function parameter + // * How to actually pass this argument to the host + // implementation, converting as necessary. + let mut add_param = |name: &Ident, abi_ty: Abi| { + match abi_ty { + Abi::I32 => { + params.push(quote! { types::I32 }); + shim_arg_decls.push(quote! { #name: i32 }); + } + Abi::I64 => { + params.push(quote! { types::I64 }); + shim_arg_decls.push(quote! { #name: i64 }); + } + Abi::F32 => { + params.push(quote! { types::F32 }); + shim_arg_decls.push(quote! { #name: f32 }); + } + Abi::F64 => { + params.push(quote! { types::F64 }); + shim_arg_decls.push(quote! { #name: f64 }); + } + } + hostcall_args.push(quote! { #name as _ }); + }; + + match &*param.tref.type_() { + witx::Type::Int(e) => match e.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), + }, + + witx::Type::Enum(e) => match e.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), + }, + + witx::Type::Flags(f) => match f.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), + }, + + witx::Type::Builtin(witx::BuiltinType::Char8) + | witx::Type::Builtin(witx::BuiltinType::S8) + | witx::Type::Builtin(witx::BuiltinType::U8) + | witx::Type::Builtin(witx::BuiltinType::S16) + | witx::Type::Builtin(witx::BuiltinType::U16) + | witx::Type::Builtin(witx::BuiltinType::S32) + | witx::Type::Builtin(witx::BuiltinType::U32) + | witx::Type::Builtin(witx::BuiltinType::USize) => { + add_param(&name, Abi::I32); + } + + witx::Type::Builtin(witx::BuiltinType::S64) + | witx::Type::Builtin(witx::BuiltinType::U64) => { + add_param(&name, Abi::I64); + } + + witx::Type::Builtin(witx::BuiltinType::F32) => { + add_param(&name, Abi::F32); + } + + witx::Type::Builtin(witx::BuiltinType::F64) => { + add_param(&name, Abi::F64); + } + + // strings/arrays have an extra ABI parameter for the length + // of the array passed. + witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { + add_param(&name, Abi::I32); + let len = format_ident!("{}_len", name); + add_param(&len, Abi::I32); + } + + witx::Type::ConstPointer(_) | witx::Type::Handle(_) | witx::Type::Pointer(_) => { + add_param(&name, Abi::I32); + } + + witx::Type::Struct(_) | witx::Type::Union(_) => panic!("unsupported argument type"), + } + } + + let mut results = func.results.iter(); + let mut ret_ty = quote! { () }; + let mut cvt_ret = quote! {}; + let mut returns = Vec::new(); + let mut handle_early_error = quote! { panic!("error: {:?}", e) }; + + // The first result is returned bare right now... + if let Some(ret) = results.next() { + handle_early_error = quote! { return e.into() }; + match &*ret.tref.type_() { + // Eventually we'll want to add support for more returned + // types, but for now let's just conform to what `*.witx` + // definitions currently use. + witx::Type::Enum(e) => match e.repr { + witx::IntRepr::U16 => { + returns.push(quote! { types::I32 }); + ret_ty = quote! { i32 }; + cvt_ret = quote! { .into() } + } + other => panic!("unsupported ret enum repr {:?}", other), + }, + other => panic!("unsupported first return {:?}", other), + } + } + + // ... and all remaining results are returned via out-poiners + for result in results { + let name = format_ident!("{}", result.name.as_str()); + params.push(quote! { types::I32 }); + shim_arg_decls.push(quote! { #name: i32 }); + hostcall_args.push(quote! { #name }); + } + + ctor_externs.push(quote! { + let my_cx = cx.clone(); + let #name_ident = wasmtime::Func::wrap( + store, + move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { + unsafe { + let mem = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + log::warn!("callee does not export a memory as \"memory\""); + let e = { #missing_mem_err }; + #handle_early_error + } + }; + // Wiggle does not expose any methods for + // functions to re-enter the WebAssembly module, + // or expose the memory via non-wiggle mechanisms. + // Therefore, creating a new BorrowChecker at the + // root of each function invocation is correct. + let bc = #runtime::BorrowChecker::new(); + let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); + #target_path::#module_id::#name_ident( + &mut my_cx.borrow_mut(), + &mem, + #(#hostcall_args),* + ) #cvt_ret + } + } + ); + }); } - let inst_type = instance_conf.name.clone(); - let inst_docs = if let Some(ref docs) = instance_conf.docs { - quote!( #[doc = #docs] ) - } else { - quote!() - }; + let type_name = module_conf.name.clone(); + let type_docs = module_conf + .docs + .as_ref() + .map(|docs| quote!( #[doc = #docs] )) + .unwrap_or_default(); let constructor_docs = format!( "Creates a new [`{}`] instance. External values are allocated into the `store` provided and configuration of the wasi instance itself should be all contained in the `cx` parameter.", - instance_conf.name.to_string() + module_conf.name.to_string() ); let ctx_type = names.ctx_type(); quote! { - #inst_docs - pub struct #inst_type { + #type_docs + pub struct #type_name { #(#fields,)* } - impl #inst_type { + impl #type_name { #[doc = #constructor_docs] pub fn new(store: &wasmtime::Store, cx: #ctx_type) -> Self { let cx = std::rc::Rc::new(std::cell::RefCell::new(cx)); From 564b0709dde7ae0762f61902fe17c6c841474d89 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 23 Jun 2020 18:11:38 -0700 Subject: [PATCH 15/17] wip --- crates/wiggle/wasmtime/macro/src/lib.rs | 341 ++++++++++++------------ 1 file changed, 174 insertions(+), 167 deletions(-) diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index ddc97a8b0f..c60b8d9c82 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -46,193 +46,200 @@ fn generate_module( target_conf: &TargetConf, missing_mem_conf: &MissingMemoryConf, ) -> TokenStream2 { - let mut fields = Vec::new(); - let mut get_exports = Vec::new(); - let mut ctor_externs = Vec::new(); - let mut ctor_fields = Vec::new(); - let mut linker_add = Vec::new(); + let fields = module.funcs().map(|f| { + let name_ident = names.func(&f.name); + quote! { pub #name_ident: wasmtime::Func } + }); + let get_exports = module.funcs().map(|f| { + let func_name = f.name.as_str(); + let name_ident = names.func(&f.name); + quote! { #func_name => Some(&self.#name_ident) } + }); + let ctor_fields = module.funcs().map(|f| names.func(&f.name)); + + let module_name = module.name.as_str(); + + let linker_add = module.funcs().map(|f| { + let func_name = f.name.as_str(); + let name_ident = names.func(&f.name); + quote! { + linker.define(#module_name, #func_name, self.#name_ident.clone())?; + } + }); let runtime = names.runtime_mod(); let target_path = &target_conf.path; let missing_mem_err = &missing_mem_conf.err; - - let module_name = module.name.as_str(); let module_id = names.module(&module.name); - for func in module.funcs() { - let func_name = func.name.as_str(); - let name_ident = names.func(&func.name); - fields.push(quote! { pub #name_ident: wasmtime::Func }); - get_exports.push(quote! { #func_name => Some(&self.#name_ident) }); - ctor_fields.push(name_ident.clone()); - linker_add.push(quote! { - linker.define(#module_name, #func_name, self.#name_ident.clone())?; - }); - if let Some(func_override) = module_conf.function_override.find(func_name) { - ctor_externs.push(quote! { - let #name_ident = wasmtime::Func::wrap(store, #func_override); - }); - continue; - } + let ctor_externs = module.funcs().map(|f| { + let name_ident = names.func(&f.name); + if let Some(func_override) = module_conf.function_override.find(&f.name.as_str()) { + quote! { let #name_ident = wasmtime::Func::wrap(store, #func_override); } + } else { + let mut shim_arg_decls = Vec::new(); + let mut params = Vec::new(); + let mut hostcall_args = Vec::new(); - let mut shim_arg_decls = Vec::new(); - let mut params = Vec::new(); - let mut hostcall_args = Vec::new(); + for param in f.params.iter() { + let name = names.func_param(¶m.name); - for param in func.params.iter() { - let name = names.func_param(¶m.name); - - // Registers a new parameter to the shim we're making with the - // given `name`, the `abi_ty` wasm type - // - // This will register a whole bunch of things: - // - // * The cranelift type for the parameter - // * Syntax to specify the actual function parameter - // * How to actually pass this argument to the host - // implementation, converting as necessary. - let mut add_param = |name: &Ident, abi_ty: Abi| { - match abi_ty { - Abi::I32 => { - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); + // Registers a new parameter to the shim we're making with the + // given `name`, the `abi_ty` wasm type + // + // This will register a whole bunch of things: + // + // * The cranelift type for the parameter + // * Syntax to specify the actual function parameter + // * How to actually pass this argument to the host + // implementation, converting as necessary. + let mut add_param = |name: &Ident, abi_ty: Abi| { + match abi_ty { + Abi::I32 => { + params.push(quote! { types::I32 }); + shim_arg_decls.push(quote! { #name: i32 }); + } + Abi::I64 => { + params.push(quote! { types::I64 }); + shim_arg_decls.push(quote! { #name: i64 }); + } + Abi::F32 => { + params.push(quote! { types::F32 }); + shim_arg_decls.push(quote! { #name: f32 }); + } + Abi::F64 => { + params.push(quote! { types::F64 }); + shim_arg_decls.push(quote! { #name: f64 }); + } } - Abi::I64 => { - params.push(quote! { types::I64 }); - shim_arg_decls.push(quote! { #name: i64 }); + hostcall_args.push(quote! { #name as _ }); + }; + + match &*param.tref.type_() { + witx::Type::Int(e) => match e.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), + }, + + witx::Type::Enum(e) => match e.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), + }, + + witx::Type::Flags(f) => match f.repr { + witx::IntRepr::U64 => add_param(&name, Abi::I64), + _ => add_param(&name, Abi::I32), + }, + + witx::Type::Builtin(witx::BuiltinType::Char8) + | witx::Type::Builtin(witx::BuiltinType::S8) + | witx::Type::Builtin(witx::BuiltinType::U8) + | witx::Type::Builtin(witx::BuiltinType::S16) + | witx::Type::Builtin(witx::BuiltinType::U16) + | witx::Type::Builtin(witx::BuiltinType::S32) + | witx::Type::Builtin(witx::BuiltinType::U32) + | witx::Type::Builtin(witx::BuiltinType::USize) => { + add_param(&name, Abi::I32); } - Abi::F32 => { - params.push(quote! { types::F32 }); - shim_arg_decls.push(quote! { #name: f32 }); + + witx::Type::Builtin(witx::BuiltinType::S64) + | witx::Type::Builtin(witx::BuiltinType::U64) => { + add_param(&name, Abi::I64); } - Abi::F64 => { - params.push(quote! { types::F64 }); - shim_arg_decls.push(quote! { #name: f64 }); + + witx::Type::Builtin(witx::BuiltinType::F32) => { + add_param(&name, Abi::F32); + } + + witx::Type::Builtin(witx::BuiltinType::F64) => { + add_param(&name, Abi::F64); + } + + // strings/arrays have an extra ABI parameter for the length + // of the array passed. + witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { + add_param(&name, Abi::I32); + let len = format_ident!("{}_len", name); + add_param(&len, Abi::I32); + } + + witx::Type::ConstPointer(_) + | witx::Type::Handle(_) + | witx::Type::Pointer(_) => { + add_param(&name, Abi::I32); + } + + witx::Type::Struct(_) | witx::Type::Union(_) => { + panic!("unsupported argument type") } } - hostcall_args.push(quote! { #name as _ }); - }; + } - match &*param.tref.type_() { - witx::Type::Int(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, + let mut results = f.results.iter(); + let mut ret_ty = quote! { () }; + let mut cvt_ret = quote! {}; + let mut returns = Vec::new(); + let mut handle_early_error = quote! { panic!("error: {:?}", e) }; - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Flags(f) => match f.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Builtin(witx::BuiltinType::Char8) - | witx::Type::Builtin(witx::BuiltinType::S8) - | witx::Type::Builtin(witx::BuiltinType::U8) - | witx::Type::Builtin(witx::BuiltinType::S16) - | witx::Type::Builtin(witx::BuiltinType::U16) - | witx::Type::Builtin(witx::BuiltinType::S32) - | witx::Type::Builtin(witx::BuiltinType::U32) - | witx::Type::Builtin(witx::BuiltinType::USize) => { - add_param(&name, Abi::I32); + // The first result is returned bare right now... + if let Some(ret) = results.next() { + handle_early_error = quote! { return e.into() }; + match &*ret.tref.type_() { + // Eventually we'll want to add support for more returned + // types, but for now let's just conform to what `*.witx` + // definitions currently use. + witx::Type::Enum(e) => match e.repr { + witx::IntRepr::U16 => { + returns.push(quote! { types::I32 }); + ret_ty = quote! { i32 }; + cvt_ret = quote! { .into() } + } + other => panic!("unsupported ret enum repr {:?}", other), + }, + other => panic!("unsupported first return {:?}", other), } + } - witx::Type::Builtin(witx::BuiltinType::S64) - | witx::Type::Builtin(witx::BuiltinType::U64) => { - add_param(&name, Abi::I64); - } + // ... and all remaining results are returned via out-poiners + for result in results { + let name = format_ident!("{}", result.name.as_str()); + params.push(quote! { types::I32 }); + shim_arg_decls.push(quote! { #name: i32 }); + hostcall_args.push(quote! { #name }); + } - witx::Type::Builtin(witx::BuiltinType::F32) => { - add_param(&name, Abi::F32); - } - - witx::Type::Builtin(witx::BuiltinType::F64) => { - add_param(&name, Abi::F64); - } - - // strings/arrays have an extra ABI parameter for the length - // of the array passed. - witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { - add_param(&name, Abi::I32); - let len = format_ident!("{}_len", name); - add_param(&len, Abi::I32); - } - - witx::Type::ConstPointer(_) | witx::Type::Handle(_) | witx::Type::Pointer(_) => { - add_param(&name, Abi::I32); - } - - witx::Type::Struct(_) | witx::Type::Union(_) => panic!("unsupported argument type"), + quote! { + let my_cx = cx.clone(); + let #name_ident = wasmtime::Func::wrap( + store, + move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { + unsafe { + let mem = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + log::warn!("callee does not export a memory as \"memory\""); + let e = { #missing_mem_err }; + #handle_early_error + } + }; + // Wiggle does not expose any methods for + // functions to re-enter the WebAssembly module, + // or expose the memory via non-wiggle mechanisms. + // Therefore, creating a new BorrowChecker at the + // root of each function invocation is correct. + let bc = #runtime::BorrowChecker::new(); + let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); + #target_path::#module_id::#name_ident( + &mut my_cx.borrow_mut(), + &mem, + #(#hostcall_args),* + ) #cvt_ret + } + } + ); } } - - let mut results = func.results.iter(); - let mut ret_ty = quote! { () }; - let mut cvt_ret = quote! {}; - let mut returns = Vec::new(); - let mut handle_early_error = quote! { panic!("error: {:?}", e) }; - - // The first result is returned bare right now... - if let Some(ret) = results.next() { - handle_early_error = quote! { return e.into() }; - match &*ret.tref.type_() { - // Eventually we'll want to add support for more returned - // types, but for now let's just conform to what `*.witx` - // definitions currently use. - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U16 => { - returns.push(quote! { types::I32 }); - ret_ty = quote! { i32 }; - cvt_ret = quote! { .into() } - } - other => panic!("unsupported ret enum repr {:?}", other), - }, - other => panic!("unsupported first return {:?}", other), - } - } - - // ... and all remaining results are returned via out-poiners - for result in results { - let name = format_ident!("{}", result.name.as_str()); - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - hostcall_args.push(quote! { #name }); - } - - ctor_externs.push(quote! { - let my_cx = cx.clone(); - let #name_ident = wasmtime::Func::wrap( - store, - move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { - unsafe { - let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, - _ => { - log::warn!("callee does not export a memory as \"memory\""); - let e = { #missing_mem_err }; - #handle_early_error - } - }; - // Wiggle does not expose any methods for - // functions to re-enter the WebAssembly module, - // or expose the memory via non-wiggle mechanisms. - // Therefore, creating a new BorrowChecker at the - // root of each function invocation is correct. - let bc = #runtime::BorrowChecker::new(); - let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); - #target_path::#module_id::#name_ident( - &mut my_cx.borrow_mut(), - &mem, - #(#hostcall_args),* - ) #cvt_ret - } - } - ); - }); - } + }); let type_name = module_conf.name.clone(); let type_docs = module_conf From 6b62b16c7d1ac02b4034cc06c34c66af86815418 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 24 Jun 2020 12:47:28 -0700 Subject: [PATCH 16/17] rewrite shim func generator with witx CoreType --- crates/wiggle/wasmtime/macro/src/lib.rs | 243 +++++++----------------- 1 file changed, 70 insertions(+), 173 deletions(-) diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index c60b8d9c82..446bcaf7d0 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; -use quote::{format_ident, quote}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; use syn::parse_macro_input; use wiggle_generate::Names; @@ -32,13 +32,6 @@ pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { quote!( #(#modules)* ).into() } -enum Abi { - I32, - I64, - F32, - F64, -} - fn generate_module( module: &witx::Module, module_conf: &ModuleConf, @@ -67,177 +60,16 @@ fn generate_module( } }); - let runtime = names.runtime_mod(); let target_path = &target_conf.path; - let missing_mem_err = &missing_mem_conf.err; let module_id = names.module(&module.name); + let target_module = quote! { #target_path::#module_id }; let ctor_externs = module.funcs().map(|f| { - let name_ident = names.func(&f.name); if let Some(func_override) = module_conf.function_override.find(&f.name.as_str()) { + let name_ident = names.func(&f.name); quote! { let #name_ident = wasmtime::Func::wrap(store, #func_override); } } else { - let mut shim_arg_decls = Vec::new(); - let mut params = Vec::new(); - let mut hostcall_args = Vec::new(); - - for param in f.params.iter() { - let name = names.func_param(¶m.name); - - // Registers a new parameter to the shim we're making with the - // given `name`, the `abi_ty` wasm type - // - // This will register a whole bunch of things: - // - // * The cranelift type for the parameter - // * Syntax to specify the actual function parameter - // * How to actually pass this argument to the host - // implementation, converting as necessary. - let mut add_param = |name: &Ident, abi_ty: Abi| { - match abi_ty { - Abi::I32 => { - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - } - Abi::I64 => { - params.push(quote! { types::I64 }); - shim_arg_decls.push(quote! { #name: i64 }); - } - Abi::F32 => { - params.push(quote! { types::F32 }); - shim_arg_decls.push(quote! { #name: f32 }); - } - Abi::F64 => { - params.push(quote! { types::F64 }); - shim_arg_decls.push(quote! { #name: f64 }); - } - } - hostcall_args.push(quote! { #name as _ }); - }; - - match &*param.tref.type_() { - witx::Type::Int(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Flags(f) => match f.repr { - witx::IntRepr::U64 => add_param(&name, Abi::I64), - _ => add_param(&name, Abi::I32), - }, - - witx::Type::Builtin(witx::BuiltinType::Char8) - | witx::Type::Builtin(witx::BuiltinType::S8) - | witx::Type::Builtin(witx::BuiltinType::U8) - | witx::Type::Builtin(witx::BuiltinType::S16) - | witx::Type::Builtin(witx::BuiltinType::U16) - | witx::Type::Builtin(witx::BuiltinType::S32) - | witx::Type::Builtin(witx::BuiltinType::U32) - | witx::Type::Builtin(witx::BuiltinType::USize) => { - add_param(&name, Abi::I32); - } - - witx::Type::Builtin(witx::BuiltinType::S64) - | witx::Type::Builtin(witx::BuiltinType::U64) => { - add_param(&name, Abi::I64); - } - - witx::Type::Builtin(witx::BuiltinType::F32) => { - add_param(&name, Abi::F32); - } - - witx::Type::Builtin(witx::BuiltinType::F64) => { - add_param(&name, Abi::F64); - } - - // strings/arrays have an extra ABI parameter for the length - // of the array passed. - witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => { - add_param(&name, Abi::I32); - let len = format_ident!("{}_len", name); - add_param(&len, Abi::I32); - } - - witx::Type::ConstPointer(_) - | witx::Type::Handle(_) - | witx::Type::Pointer(_) => { - add_param(&name, Abi::I32); - } - - witx::Type::Struct(_) | witx::Type::Union(_) => { - panic!("unsupported argument type") - } - } - } - - let mut results = f.results.iter(); - let mut ret_ty = quote! { () }; - let mut cvt_ret = quote! {}; - let mut returns = Vec::new(); - let mut handle_early_error = quote! { panic!("error: {:?}", e) }; - - // The first result is returned bare right now... - if let Some(ret) = results.next() { - handle_early_error = quote! { return e.into() }; - match &*ret.tref.type_() { - // Eventually we'll want to add support for more returned - // types, but for now let's just conform to what `*.witx` - // definitions currently use. - witx::Type::Enum(e) => match e.repr { - witx::IntRepr::U16 => { - returns.push(quote! { types::I32 }); - ret_ty = quote! { i32 }; - cvt_ret = quote! { .into() } - } - other => panic!("unsupported ret enum repr {:?}", other), - }, - other => panic!("unsupported first return {:?}", other), - } - } - - // ... and all remaining results are returned via out-poiners - for result in results { - let name = format_ident!("{}", result.name.as_str()); - params.push(quote! { types::I32 }); - shim_arg_decls.push(quote! { #name: i32 }); - hostcall_args.push(quote! { #name }); - } - - quote! { - let my_cx = cx.clone(); - let #name_ident = wasmtime::Func::wrap( - store, - move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty { - unsafe { - let mem = match caller.get_export("memory") { - Some(wasmtime::Extern::Memory(m)) => m, - _ => { - log::warn!("callee does not export a memory as \"memory\""); - let e = { #missing_mem_err }; - #handle_early_error - } - }; - // Wiggle does not expose any methods for - // functions to re-enter the WebAssembly module, - // or expose the memory via non-wiggle mechanisms. - // Therefore, creating a new BorrowChecker at the - // root of each function invocation is correct. - let bc = #runtime::BorrowChecker::new(); - let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); - #target_path::#module_id::#name_ident( - &mut my_cx.borrow_mut(), - &mem, - #(#hostcall_args),* - ) #cvt_ret - } - } - ); - } + generate_func(&f, names, missing_mem_conf, &target_module) } }); @@ -295,3 +127,68 @@ contained in the `cx` parameter.", } } } + +fn generate_func( + func: &witx::InterfaceFunc, + names: &Names, + missing_mem_conf: &MissingMemoryConf, + target_module: &TokenStream2, +) -> TokenStream2 { + let missing_mem_err = &missing_mem_conf.err; + let name_ident = names.func(&func.name); + + let coretype = func.core_type(); + + let arg_decls = coretype.args.iter().map(|arg| { + let name = names.func_core_arg(arg); + let atom = names.atom_type(arg.repr()); + quote! { #name: #atom } + }); + let arg_names = coretype.args.iter().map(|arg| names.func_core_arg(arg)); + + let (ret_ty, handle_early_error) = if let Some(ret) = &coretype.ret { + let ret_ty = match ret.signifies { + witx::CoreParamSignifies::Value(atom) => names.atom_type(atom), + _ => unreachable!("coretype ret should always be passed by value"), + }; + (quote! { #ret_ty }, quote! { return e.into(); }) + } else { + ( + quote! {()}, + quote! { panic!("unrecoverable error in {}: {}", stringify!(#name_ident), e) }, + ) + }; + + let runtime = names.runtime_mod(); + + quote! { + let my_cx = cx.clone(); + let #name_ident = wasmtime::Func::wrap( + store, + move |caller: wasmtime::Caller<'_> #(,#arg_decls)*| -> #ret_ty { + unsafe { + let mem = match caller.get_export("memory") { + Some(wasmtime::Extern::Memory(m)) => m, + _ => { + log::warn!("callee does not export a memory as \"memory\""); + let e = { #missing_mem_err }; + #handle_early_error + } + }; + // Wiggle does not expose any methods for + // functions to re-enter the WebAssembly module, + // or expose the memory via non-wiggle mechanisms. + // Therefore, creating a new BorrowChecker at the + // root of each function invocation is correct. + let bc = #runtime::BorrowChecker::new(); + let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); + #target_module::#name_ident( + &mut my_cx.borrow_mut(), + &mem, + #(#arg_names),* + ) + } + } + ); + } +} From 82c41327001b608661b3935d8ce2c2b4e255639e Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 24 Jun 2020 14:34:35 -0700 Subject: [PATCH 17/17] simpler name, add rustdocs --- crates/wasi/src/lib.rs | 2 +- crates/wiggle/wasmtime/macro/src/lib.rs | 49 ++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index bfaab7b540..d6f8e4fad8 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -6,7 +6,7 @@ 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_wasmtime_integration!({ +wasmtime_wiggle::wasmtime_integration!({ // The wiggle code to integrate with lives here: target: wasi_common::wasi, // This must be the same witx document as used above: diff --git a/crates/wiggle/wasmtime/macro/src/lib.rs b/crates/wiggle/wasmtime/macro/src/lib.rs index 446bcaf7d0..57f1e635b8 100644 --- a/crates/wiggle/wasmtime/macro/src/lib.rs +++ b/crates/wiggle/wasmtime/macro/src/lib.rs @@ -8,8 +8,45 @@ mod config; use config::{MissingMemoryConf, ModuleConf, TargetConf}; +/// Define the structs required to integrate a Wiggle implementation with Wasmtime. +/// +/// ## Arguments +/// +/// Arguments are provided using struct syntax e.g. `{ arg_name: value }`. +/// +/// * `target`: The path of the module where the Wiggle implementation is defined. +/// * `witx` or `witx_literal`: the .witx document where the interface is defined. +/// `witx` takes a list of filesystem paths, e.g. `["/path/to/file1.witx", +/// "./path/to_file2.witx"]`. Relative paths are relative to the root of the crate +/// where the macro is invoked. `witx_literal` takes a string of the witx document, e.g. +/// `"(typename $foo u8)"`. +/// * `ctx`: The context struct used for the Wiggle implementation. This must be the same +/// type as the [`wasmtime_wiggle::from_witx`] macro at `target` was invoked with. However, it +/// must be imported to the current scope so that it is a bare identifier e.g. `CtxType`, not +/// `path::to::CtxType`. +/// * `modules`: Describes how any modules in the witx document will be implemented as Wasmtime +/// instances. `modules` takes a map from the witx module name to a configuration struct, e.g. +/// `foo => { name: Foo }, bar => { name: Bar }` will generate integrations for the modules +/// named `foo` and `bar` in the witx document, as `pub struct Foo` and `pub struct Bar` +/// respectively. +/// The module configuration uses struct syntax with the following fields: +/// * `name`: required, gives the name of the struct which encapsulates the instance for +/// Wasmtime. +/// * `docs`: optional, a doc string that will be used for the definition of the struct. +/// * `function_override`: A map of witx function names to Rust function symbols for +/// functions that should not call the Wiggle-generated functions, but instead use +/// a separate implementation. This is typically used for functions that need to interact +/// with Wasmtime in a manner that Wiggle does not permit, e.g. wasi's `proc_exit` function +/// needs to return a Trap directly to the runtime. +/// Example: +/// `modules: { some_module => { name: SomeTypeName, docs: "Doc string for definition of +/// SomeTypeName here", function_override: { foo => my_own_foo } }`. +/// * `missing_memory`: Describes the error value to return in case the calling module does not +/// export a Memory as `"memory"`. This value is given in braces, e.g. `missing_memory: { +/// wasi_common::wasi::Errno::Inval }`. +/// #[proc_macro] -pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { +pub fn wasmtime_integration(args: TokenStream) -> TokenStream { 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"), @@ -175,11 +212,11 @@ fn generate_func( #handle_early_error } }; - // Wiggle does not expose any methods for - // functions to re-enter the WebAssembly module, - // or expose the memory via non-wiggle mechanisms. - // Therefore, creating a new BorrowChecker at the - // root of each function invocation is correct. + // Wiggle does not expose any methods for functions to re-enter the WebAssembly + // instance, or expose the memory via non-wiggle mechanisms. However, the + // user-defined code may end up re-entering the instance, in which case this + // is an incorrect implementation - we require exactly one BorrowChecker exist + // per instance. let bc = #runtime::BorrowChecker::new(); let mem = #runtime::WasmtimeGuestMemory::new( mem, bc ); #target_module::#name_ident(