Make Func::getN return a Result rather than an Option (#966)

This allows getN to return a detailed explanation of any type signature
mismatch, and makes it easy to just use `?` on the result of getN rather
than constructing a (necessarily vaguer) error message in the caller.
This commit is contained in:
Josh Triplett
2020-02-22 15:56:23 -08:00
committed by GitHub
parent 48202e0c31
commit aa78d491b0
3 changed files with 86 additions and 65 deletions

View File

@@ -1,5 +1,6 @@
use crate::callable::{NativeCallable, WasmtimeFn, WrappedCallable}; use crate::callable::{NativeCallable, WasmtimeFn, WrappedCallable};
use crate::{Callable, FuncType, Store, Trap, Val, ValType}; use crate::{Callable, FuncType, Store, Trap, Val, ValType};
use anyhow::{ensure, Context as _};
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use std::panic::{self, AssertUnwindSafe}; use std::panic::{self, AssertUnwindSafe};
@@ -98,7 +99,7 @@ macro_rules! getters {
$(#[$doc])* $(#[$doc])*
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn $name<$($args,)* R>(&self) pub fn $name<$($args,)* R>(&self)
-> Option<impl Fn($($args,)*) -> Result<R, Trap>> -> anyhow::Result<impl Fn($($args,)*) -> Result<R, Trap>>
where where
$($args: WasmTy,)* $($args: WasmTy,)*
R: WasmTy, R: WasmTy,
@@ -106,23 +107,19 @@ macro_rules! getters {
// Verify all the paramers match the expected parameters, and that // Verify all the paramers match the expected parameters, and that
// there are no extra parameters... // there are no extra parameters...
let mut params = self.ty().params().iter().cloned(); let mut params = self.ty().params().iter().cloned();
let n = 0;
$( $(
if !$args::matches(&mut params) { let n = n + 1;
return None; $args::matches(&mut params)
} .with_context(|| format!("Type mismatch in argument {}", n))?;
)* )*
if !params.next().is_none() { ensure!(params.next().is_none(), "Type mismatch: too many arguments (expected {})", n);
return None;
}
// ... then do the same for the results... // ... then do the same for the results...
let mut results = self.ty().results().iter().cloned(); let mut results = self.ty().results().iter().cloned();
if !R::matches(&mut results) { R::matches(&mut results)
return None; .context("Type mismatch in return type")?;
} ensure!(results.next().is_none(), "Type mismatch: too many return values (expected 1)");
if !results.next().is_none() {
return None;
}
// ... and then once we've passed the typechecks we can hand out our // ... and then once we've passed the typechecks we can hand out our
// object since our `transmute` below should be safe! // object since our `transmute` below should be safe!
@@ -130,9 +127,9 @@ macro_rules! getters {
wasmtime_runtime::Export::Function { address, vmctx, signature: _} => { wasmtime_runtime::Export::Function { address, vmctx, signature: _} => {
(*address, *vmctx) (*address, *vmctx)
} }
_ => return None, _ => panic!("expected function export"),
}; };
Some(move |$($args: $args),*| -> Result<R, Trap> { Ok(move |$($args: $args),*| -> Result<R, Trap> {
unsafe { unsafe {
let f = mem::transmute::< let f = mem::transmute::<
*const VMFunctionBody, *const VMFunctionBody,
@@ -447,7 +444,7 @@ pub trait WasmTy {
#[doc(hidden)] #[doc(hidden)]
fn push(dst: &mut Vec<ValType>); fn push(dst: &mut Vec<ValType>);
#[doc(hidden)] #[doc(hidden)]
fn matches(tys: impl Iterator<Item = ValType>) -> bool; fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()>;
#[doc(hidden)] #[doc(hidden)]
fn from_abi(vmctx: *mut VMContext, abi: Self::Abi) -> Self; fn from_abi(vmctx: *mut VMContext, abi: Self::Abi) -> Self;
#[doc(hidden)] #[doc(hidden)]
@@ -457,8 +454,8 @@ pub trait WasmTy {
impl WasmTy for () { impl WasmTy for () {
type Abi = (); type Abi = ();
fn push(_dst: &mut Vec<ValType>) {} fn push(_dst: &mut Vec<ValType>) {}
fn matches(_tys: impl Iterator<Item = ValType>) -> bool { fn matches(_tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
true Ok(())
} }
#[inline] #[inline]
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self { fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
@@ -475,8 +472,14 @@ impl WasmTy for i32 {
fn push(dst: &mut Vec<ValType>) { fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::I32); dst.push(ValType::I32);
} }
fn matches(mut tys: impl Iterator<Item = ValType>) -> bool { fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
tys.next() == Some(ValType::I32) let next = tys.next();
ensure!(
next == Some(ValType::I32),
"Type mismatch, expected i32, got {:?}",
next
);
Ok(())
} }
#[inline] #[inline]
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self { fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
@@ -493,8 +496,14 @@ impl WasmTy for i64 {
fn push(dst: &mut Vec<ValType>) { fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::I64); dst.push(ValType::I64);
} }
fn matches(mut tys: impl Iterator<Item = ValType>) -> bool { fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
tys.next() == Some(ValType::I64) let next = tys.next();
ensure!(
next == Some(ValType::I64),
"Type mismatch, expected i64, got {:?}",
next
);
Ok(())
} }
#[inline] #[inline]
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self { fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
@@ -511,8 +520,14 @@ impl WasmTy for f32 {
fn push(dst: &mut Vec<ValType>) { fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::F32); dst.push(ValType::F32);
} }
fn matches(mut tys: impl Iterator<Item = ValType>) -> bool { fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
tys.next() == Some(ValType::F32) let next = tys.next();
ensure!(
next == Some(ValType::F32),
"Type mismatch, expected f32, got {:?}",
next
);
Ok(())
} }
#[inline] #[inline]
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self { fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
@@ -529,8 +544,14 @@ impl WasmTy for f64 {
fn push(dst: &mut Vec<ValType>) { fn push(dst: &mut Vec<ValType>) {
dst.push(ValType::F64); dst.push(ValType::F64);
} }
fn matches(mut tys: impl Iterator<Item = ValType>) -> bool { fn matches(mut tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
tys.next() == Some(ValType::F64) let next = tys.next();
ensure!(
next == Some(ValType::F64),
"Type mismatch, expected f64, got {:?}",
next
);
Ok(())
} }
#[inline] #[inline]
fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self { fn from_abi(_vmctx: *mut VMContext, abi: Self::Abi) -> Self {
@@ -556,7 +577,7 @@ pub trait WasmRet {
#[doc(hidden)] #[doc(hidden)]
fn push(dst: &mut Vec<ValType>); fn push(dst: &mut Vec<ValType>);
#[doc(hidden)] #[doc(hidden)]
fn matches(tys: impl Iterator<Item = ValType>) -> bool; fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()>;
#[doc(hidden)] #[doc(hidden)]
fn into_abi(self) -> Self::Abi; fn into_abi(self) -> Self::Abi;
} }
@@ -567,7 +588,7 @@ impl<T: WasmTy> WasmRet for T {
T::push(dst) T::push(dst)
} }
fn matches(tys: impl Iterator<Item = ValType>) -> bool { fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
T::matches(tys) T::matches(tys)
} }
@@ -583,7 +604,7 @@ impl<T: WasmTy> WasmRet for Result<T, Trap> {
T::push(dst) T::push(dst)
} }
fn matches(tys: impl Iterator<Item = ValType>) -> bool { fn matches(tys: impl Iterator<Item = ValType>) -> anyhow::Result<()> {
T::matches(tys) T::matches(tys)
} }

View File

@@ -207,33 +207,33 @@ fn trap_import() -> Result<()> {
fn get_from_wrapper() { fn get_from_wrapper() {
let store = Store::default(); let store = Store::default();
let f = Func::wrap0(&store, || {}); let f = Func::wrap0(&store, || {});
assert!(f.get0::<()>().is_some()); assert!(f.get0::<()>().is_ok());
assert!(f.get0::<i32>().is_none()); assert!(f.get0::<i32>().is_err());
assert!(f.get1::<(), ()>().is_some()); assert!(f.get1::<(), ()>().is_ok());
assert!(f.get1::<i32, ()>().is_none()); assert!(f.get1::<i32, ()>().is_err());
assert!(f.get1::<i32, i32>().is_none()); assert!(f.get1::<i32, i32>().is_err());
assert!(f.get2::<(), (), ()>().is_some()); assert!(f.get2::<(), (), ()>().is_ok());
assert!(f.get2::<i32, i32, ()>().is_none()); assert!(f.get2::<i32, i32, ()>().is_err());
assert!(f.get2::<i32, i32, i32>().is_none()); assert!(f.get2::<i32, i32, i32>().is_err());
let f = Func::wrap0(&store, || -> i32 { loop {} }); let f = Func::wrap0(&store, || -> i32 { loop {} });
assert!(f.get0::<i32>().is_some()); assert!(f.get0::<i32>().is_ok());
let f = Func::wrap0(&store, || -> f32 { loop {} }); let f = Func::wrap0(&store, || -> f32 { loop {} });
assert!(f.get0::<f32>().is_some()); assert!(f.get0::<f32>().is_ok());
let f = Func::wrap0(&store, || -> f64 { loop {} }); let f = Func::wrap0(&store, || -> f64 { loop {} });
assert!(f.get0::<f64>().is_some()); assert!(f.get0::<f64>().is_ok());
let f = Func::wrap1(&store, |_: i32| {}); let f = Func::wrap1(&store, |_: i32| {});
assert!(f.get1::<i32, ()>().is_some()); assert!(f.get1::<i32, ()>().is_ok());
assert!(f.get1::<i64, ()>().is_none()); assert!(f.get1::<i64, ()>().is_err());
assert!(f.get1::<f32, ()>().is_none()); assert!(f.get1::<f32, ()>().is_err());
assert!(f.get1::<f64, ()>().is_none()); assert!(f.get1::<f64, ()>().is_err());
let f = Func::wrap1(&store, |_: i64| {}); let f = Func::wrap1(&store, |_: i64| {});
assert!(f.get1::<i64, ()>().is_some()); assert!(f.get1::<i64, ()>().is_ok());
let f = Func::wrap1(&store, |_: f32| {}); let f = Func::wrap1(&store, |_: f32| {});
assert!(f.get1::<f32, ()>().is_some()); assert!(f.get1::<f32, ()>().is_ok());
let f = Func::wrap1(&store, |_: f64| {}); let f = Func::wrap1(&store, |_: f64| {});
assert!(f.get1::<f64, ()>().is_some()); assert!(f.get1::<f64, ()>().is_ok());
} }
#[test] #[test]
@@ -247,16 +247,16 @@ fn get_from_signature() {
let store = Store::default(); let store = Store::default();
let ty = FuncType::new(Box::new([]), Box::new([])); let ty = FuncType::new(Box::new([]), Box::new([]));
let f = Func::new(&store, ty, Rc::new(Foo)); let f = Func::new(&store, ty, Rc::new(Foo));
assert!(f.get0::<()>().is_some()); assert!(f.get0::<()>().is_ok());
assert!(f.get0::<i32>().is_none()); assert!(f.get0::<i32>().is_err());
assert!(f.get1::<i32, ()>().is_none()); assert!(f.get1::<i32, ()>().is_err());
let ty = FuncType::new(Box::new([ValType::I32]), Box::new([ValType::F64])); let ty = FuncType::new(Box::new([ValType::I32]), Box::new([ValType::F64]));
let f = Func::new(&store, ty, Rc::new(Foo)); let f = Func::new(&store, ty, Rc::new(Foo));
assert!(f.get0::<()>().is_none()); assert!(f.get0::<()>().is_err());
assert!(f.get0::<i32>().is_none()); assert!(f.get0::<i32>().is_err());
assert!(f.get1::<i32, ()>().is_none()); assert!(f.get1::<i32, ()>().is_err());
assert!(f.get1::<i32, f64>().is_some()); assert!(f.get1::<i32, f64>().is_ok());
} }
#[test] #[test]
@@ -276,16 +276,16 @@ fn get_from_module() -> anyhow::Result<()> {
)?; )?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&module, &[])?;
let f0 = instance.get_export("f0").unwrap().func().unwrap(); let f0 = instance.get_export("f0").unwrap().func().unwrap();
assert!(f0.get0::<()>().is_some()); assert!(f0.get0::<()>().is_ok());
assert!(f0.get0::<i32>().is_none()); assert!(f0.get0::<i32>().is_err());
let f1 = instance.get_export("f1").unwrap().func().unwrap(); let f1 = instance.get_export("f1").unwrap().func().unwrap();
assert!(f1.get0::<()>().is_none()); assert!(f1.get0::<()>().is_err());
assert!(f1.get1::<i32, ()>().is_some()); assert!(f1.get1::<i32, ()>().is_ok());
assert!(f1.get1::<i32, f32>().is_none()); assert!(f1.get1::<i32, f32>().is_err());
let f2 = instance.get_export("f2").unwrap().func().unwrap(); let f2 = instance.get_export("f2").unwrap().func().unwrap();
assert!(f2.get0::<()>().is_none()); assert!(f2.get0::<()>().is_err());
assert!(f2.get0::<i32>().is_some()); assert!(f2.get0::<i32>().is_ok());
assert!(f2.get1::<i32, ()>().is_none()); assert!(f2.get1::<i32, ()>().is_err());
assert!(f2.get1::<i32, f32>().is_none()); assert!(f2.get1::<i32, f32>().is_err());
Ok(()) Ok(())
} }

View File

@@ -38,8 +38,8 @@ impl wasmtime::WasmTy for WasiCallerMemory {
fn push(_dst: &mut Vec<wasmtime::ValType>) {} fn push(_dst: &mut Vec<wasmtime::ValType>) {}
fn matches(_tys: impl Iterator<Item = wasmtime::ValType>) -> bool { fn matches(_tys: impl Iterator<Item = wasmtime::ValType>) -> anyhow::Result<()> {
true Ok(())
} }
fn from_abi(vmctx: *mut wasmtime_runtime::VMContext, _abi: ()) -> Self { fn from_abi(vmctx: *mut wasmtime_runtime::VMContext, _abi: ()) -> Self {