2018-01-17 15:32:33 +00:00
|
|
|
use std::any::TypeId;
|
2018-01-23 11:26:45 +00:00
|
|
|
use value::{RuntimeValue, TryInto};
|
2018-01-17 15:32:33 +00:00
|
|
|
use Error;
|
|
|
|
|
2018-01-23 16:38:49 +00:00
|
|
|
/// Safe wrapper for list of arguments.
|
2018-01-23 11:42:20 +00:00
|
|
|
#[derive(Debug)]
|
2018-01-23 11:05:31 +00:00
|
|
|
pub struct RuntimeArgs<'a>(&'a [RuntimeValue]);
|
|
|
|
|
|
|
|
impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> {
|
|
|
|
fn from(inner: &'a [RuntimeValue]) -> Self {
|
|
|
|
RuntimeArgs(inner)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> RuntimeArgs<'a> {
|
2018-01-23 11:26:45 +00:00
|
|
|
/// Extract argument by index `idx` returning error if cast is invalid or not enough arguments
|
|
|
|
pub fn nth<T>(&self, idx: usize) -> Result<T, Error> where RuntimeValue: TryInto<T, Error> {
|
|
|
|
Ok(self.nth_value(idx)?.try_into().map_err(|_| Error::Value("Invalid argument cast".to_owned()))?)
|
|
|
|
}
|
|
|
|
|
2018-01-23 11:43:09 +00:00
|
|
|
/// Extract argument as a runtime value by index `idx` returning error is not enough arguments
|
2018-01-23 11:26:45 +00:00
|
|
|
pub fn nth_value(&self, idx: usize) -> Result<RuntimeValue, Error> {
|
|
|
|
if self.0.len() <= idx {
|
|
|
|
return Err(Error::Value("Invalid argument index".to_owned()));
|
|
|
|
}
|
|
|
|
Ok(self.0[idx])
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Total number of arguments
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.0.len()
|
2018-01-23 11:05:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-24 15:49:42 +00:00
|
|
|
/// Trait that allows the host to return custom error.
|
|
|
|
///
|
|
|
|
/// It should be useful for representing custom traps,
|
|
|
|
/// troubles at instantiation time or other host specific conditions.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use std::fmt;
|
|
|
|
/// use wasmi::{Error, HostError};
|
|
|
|
///
|
|
|
|
/// #[derive(Debug)]
|
|
|
|
/// struct MyError {
|
|
|
|
/// code: u32,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl fmt::Display for MyError {
|
|
|
|
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
/// write!(f, "MyError, code={}", self.code)
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl HostError for MyError { }
|
|
|
|
///
|
|
|
|
/// fn failable_fn() -> Result<(), Error> {
|
|
|
|
/// let my_error = MyError { code: 1312 };
|
|
|
|
/// Err(Error::Host(Box::new(my_error)))
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// match failable_fn() {
|
|
|
|
/// Err(Error::Host(host_error)) => {
|
|
|
|
/// let my_error = host_error.downcast_ref::<MyError>().unwrap();
|
|
|
|
/// assert_eq!(my_error.code, 1312);
|
|
|
|
/// }
|
|
|
|
/// _ => panic!(),
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2018-01-23 13:57:09 +00:00
|
|
|
pub trait HostError: 'static + ::std::fmt::Display + ::std::fmt::Debug + Send + Sync {
|
2018-01-17 15:32:33 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
fn __private_get_type_id__(&self) -> TypeId {
|
|
|
|
TypeId::of::<Self>()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HostError {
|
|
|
|
/// Attempt to downcast this `HostError` to a concrete type by reference.
|
|
|
|
pub fn downcast_ref<T: HostError>(&self) -> Option<&T> {
|
|
|
|
if self.__private_get_type_id__() == TypeId::of::<T>() {
|
|
|
|
unsafe { Some(&*(self as *const HostError as *const T)) }
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempt to downcast this `HostError` to a concrete type by mutable
|
|
|
|
/// reference.
|
|
|
|
pub fn downcast_mut<T: HostError>(&mut self) -> Option<&mut T> {
|
|
|
|
if self.__private_get_type_id__() == TypeId::of::<T>() {
|
|
|
|
unsafe { Some(&mut *(self as *mut HostError as *mut T)) }
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-24 15:49:42 +00:00
|
|
|
/// Trait that allows to implement host functions.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use wasmi::{
|
|
|
|
/// Externals, RuntimeValue, RuntimeArgs, Error, ModuleImportResolver,
|
|
|
|
/// FuncRef, ValueType, Signature, FuncInstance
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// struct HostExternals {
|
|
|
|
/// counter: usize,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// const ADD_FUNC_INDEX: usize = 0;
|
|
|
|
///
|
|
|
|
/// impl Externals for HostExternals {
|
|
|
|
/// fn invoke_index(
|
|
|
|
/// &mut self,
|
|
|
|
/// index: usize,
|
|
|
|
/// args: RuntimeArgs,
|
|
|
|
/// ) -> Result<Option<RuntimeValue>, Error> {
|
|
|
|
/// match index {
|
|
|
|
/// ADD_FUNC_INDEX => {
|
|
|
|
/// let a: u32 = args.nth(0).unwrap();
|
|
|
|
/// let b: u32 = args.nth(1).unwrap();
|
|
|
|
/// let result = a + b;
|
|
|
|
///
|
|
|
|
/// Ok(Some(RuntimeValue::I32(result as i32)))
|
|
|
|
/// }
|
|
|
|
/// _ => panic!("Unimplemented function at {}", index),
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl HostExternals {
|
|
|
|
/// fn check_signature(
|
|
|
|
/// &self,
|
|
|
|
/// index: usize,
|
|
|
|
/// signature: &Signature
|
|
|
|
/// ) -> bool {
|
|
|
|
/// let (params, ret_ty): (&[ValueType], Option<ValueType>) = match index {
|
|
|
|
/// ADD_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)),
|
|
|
|
/// _ => return false,
|
|
|
|
/// };
|
|
|
|
/// signature.params() == params && signature.return_type() == ret_ty
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl ModuleImportResolver for HostExternals {
|
|
|
|
/// fn resolve_func(
|
|
|
|
/// &self,
|
|
|
|
/// field_name: &str,
|
|
|
|
/// signature: &Signature
|
|
|
|
/// ) -> Result<FuncRef, Error> {
|
|
|
|
/// let index = match field_name {
|
|
|
|
/// "add" => ADD_FUNC_INDEX,
|
|
|
|
/// _ => {
|
|
|
|
/// return Err(Error::Instantiation(
|
|
|
|
/// format!("Export {} not found", field_name),
|
|
|
|
/// ))
|
|
|
|
/// }
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// Ok(FuncInstance::alloc_host(
|
|
|
|
/// Signature::new(&[ValueType::I32, ValueType::I32][..], Some(ValueType::I32)),
|
|
|
|
/// ADD_FUNC_INDEX,
|
|
|
|
/// ))
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2018-01-17 15:32:33 +00:00
|
|
|
pub trait Externals {
|
|
|
|
fn invoke_index(
|
|
|
|
&mut self,
|
|
|
|
index: usize,
|
2018-01-23 11:26:45 +00:00
|
|
|
args: RuntimeArgs,
|
2018-01-17 15:32:33 +00:00
|
|
|
) -> Result<Option<RuntimeValue>, Error>;
|
|
|
|
}
|
|
|
|
|
2018-01-24 15:49:42 +00:00
|
|
|
/// Implementation of [`Externals`] that just traps on [`invoke_index`].
|
|
|
|
///
|
|
|
|
/// [`Externals`]: trait.Externals.html
|
|
|
|
/// [`invoke_index`]: trait.Externals.html#tymethod.invoke_index
|
2018-01-17 15:32:33 +00:00
|
|
|
pub struct NopExternals;
|
|
|
|
|
|
|
|
impl Externals for NopExternals {
|
|
|
|
fn invoke_index(
|
|
|
|
&mut self,
|
|
|
|
_index: usize,
|
2018-01-23 11:26:45 +00:00
|
|
|
_args: RuntimeArgs,
|
2018-01-17 15:32:33 +00:00
|
|
|
) -> Result<Option<RuntimeValue>, Error> {
|
|
|
|
Err(Error::Trap("invoke index on no-op externals".into()))
|
|
|
|
}
|
|
|
|
}
|
2018-01-23 11:05:31 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
|
|
|
|
use value::RuntimeValue;
|
2018-01-24 15:49:42 +00:00
|
|
|
use super::{RuntimeArgs, HostError};
|
2018-01-23 11:05:31 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn i32_runtime_args() {
|
|
|
|
let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into();
|
|
|
|
let val: i32 = args.nth(0).unwrap();
|
|
|
|
assert_eq!(val, 0);
|
|
|
|
}
|
|
|
|
|
2018-01-23 11:42:20 +00:00
|
|
|
#[test]
|
|
|
|
fn i64_invalid_arg_cast() {
|
|
|
|
let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
|
|
|
|
assert!(args.nth::<i32>(0).is_err());
|
|
|
|
}
|
2018-01-24 15:49:42 +00:00
|
|
|
|
|
|
|
// Tests that `HostError` trait is object safe.
|
|
|
|
fn _host_error_is_object_safe(_: &HostError) {
|
|
|
|
}
|
2018-01-23 16:38:49 +00:00
|
|
|
}
|