reorganize configuration into modules

This commit is contained in:
Pat Hickey
2020-06-23 17:42:04 -07:00
parent 69f81397a8
commit f66c1fbde9
3 changed files with 291 additions and 282 deletions

View File

@@ -15,23 +15,24 @@ wasmtime_wiggle::define_wasmtime_integration!({
ctx: WasiCtx, ctx: WasiCtx,
// This macro will emit a struct to represent the instance, // This macro will emit a struct to represent the instance,
// with this name and docs: // with this name and docs:
instance: { modules: { wasi_snapshot_preview1 =>
name: Wasi, { name: Wasi,
docs: "An instantiated instance of the wasi exports. docs: "An instantiated instance of the wasi exports.
This represents a wasi module which can be used to instantiate other This represents a wasi module which can be used to instantiate other wasm
wasm modules. This structure exports all that various fields of the modules. This structure exports all that various fields of the wasi instance
wasi instance as fields which can be used to implement your own as fields which can be used to implement your own instantiation logic, if
instantiation logic, if necessary. Additionally [`Wasi::get_export`] necessary. Additionally [`Wasi::get_export`] can be used to do name-based
can be used to do name-based resolution." 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: // Error to return when caller module is missing memory export:
missing_memory: { wasi_common::wasi::Errno::Inval }, 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 { pub fn is_wasi_module(name: &str) -> bool {

View File

@@ -1,5 +1,6 @@
use { use {
proc_macro2::{Span, TokenStream}, proc_macro2::{Span, TokenStream},
std::collections::HashMap,
syn::{ syn::{
braced, braced,
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
@@ -14,9 +15,8 @@ pub struct Config {
pub target: TargetConf, pub target: TargetConf,
pub witx: WitxConf, pub witx: WitxConf,
pub ctx: CtxConf, pub ctx: CtxConf,
pub instance: InstanceConf, pub modules: ModulesConf,
pub missing_memory: MissingMemoryConf, pub missing_memory: MissingMemoryConf,
pub function_override: FunctionOverrideConf,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -24,9 +24,8 @@ pub enum ConfigField {
Target(TargetConf), Target(TargetConf),
Witx(WitxConf), Witx(WitxConf),
Ctx(CtxConf), Ctx(CtxConf),
Instance(InstanceConf), Modules(ModulesConf),
MissingMemory(MissingMemoryConf), MissingMemory(MissingMemoryConf),
FunctionOverride(FunctionOverrideConf),
} }
mod kw { mod kw {
@@ -34,7 +33,7 @@ mod kw {
syn::custom_keyword!(witx); syn::custom_keyword!(witx);
syn::custom_keyword!(witx_literal); syn::custom_keyword!(witx_literal);
syn::custom_keyword!(ctx); syn::custom_keyword!(ctx);
syn::custom_keyword!(instance); syn::custom_keyword!(modules);
syn::custom_keyword!(name); syn::custom_keyword!(name);
syn::custom_keyword!(docs); syn::custom_keyword!(docs);
syn::custom_keyword!(missing_memory); syn::custom_keyword!(missing_memory);
@@ -60,18 +59,14 @@ impl Parse for ConfigField {
input.parse::<kw::ctx>()?; input.parse::<kw::ctx>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
Ok(ConfigField::Ctx(input.parse()?)) Ok(ConfigField::Ctx(input.parse()?))
} else if lookahead.peek(kw::instance) { } else if lookahead.peek(kw::modules) {
input.parse::<kw::instance>()?; input.parse::<kw::modules>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
Ok(ConfigField::Instance(input.parse()?)) Ok(ConfigField::Modules(input.parse()?))
} else if lookahead.peek(kw::missing_memory) { } else if lookahead.peek(kw::missing_memory) {
input.parse::<kw::missing_memory>()?; input.parse::<kw::missing_memory>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
Ok(ConfigField::MissingMemory(input.parse()?)) Ok(ConfigField::MissingMemory(input.parse()?))
} else if lookahead.peek(kw::function_override) {
input.parse::<kw::function_override>()?;
input.parse::<Token![:]>()?;
Ok(ConfigField::FunctionOverride(input.parse()?))
} else { } else {
Err(lookahead.error()) Err(lookahead.error())
} }
@@ -83,9 +78,8 @@ impl Config {
let mut target = None; let mut target = None;
let mut witx = None; let mut witx = None;
let mut ctx = None; let mut ctx = None;
let mut instance = None; let mut modules = None;
let mut missing_memory = None; let mut missing_memory = None;
let mut function_override = None;
for f in fields { for f in fields {
match f { match f {
ConfigField::Target(c) => { ConfigField::Target(c) => {
@@ -106,11 +100,11 @@ impl Config {
} }
ctx = Some(c); ctx = Some(c);
} }
ConfigField::Instance(c) => { ConfigField::Modules(c) => {
if instance.is_some() { if modules.is_some() {
return Err(Error::new(err_loc, "duplicate `instance` field")); return Err(Error::new(err_loc, "duplicate `modules` field"));
} }
instance = Some(c); modules = Some(c);
} }
ConfigField::MissingMemory(c) => { ConfigField::MissingMemory(c) => {
if missing_memory.is_some() { if missing_memory.is_some() {
@@ -118,31 +112,15 @@ impl Config {
} }
missing_memory = Some(c); 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 { Ok(Config {
target: target target: target.ok_or_else(|| Error::new(err_loc, "`target` field required"))?,
.take() witx: witx.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
.ok_or_else(|| Error::new(err_loc, "`target` field required"))?, ctx: ctx.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
witx: witx modules: modules.ok_or_else(|| Error::new(err_loc, "`modules` field required"))?,
.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"))?,
missing_memory: missing_memory missing_memory: missing_memory
.take()
.ok_or_else(|| Error::new(err_loc, "`missing_memory` field required"))?, .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), Name(Ident),
Docs(String), Docs(String),
FunctionOverride(FunctionOverrideConf),
} }
impl Parse for InstanceConfField { impl Parse for ModuleConfField {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1(); let lookahead = input.lookahead1();
if lookahead.peek(kw::name) { if lookahead.peek(kw::name) {
input.parse::<kw::name>()?; input.parse::<kw::name>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
Ok(InstanceConfField::Name(input.parse()?)) Ok(ModuleConfField::Name(input.parse()?))
} else if lookahead.peek(kw::docs) { } else if lookahead.peek(kw::docs) {
input.parse::<kw::docs>()?; input.parse::<kw::docs>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
let docs: syn::LitStr = 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::<kw::function_override>()?;
input.parse::<Token![:]>()?;
Ok(ModuleConfField::FunctionOverride(input.parse()?))
} else { } else {
Err(lookahead.error()) Err(lookahead.error())
} }
@@ -203,47 +186,82 @@ impl Parse for InstanceConfField {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InstanceConf { pub struct ModuleConf {
pub name: Ident, pub name: Ident,
pub docs: Option<String>, pub docs: Option<String>,
pub function_override: FunctionOverrideConf,
} }
impl InstanceConf { impl ModuleConf {
fn build(fields: impl Iterator<Item = InstanceConfField>, err_loc: Span) -> Result<Self> { fn build(fields: impl Iterator<Item = ModuleConfField>, err_loc: Span) -> Result<Self> {
let mut name = None; let mut name = None;
let mut docs = None; let mut docs = None;
let mut function_override = None;
for f in fields { for f in fields {
match f { match f {
InstanceConfField::Name(c) => { ModuleConfField::Name(c) => {
if name.is_some() { if name.is_some() {
return Err(Error::new(err_loc, "duplicate `name` field")); return Err(Error::new(err_loc, "duplicate `name` field"));
} }
name = Some(c); name = Some(c);
} }
InstanceConfField::Docs(c) => { ModuleConfField::Docs(c) => {
if docs.is_some() { if docs.is_some() {
return Err(Error::new(err_loc, "duplicate `docs` field")); return Err(Error::new(err_loc, "duplicate `docs` field"));
} }
docs = Some(c); 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 { Ok(ModuleConf {
name: name name: name.ok_or_else(|| Error::new(err_loc, "`name` field required"))?,
.take()
.ok_or_else(|| Error::new(err_loc, "`name` field required"))?,
docs, docs,
function_override: function_override.unwrap_or_default(),
}) })
} }
} }
impl Parse for InstanceConf { impl Parse for ModuleConf {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let contents; let contents;
let _lbrace = braced!(contents in input); let _lbrace = braced!(contents in input);
let fields: Punctuated<InstanceConfField, Token![,]> = let fields: Punctuated<ModuleConfField, Token![,]> =
contents.parse_terminated(InstanceConfField::parse)?; contents.parse_terminated(ModuleConfField::parse)?;
Ok(InstanceConf::build(fields.into_iter(), input.span())?) Ok(ModuleConf::build(fields.into_iter(), input.span())?)
}
}
#[derive(Debug, Clone)]
pub struct ModulesConf {
pub mods: HashMap<String, ModuleConf>,
}
impl ModulesConf {
pub fn iter(&self) -> impl Iterator<Item = (&String, &ModuleConf)> {
self.mods.iter()
}
}
impl Parse for ModulesConf {
fn parse(input: ParseStream) -> Result<Self> {
let contents;
let _lbrace = braced!(contents in input);
let fields: Punctuated<(String, ModuleConf), Token![,]> =
contents.parse_terminated(|i| {
let name = i.parse::<Ident>()?.to_string();
i.parse::<Token![=>]>()?;
let val = i.parse()?;
Ok((name, val))
})?;
Ok(ModulesConf {
mods: fields.into_iter().collect(),
})
} }
} }
@@ -266,10 +284,10 @@ pub struct FunctionOverrideConf {
pub funcs: Vec<FunctionOverrideField>, pub funcs: Vec<FunctionOverrideField>,
} }
impl FunctionOverrideConf { impl FunctionOverrideConf {
pub fn find(&self, module: &str, field: &str) -> Option<&Ident> { pub fn find(&self, name: &str) -> Option<&Ident> {
self.funcs self.funcs
.iter() .iter()
.find(|f| f.module == module && f.field == field) .find(|f| f.name == name)
.map(|f| &f.replacement) .map(|f| &f.replacement)
} }
} }
@@ -288,21 +306,14 @@ impl Parse for FunctionOverrideConf {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FunctionOverrideField { pub struct FunctionOverrideField {
pub module: String, pub name: String,
pub field: String,
pub replacement: Ident, pub replacement: Ident,
} }
impl Parse for FunctionOverrideField { impl Parse for FunctionOverrideField {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let module = input.parse::<Ident>()?.to_string(); let name = input.parse::<Ident>()?.to_string();
input.parse::<Token![:]>()?;
let field = input.parse::<Ident>()?.to_string();
input.parse::<Token![=>]>()?; input.parse::<Token![=>]>()?;
let replacement = input.parse::<Ident>()?; let replacement = input.parse::<Ident>()?;
Ok(FunctionOverrideField { Ok(FunctionOverrideField { name, replacement })
module,
field,
replacement,
})
} }
} }

View File

@@ -6,7 +6,7 @@ use wiggle_generate::Names;
mod config; mod config;
use config::{FunctionOverrideConf, InstanceConf, MissingMemoryConf, TargetConf}; use config::{MissingMemoryConf, ModuleConf, TargetConf};
#[proc_macro] #[proc_macro]
pub fn define_wasmtime_integration(args: TokenStream) -> TokenStream { 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 doc = config.load_document();
let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle)); let names = Names::new(&config.ctx.name, quote!(wasmtime_wiggle));
generate( let modules = config.modules.iter().map(|(name, module_conf)| {
&doc, let module = doc
&names, .module(&witx::Id::new(name))
&config.target, .unwrap_or_else(|| panic!("witx document did not contain module named '{}'", name));
&config.instance, generate_module(
&config.missing_memory, &module,
&config.function_override, &module_conf,
) &names,
.into() &config.target,
&config.missing_memory,
)
});
quote!( #(#modules)* ).into()
} }
enum Abi { enum Abi {
@@ -35,13 +39,12 @@ enum Abi {
F64, F64,
} }
fn generate( fn generate_module(
doc: &witx::Document, module: &witx::Module,
module_conf: &ModuleConf,
names: &Names, names: &Names,
target_conf: &TargetConf, target_conf: &TargetConf,
instance_conf: &InstanceConf,
missing_mem_conf: &MissingMemoryConf, missing_mem_conf: &MissingMemoryConf,
func_override_conf: &FunctionOverrideConf,
) -> TokenStream2 { ) -> TokenStream2 {
let mut fields = Vec::new(); let mut fields = Vec::new();
let mut get_exports = Vec::new(); let mut get_exports = Vec::new();
@@ -53,214 +56,208 @@ fn generate(
let target_path = &target_conf.path; let target_path = &target_conf.path;
let missing_mem_err = &missing_mem_conf.err; let missing_mem_err = &missing_mem_conf.err;
for module in doc.modules() { let module_name = module.name.as_str();
let module_name = module.name.as_str(); let module_id = names.module(&module.name);
let module_id = names.module(&module.name); for func in module.funcs() {
for func in module.funcs() { let func_name = func.name.as_str();
let name = func.name.as_str(); let name_ident = names.func(&func.name);
let name_ident = names.func(&func.name); fields.push(quote! { pub #name_ident: wasmtime::Func });
fields.push(quote! { pub #name_ident: wasmtime::Func }); get_exports.push(quote! { #func_name => Some(&self.#name_ident) });
get_exports.push(quote! { #name => Some(&self.#name_ident) }); ctor_fields.push(name_ident.clone());
ctor_fields.push(name_ident.clone()); linker_add.push(quote! {
linker_add.push(quote! { linker.define(#module_name, #func_name, self.#name_ident.clone())?;
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(&param.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 });
}
if let Some(func_override) = module_conf.function_override.find(func_name) {
ctor_externs.push(quote! { ctor_externs.push(quote! {
let my_cx = cx.clone(); let #name_ident = wasmtime::Func::wrap(store, #func_override);
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
}
}
);
}); });
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(&param.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 type_name = module_conf.name.clone();
let inst_docs = if let Some(ref docs) = instance_conf.docs { let type_docs = module_conf
quote!( #[doc = #docs] ) .docs
} else { .as_ref()
quote!() .map(|docs| quote!( #[doc = #docs] ))
}; .unwrap_or_default();
let constructor_docs = format!( let constructor_docs = format!(
"Creates a new [`{}`] instance. "Creates a new [`{}`] instance.
External values are allocated into the `store` provided and External values are allocated into the `store` provided and
configuration of the wasi instance itself should be all configuration of the wasi instance itself should be all
contained in the `cx` parameter.", contained in the `cx` parameter.",
instance_conf.name.to_string() module_conf.name.to_string()
); );
let ctx_type = names.ctx_type(); let ctx_type = names.ctx_type();
quote! { quote! {
#inst_docs #type_docs
pub struct #inst_type { pub struct #type_name {
#(#fields,)* #(#fields,)*
} }
impl #inst_type { impl #type_name {
#[doc = #constructor_docs] #[doc = #constructor_docs]
pub fn new(store: &wasmtime::Store, cx: #ctx_type) -> Self { pub fn new(store: &wasmtime::Store, cx: #ctx_type) -> Self {
let cx = std::rc::Rc::new(std::cell::RefCell::new(cx)); let cx = std::rc::Rc::new(std::cell::RefCell::new(cx));