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:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user