Merge pull request #17 from pepyakin/docs-3

Third iteration on documenation.
This commit is contained in:
Nikolay Volf 2018-01-25 23:05:45 +03:00 committed by GitHub
commit 6f3ec93952
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 326 additions and 80 deletions

View File

@ -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))
})?; })?;

View File

@ -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)]

View File

@ -69,7 +69,6 @@ impl<'a> RuntimeArgs<'a> {
/// _ => 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 {

View File

@ -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.
@ -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))
})?) })?)

View File

@ -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)]

View File

@ -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,

View File

@ -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> {

View File

@ -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);