Implement RFC 11: Redesigning Wasmtime's APIs (#2897)
Implement Wasmtime's new API as designed by RFC 11. This is quite a large commit which has had lots of discussion externally, so for more information it's best to read the RFC thread and the PR thread.
This commit is contained in:
@@ -6,21 +6,29 @@ use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use witx::{Document, Id, InterfaceFunc, Module, NamedType, TypeRef};
|
||||
|
||||
pub use crate::config::Asyncness;
|
||||
|
||||
pub struct CodegenSettings {
|
||||
pub errors: ErrorTransform,
|
||||
async_: AsyncConf,
|
||||
pub async_: AsyncConf,
|
||||
pub wasmtime: bool,
|
||||
}
|
||||
impl CodegenSettings {
|
||||
pub fn new(error_conf: &ErrorConf, async_: &AsyncConf, doc: &Document) -> Result<Self, Error> {
|
||||
pub fn new(
|
||||
error_conf: &ErrorConf,
|
||||
async_: &AsyncConf,
|
||||
doc: &Document,
|
||||
wasmtime: bool,
|
||||
) -> Result<Self, Error> {
|
||||
let errors = ErrorTransform::new(error_conf, doc)?;
|
||||
Ok(Self {
|
||||
errors,
|
||||
async_: async_.clone(),
|
||||
wasmtime,
|
||||
})
|
||||
}
|
||||
pub fn is_async(&self, module: &Module, func: &InterfaceFunc) -> bool {
|
||||
self.async_
|
||||
.is_async(module.name.as_str(), func.name.as_str())
|
||||
pub fn get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness {
|
||||
self.async_.get(module.name.as_str(), func.name.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,16 @@ pub struct Config {
|
||||
pub witx: WitxConf,
|
||||
pub errors: ErrorConf,
|
||||
pub async_: AsyncConf,
|
||||
pub wasmtime: bool,
|
||||
}
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(witx);
|
||||
syn::custom_keyword!(witx_literal);
|
||||
syn::custom_keyword!(block_on);
|
||||
syn::custom_keyword!(errors);
|
||||
syn::custom_keyword!(target);
|
||||
syn::custom_keyword!(wasmtime);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -21,12 +31,7 @@ pub enum ConfigField {
|
||||
Witx(WitxConf),
|
||||
Error(ErrorConf),
|
||||
Async(AsyncConf),
|
||||
}
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(witx);
|
||||
syn::custom_keyword!(witx_literal);
|
||||
syn::custom_keyword!(errors);
|
||||
Wasmtime(bool),
|
||||
}
|
||||
|
||||
impl Parse for ConfigField {
|
||||
@@ -48,8 +53,20 @@ impl Parse for ConfigField {
|
||||
input.parse::<Token![async]>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Async(AsyncConf {
|
||||
blocking: false,
|
||||
functions: input.parse()?,
|
||||
}))
|
||||
} else if lookahead.peek(kw::block_on) {
|
||||
input.parse::<kw::block_on>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Async(AsyncConf {
|
||||
blocking: true,
|
||||
functions: input.parse()?,
|
||||
}))
|
||||
} else if lookahead.peek(kw::wasmtime) {
|
||||
input.parse::<kw::wasmtime>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Wasmtime(input.parse::<syn::LitBool>()?.value))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
@@ -61,6 +78,7 @@ impl Config {
|
||||
let mut witx = None;
|
||||
let mut errors = None;
|
||||
let mut async_ = None;
|
||||
let mut wasmtime = None;
|
||||
for f in fields {
|
||||
match f {
|
||||
ConfigField::Witx(c) => {
|
||||
@@ -81,6 +99,12 @@ impl Config {
|
||||
}
|
||||
async_ = Some(c);
|
||||
}
|
||||
ConfigField::Wasmtime(c) => {
|
||||
if wasmtime.is_some() {
|
||||
return Err(Error::new(err_loc, "duplicate `wasmtime` field"));
|
||||
}
|
||||
wasmtime = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Config {
|
||||
@@ -89,6 +113,7 @@ impl Config {
|
||||
.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
|
||||
errors: errors.take().unwrap_or_default(),
|
||||
async_: async_.take().unwrap_or_default(),
|
||||
wasmtime: wasmtime.unwrap_or(true),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -284,9 +309,41 @@ impl Parse for ErrorConfField {
|
||||
#[derive(Clone, Default, Debug)]
|
||||
/// Modules and funcs that have async signatures
|
||||
pub struct AsyncConf {
|
||||
blocking: bool,
|
||||
functions: AsyncFunctions,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Asyncness {
|
||||
/// Wiggle function is synchronous, wasmtime Func is synchronous
|
||||
Sync,
|
||||
/// Wiggle function is asynchronous, but wasmtime Func is synchronous
|
||||
Blocking,
|
||||
/// Wiggle function and wasmtime Func are asynchronous.
|
||||
Async,
|
||||
}
|
||||
|
||||
impl Asyncness {
|
||||
pub fn is_async(&self) -> bool {
|
||||
match self {
|
||||
Self::Async => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_blocking(&self) -> bool {
|
||||
match self {
|
||||
Self::Blocking => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_sync(&self) -> bool {
|
||||
match self {
|
||||
Self::Sync => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AsyncFunctions {
|
||||
Some(HashMap<String, Vec<String>>),
|
||||
@@ -299,15 +356,36 @@ impl Default for AsyncFunctions {
|
||||
}
|
||||
|
||||
impl AsyncConf {
|
||||
pub fn is_async(&self, module: &str, function: &str) -> bool {
|
||||
pub fn get(&self, module: &str, function: &str) -> Asyncness {
|
||||
let a = if self.blocking {
|
||||
Asyncness::Blocking
|
||||
} else {
|
||||
Asyncness::Async
|
||||
};
|
||||
match &self.functions {
|
||||
AsyncFunctions::Some(fs) => fs
|
||||
.get(module)
|
||||
.and_then(|fs| fs.iter().find(|f| *f == function))
|
||||
.is_some(),
|
||||
AsyncFunctions::All => true,
|
||||
AsyncFunctions::Some(fs) => {
|
||||
if fs
|
||||
.get(module)
|
||||
.and_then(|fs| fs.iter().find(|f| *f == function))
|
||||
.is_some()
|
||||
{
|
||||
a
|
||||
} else {
|
||||
Asyncness::Sync
|
||||
}
|
||||
}
|
||||
AsyncFunctions::All => a,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_async(&self, module: &witx::Module) -> bool {
|
||||
for f in module.funcs() {
|
||||
if self.get(module.name.as_str(), f.name.as_str()).is_async() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for AsyncFunctions {
|
||||
@@ -378,3 +456,97 @@ impl Parse for AsyncConfField {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WasmtimeConfig {
|
||||
pub c: Config,
|
||||
pub target: syn::Path,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum WasmtimeConfigField {
|
||||
Core(ConfigField),
|
||||
Target(syn::Path),
|
||||
}
|
||||
impl WasmtimeConfig {
|
||||
pub fn build(fields: impl Iterator<Item = WasmtimeConfigField>, err_loc: Span) -> Result<Self> {
|
||||
let mut target = None;
|
||||
let mut cs = Vec::new();
|
||||
for f in fields {
|
||||
match f {
|
||||
WasmtimeConfigField::Target(c) => {
|
||||
if target.is_some() {
|
||||
return Err(Error::new(err_loc, "duplicate `target` field"));
|
||||
}
|
||||
target = Some(c);
|
||||
}
|
||||
WasmtimeConfigField::Core(c) => cs.push(c),
|
||||
}
|
||||
}
|
||||
let c = Config::build(cs.into_iter(), err_loc)?;
|
||||
Ok(WasmtimeConfig {
|
||||
c,
|
||||
target: target
|
||||
.take()
|
||||
.ok_or_else(|| Error::new(err_loc, "`target` field required"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for WasmtimeConfig {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let contents;
|
||||
let _lbrace = braced!(contents in input);
|
||||
let fields: Punctuated<WasmtimeConfigField, Token![,]> =
|
||||
contents.parse_terminated(WasmtimeConfigField::parse)?;
|
||||
Ok(WasmtimeConfig::build(fields.into_iter(), input.span())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for WasmtimeConfigField {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::target) {
|
||||
input.parse::<kw::target>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(WasmtimeConfigField::Target(input.parse()?))
|
||||
|
||||
// The remainder of this function is the ConfigField impl, wrapped in
|
||||
// WasmtimeConfigField::Core. This is required to get the correct lookahead error.
|
||||
} else if lookahead.peek(kw::witx) {
|
||||
input.parse::<kw::witx>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(WasmtimeConfigField::Core(ConfigField::Witx(
|
||||
WitxConf::Paths(input.parse()?),
|
||||
)))
|
||||
} else if lookahead.peek(kw::witx_literal) {
|
||||
input.parse::<kw::witx_literal>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(WasmtimeConfigField::Core(ConfigField::Witx(
|
||||
WitxConf::Literal(input.parse()?),
|
||||
)))
|
||||
} else if lookahead.peek(kw::errors) {
|
||||
input.parse::<kw::errors>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(WasmtimeConfigField::Core(ConfigField::Error(
|
||||
input.parse()?,
|
||||
)))
|
||||
} else if lookahead.peek(Token![async]) {
|
||||
input.parse::<Token![async]>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(WasmtimeConfigField::Core(ConfigField::Async(AsyncConf {
|
||||
blocking: false,
|
||||
functions: input.parse()?,
|
||||
})))
|
||||
} else if lookahead.peek(kw::block_on) {
|
||||
input.parse::<kw::block_on>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(WasmtimeConfigField::Core(ConfigField::Async(AsyncConf {
|
||||
blocking: true,
|
||||
functions: input.parse()?,
|
||||
})))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,26 @@ pub fn define_func(
|
||||
func: &witx::InterfaceFunc,
|
||||
settings: &CodegenSettings,
|
||||
) -> TokenStream {
|
||||
let (ts, _bounds) = _define_func(names, module, func, settings);
|
||||
ts
|
||||
}
|
||||
|
||||
pub fn func_bounds(
|
||||
names: &Names,
|
||||
module: &witx::Module,
|
||||
func: &witx::InterfaceFunc,
|
||||
settings: &CodegenSettings,
|
||||
) -> Vec<Ident> {
|
||||
let (_ts, bounds) = _define_func(names, module, func, settings);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn _define_func(
|
||||
names: &Names,
|
||||
module: &witx::Module,
|
||||
func: &witx::InterfaceFunc,
|
||||
settings: &CodegenSettings,
|
||||
) -> (TokenStream, Vec<Ident>) {
|
||||
let rt = names.runtime_mod();
|
||||
let ident = names.func(&func.name);
|
||||
|
||||
@@ -36,7 +56,7 @@ pub fn define_func(
|
||||
};
|
||||
|
||||
let mut body = TokenStream::new();
|
||||
let mut required_impls = vec![names.trait_name(&module.name)];
|
||||
let mut bounds = vec![names.trait_name(&module.name)];
|
||||
func.call_interface(
|
||||
&module.name,
|
||||
&mut Rust {
|
||||
@@ -49,37 +69,40 @@ pub fn define_func(
|
||||
module,
|
||||
funcname: func.name.as_str(),
|
||||
settings,
|
||||
required_impls: &mut required_impls,
|
||||
bounds: &mut bounds,
|
||||
},
|
||||
);
|
||||
|
||||
let asyncness = if settings.is_async(&module, &func) {
|
||||
quote!(async)
|
||||
} else {
|
||||
let asyncness = if settings.get_async(&module, &func).is_sync() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(async)
|
||||
};
|
||||
let mod_name = &module.name.as_str();
|
||||
let func_name = &func.name.as_str();
|
||||
quote! {
|
||||
#[allow(unreachable_code)] // deals with warnings in noreturn functions
|
||||
pub #asyncness fn #ident(
|
||||
ctx: &(impl #(#required_impls)+*),
|
||||
memory: &dyn #rt::GuestMemory,
|
||||
#(#abi_params),*
|
||||
) -> Result<#abi_ret, #rt::Trap> {
|
||||
use std::convert::TryFrom as _;
|
||||
(
|
||||
quote! {
|
||||
#[allow(unreachable_code)] // deals with warnings in noreturn functions
|
||||
pub #asyncness fn #ident(
|
||||
ctx: &mut (impl #(#bounds)+*),
|
||||
memory: &dyn #rt::GuestMemory,
|
||||
#(#abi_params),*
|
||||
) -> Result<#abi_ret, #rt::Trap> {
|
||||
use std::convert::TryFrom as _;
|
||||
|
||||
let _span = #rt::tracing::span!(
|
||||
#rt::tracing::Level::TRACE,
|
||||
"wiggle abi",
|
||||
module = #mod_name,
|
||||
function = #func_name
|
||||
);
|
||||
let _enter = _span.enter();
|
||||
let _span = #rt::tracing::span!(
|
||||
#rt::tracing::Level::TRACE,
|
||||
"wiggle abi",
|
||||
module = #mod_name,
|
||||
function = #func_name
|
||||
);
|
||||
let _enter = _span.enter();
|
||||
|
||||
#body
|
||||
}
|
||||
}
|
||||
#body
|
||||
}
|
||||
},
|
||||
bounds,
|
||||
)
|
||||
}
|
||||
|
||||
struct Rust<'a> {
|
||||
@@ -92,13 +115,13 @@ struct Rust<'a> {
|
||||
module: &'a witx::Module,
|
||||
funcname: &'a str,
|
||||
settings: &'a CodegenSettings,
|
||||
required_impls: &'a mut Vec<Ident>,
|
||||
bounds: &'a mut Vec<Ident>,
|
||||
}
|
||||
|
||||
impl Rust<'_> {
|
||||
fn required_impl(&mut self, i: Ident) {
|
||||
if !self.required_impls.contains(&i) {
|
||||
self.required_impls.push(i);
|
||||
fn bound(&mut self, i: Ident) {
|
||||
if !self.bounds.contains(&i) {
|
||||
self.bounds.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,13 +245,13 @@ impl witx::Bindgen for Rust<'_> {
|
||||
|
||||
let trait_name = self.names.trait_name(&self.module.name);
|
||||
let ident = self.names.func(&func.name);
|
||||
if self.settings.is_async(&self.module, &func) {
|
||||
if self.settings.get_async(&self.module, &func).is_sync() {
|
||||
self.src.extend(quote! {
|
||||
let ret = #trait_name::#ident(ctx, #(#args),*).await;
|
||||
let ret = #trait_name::#ident(ctx, #(#args),*);
|
||||
})
|
||||
} else {
|
||||
self.src.extend(quote! {
|
||||
let ret = #trait_name::#ident(ctx, #(#args),*);
|
||||
let ret = #trait_name::#ident(ctx, #(#args),*).await;
|
||||
})
|
||||
};
|
||||
self.src.extend(quote! {
|
||||
@@ -254,7 +277,7 @@ impl witx::Bindgen for Rust<'_> {
|
||||
let val = match self.settings.errors.for_name(ty) {
|
||||
Some(custom) => {
|
||||
let method = self.names.user_error_conversion_method(&custom);
|
||||
self.required_impl(quote::format_ident!("UserErrorConversion"));
|
||||
self.bound(quote::format_ident!("UserErrorConversion"));
|
||||
quote!(UserErrorConversion::#method(ctx, #val)?)
|
||||
}
|
||||
None => val,
|
||||
|
||||
@@ -5,6 +5,7 @@ mod lifetimes;
|
||||
mod module_trait;
|
||||
mod names;
|
||||
mod types;
|
||||
pub mod wasmtime;
|
||||
|
||||
use heck::ShoutySnakeCase;
|
||||
use lifetimes::anon_lifetime;
|
||||
@@ -12,7 +13,7 @@ use proc_macro2::{Literal, TokenStream};
|
||||
use quote::quote;
|
||||
|
||||
pub use codegen_settings::{CodegenSettings, UserErrorType};
|
||||
pub use config::Config;
|
||||
pub use config::{Config, WasmtimeConfig};
|
||||
pub use funcs::define_func;
|
||||
pub use module_trait::define_module_trait;
|
||||
pub use names::Names;
|
||||
@@ -42,7 +43,7 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings)
|
||||
let abi_typename = names.type_ref(&errtype.abi_type(), anon_lifetime());
|
||||
let user_typename = errtype.typename();
|
||||
let methodname = names.user_error_conversion_method(&errtype);
|
||||
quote!(fn #methodname(&self, e: super::#user_typename) -> Result<#abi_typename, #rt::Trap>;)
|
||||
quote!(fn #methodname(&mut self, e: super::#user_typename) -> Result<#abi_typename, #rt::Trap>;)
|
||||
});
|
||||
let user_error_conversion = quote! {
|
||||
pub trait UserErrorConversion {
|
||||
@@ -55,12 +56,20 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings)
|
||||
.funcs()
|
||||
.map(|f| define_func(&names, &module, &f, &settings));
|
||||
let modtrait = define_module_trait(&names, &module, &settings);
|
||||
let wasmtime = if settings.wasmtime {
|
||||
crate::wasmtime::link_module(&module, &names, None, &settings)
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
quote!(
|
||||
pub mod #modname {
|
||||
use super::types::*;
|
||||
pub use super::types::UserErrorConversion;
|
||||
#(#fs)*
|
||||
|
||||
#modtrait
|
||||
|
||||
#wasmtime
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
@@ -75,16 +75,16 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
let asyncness = if settings.is_async(&m, &f) {
|
||||
quote!(async)
|
||||
} else {
|
||||
let asyncness = if settings.get_async(&m, &f).is_sync() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(async)
|
||||
};
|
||||
|
||||
if is_anonymous {
|
||||
quote!(#asyncness fn #funcname(&self, #(#args),*) -> #result; )
|
||||
quote!(#asyncness fn #funcname(&mut self, #(#args),*) -> #result; )
|
||||
} else {
|
||||
quote!(#asyncness fn #funcname<#lifetime>(&self, #(#args),*) -> #result;)
|
||||
quote!(#asyncness fn #funcname<#lifetime>(&mut self, #(#args),*) -> #result;)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
186
crates/wiggle/generate/src/wasmtime.rs
Normal file
186
crates/wiggle/generate/src/wasmtime.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
use crate::config::Asyncness;
|
||||
use crate::funcs::func_bounds;
|
||||
use crate::{CodegenSettings, Names};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn link_module(
|
||||
module: &witx::Module,
|
||||
names: &Names,
|
||||
target_path: Option<&syn::Path>,
|
||||
settings: &CodegenSettings,
|
||||
) -> TokenStream {
|
||||
let module_ident = names.module(&module.name);
|
||||
|
||||
let send_bound = if settings.async_.contains_async(module) {
|
||||
quote! { + Send, T: Send }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let mut bodies = Vec::new();
|
||||
let mut bounds = HashSet::new();
|
||||
for f in module.funcs() {
|
||||
let asyncness = settings.async_.get(module.name.as_str(), f.name.as_str());
|
||||
bodies.push(generate_func(&module, &f, names, target_path, asyncness));
|
||||
let bound = func_bounds(names, module, &f, settings);
|
||||
for b in bound {
|
||||
bounds.insert(b);
|
||||
}
|
||||
}
|
||||
|
||||
let ctx_bound = if let Some(target_path) = target_path {
|
||||
let bounds = bounds
|
||||
.into_iter()
|
||||
.map(|b| quote!(#target_path::#module_ident::#b));
|
||||
quote!( #(#bounds)+* #send_bound )
|
||||
} else {
|
||||
let bounds = bounds.into_iter();
|
||||
quote!( #(#bounds)+* #send_bound )
|
||||
};
|
||||
|
||||
let func_name = if target_path.is_none() {
|
||||
format_ident!("add_to_linker")
|
||||
} else {
|
||||
format_ident!("add_{}_to_linker", module_ident)
|
||||
};
|
||||
|
||||
let rt = names.runtime_mod();
|
||||
|
||||
quote! {
|
||||
/// Adds all instance items to the specified `Linker`.
|
||||
pub fn #func_name<T, U>(
|
||||
linker: &mut #rt::wasmtime_crate::Linker<T>,
|
||||
get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
|
||||
) -> #rt::anyhow::Result<()>
|
||||
where
|
||||
U: #ctx_bound #send_bound
|
||||
{
|
||||
#(#bodies)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_func(
|
||||
module: &witx::Module,
|
||||
func: &witx::InterfaceFunc,
|
||||
names: &Names,
|
||||
target_path: Option<&syn::Path>,
|
||||
asyncness: Asyncness,
|
||||
) -> TokenStream {
|
||||
let rt = names.runtime_mod();
|
||||
|
||||
let module_str = module.name.as_str();
|
||||
let module_ident = names.module(&module.name);
|
||||
|
||||
let field_str = func.name.as_str();
|
||||
let field_ident = names.func(&func.name);
|
||||
|
||||
let (params, results) = func.wasm_signature();
|
||||
|
||||
let arg_names = (0..params.len())
|
||||
.map(|i| Ident::new(&format!("arg{}", i), Span::call_site()))
|
||||
.collect::<Vec<_>>();
|
||||
let arg_decls = params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, ty)| {
|
||||
let name = &arg_names[i];
|
||||
let wasm = names.wasm_type(*ty);
|
||||
quote! { #name: #wasm }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ret_ty = match results.len() {
|
||||
0 => quote!(()),
|
||||
1 => names.wasm_type(results[0]),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
let await_ = if asyncness.is_sync() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(.await)
|
||||
};
|
||||
|
||||
let abi_func = if let Some(target_path) = target_path {
|
||||
quote!( #target_path::#module_ident::#field_ident )
|
||||
} else {
|
||||
quote!( #field_ident )
|
||||
};
|
||||
|
||||
let body = quote! {
|
||||
let mem = match caller.get_export("memory") {
|
||||
Some(#rt::wasmtime_crate::Extern::Memory(m)) => m,
|
||||
_ => {
|
||||
return Err(#rt::wasmtime_crate::Trap::new("missing required memory export"));
|
||||
}
|
||||
};
|
||||
// Note the unsafety here. Our goal is to simultaneously borrow the
|
||||
// memory and custom data from `caller`, and the store it's connected
|
||||
// to. Rust will not let us do that, however, because we must call two
|
||||
// separate methods (both of which borrow the whole `caller`) and one of
|
||||
// our borrows is mutable (the custom data).
|
||||
//
|
||||
// This operation, however, is safe because these borrows do not overlap
|
||||
// and in the process of borrowing them mutability doesn't actually
|
||||
// touch anything. This is akin to mutably borrowing two indices in an
|
||||
// array, which is safe so long as the indices are separate.
|
||||
//
|
||||
// TODO: depending on how common this is for other users to run into we
|
||||
// may wish to consider adding a dedicated method for this. For now the
|
||||
// future of `GuestPtr` may be a bit hazy, so let's just get this
|
||||
// working from the previous iteration for now.
|
||||
let (ctx, mem) = unsafe {
|
||||
let mem = &mut *(mem.data_mut(&mut caller) as *mut [u8]);
|
||||
(get_cx(caller.data_mut()), #rt::wasmtime::WasmtimeGuestMemory::new(mem))
|
||||
};
|
||||
match #abi_func(ctx, &mem #(, #arg_names)*) #await_ {
|
||||
Ok(r) => Ok(<#ret_ty>::from(r)),
|
||||
Err(#rt::Trap::String(err)) => Err(#rt::wasmtime_crate::Trap::new(err)),
|
||||
Err(#rt::Trap::I32Exit(err)) => Err(#rt::wasmtime_crate::Trap::i32_exit(err)),
|
||||
}
|
||||
};
|
||||
|
||||
match asyncness {
|
||||
Asyncness::Async => {
|
||||
let wrapper = format_ident!("func_wrap{}_async", params.len());
|
||||
quote! {
|
||||
linker.#wrapper(
|
||||
#module_str,
|
||||
#field_str,
|
||||
move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| {
|
||||
Box::new(async move { #body })
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Asyncness::Blocking => {
|
||||
quote! {
|
||||
linker.func_wrap(
|
||||
#module_str,
|
||||
#field_str,
|
||||
move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, #rt::wasmtime_crate::Trap> {
|
||||
let result = async { #body };
|
||||
#rt::run_in_dummy_executor(result)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Asyncness::Sync => {
|
||||
quote! {
|
||||
linker.func_wrap(
|
||||
#module_str,
|
||||
#field_str,
|
||||
move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, #rt::wasmtime_crate::Trap> {
|
||||
#body
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user