Second iteration on documenation.
This commit is contained in:
parent
f6f23accdd
commit
230abc6a91
121
src/host.rs
121
src/host.rs
|
@ -13,7 +13,6 @@ impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RuntimeArgs<'a> {
|
impl<'a> RuntimeArgs<'a> {
|
||||||
|
|
||||||
/// Extract argument by index `idx` returning error if cast is invalid or not enough arguments
|
/// 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> {
|
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()))?)
|
Ok(self.nth_value(idx)?.try_into().map_err(|_| Error::Value("Invalid argument cast".to_owned()))?)
|
||||||
|
@ -33,7 +32,44 @@ impl<'a> RuntimeArgs<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Custom user error.
|
/// 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!(),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
pub trait HostError: 'static + ::std::fmt::Display + ::std::fmt::Debug + Send + Sync {
|
pub trait HostError: 'static + ::std::fmt::Display + ::std::fmt::Debug + Send + Sync {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn __private_get_type_id__(&self) -> TypeId {
|
fn __private_get_type_id__(&self) -> TypeId {
|
||||||
|
@ -62,6 +98,77 @@ impl HostError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
/// ))
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub trait Externals {
|
pub trait Externals {
|
||||||
fn invoke_index(
|
fn invoke_index(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -70,6 +177,10 @@ pub trait Externals {
|
||||||
) -> Result<Option<RuntimeValue>, Error>;
|
) -> Result<Option<RuntimeValue>, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implementation of [`Externals`] that just traps on [`invoke_index`].
|
||||||
|
///
|
||||||
|
/// [`Externals`]: trait.Externals.html
|
||||||
|
/// [`invoke_index`]: trait.Externals.html#tymethod.invoke_index
|
||||||
pub struct NopExternals;
|
pub struct NopExternals;
|
||||||
|
|
||||||
impl Externals for NopExternals {
|
impl Externals for NopExternals {
|
||||||
|
@ -86,7 +197,7 @@ impl Externals for NopExternals {
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use value::RuntimeValue;
|
use value::RuntimeValue;
|
||||||
use super::RuntimeArgs;
|
use super::{RuntimeArgs, HostError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn i32_runtime_args() {
|
fn i32_runtime_args() {
|
||||||
|
@ -100,4 +211,8 @@ mod tests {
|
||||||
let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
|
let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
|
||||||
assert!(args.nth::<i32>(0).is_err());
|
assert!(args.nth::<i32>(0).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that `HostError` trait is object safe.
|
||||||
|
fn _host_error_is_object_safe(_: &HostError) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,36 +7,95 @@ use module::ModuleRef;
|
||||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||||
use {Error, Signature};
|
use {Error, Signature};
|
||||||
|
|
||||||
|
|
||||||
|
/// Resolver of a module's dependencies.
|
||||||
|
///
|
||||||
|
/// A module have dependencies in a form of a list of imports (i.e.
|
||||||
|
/// tuple of a (`module_name`, `field_name`, `descriptor`)).
|
||||||
|
///
|
||||||
|
/// The job of implementations of this trait is to provide on each
|
||||||
|
/// import a corresponding concrete reference.
|
||||||
pub trait ImportResolver {
|
pub trait ImportResolver {
|
||||||
|
|
||||||
|
/// Resolve a function.
|
||||||
|
///
|
||||||
|
/// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match.
|
||||||
|
/// Otherwise, link-time error will occur.
|
||||||
fn resolve_func(
|
fn resolve_func(
|
||||||
&self,
|
&self,
|
||||||
module_name: &str,
|
_module_name: &str,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
func_type: &Signature,
|
_signature: &Signature,
|
||||||
) -> Result<FuncRef, Error>;
|
) -> Result<FuncRef, Error>;
|
||||||
|
|
||||||
|
/// Resolve a global variable.
|
||||||
|
///
|
||||||
|
/// Returned global should match given `descriptor`, i.e. type and mutability
|
||||||
|
/// should match. Otherwise, link-time error will occur.
|
||||||
fn resolve_global(
|
fn resolve_global(
|
||||||
&self,
|
&self,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
global_type: &GlobalDescriptor,
|
descriptor: &GlobalDescriptor,
|
||||||
) -> Result<GlobalRef, Error>;
|
) -> Result<GlobalRef, Error>;
|
||||||
|
|
||||||
|
/// Resolve a memory.
|
||||||
|
///
|
||||||
|
/// Returned memory should match requested memory (described by the `descriptor`),
|
||||||
|
/// i.e. initial size of a returned memory should be equal or larger than requested memory.
|
||||||
|
/// Furthermore, if requested memory have maximum size, returned memory either should have
|
||||||
|
/// equal or larger maximum size or have no maximum size at all.
|
||||||
|
/// If returned memory doesn't match the requested then link-time error will occur.
|
||||||
fn resolve_memory(
|
fn resolve_memory(
|
||||||
&self,
|
&self,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
memory_type: &MemoryDescriptor,
|
descriptor: &MemoryDescriptor,
|
||||||
) -> Result<MemoryRef, Error>;
|
) -> Result<MemoryRef, Error>;
|
||||||
|
|
||||||
|
/// Resolve a table.
|
||||||
|
///
|
||||||
|
/// Returned table should match requested table (described by the `descriptor`),
|
||||||
|
/// i.e. initial size of a returned table should be equal or larger than requested table.
|
||||||
|
/// Furthermore, if requested memory have maximum size, returned memory either should have
|
||||||
|
/// equal or larger maximum size or have no maximum size at all.
|
||||||
|
/// If returned table doesn't match the requested then link-time error will occur.
|
||||||
fn resolve_table(
|
fn resolve_table(
|
||||||
&self,
|
&self,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
table_type: &TableDescriptor,
|
descriptor: &TableDescriptor,
|
||||||
) -> Result<TableRef, Error>;
|
) -> Result<TableRef, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience builder of [`ImportResolver`].
|
||||||
|
///
|
||||||
|
/// With help of this builder, you can easily create [`ImportResolver`], just by
|
||||||
|
/// adding needed [resolvers][`ModuleImportResolver`] by names.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use wasmi::{load_from_buffer, ModuleInstance, ImportsBuilder};
|
||||||
|
/// #
|
||||||
|
/// # struct EnvModuleResolver;
|
||||||
|
/// # impl ::wasmi::ModuleImportResolver for EnvModuleResolver { }
|
||||||
|
/// # fn func() -> Result<(), ::wasmi::Error> {
|
||||||
|
/// # let module = load_from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap();
|
||||||
|
/// # let other_instance = ModuleInstance::new(&module, &ImportsBuilder::default())?.assert_no_start();
|
||||||
|
///
|
||||||
|
/// let imports = ImportsBuilder::new()
|
||||||
|
/// .with_resolver("env", &EnvModuleResolver)
|
||||||
|
/// // Note, that ModuleInstance can be a resolver too.
|
||||||
|
/// .with_resolver("other_instance", &other_instance);
|
||||||
|
/// let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||||
|
/// [`ModuleImportResolver`]: trait.ModuleImportResolver.html
|
||||||
pub struct ImportsBuilder<'a> {
|
pub struct ImportsBuilder<'a> {
|
||||||
modules: HashMap<String, &'a ModuleImportResolver>,
|
modules: HashMap<String, &'a ModuleImportResolver>,
|
||||||
}
|
}
|
||||||
|
@ -48,10 +107,12 @@ impl<'a> Default for ImportsBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ImportsBuilder<'a> {
|
impl<'a> ImportsBuilder<'a> {
|
||||||
|
/// Create an empty `ImportsBuilder`.
|
||||||
pub fn new() -> ImportsBuilder<'a> {
|
pub fn new() -> ImportsBuilder<'a> {
|
||||||
ImportsBuilder { modules: HashMap::new() }
|
ImportsBuilder { modules: HashMap::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register an resolver by a name.
|
||||||
pub fn with_resolver<N: Into<String>>(
|
pub fn with_resolver<N: Into<String>>(
|
||||||
mut self,
|
mut self,
|
||||||
name: N,
|
name: N,
|
||||||
|
@ -61,11 +122,12 @@ impl<'a> ImportsBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register an resolver by a name.
|
||||||
pub fn push_resolver<N: Into<String>>(&mut self, name: N, resolver: &'a ModuleImportResolver) {
|
pub fn push_resolver<N: Into<String>>(&mut self, name: N, resolver: &'a ModuleImportResolver) {
|
||||||
self.modules.insert(name.into(), resolver);
|
self.modules.insert(name.into(), resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> {
|
fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> {
|
||||||
self.modules.get(name).cloned()
|
self.modules.get(name).cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +178,15 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Version of [`ImportResolver`] specialized for a single module.
|
||||||
|
///
|
||||||
|
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||||
pub trait ModuleImportResolver {
|
pub trait ModuleImportResolver {
|
||||||
|
/// Resolve a function.
|
||||||
|
///
|
||||||
|
/// See [`ImportResolver::resolve_func`] for details.
|
||||||
|
///
|
||||||
|
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
|
||||||
fn resolve_func(
|
fn resolve_func(
|
||||||
&self,
|
&self,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
|
@ -127,6 +197,11 @@ pub trait ModuleImportResolver {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a global variable.
|
||||||
|
///
|
||||||
|
/// See [`ImportResolver::resolve_global`] for details.
|
||||||
|
///
|
||||||
|
/// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global
|
||||||
fn resolve_global(
|
fn resolve_global(
|
||||||
&self,
|
&self,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
|
@ -137,6 +212,11 @@ pub trait ModuleImportResolver {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a memory.
|
||||||
|
///
|
||||||
|
/// See [`ImportResolver::resolve_memory`] for details.
|
||||||
|
///
|
||||||
|
/// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory
|
||||||
fn resolve_memory(
|
fn resolve_memory(
|
||||||
&self,
|
&self,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
|
@ -147,6 +227,11 @@ pub trait ModuleImportResolver {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a table.
|
||||||
|
///
|
||||||
|
/// See [`ImportResolver::resolve_table`] for details.
|
||||||
|
///
|
||||||
|
/// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table
|
||||||
fn resolve_table(
|
fn resolve_table(
|
||||||
&self,
|
&self,
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
|
|
Loading…
Reference in New Issue