Merge pull request #17 from pepyakin/docs-3
Third iteration on documenation.
This commit is contained in:
commit
6f3ec93952
|
@ -339,6 +339,7 @@ fn run_action(
|
||||||
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
||||||
})?
|
})?
|
||||||
.as_global()
|
.as_global()
|
||||||
|
.cloned()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -15,7 +15,6 @@ use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
|
||||||
/// This reference has a reference-counting semantics.
|
/// This reference has a reference-counting semantics.
|
||||||
///
|
///
|
||||||
/// [`FuncInstance`]: struct.FuncInstance.html
|
/// [`FuncInstance`]: struct.FuncInstance.html
|
||||||
///
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FuncRef(Rc<FuncInstance>);
|
pub struct FuncRef(Rc<FuncInstance>);
|
||||||
|
|
||||||
|
@ -30,8 +29,8 @@ impl ::std::ops::Deref for FuncRef {
|
||||||
///
|
///
|
||||||
/// Functions are the unit of orgianization of code in WebAssembly. Each function takes a sequence of values
|
/// Functions are the unit of orgianization of code in WebAssembly. Each function takes a sequence of values
|
||||||
/// as parameters and either optionally return a value or trap.
|
/// as parameters and either optionally return a value or trap.
|
||||||
/// Functions can call other function (including itself, i.e recursively) and imported functions
|
/// Functions can call other function including itself (i.e recursive calls are allowed) and imported functions
|
||||||
/// (i.e defined in another module).
|
/// (i.e functions defined in another module or by the host environment).
|
||||||
///
|
///
|
||||||
/// Functions can be defined either:
|
/// Functions can be defined either:
|
||||||
///
|
///
|
||||||
|
@ -40,7 +39,6 @@ impl ::std::ops::Deref for FuncRef {
|
||||||
/// See more in [`Externals`].
|
/// See more in [`Externals`].
|
||||||
///
|
///
|
||||||
/// [`Externals`]: trait.Externals.html
|
/// [`Externals`]: trait.Externals.html
|
||||||
///
|
|
||||||
pub struct FuncInstance(FuncInstanceInternal);
|
pub struct FuncInstance(FuncInstanceInternal);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
119
src/host.rs
119
src/host.rs
|
@ -45,31 +45,30 @@ impl<'a> RuntimeArgs<'a> {
|
||||||
///
|
///
|
||||||
/// #[derive(Debug)]
|
/// #[derive(Debug)]
|
||||||
/// struct MyError {
|
/// struct MyError {
|
||||||
/// code: u32,
|
/// code: u32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl fmt::Display for MyError {
|
/// impl fmt::Display for MyError {
|
||||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
/// write!(f, "MyError, code={}", self.code)
|
/// write!(f, "MyError, code={}", self.code)
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl HostError for MyError { }
|
/// impl HostError for MyError { }
|
||||||
///
|
///
|
||||||
/// fn failable_fn() -> Result<(), Error> {
|
/// fn failable_fn() -> Result<(), Error> {
|
||||||
/// let my_error = MyError { code: 1312 };
|
/// let my_error = MyError { code: 1312 };
|
||||||
/// Err(Error::Host(Box::new(my_error)))
|
/// Err(Error::Host(Box::new(my_error)))
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// match failable_fn() {
|
/// match failable_fn() {
|
||||||
/// Err(Error::Host(host_error)) => {
|
/// Err(Error::Host(host_error)) => {
|
||||||
/// let my_error = host_error.downcast_ref::<MyError>().unwrap();
|
/// let my_error = host_error.downcast_ref::<MyError>().unwrap();
|
||||||
/// assert_eq!(my_error.code, 1312);
|
/// assert_eq!(my_error.code, 1312);
|
||||||
/// }
|
/// }
|
||||||
/// _ => panic!(),
|
/// _ => 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 {
|
||||||
|
@ -104,69 +103,69 @@ impl HostError {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use wasmi::{
|
/// use wasmi::{
|
||||||
/// Externals, RuntimeValue, RuntimeArgs, Error, ModuleImportResolver,
|
/// Externals, RuntimeValue, RuntimeArgs, Error, ModuleImportResolver,
|
||||||
/// FuncRef, ValueType, Signature, FuncInstance
|
/// FuncRef, ValueType, Signature, FuncInstance
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// struct HostExternals {
|
/// struct HostExternals {
|
||||||
/// counter: usize,
|
/// counter: usize,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// const ADD_FUNC_INDEX: usize = 0;
|
/// const ADD_FUNC_INDEX: usize = 0;
|
||||||
///
|
///
|
||||||
/// impl Externals for HostExternals {
|
/// impl Externals for HostExternals {
|
||||||
/// fn invoke_index(
|
/// fn invoke_index(
|
||||||
/// &mut self,
|
/// &mut self,
|
||||||
/// index: usize,
|
/// index: usize,
|
||||||
/// args: RuntimeArgs,
|
/// args: RuntimeArgs,
|
||||||
/// ) -> Result<Option<RuntimeValue>, Error> {
|
/// ) -> Result<Option<RuntimeValue>, Error> {
|
||||||
/// match index {
|
/// match index {
|
||||||
/// ADD_FUNC_INDEX => {
|
/// ADD_FUNC_INDEX => {
|
||||||
/// let a: u32 = args.nth(0).unwrap();
|
/// let a: u32 = args.nth(0).unwrap();
|
||||||
/// let b: u32 = args.nth(1).unwrap();
|
/// let b: u32 = args.nth(1).unwrap();
|
||||||
/// let result = a + b;
|
/// let result = a + b;
|
||||||
///
|
///
|
||||||
/// Ok(Some(RuntimeValue::I32(result as i32)))
|
/// Ok(Some(RuntimeValue::I32(result as i32)))
|
||||||
/// }
|
/// }
|
||||||
/// _ => panic!("Unimplemented function at {}", index),
|
/// _ => panic!("Unimplemented function at {}", index),
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl HostExternals {
|
/// impl HostExternals {
|
||||||
/// fn check_signature(
|
/// fn check_signature(
|
||||||
/// &self,
|
/// &self,
|
||||||
/// index: usize,
|
/// index: usize,
|
||||||
/// signature: &Signature
|
/// signature: &Signature
|
||||||
/// ) -> bool {
|
/// ) -> bool {
|
||||||
/// let (params, ret_ty): (&[ValueType], Option<ValueType>) = match index {
|
/// let (params, ret_ty): (&[ValueType], Option<ValueType>) = match index {
|
||||||
/// ADD_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)),
|
/// ADD_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)),
|
||||||
/// _ => return false,
|
/// _ => return false,
|
||||||
/// };
|
/// };
|
||||||
/// signature.params() == params && signature.return_type() == ret_ty
|
/// signature.params() == params && signature.return_type() == ret_ty
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl ModuleImportResolver for HostExternals {
|
/// impl ModuleImportResolver for HostExternals {
|
||||||
/// fn resolve_func(
|
/// fn resolve_func(
|
||||||
/// &self,
|
/// &self,
|
||||||
/// field_name: &str,
|
/// field_name: &str,
|
||||||
/// signature: &Signature
|
/// signature: &Signature
|
||||||
/// ) -> Result<FuncRef, Error> {
|
/// ) -> Result<FuncRef, Error> {
|
||||||
/// let index = match field_name {
|
/// let index = match field_name {
|
||||||
/// "add" => ADD_FUNC_INDEX,
|
/// "add" => ADD_FUNC_INDEX,
|
||||||
/// _ => {
|
/// _ => {
|
||||||
/// return Err(Error::Instantiation(
|
/// return Err(Error::Instantiation(
|
||||||
/// format!("Export {} not found", field_name),
|
/// format!("Export {} not found", field_name),
|
||||||
/// ))
|
/// ))
|
||||||
/// }
|
/// }
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// Ok(FuncInstance::alloc_host(
|
/// Ok(FuncInstance::alloc_host(
|
||||||
/// Signature::new(&[ValueType::I32, ValueType::I32][..], Some(ValueType::I32)),
|
/// Signature::new(&[ValueType::I32, ValueType::I32][..], Some(ValueType::I32)),
|
||||||
/// ADD_FUNC_INDEX,
|
/// ADD_FUNC_INDEX,
|
||||||
/// ))
|
/// ))
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub trait Externals {
|
pub trait Externals {
|
||||||
|
|
|
@ -15,6 +15,10 @@ use {Error, Signature};
|
||||||
///
|
///
|
||||||
/// The job of implementations of this trait is to provide on each
|
/// The job of implementations of this trait is to provide on each
|
||||||
/// import a corresponding concrete reference.
|
/// import a corresponding concrete reference.
|
||||||
|
///
|
||||||
|
/// For simple use-cases you can use [`ImportsBuilder`].
|
||||||
|
///
|
||||||
|
/// [`ImportsBuilder`]: struct.ImportsBuilder.html
|
||||||
pub trait ImportResolver {
|
pub trait ImportResolver {
|
||||||
|
|
||||||
/// Resolve a function.
|
/// Resolve a function.
|
||||||
|
@ -85,9 +89,9 @@ pub trait ImportResolver {
|
||||||
/// # let other_instance = ModuleInstance::new(&module, &ImportsBuilder::default())?.assert_no_start();
|
/// # let other_instance = ModuleInstance::new(&module, &ImportsBuilder::default())?.assert_no_start();
|
||||||
///
|
///
|
||||||
/// let imports = ImportsBuilder::new()
|
/// let imports = ImportsBuilder::new()
|
||||||
/// .with_resolver("env", &EnvModuleResolver)
|
/// .with_resolver("env", &EnvModuleResolver)
|
||||||
/// // Note, that ModuleInstance can be a resolver too.
|
/// // Note, that ModuleInstance can be a resolver too.
|
||||||
/// .with_resolver("other_instance", &other_instance);
|
/// .with_resolver("other_instance", &other_instance);
|
||||||
/// let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
|
/// let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
|
||||||
///
|
///
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
|
@ -254,6 +258,7 @@ impl ModuleImportResolver for ModuleRef {
|
||||||
Error::Instantiation(format!("Export {} not found", field_name))
|
Error::Instantiation(format!("Export {} not found", field_name))
|
||||||
})?
|
})?
|
||||||
.as_func()
|
.as_func()
|
||||||
|
.cloned()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::Instantiation(format!("Export {} is not a function", field_name))
|
Error::Instantiation(format!("Export {} is not a function", field_name))
|
||||||
})?)
|
})?)
|
||||||
|
@ -269,6 +274,7 @@ impl ModuleImportResolver for ModuleRef {
|
||||||
Error::Instantiation(format!("Export {} not found", field_name))
|
Error::Instantiation(format!("Export {} not found", field_name))
|
||||||
})?
|
})?
|
||||||
.as_global()
|
.as_global()
|
||||||
|
.cloned()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::Instantiation(format!("Export {} is not a global", field_name))
|
Error::Instantiation(format!("Export {} is not a global", field_name))
|
||||||
})?)
|
})?)
|
||||||
|
@ -284,6 +290,7 @@ impl ModuleImportResolver for ModuleRef {
|
||||||
Error::Instantiation(format!("Export {} not found", field_name))
|
Error::Instantiation(format!("Export {} not found", field_name))
|
||||||
})?
|
})?
|
||||||
.as_memory()
|
.as_memory()
|
||||||
|
.cloned()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::Instantiation(format!("Export {} is not a memory", field_name))
|
Error::Instantiation(format!("Export {} is not a memory", field_name))
|
||||||
})?)
|
})?)
|
||||||
|
@ -299,6 +306,7 @@ impl ModuleImportResolver for ModuleRef {
|
||||||
Error::Instantiation(format!("Export {} not found", field_name))
|
Error::Instantiation(format!("Export {} not found", field_name))
|
||||||
})?
|
})?
|
||||||
.as_table()
|
.as_table()
|
||||||
|
.cloned()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::Instantiation(format!("Export {} is not a table", field_name))
|
Error::Instantiation(format!("Export {} is not a table", field_name))
|
||||||
})?)
|
})?)
|
||||||
|
|
96
src/lib.rs
96
src/lib.rs
|
@ -1,4 +1,98 @@
|
||||||
//! WebAssembly interpreter module.
|
//! # wasmi
|
||||||
|
//!
|
||||||
|
//! This library allows to load WebAssembly modules in binary format and invoke functions on them.
|
||||||
|
//!
|
||||||
|
//! # Introduction
|
||||||
|
//!
|
||||||
|
//! WebAssembly (wasm) is a safe, portable, compact format that designed for efficient execution.
|
||||||
|
//!
|
||||||
|
//! Wasm code is distributed in a form of modules, that contains definitions of:
|
||||||
|
//!
|
||||||
|
//! - functions,
|
||||||
|
//! - global variables,
|
||||||
|
//! - linear memories,
|
||||||
|
//! - tables.
|
||||||
|
//!
|
||||||
|
//! and this definitions can be imported. Also, each definition can be exported.
|
||||||
|
//!
|
||||||
|
//! In addition to definitions, modules can define initialization data for their memories or tables that takes the
|
||||||
|
//! form of segments copied to given offsets. They can also define a `start` function that is automatically executed.
|
||||||
|
//!
|
||||||
|
//! ## Loading and Validation
|
||||||
|
//!
|
||||||
|
//! Before execution a module should be validated. This process checks that module is well-formed
|
||||||
|
//! and makes only allowed operations.
|
||||||
|
//!
|
||||||
|
//! Valid modules can't access memory out of it's sandbox, can't cause stack underflow
|
||||||
|
//! and can call functions only with correct signatures.
|
||||||
|
//!
|
||||||
|
//! ## Instantiatiation
|
||||||
|
//!
|
||||||
|
//! In order to execute code in wasm module it should be instatiated.
|
||||||
|
//! Instantiation includes the following steps:
|
||||||
|
//!
|
||||||
|
//! 1. Create an empty module instance,
|
||||||
|
//! 2. Resolve definition instances for each declared import in the module,
|
||||||
|
//! 3. Instantiate definitions declared in the module (e.g. allocate global variables, allocate linear memory, etc),
|
||||||
|
//! 4. Initialize memory and table contents by copiying segments into them,
|
||||||
|
//! 5. Execute `start` function, if any.
|
||||||
|
//!
|
||||||
|
//! After these steps, module instance are ready to execute functions.
|
||||||
|
//!
|
||||||
|
//! ## Execution
|
||||||
|
//!
|
||||||
|
//! It is allowed to only execute functions which are exported by a module.
|
||||||
|
//! Functions can either return a result or trap (e.g. there can't be linking-error at the middle of execution).
|
||||||
|
//! This property is ensured by the validation process.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! extern crate wasmi;
|
||||||
|
//! extern crate wabt;
|
||||||
|
//!
|
||||||
|
//! use wasmi::{ModuleInstance, ImportsBuilder, NopExternals, RuntimeValue};
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! // Parse WAT (WebAssembly Text format) into wasm bytecode.
|
||||||
|
//! let wasm_binary: Vec<u8> =
|
||||||
|
//! wabt::wat2wasm(
|
||||||
|
//! r#"
|
||||||
|
//! (module
|
||||||
|
//! (func (export "test") (result i32)
|
||||||
|
//! i32.const 1337
|
||||||
|
//! )
|
||||||
|
//! )
|
||||||
|
//! "#,
|
||||||
|
//! )
|
||||||
|
//! .expect("failed to parse wat");
|
||||||
|
//!
|
||||||
|
//! // Load wasm binary and prepare it for instantiation.
|
||||||
|
//! let module = wasmi::load_from_buffer(&wasm_binary)
|
||||||
|
//! .expect("failed to load wasm");
|
||||||
|
//!
|
||||||
|
//! // Instantiate a module with empty imports and
|
||||||
|
//! // asserting that there is no `start` function.
|
||||||
|
//! let instance =
|
||||||
|
//! ModuleInstance::new(
|
||||||
|
//! &module,
|
||||||
|
//! &ImportsBuilder::default()
|
||||||
|
//! )
|
||||||
|
//! .expect("failed to instantiate wasm module")
|
||||||
|
//! .assert_no_start();
|
||||||
|
//!
|
||||||
|
//! // Finally, invoke exported function "test" with no parameters
|
||||||
|
//! // and empty external function executor.
|
||||||
|
//! assert_eq!(
|
||||||
|
//! instance.invoke_export(
|
||||||
|
//! "test",
|
||||||
|
//! &[],
|
||||||
|
//! &mut NopExternals,
|
||||||
|
//! ).expect("failed to execute export"),
|
||||||
|
//! Some(RuntimeValue::I32(1337)),
|
||||||
|
//! );
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
// TODO(pepyakin): Fix this asap https://github.com/pepyakin/wasmi/issues/3
|
// TODO(pepyakin): Fix this asap https://github.com/pepyakin/wasmi/issues/3
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
|
@ -38,6 +38,8 @@ impl ::std::ops::Deref for MemoryRef {
|
||||||
/// A memory is created with an initial size but can be grown dynamically.
|
/// A memory is created with an initial size but can be grown dynamically.
|
||||||
/// The growth can be limited by specifying maximum size.
|
/// The growth can be limited by specifying maximum size.
|
||||||
/// The size of a memory is always a integer multiple of a page size - 64KiB.
|
/// The size of a memory is always a integer multiple of a page size - 64KiB.
|
||||||
|
///
|
||||||
|
/// At the moment, wasm doesn't provide any way to shrink the memory.
|
||||||
pub struct MemoryInstance {
|
pub struct MemoryInstance {
|
||||||
/// Memofy limits.
|
/// Memofy limits.
|
||||||
limits: ResizableLimits,
|
limits: ResizableLimits,
|
||||||
|
|
167
src/module.rs
167
src/module.rs
|
@ -13,6 +13,11 @@ use host::Externals;
|
||||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||||
|
|
||||||
|
/// Reference to a [`ModuleInstance`].
|
||||||
|
///
|
||||||
|
/// This reference has a reference-counting semantics.
|
||||||
|
///
|
||||||
|
/// [`ModuleInstance`]: struct.ModuleInstance.html
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ModuleRef(pub(crate) Rc<ModuleInstance>);
|
pub struct ModuleRef(pub(crate) Rc<ModuleInstance>);
|
||||||
|
|
||||||
|
@ -23,6 +28,7 @@ impl ::std::ops::Deref for ModuleRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An external value is the runtime representation of an entity that can be imported or exported.
|
||||||
pub enum ExternVal {
|
pub enum ExternVal {
|
||||||
Func(FuncRef),
|
Func(FuncRef),
|
||||||
Table(TableRef),
|
Table(TableRef),
|
||||||
|
@ -57,35 +63,64 @@ impl fmt::Debug for ExternVal {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternVal {
|
impl ExternVal {
|
||||||
pub fn as_func(&self) -> Option<FuncRef> {
|
/// Get underlying function reference if this `ExternVal` contains
|
||||||
|
/// a function, or `None` if it is some other kind.
|
||||||
|
pub fn as_func(&self) -> Option<&FuncRef> {
|
||||||
match *self {
|
match *self {
|
||||||
ExternVal::Func(ref func) => Some(func.clone()),
|
ExternVal::Func(ref func) => Some(func),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_table(&self) -> Option<TableRef> {
|
/// Get underlying table reference if this `ExternVal` contains
|
||||||
|
/// a table, or `None` if it is some other kind.
|
||||||
|
pub fn as_table(&self) -> Option<&TableRef> {
|
||||||
match *self {
|
match *self {
|
||||||
ExternVal::Table(ref table) => Some(table.clone()),
|
ExternVal::Table(ref table) => Some(table),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_memory(&self) -> Option<MemoryRef> {
|
/// Get underlying memory reference if this `ExternVal` contains
|
||||||
|
/// a memory, or `None` if it is some other kind.
|
||||||
|
pub fn as_memory(&self) -> Option<&MemoryRef> {
|
||||||
match *self {
|
match *self {
|
||||||
ExternVal::Memory(ref memory) => Some(memory.clone()),
|
ExternVal::Memory(ref memory) => Some(memory),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_global(&self) -> Option<GlobalRef> {
|
/// Get underlying global variable reference if this `ExternVal` contains
|
||||||
|
/// a global, or `None` if it is some other kind.
|
||||||
|
pub fn as_global(&self) -> Option<&GlobalRef> {
|
||||||
match *self {
|
match *self {
|
||||||
ExternVal::Global(ref global) => Some(global.clone()),
|
ExternVal::Global(ref global) => Some(global),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A module instance is the runtime representation of a [module][`LoadedModule`].
|
||||||
|
///
|
||||||
|
/// It is created by instantiating a [module][`LoadedModule`], and collects runtime representations
|
||||||
|
/// of all entities that are imported or defined by the module, namely:
|
||||||
|
///
|
||||||
|
/// - [functions][`FuncInstance`],
|
||||||
|
/// - [memories][`MemoryInstance`],
|
||||||
|
/// - [tables][`TableInstance`],
|
||||||
|
/// - [globals][`GlobalInstance`],
|
||||||
|
///
|
||||||
|
/// In order to instantiate a module you need to provide entities to satisfy
|
||||||
|
/// every module's imports (i.e. wasm modules don't have optional imports).
|
||||||
|
///
|
||||||
|
/// After module is instantiated you can start invoking it's exported functions with [`invoke_export`].
|
||||||
|
///
|
||||||
|
/// [`LoadedModule`]: struct.LoadedModule.html
|
||||||
|
/// [`FuncInstance`]: struct.FuncInstance.html
|
||||||
|
/// [`MemoryInstance`]: struct.MemoryInstance.html
|
||||||
|
/// [`TableInstance`]: struct.TableInstance.html
|
||||||
|
/// [`GlobalInstance`]: struct.GlobalInstance.html
|
||||||
|
/// [`invoke_export`]: #method.invoke_export
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ModuleInstance {
|
pub struct ModuleInstance {
|
||||||
signatures: RefCell<Vec<Rc<Signature>>>,
|
signatures: RefCell<Vec<Rc<Signature>>>,
|
||||||
|
@ -124,10 +159,6 @@ impl ModuleInstance {
|
||||||
self.funcs.borrow().get(idx as usize).cloned()
|
self.funcs.borrow().get(idx as usize).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_by_name(&self, name: &str) -> Option<ExternVal> {
|
|
||||||
self.exports.borrow().get(name).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn signature_by_index(&self, idx: u32) -> Option<Rc<Signature>> {
|
pub(crate) fn signature_by_index(&self, idx: u32) -> Option<Rc<Signature>> {
|
||||||
self.signatures.borrow().get(idx as usize).cloned()
|
self.signatures.borrow().get(idx as usize).cloned()
|
||||||
}
|
}
|
||||||
|
@ -369,6 +400,63 @@ impl ModuleInstance {
|
||||||
Ok(module_ref)
|
Ok(module_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Instantiate a [module][`LoadedModule`].
|
||||||
|
///
|
||||||
|
/// Note that in case of successful instantiation this function returns a reference to
|
||||||
|
/// a module which `start` function is not called.
|
||||||
|
/// In order to complete instantiatiation `start` function must be called. However, there are
|
||||||
|
/// situations where you might need to do additional setup before calling `start` function.
|
||||||
|
/// For such sitations this separation might be useful.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns `Err` if the module cannot be instantiated.
|
||||||
|
///
|
||||||
|
/// This can happen if one of the imports can't
|
||||||
|
/// be satisfied (e.g module isn't registered in `imports` [resolver][`ImportResolver`]) or
|
||||||
|
/// there is a mismatch between requested import and provided (e.g. module requested memory with no
|
||||||
|
/// maximum size limit, however, was provided memory with the maximum size limit).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use wasmi::{load_from_buffer, ModuleInstance, ImportsBuilder, NopExternals};
|
||||||
|
/// # fn func() -> Result<(), ::wasmi::Error> {
|
||||||
|
/// # let module = load_from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap();
|
||||||
|
///
|
||||||
|
/// // ModuleInstance::new returns instance which `start` function isn't called.
|
||||||
|
/// let not_started = ModuleInstance::new(
|
||||||
|
/// &module,
|
||||||
|
/// &ImportsBuilder::default()
|
||||||
|
/// )?;
|
||||||
|
/// // Call `start` function if any.
|
||||||
|
/// let instance = not_started.run_start(&mut NopExternals)?;
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If you sure that the module doesn't have `start` function you can use [`assert_no_start`] to get
|
||||||
|
/// instantiated module without calling `start` function.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use wasmi::{load_from_buffer, ModuleInstance, ImportsBuilder, NopExternals};
|
||||||
|
/// # fn func() -> Result<(), ::wasmi::Error> {
|
||||||
|
/// # let module = load_from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap();
|
||||||
|
///
|
||||||
|
/// // This will panic if the module actually contain `start` function.
|
||||||
|
/// let not_started = ModuleInstance::new(
|
||||||
|
/// &module,
|
||||||
|
/// &ImportsBuilder::default()
|
||||||
|
/// )?.assert_no_start();
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`LoadedModule`]: struct.LoadedModule.html
|
||||||
|
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||||
|
/// [`assert_no_start`]: struct.NotStartedModuleRef.html#method.assert_no_start
|
||||||
pub fn new<'m, I: ImportResolver>(
|
pub fn new<'m, I: ImportResolver>(
|
||||||
loaded_module: &'m LoadedModule,
|
loaded_module: &'m LoadedModule,
|
||||||
imports: &I,
|
imports: &I,
|
||||||
|
@ -415,6 +503,54 @@ impl ModuleInstance {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invoke exported function by a name.
|
||||||
|
///
|
||||||
|
/// This function finds exported function by a name, and calls it with provided arguments and
|
||||||
|
/// external state.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns `Err` if:
|
||||||
|
///
|
||||||
|
/// - there are no export with a given name or this export is not a function,
|
||||||
|
/// - given arguments doesn't match to function signature,
|
||||||
|
/// - trap occured at the execution time,
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Invoke a function that takes two numbers and returns sum of them.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate wasmi;
|
||||||
|
/// # extern crate wabt;
|
||||||
|
/// # use wasmi::{ModuleInstance, ImportsBuilder, NopExternals, RuntimeValue};
|
||||||
|
/// # fn main() {
|
||||||
|
/// # let wasm_binary: Vec<u8> = wabt::wat2wasm(
|
||||||
|
/// # r#"
|
||||||
|
/// # (module
|
||||||
|
/// # (func (export "add") (param i32 i32) (result i32)
|
||||||
|
/// # get_local 0
|
||||||
|
/// # get_local 1
|
||||||
|
/// # i32.add
|
||||||
|
/// # )
|
||||||
|
/// # )
|
||||||
|
/// # "#,
|
||||||
|
/// # ).expect("failed to parse wat");
|
||||||
|
/// # let module = wasmi::load_from_buffer(&wasm_binary).expect("failed to load wasm");
|
||||||
|
/// # let instance = ModuleInstance::new(
|
||||||
|
/// # &module,
|
||||||
|
/// # &ImportsBuilder::default()
|
||||||
|
/// # ).expect("failed to instantiate wasm module").assert_no_start();
|
||||||
|
/// assert_eq!(
|
||||||
|
/// instance.invoke_export(
|
||||||
|
/// "add",
|
||||||
|
/// &[RuntimeValue::I32(5), RuntimeValue::I32(3)],
|
||||||
|
/// &mut NopExternals,
|
||||||
|
/// ).expect("failed to execute export"),
|
||||||
|
/// Some(RuntimeValue::I32(8)),
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn invoke_export<E: Externals>(
|
pub fn invoke_export<E: Externals>(
|
||||||
&self,
|
&self,
|
||||||
func_name: &str,
|
func_name: &str,
|
||||||
|
@ -438,6 +574,13 @@ impl ModuleInstance {
|
||||||
|
|
||||||
FuncInstance::invoke(&func_instance, args, externals)
|
FuncInstance::invoke(&func_instance, args, externals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find export by a name.
|
||||||
|
///
|
||||||
|
/// Returns `None` if there is no export with such name.
|
||||||
|
pub fn export_by_name(&self, name: &str) -> Option<ExternVal> {
|
||||||
|
self.exports.borrow().get(name).cloned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NotStartedModuleRef<'a> {
|
pub struct NotStartedModuleRef<'a> {
|
||||||
|
|
|
@ -345,6 +345,7 @@ fn pull_internal_mem_from_module() {
|
||||||
.export_by_name("mem")
|
.export_by_name("mem")
|
||||||
.expect("Module expected to have 'mem' export")
|
.expect("Module expected to have 'mem' export")
|
||||||
.as_memory()
|
.as_memory()
|
||||||
|
.cloned()
|
||||||
.expect("'mem' export should be a memory");
|
.expect("'mem' export should be a memory");
|
||||||
|
|
||||||
env.memory = Some(internal_mem);
|
env.memory = Some(internal_mem);
|
||||||
|
|
Loading…
Reference in New Issue