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:
@@ -17,14 +17,33 @@ wiggle-macro = { path = "macro", version = "0.27.0" }
|
||||
tracing = "0.1.15"
|
||||
bitflags = "1.2"
|
||||
async-trait = "0.1.42"
|
||||
wasmtime = { path = "../wasmtime", version = "0.27.0", optional = true, default-features = false }
|
||||
anyhow = "1.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[dev-dependencies]
|
||||
wiggle-test = { path = "test-helpers" }
|
||||
anyhow = "1"
|
||||
proptest = "1.0.0"
|
||||
|
||||
[[test]]
|
||||
name = "wasmtime_async"
|
||||
path = "tests/wasmtime_async.rs"
|
||||
required-features = ["wasmtime_async", "wasmtime/wat"]
|
||||
|
||||
[[test]]
|
||||
name = "wasmtime_sync"
|
||||
path = "tests/wasmtime_sync.rs"
|
||||
required-features = ["wasmtime_integration", "wasmtime/wat"]
|
||||
|
||||
[[test]]
|
||||
name = "wasmtime_integration"
|
||||
path = "tests/wasmtime_integration.rs"
|
||||
required-features = ["wasmtime_integration", "wasmtime/wat"]
|
||||
|
||||
|
||||
[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
|
||||
@@ -38,4 +57,9 @@ wiggle_metadata = ['witx', "wiggle-macro/wiggle_metadata"]
|
||||
# the logs out of wiggle-generated libraries.
|
||||
tracing_log = [ "tracing/log" ]
|
||||
|
||||
default = ["wiggle_metadata" ]
|
||||
# Generate adapters for wasmtime, and expose the wasmtime_integration macro.
|
||||
wasmtime_integration = [ "wasmtime", "wiggle-macro/wasmtime" ]
|
||||
# Support for async in the wasmtime crates.
|
||||
wasmtime_async = [ "wasmtime_integration", "wasmtime/async" ]
|
||||
|
||||
default = ["wiggle_metadata", "wasmtime_integration" ]
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "wiggle-borrow"
|
||||
version = "0.27.0"
|
||||
authors = ["Pat Hickey <phickey@fastly.com>", "Jakub Konka <kubkonk@jakubkonka.com>", "Alex Crichton <alex@alexcrichton.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
description = "A run-time borrow checker for use with Wiggle"
|
||||
categories = ["wasm"]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
include = ["src/**/*", "LICENSE"]
|
||||
|
||||
[dependencies]
|
||||
wiggle = { path = "..", version = "0.27.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
@@ -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
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,4 +31,5 @@ proc-macro2 = "1.0"
|
||||
wiggle = { path = ".." }
|
||||
|
||||
[features]
|
||||
wasmtime = []
|
||||
wiggle_metadata = []
|
||||
|
||||
@@ -17,15 +17,19 @@ use syn::parse_macro_input;
|
||||
/// * For each `@interface func` defined in a witx module, an abi-level
|
||||
/// function is generated which takes ABI-level arguments, along with
|
||||
/// a ref that impls the module trait, and a `GuestMemory` implementation.
|
||||
/// Users typically won't use these abi-level functions: The `wasmtime-wiggle`
|
||||
/// and `lucet-wiggle` crates adapt these to work with a particular WebAssembly
|
||||
/// engine.
|
||||
/// Users typically won't use these abi-level functions: Either the
|
||||
/// `wasmtime_integration` macro or the `lucet-wiggle` crates adapt these
|
||||
/// to work with a particular WebAssembly engine.
|
||||
///
|
||||
/// * A public "module trait" is defined (called the module name, in
|
||||
/// SnakeCase) which has a `&self` method for each function in the
|
||||
/// module. These methods takes idiomatic Rust types for each argument
|
||||
/// and return `Result<($return_types),$error_type>`
|
||||
///
|
||||
/// * When the `wiggle` crate is built with the `wasmtime_integration`
|
||||
/// feature, each module contains an `add_to_linker` function to add it to
|
||||
/// a `wasmtime::Linker`.
|
||||
///
|
||||
/// Arguments are provided using Rust struct value syntax.
|
||||
///
|
||||
/// * `witx` takes a list of string literal paths. Paths are relative to the
|
||||
@@ -95,11 +99,11 @@ use syn::parse_macro_input;
|
||||
/// /// whereas the witx-defined types like `Excuse` and `Errno` come
|
||||
/// /// from the `pub mod types` emitted by the `wiggle::from_witx!`
|
||||
/// /// invocation above.
|
||||
/// fn int_float_args(&self, _int: u32, _floats: &GuestPtr<[f32]>)
|
||||
/// fn int_float_args(&mut self, _int: u32, _floats: &GuestPtr<[f32]>)
|
||||
/// -> Result<(), YourRichError> {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
/// async fn double_int_return_float(&self, int: u32)
|
||||
/// async fn double_int_return_float(&mut self, int: u32)
|
||||
/// -> Result<f32, YourRichError> {
|
||||
/// Ok(int.checked_mul(2).ok_or(YourRichError::Overflow)? as f32)
|
||||
/// }
|
||||
@@ -121,7 +125,7 @@ use syn::parse_macro_input;
|
||||
/// /// to terminate WebAssembly execution by trapping.
|
||||
///
|
||||
/// impl types::UserErrorConversion for YourCtxType {
|
||||
/// fn errno_from_your_rich_error(&self, e: YourRichError)
|
||||
/// fn errno_from_your_rich_error(&mut self, e: YourRichError)
|
||||
/// -> Result<types::Errno, wiggle::Trap>
|
||||
/// {
|
||||
/// println!("Rich error: {:?}", e);
|
||||
@@ -144,11 +148,15 @@ pub fn from_witx(args: TokenStream) -> TokenStream {
|
||||
let doc = config.load_document();
|
||||
let names = wiggle_generate::Names::new(quote!(wiggle));
|
||||
|
||||
let error_transform =
|
||||
wiggle_generate::CodegenSettings::new(&config.errors, &config.async_, &doc)
|
||||
.expect("validating codegen settings");
|
||||
let settings = wiggle_generate::CodegenSettings::new(
|
||||
&config.errors,
|
||||
&config.async_,
|
||||
&doc,
|
||||
cfg!(feature = "wasmtime") && config.wasmtime,
|
||||
)
|
||||
.expect("validating codegen settings");
|
||||
|
||||
let code = wiggle_generate::generate(&doc, &names, &error_transform);
|
||||
let code = wiggle_generate::generate(&doc, &names, &settings);
|
||||
let metadata = if cfg!(feature = "wiggle_metadata") {
|
||||
wiggle_generate::generate_metadata(&doc, &names)
|
||||
} else {
|
||||
@@ -163,7 +171,35 @@ pub fn async_trait(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let _ = parse_macro_input!(attr as syn::parse::Nothing);
|
||||
let item = proc_macro2::TokenStream::from(item);
|
||||
TokenStream::from(quote! {
|
||||
#[wiggle::async_trait_crate::async_trait(?Send)]
|
||||
#[wiggle::async_trait_crate::async_trait]
|
||||
#item
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasmtime")]
|
||||
/// 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.
|
||||
#[proc_macro]
|
||||
pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
|
||||
let config = parse_macro_input!(args as wiggle_generate::WasmtimeConfig);
|
||||
let doc = config.c.load_document();
|
||||
let names = wiggle_generate::Names::new(quote!(wiggle));
|
||||
|
||||
let settings = wiggle_generate::CodegenSettings::new(
|
||||
&config.c.errors,
|
||||
&config.c.async_,
|
||||
&doc,
|
||||
cfg!(feature = "wasmtime"),
|
||||
)
|
||||
.expect("validating codegen settings");
|
||||
|
||||
let modules = doc.modules().map(|module| {
|
||||
wiggle_generate::wasmtime::link_module(&module, &names, Some(&config.target), &settings)
|
||||
});
|
||||
quote!( #(#modules)* ).into()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
use std::cell::RefCell;
|
||||
use crate::{BorrowHandle, GuestError, Region};
|
||||
use std::collections::HashMap;
|
||||
use wiggle::{BorrowHandle, GuestError, Region};
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub struct BorrowChecker {
|
||||
/// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking
|
||||
/// overlap, the method calls on this member will be confusing.
|
||||
bc: RefCell<InnerBorrowChecker>,
|
||||
///
|
||||
/// Also, unfortunately, for now this uses a `Mutex`. The reason for that is
|
||||
/// that this is shared as `&BorrowChecker` in a bunch of `GuestPtr` values.
|
||||
/// Through this sharing we still want each `GuestPtr` to be `Send` and the
|
||||
/// "naive" way to make `&T` `Send` with interior mutability is to use a
|
||||
/// `Mutex`. Fixing this will likely require rethinking `GuestPtr` one way
|
||||
/// or another. That needs to happen for other reasons as well (for example
|
||||
/// to allow for wasm calls to happen while `GuestPtr` values are active),
|
||||
/// so it's hoped that in a later rethinking of `GuestPtr` we can revisit
|
||||
/// this and remove this `Mutex`.
|
||||
bc: Mutex<InnerBorrowChecker>,
|
||||
}
|
||||
|
||||
impl BorrowChecker {
|
||||
@@ -19,7 +29,7 @@ impl BorrowChecker {
|
||||
/// shared or mutable borrow.
|
||||
pub fn new() -> Self {
|
||||
BorrowChecker {
|
||||
bc: RefCell::new(InnerBorrowChecker::new()),
|
||||
bc: Mutex::new(InnerBorrowChecker::new()),
|
||||
}
|
||||
}
|
||||
/// Indicates whether any outstanding shared or mutable borrows are known
|
||||
@@ -27,25 +37,25 @@ impl BorrowChecker {
|
||||
/// to be safe to recursively call into a WebAssembly module, or to
|
||||
/// manipulate the WebAssembly memory by any other means.
|
||||
pub fn has_outstanding_borrows(&self) -> bool {
|
||||
self.bc.borrow().has_outstanding_borrows()
|
||||
self.bc.lock().unwrap().has_outstanding_borrows()
|
||||
}
|
||||
pub fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
|
||||
self.bc.borrow_mut().shared_borrow(r)
|
||||
self.bc.lock().unwrap().shared_borrow(r)
|
||||
}
|
||||
pub fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
|
||||
self.bc.borrow_mut().mut_borrow(r)
|
||||
self.bc.lock().unwrap().mut_borrow(r)
|
||||
}
|
||||
pub fn shared_unborrow(&self, h: BorrowHandle) {
|
||||
self.bc.borrow_mut().shared_unborrow(h)
|
||||
self.bc.lock().unwrap().shared_unborrow(h)
|
||||
}
|
||||
pub fn mut_unborrow(&self, h: BorrowHandle) {
|
||||
self.bc.borrow_mut().mut_unborrow(h)
|
||||
self.bc.lock().unwrap().mut_unborrow(h)
|
||||
}
|
||||
pub fn is_shared_borrowed(&self, r: Region) -> bool {
|
||||
self.bc.borrow().is_shared_borrowed(r)
|
||||
self.bc.lock().unwrap().is_shared_borrowed(r)
|
||||
}
|
||||
pub fn is_mut_borrowed(&self, r: Region) -> bool {
|
||||
self.bc.borrow().is_mut_borrowed(r)
|
||||
self.bc.lock().unwrap().is_mut_borrowed(r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
use std::marker;
|
||||
use std::rc::Rc;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use wiggle_macro::{async_trait, from_witx};
|
||||
|
||||
#[cfg(feature = "wasmtime")]
|
||||
pub use anyhow;
|
||||
#[cfg(feature = "wasmtime")]
|
||||
pub use wiggle_macro::wasmtime_integration;
|
||||
|
||||
pub use bitflags;
|
||||
|
||||
#[cfg(feature = "wiggle_metadata")]
|
||||
pub use witx;
|
||||
|
||||
pub mod borrow;
|
||||
mod error;
|
||||
mod guest_type;
|
||||
mod region;
|
||||
@@ -27,6 +30,14 @@ pub mod async_trait_crate {
|
||||
pub use async_trait::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasmtime")]
|
||||
pub mod wasmtime;
|
||||
|
||||
#[cfg(feature = "wasmtime")]
|
||||
pub mod wasmtime_crate {
|
||||
pub use wasmtime::*;
|
||||
}
|
||||
|
||||
/// A trait which abstracts how to get at the region of host memory taht
|
||||
/// contains guest memory.
|
||||
///
|
||||
@@ -79,7 +90,7 @@ pub mod async_trait_crate {
|
||||
/// the contents of a WebAssembly memory, all `GuestSlice`s and `GuestStr`s
|
||||
/// for the memory must be dropped, at which point
|
||||
/// `GuestMemory::has_outstanding_borrows()` will return `false`.
|
||||
pub unsafe trait GuestMemory {
|
||||
pub unsafe trait GuestMemory: Send + Sync {
|
||||
/// Returns the base allocation of this guest memory, located in host
|
||||
/// memory.
|
||||
///
|
||||
@@ -275,33 +286,6 @@ unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Box<T> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Rc<T> {
|
||||
fn base(&self) -> (*mut u8, u32) {
|
||||
T::base(self)
|
||||
}
|
||||
fn has_outstanding_borrows(&self) -> bool {
|
||||
T::has_outstanding_borrows(self)
|
||||
}
|
||||
fn is_mut_borrowed(&self, r: Region) -> bool {
|
||||
T::is_mut_borrowed(self, r)
|
||||
}
|
||||
fn is_shared_borrowed(&self, r: Region) -> bool {
|
||||
T::is_shared_borrowed(self, r)
|
||||
}
|
||||
fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
|
||||
T::mut_borrow(self, r)
|
||||
}
|
||||
fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
|
||||
T::shared_borrow(self, r)
|
||||
}
|
||||
fn mut_unborrow(&self, h: BorrowHandle) {
|
||||
T::mut_unborrow(self, h)
|
||||
}
|
||||
fn shared_unborrow(&self, h: BorrowHandle) {
|
||||
T::shared_unborrow(self, h)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Arc<T> {
|
||||
fn base(&self) -> (*mut u8, u32) {
|
||||
T::base(self)
|
||||
@@ -382,7 +366,6 @@ unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Arc<T> {
|
||||
pub struct GuestPtr<'a, T: ?Sized + Pointee> {
|
||||
mem: &'a (dyn GuestMemory + 'a),
|
||||
pointer: T::Pointer,
|
||||
_marker: marker::PhantomData<&'a Cell<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> {
|
||||
@@ -392,11 +375,7 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> {
|
||||
/// value is a `u32` offset into guest memory. For slices and strings,
|
||||
/// `pointer` is a `(u32, u32)` offset/length pair.
|
||||
pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'a, T> {
|
||||
GuestPtr {
|
||||
mem,
|
||||
pointer,
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
GuestPtr { mem, pointer }
|
||||
}
|
||||
|
||||
/// Returns the offset of this pointer in guest memory.
|
||||
@@ -630,7 +609,7 @@ impl<'a, T> GuestPtr<'a, [T]> {
|
||||
/// of this guest pointer is not equal to the length of the slice provided.
|
||||
pub fn copy_from_slice(&self, slice: &[T]) -> Result<(), GuestError>
|
||||
where
|
||||
T: GuestTypeTransparent<'a> + Copy,
|
||||
T: GuestTypeTransparent<'a> + Copy + 'a,
|
||||
{
|
||||
// bounds check ...
|
||||
let mut self_slice = self.as_slice_mut()?;
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
pub use wasmtime_wiggle_macro::*;
|
||||
pub use wiggle::*;
|
||||
|
||||
use wiggle_borrow::BorrowChecker;
|
||||
use crate::borrow::BorrowChecker;
|
||||
use crate::{BorrowHandle, GuestError, GuestMemory, Region};
|
||||
|
||||
/// Lightweight `wasmtime::Memory` wrapper so we can implement the
|
||||
/// `wiggle::GuestMemory` trait on it.
|
||||
pub struct WasmtimeGuestMemory {
|
||||
mem: wasmtime::Memory,
|
||||
pub struct WasmtimeGuestMemory<'a> {
|
||||
mem: &'a mut [u8],
|
||||
bc: BorrowChecker,
|
||||
}
|
||||
|
||||
impl WasmtimeGuestMemory {
|
||||
pub fn new(mem: wasmtime::Memory) -> Self {
|
||||
impl<'a> WasmtimeGuestMemory<'a> {
|
||||
pub fn new(mem: &'a mut [u8]) -> Self {
|
||||
Self {
|
||||
mem,
|
||||
// Wiggle does not expose any methods for functions to re-enter
|
||||
@@ -28,9 +26,9 @@ impl WasmtimeGuestMemory {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl GuestMemory for WasmtimeGuestMemory {
|
||||
unsafe impl GuestMemory for WasmtimeGuestMemory<'_> {
|
||||
fn base(&self) -> (*mut u8, u32) {
|
||||
(self.mem.data_ptr(), self.mem.data_size() as _)
|
||||
(self.mem.as_ptr() as *mut u8, self.mem.len() as u32)
|
||||
}
|
||||
fn has_outstanding_borrows(&self) -> bool {
|
||||
self.bc.has_outstanding_borrows()
|
||||
@@ -14,7 +14,6 @@ publish = false
|
||||
[dependencies]
|
||||
proptest = "1.0.0"
|
||||
wiggle = { path = "..", features = ["tracing_log"] }
|
||||
wiggle-borrow = { path = "../borrow" }
|
||||
|
||||
[dev-dependencies]
|
||||
thiserror = "1.0"
|
||||
|
||||
@@ -32,7 +32,7 @@ impl_errno!(types::Errno);
|
||||
/// When the `errors` mapping in witx is non-empty, we need to impl the
|
||||
/// types::UserErrorConversion trait that wiggle generates from that mapping.
|
||||
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
||||
fn errno_from_rich_error(&self, e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||
fn errno_from_rich_error(&mut self, e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||
wiggle::tracing::debug!(
|
||||
rich_error = wiggle::tracing::field::debug(&e),
|
||||
"error conversion"
|
||||
@@ -49,7 +49,7 @@ impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> {
|
||||
fn foo(&self, strike: u32, _s: &types::S) -> Result<types::T, RichError> {
|
||||
fn foo(&mut self, strike: u32, _s: &types::S) -> Result<types::T, RichError> {
|
||||
// We use the argument to this function to exercise all of the
|
||||
// possible error cases we could hit here
|
||||
match strike {
|
||||
@@ -78,12 +78,12 @@ fn main() {
|
||||
env_logger::init();
|
||||
}
|
||||
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Exercise each of the branches in `foo`.
|
||||
// Start with the success case:
|
||||
let r0 = one_error_conversion::foo(&ctx, &host_memory, 0, 0, 8);
|
||||
let r0 = one_error_conversion::foo(&mut ctx, &host_memory, 0, 0, 8);
|
||||
assert_eq!(
|
||||
r0,
|
||||
Ok(types::Errno::Ok as i32),
|
||||
@@ -92,7 +92,7 @@ fn main() {
|
||||
assert!(ctx.log.borrow().is_empty(), "No error log for strike=0");
|
||||
|
||||
// First error case:
|
||||
let r1 = one_error_conversion::foo(&ctx, &host_memory, 1, 0, 8);
|
||||
let r1 = one_error_conversion::foo(&mut ctx, &host_memory, 1, 0, 8);
|
||||
assert_eq!(
|
||||
r1,
|
||||
Ok(types::Errno::PicketLine as i32),
|
||||
@@ -105,7 +105,7 @@ fn main() {
|
||||
);
|
||||
|
||||
// Second error case:
|
||||
let r2 = one_error_conversion::foo(&ctx, &host_memory, 2, 0, 8);
|
||||
let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2, 0, 8);
|
||||
assert_eq!(
|
||||
r2,
|
||||
Ok(types::Errno::InvalidArg as i32),
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use proptest::prelude::*;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::marker;
|
||||
use wiggle::{BorrowHandle, GuestMemory, Region};
|
||||
|
||||
use wiggle_borrow::BorrowChecker;
|
||||
use wiggle::{borrow::BorrowChecker, BorrowHandle, GuestMemory, Region};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemAreas(Vec<MemArea>);
|
||||
@@ -49,6 +47,9 @@ struct HostBuffer {
|
||||
cell: UnsafeCell<[u8; 4096]>,
|
||||
}
|
||||
|
||||
unsafe impl Send for HostBuffer {}
|
||||
unsafe impl Sync for HostBuffer {}
|
||||
|
||||
pub struct HostMemory {
|
||||
buffer: HostBuffer,
|
||||
bc: BorrowChecker,
|
||||
|
||||
@@ -9,11 +9,14 @@ wiggle::from_witx!({
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> atoms::Atoms for WasiCtx<'a> {
|
||||
fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||
Ok(())
|
||||
}
|
||||
fn double_int_return_float(&self, an_int: u32) -> Result<types::AliasToFloat, types::Errno> {
|
||||
fn double_int_return_float(
|
||||
&mut self,
|
||||
an_int: u32,
|
||||
) -> Result<types::AliasToFloat, types::Errno> {
|
||||
Ok((an_int as f32) * 2.0)
|
||||
}
|
||||
}
|
||||
@@ -28,10 +31,10 @@ struct IntFloatExercise {
|
||||
|
||||
impl IntFloatExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = atoms::int_float_args(&ctx, &host_memory, self.an_int as i32, self.an_float);
|
||||
let e = atoms::int_float_args(&mut ctx, &host_memory, self.an_int as i32, self.an_float);
|
||||
|
||||
assert_eq!(e, Ok(types::Errno::Ok as i32), "int_float_args error");
|
||||
}
|
||||
@@ -57,11 +60,11 @@ struct DoubleIntExercise {
|
||||
|
||||
impl DoubleIntExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = atoms::double_int_return_float(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
|
||||
@@ -14,12 +14,12 @@ impl_errno!(types::Errno);
|
||||
|
||||
#[wiggle::async_trait]
|
||||
impl<'a> atoms::Atoms for WasiCtx<'a> {
|
||||
async fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
async fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||
Ok(())
|
||||
}
|
||||
async fn double_int_return_float(
|
||||
&self,
|
||||
&mut self,
|
||||
an_int: u32,
|
||||
) -> Result<types::AliasToFloat, types::Errno> {
|
||||
Ok((an_int as f32) * 2.0)
|
||||
@@ -36,11 +36,11 @@ struct IntFloatExercise {
|
||||
|
||||
impl IntFloatExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = run(atoms::int_float_args(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.an_int as i32,
|
||||
self.an_float,
|
||||
@@ -70,11 +70,11 @@ struct DoubleIntExercise {
|
||||
|
||||
impl DoubleIntExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = run(atoms::double_int_return_float(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
|
||||
@@ -31,7 +31,7 @@ mod convert_just_errno {
|
||||
/// When the `errors` mapping in witx is non-empty, we need to impl the
|
||||
/// types::UserErrorConversion trait that wiggle generates from that mapping.
|
||||
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
||||
fn errno_from_rich_error(&self, e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||
fn errno_from_rich_error(&mut self, e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||
// WasiCtx can collect a Vec<String> log so we can test this. We're
|
||||
// logging the Display impl that `thiserror::Error` provides us.
|
||||
self.log.borrow_mut().push(e.to_string());
|
||||
@@ -44,7 +44,7 @@ mod convert_just_errno {
|
||||
}
|
||||
|
||||
impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> {
|
||||
fn foo(&self, strike: u32) -> Result<(), RichError> {
|
||||
fn foo(&mut self, strike: u32) -> Result<(), RichError> {
|
||||
// We use the argument to this function to exercise all of the
|
||||
// possible error cases we could hit here
|
||||
match strike {
|
||||
@@ -57,12 +57,12 @@ mod convert_just_errno {
|
||||
|
||||
#[test]
|
||||
fn one_error_conversion_test() {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Exercise each of the branches in `foo`.
|
||||
// Start with the success case:
|
||||
let r0 = one_error_conversion::foo(&ctx, &host_memory, 0);
|
||||
let r0 = one_error_conversion::foo(&mut ctx, &host_memory, 0);
|
||||
assert_eq!(
|
||||
r0,
|
||||
Ok(types::Errno::Ok as i32),
|
||||
@@ -71,7 +71,7 @@ mod convert_just_errno {
|
||||
assert!(ctx.log.borrow().is_empty(), "No error log for strike=0");
|
||||
|
||||
// First error case:
|
||||
let r1 = one_error_conversion::foo(&ctx, &host_memory, 1);
|
||||
let r1 = one_error_conversion::foo(&mut ctx, &host_memory, 1);
|
||||
assert_eq!(
|
||||
r1,
|
||||
Ok(types::Errno::PicketLine as i32),
|
||||
@@ -84,7 +84,7 @@ mod convert_just_errno {
|
||||
);
|
||||
|
||||
// Second error case:
|
||||
let r2 = one_error_conversion::foo(&ctx, &host_memory, 2);
|
||||
let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2);
|
||||
assert_eq!(
|
||||
r2,
|
||||
Ok(types::Errno::InvalidArg as i32),
|
||||
@@ -140,11 +140,11 @@ mod convert_multiple_error_types {
|
||||
// each member of the `errors` mapping.
|
||||
// Bodies elided.
|
||||
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
||||
fn errno_from_rich_error(&self, _e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||
fn errno_from_rich_error(&mut self, _e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn errno2_from_another_rich_error(
|
||||
&self,
|
||||
&mut self,
|
||||
_e: AnotherRichError,
|
||||
) -> Result<types::Errno2, wiggle::Trap> {
|
||||
unimplemented!()
|
||||
@@ -153,13 +153,13 @@ mod convert_multiple_error_types {
|
||||
|
||||
// And here's the witx module trait impl, bodies elided
|
||||
impl<'a> two_error_conversions::TwoErrorConversions for WasiCtx<'a> {
|
||||
fn foo(&self, _: u32) -> Result<(), RichError> {
|
||||
fn foo(&mut self, _: u32) -> Result<(), RichError> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn bar(&self, _: u32) -> Result<(), AnotherRichError> {
|
||||
fn bar(&mut self, _: u32) -> Result<(), AnotherRichError> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn baz(&self, _: u32) -> wiggle::Trap {
|
||||
fn baz(&mut self, _: u32) -> wiggle::Trap {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> flags::Flags for WasiCtx<'a> {
|
||||
fn configure_car(
|
||||
&self,
|
||||
&mut self,
|
||||
old_config: types::CarConfig,
|
||||
other_config_ptr: &GuestPtr<types::CarConfig>,
|
||||
) -> Result<types::CarConfig, types::Errno> {
|
||||
@@ -62,7 +62,7 @@ impl ConfigureCarExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate input ptr
|
||||
@@ -72,7 +72,7 @@ impl ConfigureCarExercise {
|
||||
.expect("deref ptr mut to CarConfig");
|
||||
|
||||
let res = flags::configure_car(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.old_config.bits() as i32,
|
||||
self.other_config_by_ptr.ptr as i32,
|
||||
|
||||
@@ -11,10 +11,10 @@ wiggle::from_witx!({
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> handle_examples::HandleExamples for WasiCtx<'a> {
|
||||
fn fd_create(&self) -> Result<types::Fd, types::Errno> {
|
||||
fn fd_create(&mut self) -> Result<types::Fd, types::Errno> {
|
||||
Ok(types::Fd::from(FD_VAL))
|
||||
}
|
||||
fn fd_consume(&self, fd: types::Fd) -> Result<(), types::Errno> {
|
||||
fn fd_consume(&mut self, fd: types::Fd) -> Result<(), types::Errno> {
|
||||
println!("FD_CONSUME {}", fd);
|
||||
if fd == types::Fd::from(FD_VAL) {
|
||||
Ok(())
|
||||
@@ -31,10 +31,10 @@ struct HandleExercise {
|
||||
|
||||
impl HandleExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = handle_examples::fd_create(&ctx, &host_memory, self.return_loc.ptr as i32);
|
||||
let e = handle_examples::fd_create(&mut ctx, &host_memory, self.return_loc.ptr as i32);
|
||||
|
||||
assert_eq!(e, Ok(types::Errno::Ok as i32), "fd_create error");
|
||||
|
||||
@@ -45,11 +45,11 @@ impl HandleExercise {
|
||||
|
||||
assert_eq!(h_got, 123, "fd_create return val");
|
||||
|
||||
let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32);
|
||||
let e = handle_examples::fd_consume(&mut ctx, &host_memory, h_got as i32);
|
||||
|
||||
assert_eq!(e, Ok(types::Errno::Ok as i32), "fd_consume error");
|
||||
|
||||
let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32 + 1);
|
||||
let e = handle_examples::fd_consume(&mut ctx, &host_memory, h_got as i32 + 1);
|
||||
|
||||
assert_eq!(
|
||||
e,
|
||||
|
||||
@@ -10,7 +10,7 @@ wiggle::from_witx!({
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> ints::Ints for WasiCtx<'a> {
|
||||
fn cookie_cutter(&self, init_cookie: types::Cookie) -> Result<types::Bool, types::Errno> {
|
||||
fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result<types::Bool, types::Errno> {
|
||||
let res = if init_cookie == types::COOKIE_START {
|
||||
types::Bool::True
|
||||
} else {
|
||||
@@ -43,11 +43,11 @@ impl CookieCutterExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let res = ints::cookie_cutter(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.cookie as i64,
|
||||
self.return_ptr_loc.ptr as i32,
|
||||
|
||||
@@ -33,7 +33,7 @@ mod module_trait_fn_and_arg_test {
|
||||
});
|
||||
impl<'a> self_::Self_ for WasiCtx<'a> {
|
||||
#[allow(unused_variables)]
|
||||
fn fn_(&self, use_: u32, virtual_: u32) {
|
||||
fn fn_(&mut self, use_: u32, virtual_: u32) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> lists::Lists for WasiCtx<'a> {
|
||||
fn reduce_excuses(
|
||||
&self,
|
||||
&mut self,
|
||||
excuses: &types::ConstExcuseArray,
|
||||
) -> Result<types::Excuse, types::Errno> {
|
||||
let last = &excuses
|
||||
@@ -23,7 +23,7 @@ impl<'a> lists::Lists for WasiCtx<'a> {
|
||||
Ok(last.read().expect("dereferencing ptr should succeed"))
|
||||
}
|
||||
|
||||
fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> {
|
||||
fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> {
|
||||
for excuse in excuses.iter() {
|
||||
let ptr_to_excuse = excuse
|
||||
.expect("valid ptr to ptr")
|
||||
@@ -74,7 +74,7 @@ impl ReduceExcusesExcercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate memory with pointers to generated Excuse values
|
||||
@@ -97,7 +97,7 @@ impl ReduceExcusesExcercise {
|
||||
}
|
||||
|
||||
let res = lists::reduce_excuses(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.array_ptr_loc.ptr as i32,
|
||||
self.excuse_ptr_locs.len() as i32,
|
||||
@@ -162,7 +162,7 @@ impl PopulateExcusesExcercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate array with valid pointers to Excuse type in memory
|
||||
@@ -177,7 +177,7 @@ impl PopulateExcusesExcercise {
|
||||
}
|
||||
|
||||
let res = lists::populate_excuses(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.array_ptr_loc.ptr as i32,
|
||||
self.elements.len() as i32,
|
||||
@@ -210,7 +210,7 @@ proptest! {
|
||||
|
||||
impl<'a> array_traversal::ArrayTraversal for WasiCtx<'a> {
|
||||
fn sum_of_element(
|
||||
&self,
|
||||
&mut self,
|
||||
elements: &GuestPtr<[types::PairInts]>,
|
||||
index: u32,
|
||||
) -> Result<i32, types::Errno> {
|
||||
@@ -219,7 +219,7 @@ impl<'a> array_traversal::ArrayTraversal for WasiCtx<'a> {
|
||||
Ok(pair.first.wrapping_add(pair.second))
|
||||
}
|
||||
fn sum_of_elements(
|
||||
&self,
|
||||
&mut self,
|
||||
elements: &GuestPtr<[types::PairInts]>,
|
||||
start: u32,
|
||||
end: u32,
|
||||
@@ -288,7 +288,7 @@ impl SumElementsExercise {
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate array
|
||||
@@ -301,7 +301,7 @@ impl SumElementsExercise {
|
||||
}
|
||||
|
||||
let res = array_traversal::sum_of_element(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.element_loc.ptr as i32,
|
||||
self.elements.len() as i32,
|
||||
@@ -320,7 +320,7 @@ impl SumElementsExercise {
|
||||
|
||||
// Off the end of the array:
|
||||
let res = array_traversal::sum_of_element(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.element_loc.ptr as i32,
|
||||
self.elements.len() as i32,
|
||||
@@ -334,7 +334,7 @@ impl SumElementsExercise {
|
||||
);
|
||||
|
||||
let res = array_traversal::sum_of_elements(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.element_loc.ptr as i32,
|
||||
self.elements.len() as i32,
|
||||
@@ -373,7 +373,7 @@ impl SumElementsExercise {
|
||||
|
||||
// Index an array off the end of the array:
|
||||
let res = array_traversal::sum_of_elements(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.element_loc.ptr as i32,
|
||||
self.elements.len() as i32,
|
||||
|
||||
@@ -10,7 +10,7 @@ impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> pointers::Pointers for WasiCtx<'a> {
|
||||
fn pointers_and_enums<'b>(
|
||||
&self,
|
||||
&mut self,
|
||||
input1: types::Excuse,
|
||||
input2_ptr: &GuestPtr<'b, types::Excuse>,
|
||||
input3_ptr: &GuestPtr<'b, types::Excuse>,
|
||||
@@ -125,7 +125,7 @@ impl PointersAndEnumsExercise {
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
@@ -149,7 +149,7 @@ impl PointersAndEnumsExercise {
|
||||
.expect("input4 ptr ref_mut");
|
||||
|
||||
let e = pointers::pointers_and_enums(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input1 as i32,
|
||||
self.input2_loc.ptr as i32,
|
||||
|
||||
@@ -9,11 +9,11 @@ wiggle::from_witx!({
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> records::Records for WasiCtx<'a> {
|
||||
fn sum_of_pair(&self, an_pair: &types::PairInts) -> Result<i64, types::Errno> {
|
||||
fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result<i64, types::Errno> {
|
||||
Ok(an_pair.first as i64 + an_pair.second as i64)
|
||||
}
|
||||
|
||||
fn sum_of_pair_of_ptrs(&self, an_pair: &types::PairIntPtrs) -> Result<i64, types::Errno> {
|
||||
fn sum_of_pair_of_ptrs(&mut self, an_pair: &types::PairIntPtrs) -> Result<i64, types::Errno> {
|
||||
let first = an_pair
|
||||
.first
|
||||
.read()
|
||||
@@ -25,7 +25,7 @@ impl<'a> records::Records for WasiCtx<'a> {
|
||||
Ok(first as i64 + second as i64)
|
||||
}
|
||||
|
||||
fn sum_of_int_and_ptr(&self, an_pair: &types::PairIntAndPtr) -> Result<i64, types::Errno> {
|
||||
fn sum_of_int_and_ptr(&mut self, an_pair: &types::PairIntAndPtr) -> Result<i64, types::Errno> {
|
||||
let first = an_pair
|
||||
.first
|
||||
.read()
|
||||
@@ -34,7 +34,7 @@ impl<'a> records::Records for WasiCtx<'a> {
|
||||
Ok(first as i64 + second)
|
||||
}
|
||||
|
||||
fn return_pair_ints(&self) -> Result<types::PairInts, types::Errno> {
|
||||
fn return_pair_ints(&mut self) -> Result<types::PairInts, types::Errno> {
|
||||
Ok(types::PairInts {
|
||||
first: 10,
|
||||
second: 20,
|
||||
@@ -42,7 +42,7 @@ impl<'a> records::Records for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn return_pair_of_ptrs<'b>(
|
||||
&self,
|
||||
&mut self,
|
||||
first: &GuestPtr<'b, i32>,
|
||||
second: &GuestPtr<'b, i32>,
|
||||
) -> Result<types::PairIntPtrs<'b>, types::Errno> {
|
||||
@@ -52,7 +52,10 @@ impl<'a> records::Records for WasiCtx<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn sum_array<'b>(&self, record_of_list: &types::RecordOfList<'b>) -> Result<u16, types::Errno> {
|
||||
fn sum_array<'b>(
|
||||
&mut self,
|
||||
record_of_list: &types::RecordOfList<'b>,
|
||||
) -> Result<u16, types::Errno> {
|
||||
// my kingdom for try blocks
|
||||
fn aux(record_of_list: &types::RecordOfList) -> Result<u16, wiggle::GuestError> {
|
||||
let mut s = 0;
|
||||
@@ -99,7 +102,7 @@ impl SumOfPairExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
@@ -111,7 +114,7 @@ impl SumOfPairExercise {
|
||||
.write(self.input.second)
|
||||
.expect("input ref_mut");
|
||||
let sum_err = records::sum_of_pair(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
@@ -187,7 +190,7 @@ impl SumPairPtrsExercise {
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
@@ -209,7 +212,7 @@ impl SumPairPtrsExercise {
|
||||
.expect("input_struct ref");
|
||||
|
||||
let res = records::sum_of_pair_of_ptrs(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input_struct_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
@@ -275,7 +278,7 @@ impl SumIntAndPtrExercise {
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
@@ -292,7 +295,7 @@ impl SumIntAndPtrExercise {
|
||||
.expect("input_struct ref");
|
||||
|
||||
let res = records::sum_of_int_and_ptr(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input_struct_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
@@ -332,10 +335,10 @@ impl ReturnPairInts {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let err = records::return_pair_ints(&ctx, &host_memory, self.return_loc.ptr as i32);
|
||||
let err = records::return_pair_ints(&mut ctx, &host_memory, self.return_loc.ptr as i32);
|
||||
|
||||
assert_eq!(err, Ok(types::Errno::Ok as i32), "return struct errno");
|
||||
|
||||
@@ -397,7 +400,7 @@ impl ReturnPairPtrsExercise {
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
@@ -410,7 +413,7 @@ impl ReturnPairPtrsExercise {
|
||||
.expect("input_second ref");
|
||||
|
||||
let res = records::return_pair_of_ptrs(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input_first_loc.ptr as i32,
|
||||
self.input_second_loc.ptr as i32,
|
||||
@@ -498,7 +501,7 @@ impl SumArrayExercise {
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Write inputs to memory as an array
|
||||
@@ -522,7 +525,7 @@ impl SumArrayExercise {
|
||||
|
||||
// Call wiggle-generated func
|
||||
let res = records::sum_array(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input_struct_loc.ptr as i32,
|
||||
self.output_loc.ptr as i32,
|
||||
|
||||
@@ -9,14 +9,14 @@ wiggle::from_witx!({
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> strings::Strings for WasiCtx<'a> {
|
||||
fn hello_string(&self, a_string: &GuestPtr<str>) -> Result<u32, types::Errno> {
|
||||
fn hello_string(&mut self, a_string: &GuestPtr<str>) -> Result<u32, types::Errno> {
|
||||
let s = a_string.as_str().expect("should be valid string");
|
||||
println!("a_string='{}'", &*s);
|
||||
Ok(s.len() as u32)
|
||||
}
|
||||
|
||||
fn multi_string(
|
||||
&self,
|
||||
&mut self,
|
||||
a: &GuestPtr<str>,
|
||||
b: &GuestPtr<str>,
|
||||
c: &GuestPtr<str>,
|
||||
@@ -69,7 +69,7 @@ impl HelloStringExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate string in guest's memory
|
||||
@@ -81,7 +81,7 @@ impl HelloStringExercise {
|
||||
}
|
||||
|
||||
let res = strings::hello_string(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.string_ptr_loc.ptr as i32,
|
||||
self.test_word.len() as i32,
|
||||
@@ -181,7 +181,7 @@ impl MultiStringExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let write_string = |val: &str, loc: MemArea| {
|
||||
@@ -198,7 +198,7 @@ impl MultiStringExercise {
|
||||
write_string(&self.c, self.sc_ptr_loc);
|
||||
|
||||
let res = strings::multi_string(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.sa_ptr_loc.ptr as i32,
|
||||
self.a.len() as i32,
|
||||
@@ -260,7 +260,7 @@ impl OverlappingStringExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let write_string = |val: &str, loc: MemArea| {
|
||||
@@ -276,7 +276,7 @@ impl OverlappingStringExercise {
|
||||
|
||||
let a_len = self.a.as_bytes().len() as i32;
|
||||
let res = strings::multi_string(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.sa_ptr_loc.ptr as i32,
|
||||
a_len,
|
||||
|
||||
@@ -31,7 +31,7 @@ fn mult_zero_nan(a: f32, b: u32) -> f32 {
|
||||
}
|
||||
|
||||
impl<'a> variant_example::VariantExample for WasiCtx<'a> {
|
||||
fn get_tag(&self, u: &types::Reason) -> Result<types::Excuse, types::Errno> {
|
||||
fn get_tag(&mut self, u: &types::Reason) -> Result<types::Excuse, types::Errno> {
|
||||
println!("GET TAG: {:?}", u);
|
||||
match u {
|
||||
types::Reason::DogAte { .. } => Ok(types::Excuse::DogAte),
|
||||
@@ -39,7 +39,11 @@ impl<'a> variant_example::VariantExample for WasiCtx<'a> {
|
||||
types::Reason::Sleeping { .. } => Ok(types::Excuse::Sleeping),
|
||||
}
|
||||
}
|
||||
fn reason_mult(&self, u: &types::ReasonMut<'_>, multiply_by: u32) -> Result<(), types::Errno> {
|
||||
fn reason_mult(
|
||||
&mut self,
|
||||
u: &types::ReasonMut<'_>,
|
||||
multiply_by: u32,
|
||||
) -> Result<(), types::Errno> {
|
||||
match u {
|
||||
types::ReasonMut::DogAte(fptr) => {
|
||||
let val = fptr.read().expect("valid pointer");
|
||||
@@ -104,7 +108,7 @@ impl GetTagExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let discriminant = reason_tag(&self.input) as u8;
|
||||
@@ -126,7 +130,7 @@ impl GetTagExercise {
|
||||
types::Reason::Sleeping => {} // Do nothing
|
||||
}
|
||||
let e = variant_example::get_tag(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
@@ -181,7 +185,7 @@ impl ReasonMultExercise {
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let mut ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let discriminant = reason_tag(&self.input) as u8;
|
||||
@@ -210,7 +214,7 @@ impl ReasonMultExercise {
|
||||
types::Reason::Sleeping => {} // Do nothing
|
||||
}
|
||||
let e = variant_example::reason_mult(
|
||||
&ctx,
|
||||
&mut ctx,
|
||||
&host_memory,
|
||||
self.input_loc.ptr as i32,
|
||||
self.multiply_by as i32,
|
||||
|
||||
@@ -32,32 +32,32 @@ impl GuestErrorType for types::Errno {
|
||||
}
|
||||
|
||||
impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
fn args_get(&self, _argv: &GuestPtr<GuestPtr<u8>>, _argv_buf: &GuestPtr<u8>) -> Result<()> {
|
||||
fn args_get(&mut self, _argv: &GuestPtr<GuestPtr<u8>>, _argv_buf: &GuestPtr<u8>) -> Result<()> {
|
||||
unimplemented!("args_get")
|
||||
}
|
||||
|
||||
fn args_sizes_get(&self) -> Result<(types::Size, types::Size)> {
|
||||
fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size)> {
|
||||
unimplemented!("args_sizes_get")
|
||||
}
|
||||
|
||||
fn environ_get(
|
||||
&self,
|
||||
&mut self,
|
||||
_environ: &GuestPtr<GuestPtr<u8>>,
|
||||
_environ_buf: &GuestPtr<u8>,
|
||||
) -> Result<()> {
|
||||
unimplemented!("environ_get")
|
||||
}
|
||||
|
||||
fn environ_sizes_get(&self) -> Result<(types::Size, types::Size)> {
|
||||
fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size)> {
|
||||
unimplemented!("environ_sizes_get")
|
||||
}
|
||||
|
||||
fn clock_res_get(&self, _id: types::Clockid) -> Result<types::Timestamp> {
|
||||
fn clock_res_get(&mut self, _id: types::Clockid) -> Result<types::Timestamp> {
|
||||
unimplemented!("clock_res_get")
|
||||
}
|
||||
|
||||
fn clock_time_get(
|
||||
&self,
|
||||
&mut self,
|
||||
_id: types::Clockid,
|
||||
_precision: types::Timestamp,
|
||||
) -> Result<types::Timestamp> {
|
||||
@@ -65,7 +65,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn fd_advise(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_offset: types::Filesize,
|
||||
_len: types::Filesize,
|
||||
@@ -75,7 +75,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn fd_allocate(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_offset: types::Filesize,
|
||||
_len: types::Filesize,
|
||||
@@ -83,24 +83,24 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("fd_allocate")
|
||||
}
|
||||
|
||||
fn fd_close(&self, _fd: types::Fd) -> Result<()> {
|
||||
fn fd_close(&mut self, _fd: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_close")
|
||||
}
|
||||
|
||||
fn fd_datasync(&self, _fd: types::Fd) -> Result<()> {
|
||||
fn fd_datasync(&mut self, _fd: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_datasync")
|
||||
}
|
||||
|
||||
fn fd_fdstat_get(&self, _fd: types::Fd) -> Result<types::Fdstat> {
|
||||
fn fd_fdstat_get(&mut self, _fd: types::Fd) -> Result<types::Fdstat> {
|
||||
unimplemented!("fd_fdstat_get")
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_flags(&self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> {
|
||||
fn fd_fdstat_set_flags(&mut self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> {
|
||||
unimplemented!("fd_fdstat_set_flags")
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_rights(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_fs_rights_base: types::Rights,
|
||||
_fs_rights_inherting: types::Rights,
|
||||
@@ -108,16 +108,16 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("fd_fdstat_set_rights")
|
||||
}
|
||||
|
||||
fn fd_filestat_get(&self, _fd: types::Fd) -> Result<types::Filestat> {
|
||||
fn fd_filestat_get(&mut self, _fd: types::Fd) -> Result<types::Filestat> {
|
||||
unimplemented!("fd_filestat_get")
|
||||
}
|
||||
|
||||
fn fd_filestat_set_size(&self, _fd: types::Fd, _size: types::Filesize) -> Result<()> {
|
||||
fn fd_filestat_set_size(&mut self, _fd: types::Fd, _size: types::Filesize) -> Result<()> {
|
||||
unimplemented!("fd_filestat_set_size")
|
||||
}
|
||||
|
||||
fn fd_filestat_set_times(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_atim: types::Timestamp,
|
||||
_mtim: types::Timestamp,
|
||||
@@ -127,7 +127,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn fd_pread(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
iovs: &types::IovecArray<'_>,
|
||||
_offset: types::Filesize,
|
||||
@@ -158,12 +158,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("fd_pread")
|
||||
}
|
||||
|
||||
fn fd_prestat_get(&self, _fd: types::Fd) -> Result<types::Prestat> {
|
||||
fn fd_prestat_get(&mut self, _fd: types::Fd) -> Result<types::Prestat> {
|
||||
unimplemented!("fd_prestat_get")
|
||||
}
|
||||
|
||||
fn fd_prestat_dir_name(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_path: &GuestPtr<u8>,
|
||||
_path_len: types::Size,
|
||||
@@ -172,7 +172,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn fd_pwrite(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_ciovs: &types::CiovecArray<'_>,
|
||||
_offset: types::Filesize,
|
||||
@@ -180,12 +180,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("fd_pwrite")
|
||||
}
|
||||
|
||||
fn fd_read(&self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result<types::Size> {
|
||||
fn fd_read(&mut self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result<types::Size> {
|
||||
unimplemented!("fd_read")
|
||||
}
|
||||
|
||||
fn fd_readdir(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_buf: &GuestPtr<u8>,
|
||||
_buf_len: types::Size,
|
||||
@@ -194,12 +194,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("fd_readdir")
|
||||
}
|
||||
|
||||
fn fd_renumber(&self, _fd: types::Fd, _to: types::Fd) -> Result<()> {
|
||||
fn fd_renumber(&mut self, _fd: types::Fd, _to: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_renumber")
|
||||
}
|
||||
|
||||
fn fd_seek(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_offset: types::Filedelta,
|
||||
_whence: types::Whence,
|
||||
@@ -207,24 +207,24 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("fd_seek")
|
||||
}
|
||||
|
||||
fn fd_sync(&self, _fd: types::Fd) -> Result<()> {
|
||||
fn fd_sync(&mut self, _fd: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_sync")
|
||||
}
|
||||
|
||||
fn fd_tell(&self, _fd: types::Fd) -> Result<types::Filesize> {
|
||||
fn fd_tell(&mut self, _fd: types::Fd) -> Result<types::Filesize> {
|
||||
unimplemented!("fd_tell")
|
||||
}
|
||||
|
||||
fn fd_write(&self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result<types::Size> {
|
||||
fn fd_write(&mut self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result<types::Size> {
|
||||
unimplemented!("fd_write")
|
||||
}
|
||||
|
||||
fn path_create_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
fn path_create_directory(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
unimplemented!("path_create_directory")
|
||||
}
|
||||
|
||||
fn path_filestat_get(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_flags: types::Lookupflags,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
@@ -233,7 +233,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn path_filestat_set_times(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_flags: types::Lookupflags,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
@@ -245,7 +245,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn path_link(
|
||||
&self,
|
||||
&mut self,
|
||||
_old_fd: types::Fd,
|
||||
_old_flags: types::Lookupflags,
|
||||
_old_path: &GuestPtr<'_, str>,
|
||||
@@ -256,7 +256,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn path_open(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_dirflags: types::Lookupflags,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
@@ -269,7 +269,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn path_readlink(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
_buf: &GuestPtr<u8>,
|
||||
@@ -278,12 +278,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("path_readlink")
|
||||
}
|
||||
|
||||
fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
fn path_remove_directory(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
unimplemented!("path_remove_directory")
|
||||
}
|
||||
|
||||
fn path_rename(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_old_path: &GuestPtr<'_, str>,
|
||||
_new_fd: types::Fd,
|
||||
@@ -293,7 +293,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn path_symlink(
|
||||
&self,
|
||||
&mut self,
|
||||
_old_path: &GuestPtr<'_, str>,
|
||||
_fd: types::Fd,
|
||||
_new_path: &GuestPtr<'_, str>,
|
||||
@@ -301,12 +301,12 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("path_symlink")
|
||||
}
|
||||
|
||||
fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
fn path_unlink_file(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
unimplemented!("path_unlink_file")
|
||||
}
|
||||
|
||||
fn poll_oneoff(
|
||||
&self,
|
||||
&mut self,
|
||||
_in_: &GuestPtr<types::Subscription>,
|
||||
_out: &GuestPtr<types::Event>,
|
||||
_nsubscriptions: types::Size,
|
||||
@@ -314,24 +314,24 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("poll_oneoff")
|
||||
}
|
||||
|
||||
fn proc_exit(&self, _rval: types::Exitcode) -> wiggle::Trap {
|
||||
fn proc_exit(&mut self, _rval: types::Exitcode) -> wiggle::Trap {
|
||||
unimplemented!("proc_exit")
|
||||
}
|
||||
|
||||
fn proc_raise(&self, _sig: types::Signal) -> Result<()> {
|
||||
fn proc_raise(&mut self, _sig: types::Signal) -> Result<()> {
|
||||
unimplemented!("proc_raise")
|
||||
}
|
||||
|
||||
fn sched_yield(&self) -> Result<()> {
|
||||
fn sched_yield(&mut self) -> Result<()> {
|
||||
unimplemented!("sched_yield")
|
||||
}
|
||||
|
||||
fn random_get(&self, _buf: &GuestPtr<u8>, _buf_len: types::Size) -> Result<()> {
|
||||
fn random_get(&mut self, _buf: &GuestPtr<u8>, _buf_len: types::Size) -> Result<()> {
|
||||
unimplemented!("random_get")
|
||||
}
|
||||
|
||||
fn sock_recv(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_ri_data: &types::IovecArray<'_>,
|
||||
_ri_flags: types::Riflags,
|
||||
@@ -340,7 +340,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
}
|
||||
|
||||
fn sock_send(
|
||||
&self,
|
||||
&mut self,
|
||||
_fd: types::Fd,
|
||||
_si_data: &types::CiovecArray<'_>,
|
||||
_si_flags: types::Siflags,
|
||||
@@ -348,7 +348,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
unimplemented!("sock_send")
|
||||
}
|
||||
|
||||
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> {
|
||||
fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> {
|
||||
unimplemented!("sock_shutdown")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
use wasmtime::{Config, Engine, Linker, Module, Store};
|
||||
|
||||
wasmtime_wiggle::from_witx!({
|
||||
wiggle::from_witx!({
|
||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||
async: {
|
||||
atoms::{double_int_return_float}
|
||||
}
|
||||
});
|
||||
|
||||
wasmtime_wiggle::wasmtime_integration!({
|
||||
target: crate,
|
||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||
ctx: Ctx,
|
||||
modules: { atoms => { name: Atoms } },
|
||||
async: {
|
||||
atoms::double_int_return_float
|
||||
}
|
||||
});
|
||||
|
||||
pub struct Ctx;
|
||||
impl wiggle::GuestErrorType for types::Errno {
|
||||
fn success() -> Self {
|
||||
@@ -28,28 +17,32 @@ impl wiggle::GuestErrorType for types::Errno {
|
||||
}
|
||||
}
|
||||
|
||||
#[wasmtime_wiggle::async_trait]
|
||||
#[wiggle::async_trait]
|
||||
impl atoms::Atoms for Ctx {
|
||||
fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||
Ok(())
|
||||
}
|
||||
async fn double_int_return_float(
|
||||
&self,
|
||||
&mut self,
|
||||
an_int: u32,
|
||||
) -> Result<types::AliasToFloat, types::Errno> {
|
||||
Ok((an_int as f32) * 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_sync_func(linker: &wasmtime::Linker) {
|
||||
let shim_mod = shim_module(linker.store());
|
||||
let shim_inst = run(linker.instantiate_async(&shim_mod)).unwrap();
|
||||
#[test]
|
||||
fn test_sync_host_func() {
|
||||
let mut store = async_store();
|
||||
let mut linker = Linker::new(store.engine());
|
||||
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
||||
let shim_mod = shim_module(linker.engine());
|
||||
let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap();
|
||||
|
||||
let results = run(shim_inst
|
||||
.get_func("int_float_args_shim")
|
||||
.get_func(&mut store, "int_float_args_shim")
|
||||
.unwrap()
|
||||
.call_async(&[0i32.into(), 123.45f32.into()]))
|
||||
.call_async(&mut store, &[0i32.into(), 123.45f32.into()]))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
@@ -60,17 +53,22 @@ fn run_sync_func(linker: &wasmtime::Linker) {
|
||||
);
|
||||
}
|
||||
|
||||
fn run_async_func(linker: &wasmtime::Linker) {
|
||||
let shim_mod = shim_module(linker.store());
|
||||
let shim_inst = run(linker.instantiate_async(&shim_mod)).unwrap();
|
||||
#[test]
|
||||
fn test_async_host_func() {
|
||||
let mut store = async_store();
|
||||
let mut linker = Linker::new(store.engine());
|
||||
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
||||
|
||||
let shim_mod = shim_module(linker.engine());
|
||||
let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap();
|
||||
|
||||
let input: i32 = 123;
|
||||
let result_location: i32 = 0;
|
||||
|
||||
let results = run(shim_inst
|
||||
.get_func("double_int_return_float_shim")
|
||||
.get_func(&mut store, "double_int_return_float_shim")
|
||||
.unwrap()
|
||||
.call_async(&[input.into(), result_location.into()]))
|
||||
.call_async(&mut store, &[input.into(), result_location.into()]))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
@@ -81,70 +79,14 @@ fn run_async_func(linker: &wasmtime::Linker) {
|
||||
);
|
||||
|
||||
// The actual result is in memory:
|
||||
let mem = shim_inst.get_memory("memory").unwrap();
|
||||
let mem = shim_inst.get_memory(&mut store, "memory").unwrap();
|
||||
let mut result_bytes: [u8; 4] = [0, 0, 0, 0];
|
||||
mem.read(result_location as usize, &mut result_bytes)
|
||||
mem.read(&store, result_location as usize, &mut result_bytes)
|
||||
.unwrap();
|
||||
let result = f32::from_le_bytes(result_bytes);
|
||||
assert_eq!((input * 2) as f32, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_host_func() {
|
||||
let store = async_store();
|
||||
|
||||
let ctx = Rc::new(RefCell::new(Ctx));
|
||||
let atoms = Atoms::new(&store, ctx.clone());
|
||||
|
||||
let mut linker = wasmtime::Linker::new(&store);
|
||||
atoms.add_to_linker(&mut linker).unwrap();
|
||||
|
||||
run_sync_func(&linker);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_host_func() {
|
||||
let store = async_store();
|
||||
|
||||
let ctx = Rc::new(RefCell::new(Ctx));
|
||||
let atoms = Atoms::new(&store, ctx.clone());
|
||||
|
||||
let mut linker = wasmtime::Linker::new(&store);
|
||||
atoms.add_to_linker(&mut linker).unwrap();
|
||||
|
||||
run_async_func(&linker);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_config_host_func() {
|
||||
let mut config = wasmtime::Config::new();
|
||||
config.async_support(true);
|
||||
Atoms::add_to_config(&mut config);
|
||||
|
||||
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||
let store = wasmtime::Store::new(&engine);
|
||||
|
||||
assert!(Atoms::set_context(&store, Ctx).is_ok());
|
||||
|
||||
let linker = wasmtime::Linker::new(&store);
|
||||
run_sync_func(&linker);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_config_host_func() {
|
||||
let mut config = wasmtime::Config::new();
|
||||
config.async_support(true);
|
||||
Atoms::add_to_config(&mut config);
|
||||
|
||||
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||
let store = wasmtime::Store::new(&engine);
|
||||
|
||||
assert!(Atoms::set_context(&store, Ctx).is_ok());
|
||||
|
||||
let linker = wasmtime::Linker::new(&store);
|
||||
run_async_func(&linker);
|
||||
}
|
||||
|
||||
fn run<F: Future>(future: F) -> F::Output {
|
||||
let mut f = Pin::from(Box::new(future));
|
||||
let waker = dummy_waker();
|
||||
@@ -179,18 +121,19 @@ fn dummy_waker() -> Waker {
|
||||
}
|
||||
}
|
||||
|
||||
fn async_store() -> wasmtime::Store {
|
||||
wasmtime::Store::new(
|
||||
&wasmtime::Engine::new(wasmtime::Config::new().async_support(true)).unwrap(),
|
||||
fn async_store() -> Store<Ctx> {
|
||||
Store::new(
|
||||
&Engine::new(Config::new().async_support(true)).unwrap(),
|
||||
Ctx,
|
||||
)
|
||||
}
|
||||
|
||||
// Wiggle expects the caller to have an exported memory. Wasmtime can only
|
||||
// provide this if the caller is a WebAssembly module, so we need to write
|
||||
// a shim module:
|
||||
fn shim_module(store: &wasmtime::Store) -> wasmtime::Module {
|
||||
wasmtime::Module::new(
|
||||
store.engine(),
|
||||
fn shim_module(engine: &Engine) -> Module {
|
||||
Module::new(
|
||||
engine,
|
||||
r#"
|
||||
(module
|
||||
(memory 1)
|
||||
134
crates/wiggle/tests/wasmtime_integration.rs
Normal file
134
crates/wiggle/tests/wasmtime_integration.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use wasmtime::{Engine, Linker, Module, Store};
|
||||
|
||||
// from_witx invocation says the func is async. This context doesn't support async!
|
||||
wiggle::from_witx!({
|
||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||
async: {
|
||||
atoms::{double_int_return_float}
|
||||
}
|
||||
});
|
||||
|
||||
pub mod integration {
|
||||
// The integration invocation says the func is blocking, so it will still work.
|
||||
wiggle::wasmtime_integration!({
|
||||
target: crate,
|
||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||
block_on: {
|
||||
atoms::{double_int_return_float}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub struct Ctx;
|
||||
impl wiggle::GuestErrorType for types::Errno {
|
||||
fn success() -> Self {
|
||||
types::Errno::Ok
|
||||
}
|
||||
}
|
||||
|
||||
#[wiggle::async_trait]
|
||||
impl atoms::Atoms for Ctx {
|
||||
fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||
Ok(())
|
||||
}
|
||||
async fn double_int_return_float(
|
||||
&mut self,
|
||||
an_int: u32,
|
||||
) -> Result<types::AliasToFloat, types::Errno> {
|
||||
Ok((an_int as f32) * 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_host_func() {
|
||||
let engine = Engine::default();
|
||||
let mut linker = Linker::new(&engine);
|
||||
integration::add_atoms_to_linker(&mut linker, |cx| cx).unwrap();
|
||||
let mut store = store(&engine);
|
||||
let shim_mod = shim_module(&engine);
|
||||
let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
|
||||
|
||||
let results = shim_inst
|
||||
.get_func(&mut store, "int_float_args_shim")
|
||||
.unwrap()
|
||||
.call(&mut store, &[0i32.into(), 123.45f32.into()])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
assert_eq!(
|
||||
results[0].unwrap_i32(),
|
||||
types::Errno::Ok as i32,
|
||||
"int_float_args errno"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_host_func() {
|
||||
let engine = Engine::default();
|
||||
let mut linker = Linker::new(&engine);
|
||||
integration::add_atoms_to_linker(&mut linker, |cx| cx).unwrap();
|
||||
let mut store = store(&engine);
|
||||
|
||||
let shim_mod = shim_module(&engine);
|
||||
let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
|
||||
|
||||
let input: i32 = 123;
|
||||
let result_location: i32 = 0;
|
||||
|
||||
let results = shim_inst
|
||||
.get_func(&mut store, "double_int_return_float_shim")
|
||||
.unwrap()
|
||||
.call(&mut store, &[input.into(), result_location.into()])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
assert_eq!(
|
||||
results[0].unwrap_i32(),
|
||||
types::Errno::Ok as i32,
|
||||
"double_int_return_float errno"
|
||||
);
|
||||
|
||||
// The actual result is in memory:
|
||||
let mem = shim_inst.get_memory(&mut store, "memory").unwrap();
|
||||
let mut result_bytes: [u8; 4] = [0, 0, 0, 0];
|
||||
mem.read(&store, result_location as usize, &mut result_bytes)
|
||||
.unwrap();
|
||||
let result = f32::from_le_bytes(result_bytes);
|
||||
assert_eq!((input * 2) as f32, result);
|
||||
}
|
||||
|
||||
fn store(engine: &Engine) -> Store<Ctx> {
|
||||
Store::new(engine, Ctx)
|
||||
}
|
||||
|
||||
// Wiggle expects the caller to have an exported memory. Wasmtime can only
|
||||
// provide this if the caller is a WebAssembly module, so we need to write
|
||||
// a shim module:
|
||||
fn shim_module(engine: &Engine) -> Module {
|
||||
Module::new(
|
||||
engine,
|
||||
r#"
|
||||
(module
|
||||
(memory 1)
|
||||
(export "memory" (memory 0))
|
||||
(import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32)))
|
||||
(import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32)))
|
||||
|
||||
(func $int_float_args_shim (param i32 f32) (result i32)
|
||||
local.get 0
|
||||
local.get 1
|
||||
call $int_float_args
|
||||
)
|
||||
(func $double_int_return_float_shim (param i32 i32) (result i32)
|
||||
local.get 0
|
||||
local.get 1
|
||||
call $double_int_return_float
|
||||
)
|
||||
(export "int_float_args_shim" (func $int_float_args_shim))
|
||||
(export "double_int_return_float_shim" (func $double_int_return_float_shim))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
122
crates/wiggle/tests/wasmtime_sync.rs
Normal file
122
crates/wiggle/tests/wasmtime_sync.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use wasmtime::{Engine, Linker, Module, Store};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||
block_on: {
|
||||
atoms::double_int_return_float
|
||||
}
|
||||
});
|
||||
|
||||
pub struct Ctx;
|
||||
impl wiggle::GuestErrorType for types::Errno {
|
||||
fn success() -> Self {
|
||||
types::Errno::Ok
|
||||
}
|
||||
}
|
||||
|
||||
#[wiggle::async_trait]
|
||||
impl atoms::Atoms for Ctx {
|
||||
fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||
Ok(())
|
||||
}
|
||||
async fn double_int_return_float(
|
||||
&mut self,
|
||||
an_int: u32,
|
||||
) -> Result<types::AliasToFloat, types::Errno> {
|
||||
Ok((an_int as f32) * 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_host_func() {
|
||||
let engine = Engine::default();
|
||||
let mut linker = Linker::new(&engine);
|
||||
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
||||
let mut store = store(&engine);
|
||||
let shim_mod = shim_module(&engine);
|
||||
let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
|
||||
|
||||
let results = shim_inst
|
||||
.get_func(&mut store, "int_float_args_shim")
|
||||
.unwrap()
|
||||
.call(&mut store, &[0i32.into(), 123.45f32.into()])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
assert_eq!(
|
||||
results[0].unwrap_i32(),
|
||||
types::Errno::Ok as i32,
|
||||
"int_float_args errno"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_host_func() {
|
||||
let engine = Engine::default();
|
||||
let mut linker = Linker::new(&engine);
|
||||
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
||||
let mut store = store(&engine);
|
||||
|
||||
let shim_mod = shim_module(&engine);
|
||||
let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
|
||||
|
||||
let input: i32 = 123;
|
||||
let result_location: i32 = 0;
|
||||
|
||||
let results = shim_inst
|
||||
.get_func(&mut store, "double_int_return_float_shim")
|
||||
.unwrap()
|
||||
.call(&mut store, &[input.into(), result_location.into()])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
assert_eq!(
|
||||
results[0].unwrap_i32(),
|
||||
types::Errno::Ok as i32,
|
||||
"double_int_return_float errno"
|
||||
);
|
||||
|
||||
// The actual result is in memory:
|
||||
let mem = shim_inst.get_memory(&mut store, "memory").unwrap();
|
||||
let mut result_bytes: [u8; 4] = [0, 0, 0, 0];
|
||||
mem.read(&store, result_location as usize, &mut result_bytes)
|
||||
.unwrap();
|
||||
let result = f32::from_le_bytes(result_bytes);
|
||||
assert_eq!((input * 2) as f32, result);
|
||||
}
|
||||
|
||||
fn store(engine: &Engine) -> Store<Ctx> {
|
||||
Store::new(engine, Ctx)
|
||||
}
|
||||
|
||||
// Wiggle expects the caller to have an exported memory. Wasmtime can only
|
||||
// provide this if the caller is a WebAssembly module, so we need to write
|
||||
// a shim module:
|
||||
fn shim_module(engine: &Engine) -> Module {
|
||||
Module::new(
|
||||
engine,
|
||||
r#"
|
||||
(module
|
||||
(memory 1)
|
||||
(export "memory" (memory 0))
|
||||
(import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32)))
|
||||
(import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32)))
|
||||
|
||||
(func $int_float_args_shim (param i32 f32) (result i32)
|
||||
local.get 0
|
||||
local.get 1
|
||||
call $int_float_args
|
||||
)
|
||||
(func $double_int_return_float_shim (param i32 i32) (result i32)
|
||||
local.get 0
|
||||
local.get 1
|
||||
call $double_int_return_float
|
||||
)
|
||||
(export "int_float_args_shim" (func $int_float_args_shim))
|
||||
(export "double_int_return_float_shim" (func $double_int_return_float_shim))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
[package]
|
||||
name = "wasmtime-wiggle"
|
||||
version = "0.27.0"
|
||||
authors = ["Pat Hickey <phickey@fastly.com>", "Jakub Konka <kubkonk@jakubkonka.com>", "Alex Crichton <alex@alexcrichton.com>"]
|
||||
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.27.0", default-features = false }
|
||||
wasmtime-wiggle-macro = { path = "./macro", version = "0.27.0" }
|
||||
witx = { version = "0.9.0", path = "../../wasi-common/WASI/tools/witx", optional = true }
|
||||
wiggle = { path = "..", version = "0.27.0" }
|
||||
wiggle-borrow = { path = "../borrow", version = "0.27.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1"
|
||||
proptest = "1.0.0"
|
||||
|
||||
[[test]]
|
||||
name = "atoms_async"
|
||||
path = "tests/atoms_async.rs"
|
||||
required-features = ["async", "wasmtime/wat"]
|
||||
|
||||
[[test]]
|
||||
name = "atoms_sync"
|
||||
path = "tests/atoms_sync.rs"
|
||||
required-features = ["wasmtime/wat"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
# Async support for wasmtime
|
||||
async = [ 'wasmtime/async', 'wasmtime-wiggle-macro/async' ]
|
||||
|
||||
# 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", "async"]
|
||||
@@ -1,29 +0,0 @@
|
||||
[package]
|
||||
name = "wasmtime-wiggle-macro"
|
||||
version = "0.27.0"
|
||||
authors = ["Pat Hickey <phickey@fastly.com>", "Jakub Konka <kubkonk@jakubkonka.com>", "Alex Crichton <alex@alexcrichton.com>"]
|
||||
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 = { version = "0.9.0", path = "../../../wasi-common/WASI/tools/witx" }
|
||||
wiggle-generate = { path = "../../generate", version = "0.27.0" }
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0", features = ["full", "extra-traits"] }
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
async = []
|
||||
default = []
|
||||
@@ -1,324 +0,0 @@
|
||||
use wiggle_generate::config::AsyncFunctions;
|
||||
use {
|
||||
proc_macro2::Span,
|
||||
std::collections::HashMap,
|
||||
syn::{
|
||||
braced,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Error, Ident, Path, Result, Token,
|
||||
},
|
||||
wiggle_generate::config::WitxConf,
|
||||
};
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub target: TargetConf,
|
||||
pub witx: WitxConf,
|
||||
pub ctx: CtxConf,
|
||||
pub modules: ModulesConf,
|
||||
pub async_: AsyncConf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConfigField {
|
||||
Target(TargetConf),
|
||||
Witx(WitxConf),
|
||||
Ctx(CtxConf),
|
||||
Modules(ModulesConf),
|
||||
Async(AsyncConf),
|
||||
}
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(target);
|
||||
syn::custom_keyword!(witx);
|
||||
syn::custom_keyword!(witx_literal);
|
||||
syn::custom_keyword!(ctx);
|
||||
syn::custom_keyword!(modules);
|
||||
syn::custom_keyword!(name);
|
||||
syn::custom_keyword!(docs);
|
||||
syn::custom_keyword!(function_override);
|
||||
syn::custom_keyword!(block_on);
|
||||
}
|
||||
|
||||
impl Parse for ConfigField {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::target) {
|
||||
input.parse::<kw::target>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Target(input.parse()?))
|
||||
} else if lookahead.peek(kw::witx) {
|
||||
input.parse::<kw::witx>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Witx(WitxConf::Paths(input.parse()?)))
|
||||
} else if lookahead.peek(kw::witx_literal) {
|
||||
input.parse::<kw::witx_literal>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?)))
|
||||
} else if lookahead.peek(kw::ctx) {
|
||||
input.parse::<kw::ctx>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Ctx(input.parse()?))
|
||||
} else if lookahead.peek(kw::modules) {
|
||||
input.parse::<kw::modules>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ConfigField::Modules(input.parse()?))
|
||||
} else if lookahead.peek(Token![async]) {
|
||||
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 {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn build(fields: impl Iterator<Item = ConfigField>, err_loc: Span) -> Result<Self> {
|
||||
let mut target = None;
|
||||
let mut witx = None;
|
||||
let mut ctx = None;
|
||||
let mut modules = None;
|
||||
let mut async_ = 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"));
|
||||
}
|
||||
witx = Some(c);
|
||||
}
|
||||
ConfigField::Ctx(c) => {
|
||||
if ctx.is_some() {
|
||||
return Err(Error::new(err_loc, "duplicate `ctx` field"));
|
||||
}
|
||||
ctx = Some(c);
|
||||
}
|
||||
ConfigField::Modules(c) => {
|
||||
if modules.is_some() {
|
||||
return Err(Error::new(err_loc, "duplicate `modules` field"));
|
||||
}
|
||||
modules = Some(c);
|
||||
}
|
||||
ConfigField::Async(c) => {
|
||||
if async_.is_some() {
|
||||
return Err(Error::new(err_loc, "duplicate `async` field"));
|
||||
}
|
||||
async_ = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Config {
|
||||
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"))?,
|
||||
async_: async_.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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<Self> {
|
||||
let contents;
|
||||
let _lbrace = braced!(contents in input);
|
||||
let fields: Punctuated<ConfigField, Token![,]> =
|
||||
contents.parse_terminated(ConfigField::parse)?;
|
||||
Ok(Config::build(fields.into_iter(), input.span())?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CtxConf {
|
||||
pub name: syn::Type,
|
||||
}
|
||||
|
||||
impl Parse for CtxConf {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(CtxConf {
|
||||
name: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TargetConf {
|
||||
pub path: Path,
|
||||
}
|
||||
|
||||
impl Parse for TargetConf {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(TargetConf {
|
||||
path: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enum ModuleConfField {
|
||||
Name(Ident),
|
||||
Docs(String),
|
||||
}
|
||||
|
||||
impl Parse for ModuleConfField {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::name) {
|
||||
input.parse::<kw::name>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(ModuleConfField::Name(input.parse()?))
|
||||
} else if lookahead.peek(kw::docs) {
|
||||
input.parse::<kw::docs>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
let docs: syn::LitStr = input.parse()?;
|
||||
Ok(ModuleConfField::Docs(docs.value()))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleConf {
|
||||
pub name: Ident,
|
||||
pub docs: Option<String>,
|
||||
}
|
||||
|
||||
impl ModuleConf {
|
||||
fn build(fields: impl Iterator<Item = ModuleConfField>, err_loc: Span) -> Result<Self> {
|
||||
let mut name = None;
|
||||
let mut docs = None;
|
||||
for f in fields {
|
||||
match f {
|
||||
ModuleConfField::Name(c) => {
|
||||
if name.is_some() {
|
||||
return Err(Error::new(err_loc, "duplicate `name` field"));
|
||||
}
|
||||
name = Some(c);
|
||||
}
|
||||
ModuleConfField::Docs(c) => {
|
||||
if docs.is_some() {
|
||||
return Err(Error::new(err_loc, "duplicate `docs` field"));
|
||||
}
|
||||
docs = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ModuleConf {
|
||||
name: name.ok_or_else(|| Error::new(err_loc, "`name` field required"))?,
|
||||
docs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for ModuleConf {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let contents;
|
||||
let _lbrace = braced!(contents in input);
|
||||
let fields: Punctuated<ModuleConfField, Token![,]> =
|
||||
contents.parse_terminated(ModuleConfField::parse)?;
|
||||
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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[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_sync(&self) -> bool {
|
||||
match self {
|
||||
Asyncness::Sync => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncConf {
|
||||
pub fn is_async(&self, module: &str, function: &str) -> Asyncness {
|
||||
let a = if self.blocking {
|
||||
Asyncness::Blocking
|
||||
} else {
|
||||
Asyncness::Async
|
||||
};
|
||||
match &self.functions {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,397 +0,0 @@
|
||||
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;
|
||||
|
||||
use config::{AsyncConf, Asyncness, 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 } }`.
|
||||
///
|
||||
#[proc_macro]
|
||||
pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
|
||||
let config = parse_macro_input!(args as config::Config);
|
||||
let doc = config.load_document();
|
||||
let names = Names::new(quote!(wasmtime_wiggle));
|
||||
|
||||
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.ctx.name,
|
||||
&config.async_,
|
||||
)
|
||||
});
|
||||
quote!( #(#modules)* ).into()
|
||||
}
|
||||
|
||||
fn generate_module(
|
||||
module: &witx::Module,
|
||||
module_conf: &ModuleConf,
|
||||
names: &Names,
|
||||
target_conf: &TargetConf,
|
||||
ctx_type: &syn::Type,
|
||||
async_conf: &AsyncConf,
|
||||
) -> TokenStream2 {
|
||||
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 target_path = &target_conf.path;
|
||||
let module_id = names.module(&module.name);
|
||||
let target_module = quote! { #target_path::#module_id };
|
||||
|
||||
let mut fns = Vec::new();
|
||||
let mut ctor_externs = Vec::new();
|
||||
let mut host_funcs = Vec::new();
|
||||
|
||||
for f in module.funcs() {
|
||||
let asyncness = async_conf.is_async(module.name.as_str(), f.name.as_str());
|
||||
match asyncness {
|
||||
Asyncness::Blocking => {}
|
||||
Asyncness::Async => {
|
||||
assert!(
|
||||
cfg!(feature = "async"),
|
||||
"generating async wasmtime Funcs requires cargo feature \"async\""
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
generate_func(
|
||||
&module_id,
|
||||
&f,
|
||||
names,
|
||||
&target_module,
|
||||
ctx_type,
|
||||
asyncness,
|
||||
&mut fns,
|
||||
&mut ctor_externs,
|
||||
&mut host_funcs,
|
||||
);
|
||||
}
|
||||
|
||||
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 instance itself should be all
|
||||
contained in the `cx` parameter.",
|
||||
module_conf.name.to_string()
|
||||
);
|
||||
|
||||
let config_adder_definitions = host_funcs.iter().map(|(func_name, body)| {
|
||||
let adder_func = format_ident!("add_{}_to_config", names.func(&func_name));
|
||||
let docs = format!(
|
||||
"Add the host function for `{}` to a config under a given module and field name.",
|
||||
func_name.as_str()
|
||||
);
|
||||
quote! {
|
||||
#[doc = #docs]
|
||||
pub fn #adder_func(config: &mut wasmtime::Config, module: &str, field: &str) {
|
||||
#body
|
||||
}
|
||||
}
|
||||
});
|
||||
let config_adder_invocations = host_funcs.iter().map(|(func_name, _body)| {
|
||||
let adder_func = format_ident!("add_{}_to_config", names.func(&func_name));
|
||||
let module = module.name.as_str();
|
||||
let field = func_name.as_str();
|
||||
quote! {
|
||||
Self::#adder_func(config, #module, #field);
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#type_docs
|
||||
pub struct #type_name {
|
||||
#(#fields,)*
|
||||
}
|
||||
|
||||
impl #type_name {
|
||||
#[doc = #constructor_docs]
|
||||
pub fn new(store: &wasmtime::Store, ctx: std::rc::Rc<std::cell::RefCell<#ctx_type>>) -> Self {
|
||||
#(#ctor_externs)*
|
||||
|
||||
Self {
|
||||
#(#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 instance items to the specified `Linker`.
|
||||
pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> {
|
||||
#(#linker_add)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds the host functions to the given [`wasmtime::Config`].
|
||||
///
|
||||
/// Host functions added to the config expect [`set_context`] to be called.
|
||||
///
|
||||
/// Host functions will trap if the context is not set in the calling [`wasmtime::Store`].
|
||||
pub fn add_to_config(config: &mut wasmtime::Config) {
|
||||
#(#config_adder_invocations)*
|
||||
}
|
||||
|
||||
#(#config_adder_definitions)*
|
||||
|
||||
/// Sets the context in the given store.
|
||||
///
|
||||
/// Context must be set in the store when using [`add_to_config`] and prior to any
|
||||
/// host function being called.
|
||||
///
|
||||
/// If the context is already set in the store, the given context is returned as an error.
|
||||
pub fn set_context(store: &wasmtime::Store, ctx: #ctx_type) -> Result<(), #ctx_type> {
|
||||
store.set(std::rc::Rc::new(std::cell::RefCell::new(ctx))).map_err(|ctx| {
|
||||
match std::rc::Rc::try_unwrap(ctx) {
|
||||
Ok(ctx) => ctx.into_inner(),
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#(#fns)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_func(
|
||||
module_ident: &Ident,
|
||||
func: &witx::InterfaceFunc,
|
||||
names: &Names,
|
||||
target_module: &TokenStream2,
|
||||
ctx_type: &syn::Type,
|
||||
asyncness: Asyncness,
|
||||
fns: &mut Vec<TokenStream2>,
|
||||
ctors: &mut Vec<TokenStream2>,
|
||||
host_funcs: &mut Vec<(witx::Id, TokenStream2)>,
|
||||
) {
|
||||
let rt = names.runtime_mod();
|
||||
let name_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 async_ = if asyncness.is_sync() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(async)
|
||||
};
|
||||
let await_ = if asyncness.is_sync() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(.await)
|
||||
};
|
||||
|
||||
let runtime = names.runtime_mod();
|
||||
let fn_ident = format_ident!("{}_{}", module_ident, name_ident);
|
||||
|
||||
fns.push(quote! {
|
||||
#async_ fn #fn_ident(caller: &wasmtime::Caller<'_>, ctx: &mut #ctx_type #(, #arg_decls)*) -> Result<#ret_ty, wasmtime::Trap> {
|
||||
unsafe {
|
||||
let mem = match caller.get_export("memory") {
|
||||
Some(wasmtime::Extern::Memory(m)) => m,
|
||||
_ => {
|
||||
return Err(wasmtime::Trap::new("missing required memory export"));
|
||||
}
|
||||
};
|
||||
let mem = #runtime::WasmtimeGuestMemory::new(mem);
|
||||
match #target_module::#name_ident(ctx, &mem #(, #arg_names)*) #await_ {
|
||||
Ok(r) => Ok(r.into()),
|
||||
Err(wasmtime_wiggle::Trap::String(err)) => Err(wasmtime::Trap::new(err)),
|
||||
Err(wasmtime_wiggle::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
match asyncness {
|
||||
Asyncness::Async => {
|
||||
let wrapper = format_ident!("wrap{}_async", params.len());
|
||||
ctors.push(quote! {
|
||||
let #name_ident = wasmtime::Func::#wrapper(
|
||||
store,
|
||||
ctx.clone(),
|
||||
move |caller: wasmtime::Caller<'_>, my_ctx: &std::rc::Rc<std::cell::RefCell<_>> #(,#arg_decls)*|
|
||||
-> Box<dyn std::future::Future<Output = Result<#ret_ty, wasmtime::Trap>>> {
|
||||
Box::new(async move { Self::#fn_ident(&caller, &mut my_ctx.borrow_mut() #(, #arg_names)*).await })
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
Asyncness::Blocking => {
|
||||
// Emit a synchronous function. Self::#fn_ident returns a Future, so we need to
|
||||
// use a dummy executor to let any synchronous code inside there execute correctly. If
|
||||
// the future ends up Pending, this func will Trap.
|
||||
ctors.push(quote! {
|
||||
let my_ctx = ctx.clone();
|
||||
let #name_ident = wasmtime::Func::wrap(
|
||||
store,
|
||||
move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> {
|
||||
#rt::run_in_dummy_executor(Self::#fn_ident(&caller, &mut my_ctx.borrow_mut() #(, #arg_names)*))
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
Asyncness::Sync => {
|
||||
ctors.push(quote! {
|
||||
let my_ctx = ctx.clone();
|
||||
let #name_ident = wasmtime::Func::wrap(
|
||||
store,
|
||||
move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> {
|
||||
Self::#fn_ident(&caller, &mut my_ctx.borrow_mut() #(, #arg_names)*)
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let host_wrapper = match asyncness {
|
||||
Asyncness::Async => {
|
||||
let wrapper = format_ident!("wrap{}_host_func_async", params.len());
|
||||
quote! {
|
||||
config.#wrapper(
|
||||
module,
|
||||
field,
|
||||
move |caller #(,#arg_decls)*|
|
||||
-> Box<dyn std::future::Future<Output = Result<#ret_ty, wasmtime::Trap>>> {
|
||||
Box::new(async move {
|
||||
let ctx = caller.store()
|
||||
.get::<std::rc::Rc<std::cell::RefCell<#ctx_type>>>()
|
||||
.ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?;
|
||||
let result = Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*).await;
|
||||
result
|
||||
})
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Asyncness::Blocking => {
|
||||
// Emit a synchronous host function. Self::#fn_ident returns a Future, so we need to
|
||||
// use a dummy executor to let any synchronous code inside there execute correctly. If
|
||||
// the future ends up Pending, this func will Trap.
|
||||
quote! {
|
||||
config.wrap_host_func(
|
||||
module,
|
||||
field,
|
||||
move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> {
|
||||
let ctx = caller
|
||||
.store()
|
||||
.get::<std::rc::Rc<std::cell::RefCell<#ctx_type>>>()
|
||||
.ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?;
|
||||
#rt::run_in_dummy_executor(Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*))
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Asyncness::Sync => {
|
||||
quote! {
|
||||
config.wrap_host_func(
|
||||
module,
|
||||
field,
|
||||
move |caller: wasmtime::Caller #(, #arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> {
|
||||
let ctx = caller
|
||||
.store()
|
||||
.get::<std::rc::Rc<std::cell::RefCell<#ctx_type>>>()
|
||||
.ok_or_else(|| wasmtime::Trap::new("context is missing in the store"))?;
|
||||
Self::#fn_ident(&caller, &mut ctx.borrow_mut() #(, #arg_names)*)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
host_funcs.push((func.name.clone(), host_wrapper));
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
(typename $errno
|
||||
(enum (@witx tag u32)
|
||||
;;; Success
|
||||
$ok
|
||||
;;; Invalid argument
|
||||
$invalid_arg
|
||||
;;; I really don't want to
|
||||
$dont_want_to
|
||||
;;; I am physically unable to
|
||||
$physically_unable
|
||||
;;; Well, that's a picket line alright!
|
||||
$picket_line))
|
||||
|
||||
(typename $alias_to_float f32)
|
||||
|
||||
(module $atoms
|
||||
(@interface func (export "int_float_args")
|
||||
(param $an_int u32)
|
||||
(param $an_float f32)
|
||||
(result $error (expected (error $errno))))
|
||||
(@interface func (export "double_int_return_float")
|
||||
(param $an_int u32)
|
||||
(result $error (expected $alias_to_float (error $errno))))
|
||||
)
|
||||
@@ -1,176 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
wasmtime_wiggle::from_witx!({
|
||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||
async: {
|
||||
atoms::{double_int_return_float}
|
||||
}
|
||||
});
|
||||
|
||||
wasmtime_wiggle::wasmtime_integration!({
|
||||
target: crate,
|
||||
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
||||
ctx: Ctx,
|
||||
modules: { atoms => { name: Atoms } },
|
||||
block_on: {
|
||||
atoms::double_int_return_float
|
||||
}
|
||||
});
|
||||
|
||||
pub struct Ctx;
|
||||
impl wiggle::GuestErrorType for types::Errno {
|
||||
fn success() -> Self {
|
||||
types::Errno::Ok
|
||||
}
|
||||
}
|
||||
|
||||
#[wasmtime_wiggle::async_trait]
|
||||
impl atoms::Atoms for Ctx {
|
||||
fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||
Ok(())
|
||||
}
|
||||
async fn double_int_return_float(
|
||||
&self,
|
||||
an_int: u32,
|
||||
) -> Result<types::AliasToFloat, types::Errno> {
|
||||
Ok((an_int as f32) * 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_int_float_args(linker: &wasmtime::Linker) {
|
||||
let shim_mod = shim_module(linker.store());
|
||||
let shim_inst = linker.instantiate(&shim_mod).unwrap();
|
||||
|
||||
let results = shim_inst
|
||||
.get_func("int_float_args_shim")
|
||||
.unwrap()
|
||||
.call(&[0i32.into(), 123.45f32.into()])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
assert_eq!(
|
||||
results[0].unwrap_i32(),
|
||||
types::Errno::Ok as i32,
|
||||
"int_float_args errno"
|
||||
);
|
||||
}
|
||||
|
||||
fn run_double_int_return_float(linker: &wasmtime::Linker) {
|
||||
let shim_mod = shim_module(linker.store());
|
||||
let shim_inst = linker.instantiate(&shim_mod).unwrap();
|
||||
|
||||
let input: i32 = 123;
|
||||
let result_location: i32 = 0;
|
||||
|
||||
let results = shim_inst
|
||||
.get_func("double_int_return_float_shim")
|
||||
.unwrap()
|
||||
.call(&[input.into(), result_location.into()])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.len(), 1, "one return value");
|
||||
assert_eq!(
|
||||
results[0].unwrap_i32(),
|
||||
types::Errno::Ok as i32,
|
||||
"double_int_return_float errno"
|
||||
);
|
||||
|
||||
// The actual result is in memory:
|
||||
let mem = shim_inst.get_memory("memory").unwrap();
|
||||
let mut result_bytes: [u8; 4] = [0, 0, 0, 0];
|
||||
mem.read(result_location as usize, &mut result_bytes)
|
||||
.unwrap();
|
||||
let result = f32::from_le_bytes(result_bytes);
|
||||
assert_eq!((input * 2) as f32, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_host_func() {
|
||||
let store = store();
|
||||
|
||||
let ctx = Rc::new(RefCell::new(Ctx));
|
||||
let atoms = Atoms::new(&store, ctx.clone());
|
||||
|
||||
let mut linker = wasmtime::Linker::new(&store);
|
||||
atoms.add_to_linker(&mut linker).unwrap();
|
||||
|
||||
run_int_float_args(&linker);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_host_func() {
|
||||
let store = store();
|
||||
|
||||
let ctx = Rc::new(RefCell::new(Ctx));
|
||||
let atoms = Atoms::new(&store, ctx.clone());
|
||||
|
||||
let mut linker = wasmtime::Linker::new(&store);
|
||||
atoms.add_to_linker(&mut linker).unwrap();
|
||||
|
||||
run_double_int_return_float(&linker);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_config_host_func() {
|
||||
let mut config = wasmtime::Config::new();
|
||||
Atoms::add_to_config(&mut config);
|
||||
|
||||
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||
let store = wasmtime::Store::new(&engine);
|
||||
|
||||
assert!(Atoms::set_context(&store, Ctx).is_ok());
|
||||
|
||||
let linker = wasmtime::Linker::new(&store);
|
||||
run_int_float_args(&linker);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_config_host_func() {
|
||||
let mut config = wasmtime::Config::new();
|
||||
Atoms::add_to_config(&mut config);
|
||||
|
||||
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||
let store = wasmtime::Store::new(&engine);
|
||||
|
||||
assert!(Atoms::set_context(&store, Ctx).is_ok());
|
||||
|
||||
let linker = wasmtime::Linker::new(&store);
|
||||
run_double_int_return_float(&linker);
|
||||
}
|
||||
|
||||
fn store() -> wasmtime::Store {
|
||||
wasmtime::Store::new(&wasmtime::Engine::new(&wasmtime::Config::new()).unwrap())
|
||||
}
|
||||
|
||||
// Wiggle expects the caller to have an exported memory. Wasmtime can only
|
||||
// provide this if the caller is a WebAssembly module, so we need to write
|
||||
// a shim module:
|
||||
fn shim_module(store: &wasmtime::Store) -> wasmtime::Module {
|
||||
wasmtime::Module::new(
|
||||
store.engine(),
|
||||
r#"
|
||||
(module
|
||||
(memory 1)
|
||||
(export "memory" (memory 0))
|
||||
(import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32)))
|
||||
(import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32)))
|
||||
|
||||
(func $int_float_args_shim (param i32 f32) (result i32)
|
||||
local.get 0
|
||||
local.get 1
|
||||
call $int_float_args
|
||||
)
|
||||
(func $double_int_return_float_shim (param i32 i32) (result i32)
|
||||
local.get 0
|
||||
local.get 1
|
||||
call $double_int_return_float
|
||||
)
|
||||
(export "int_float_args_shim" (func $int_float_args_shim))
|
||||
(export "double_int_return_float_shim" (func $double_int_return_float_shim))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
Reference in New Issue
Block a user