Add docs (#32)

This commit is contained in:
Sergey Pepyakin 2018-02-01 19:46:33 +03:00 committed by Nikolay Volf
parent c96735d6d6
commit fa82dee676
9 changed files with 154 additions and 19 deletions

View File

@ -10,7 +10,6 @@ use parity_wasm::elements::{ValueType as EValueType};
/// This reference has a reference-counting semantics.
///
/// [`GlobalInstance`]: struct.GlobalInstance.html
///
#[derive(Clone, Debug)]
pub struct GlobalRef(Rc<GlobalInstance>);
@ -37,7 +36,10 @@ pub struct GlobalInstance {
}
impl GlobalInstance {
/// Allocate a global variable instance.
///
/// Since it is possible to export only immutable globals,
/// users likely want to set `mutable` to `false`.
pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef {
GlobalRef(Rc::new(GlobalInstance {
val: Cell::new(val),
@ -45,6 +47,12 @@ impl GlobalInstance {
}))
}
/// Change the value of this global variable.
///
/// # Errors
///
/// Returns `Err` if this global isn't mutable or if
/// type of `val` doesn't match global's type.
pub fn set(&self, val: RuntimeValue) -> Result<(), Error> {
if !self.mutable {
return Err(Error::Global("Attempt to change an immutable variable".into()));
@ -56,14 +64,19 @@ impl GlobalInstance {
Ok(())
}
/// Get the value of this global variable.
pub fn get(&self) -> RuntimeValue {
self.val.get()
}
/// Returns if this global variable is mutable.
///
/// Imported and/or exported globals are always immutable.
pub fn is_mutable(&self) -> bool {
self.mutable
}
/// Returns value type of this global variable.
pub fn value_type(&self) -> ValueType {
self.val.get().value_type()
}

View File

@ -187,6 +187,7 @@ impl HostError {
/// }
/// ```
pub trait Externals {
/// Perform invoke of a host function by specified `index`.
fn invoke_index(
&mut self,
index: usize,

View File

@ -94,8 +94,7 @@
//! }
//! ```
// TODO(pepyakin): Fix this asap https://github.com/pepyakin/wasmi/issues/3
#![allow(missing_docs)]
#![warn(missing_docs)]
#[cfg(test)]
extern crate wabt;

View File

@ -88,7 +88,20 @@ impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>>
}
impl MemoryInstance {
/// Allocate a memory instance.
///
/// The memory allocated with initial number of pages specified by `initial_pages`.
/// Minimal possible value for `initial_pages` is 0 and maximum possible is `65536`.
/// (Since maximum addressible memory is 4GiB = 65536 * 64KiB).
///
/// It is possible to limit maximum number of pages this memory instance can have by specifying
/// `maximum_page`. If not specified, this memory instance would be able to allocate up to 4GiB.
///
/// Allocated memory is always zeroed.
///
/// # Errors
///
/// Returns `Err` if `initial_pages` is greater than `maximum_pages`.
pub fn alloc(initial_pages: u32, maximum_pages: Option<u32>) -> Result<MemoryRef, Error> {
let memory = MemoryInstance::new(ResizableLimits::new(initial_pages, maximum_pages))?;
Ok(MemoryRef(Rc::new(memory)))
@ -121,10 +134,15 @@ impl MemoryInstance {
&self.limits
}
/// Returns number of pages this `MemoryInstance` was created with.
pub fn initial_pages(&self) -> u32 {
self.limits.initial()
}
/// Returns maximum amount of pages this `MemoryInstance` can grow to.
///
/// Returns `None` if there is no limit set.
/// This means that memory can grow up to 4GiB.
pub fn maximum_pages(&self) -> Option<u32> {
self.limits.maximum()
}
@ -134,7 +152,7 @@ impl MemoryInstance {
self.buffer.borrow().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
}
/// Get data at given offset.
/// Copy data from memory at given offset.
pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
let buffer = self.buffer.borrow();
let region = self.checked_region(&buffer, offset as usize, size)?;
@ -142,7 +160,7 @@ impl MemoryInstance {
Ok(region.slice().to_vec())
}
/// Write memory slice into another slice
/// Copy data from given offset in the memory into `target` slice.
pub fn get_into(&self, offset: u32, target: &mut [u8]) -> Result<(), Error> {
let buffer = self.buffer.borrow();
let region = self.checked_region(&buffer, offset as usize, target.len())?;
@ -152,7 +170,7 @@ impl MemoryInstance {
Ok(())
}
/// Set data at given offset.
/// Copy data in the memory at given offset.
pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut();
let range = self.checked_region(&buffer, offset as usize, value.len())?.range();
@ -164,6 +182,10 @@ impl MemoryInstance {
/// Increases the size of the linear memory by given number of pages.
/// Returns previous memory size (in pages) if succeeds.
///
/// # Errors
///
/// Returns `Err` if tried to allocate more memory than permited by limit.
pub fn grow(&self, pages: u32) -> Result<u32, Error> {
let mut buffer = self.buffer.borrow_mut();
let old_size = buffer.len() as u32;

View File

@ -30,11 +30,26 @@ impl ::std::ops::Deref for ModuleRef {
}
}
/// An external value is the runtime representation of an entity that can be imported or exported.
/// An external value is the runtime representation of an entity
/// that can be imported or exported.
pub enum ExternVal {
/// [Function][`FuncInstance`].
///
/// [`FuncInstance`]: struct.FuncInstance.html
Func(FuncRef),
/// [Table][`TableInstance`].
///
/// [`TableInstance`]: struct.TableInstance.html
Table(TableRef),
/// [Memory][`MemoryInstance`].
///
/// [`MemoryInstance`]: struct.MemoryInstance.html
Memory(MemoryRef),
/// [Global][`GlobalInstance`].
///
/// Should be immutable.
///
/// [`GlobalInstance`]: struct.GlobalInstance.html
Global(GlobalRef),
}
@ -592,9 +607,14 @@ impl ModuleInstance {
/// Mostly instantiated [`ModuleRef`].
///
/// The last step of instantiation is call to `start` function (if any).
/// At this point memory segments and tables are copied. However, `start` function (if any) is not called.
/// To get [fully instantiated module instance][`ModuleRef`], [running `start` function][`run_start`] is required.
///
/// You can still access not fully initialized instance by calling [`not_started_instance`],
/// but keep in mind, that this is sort of escape hatch: module really might depend on initialization
/// done in `start` function. It's definetely not recommended to call any exports on [`ModuleRef`]
/// returned by this function.
///
/// If you sure, that there is no `start` function (e.g. because you created it without one), you can
/// call [`assert_no_start`] which returns [`ModuleRef`] without calling `start` function. However,
/// it will panic if module contains `start` function.
@ -602,16 +622,31 @@ impl ModuleInstance {
/// [`ModuleRef`]: struct.ModuleRef.html
/// [`run_start`]: #method.run_start
/// [`assert_no_start`]: #method.assert_no_start
/// [`not_started_instance`]: #method.not_started_instance
pub struct NotStartedModuleRef<'a> {
loaded_module: &'a Module,
instance: ModuleRef,
}
impl<'a> NotStartedModuleRef<'a> {
/// Returns not fully initialized instance.
///
/// To fully initialize the instance you need to call either [`run_start`] or
/// [`assert_no_start`]. See struct documentation for details.
///
/// [`NotStartedModuleRef`]: struct.NotStartedModuleRef.html
/// [`ModuleRef`]: struct.ModuleRef.html
/// [`run_start`]: #method.run_start
/// [`assert_no_start`]: #method.assert_no_start
pub fn not_started_instance(&self) -> &ModuleRef {
&self.instance
}
/// Executes `start` function (if any) and returns fully instantiated module.
///
/// # Errors
///
/// Returns `Err` if start function traps.
pub fn run_start<E: Externals>(self, state: &mut E) -> Result<ModuleRef, Trap> {
if let Some(start_fn_idx) = self.loaded_module.module().start_section() {
let start_func = self.instance.func_by_index(start_fn_idx).expect(
@ -622,8 +657,15 @@ impl<'a> NotStartedModuleRef<'a> {
Ok(self.instance)
}
/// Returns fully instantiated module without running `start` function.
///
/// # Panics
///
/// This function panics if original module contains `start` function.
pub fn assert_no_start(self) -> ModuleRef {
assert!(self.loaded_module.module().start_section().is_none());
if self.loaded_module.module().start_section().is_some() {
panic!("assert_no_start called on module with `start` function");
}
self.instance
}
}
@ -686,3 +728,27 @@ pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
Ok(())
}
#[cfg(test)]
mod tests {
use imports::ImportsBuilder;
use super::{ModuleInstance};
use tests::parse_wat;
#[should_panic]
#[test]
fn assert_no_start_panics_on_module_with_start() {
let module_with_start = parse_wat(
r#"
(module
(func $f)
(start $f))
"#
);
ModuleInstance::new(
&module_with_start,
&ImportsBuilder::default()
).unwrap().assert_no_start();
}
}

View File

@ -53,7 +53,16 @@ impl fmt::Debug for TableInstance {
}
impl TableInstance {
/// Allocate a table instance.
///
/// The table allocated with initial size, specified by `initial_size`.
/// Maximum size can be specified by `maximum_size`.
///
/// All table elements are allocated uninitialized.
///
/// # Errors
///
/// Returns `Err` if `initial_size` is greater than `maximum_size`.
pub fn alloc(initial_size: u32, maximum_size: Option<u32>) -> Result<TableRef, Error> {
let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?;
Ok(TableRef(Rc::new(table)))
@ -72,18 +81,26 @@ impl TableInstance {
&self.limits
}
/// Returns size this table was created with.
pub fn initial_size(&self) -> u32 {
self.limits.initial()
}
/// Returns maximum size `TableInstance` can grow to.
pub fn maximum_size(&self) -> Option<u32> {
self.limits.maximum()
}
/// Returns current size of the table.
pub fn current_size(&self) -> u32 {
self.buffer.borrow().len() as u32
}
/// Increases the size of the table by given number of elements.
///
/// # Errors
///
/// Returns `Err` if tried to allocate more elements than permited by limit.
pub fn grow(&self, by: u32) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut();
let maximum_size = self.maximum_size().unwrap_or(u32::MAX);

View File

@ -1,10 +1,10 @@
use {
Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef,
RuntimeValue, RuntimeArgs, Module, TableDescriptor, MemoryDescriptor, Trap,
RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap,
};
use types::ValueType;
use wabt::wat2wasm;
use super::parse_wat;
#[derive(Debug, Clone, PartialEq)]
struct HostErrorWithCode {
@ -200,11 +200,6 @@ impl ModuleImportResolver for TestHost {
}
}
fn parse_wat(source: &str) -> Module {
let wasm_binary = wat2wasm(source).expect("Failed to parse wat source");
Module::from_buffer(wasm_binary).expect("Failed to load parsed module")
}
#[test]
fn call_host_func() {
let module = parse_wat(

View File

@ -1,3 +1,6 @@
use wabt;
use {Module};
mod host;
mod wasm;
@ -13,3 +16,8 @@ fn assert_error_properties() {
assert_sync::<Error>();
assert_std_err_impl::<Error>();
}
pub fn parse_wat(source: &str) -> Module {
let wasm_binary = wabt::wat2wasm(source).expect("Failed to parse wat source");
Module::from_buffer(wasm_binary).expect("Failed to load parsed module")
}

View File

@ -18,6 +18,8 @@ pub struct Signature {
}
impl Signature {
/// Creates new signature with givens
/// parameter types and optional return type.
pub fn new<C: Into<Cow<'static, [ValueType]>>>(
params: C,
return_type: Option<ValueType>
@ -28,10 +30,12 @@ impl Signature {
}
}
/// Returns parameter types of this signature.
pub fn params(&self) -> &[ValueType] {
&self.params.as_ref()
}
/// Returns return type of this signature.
pub fn return_type(&self) -> Option<ValueType> {
self.return_type
}
@ -54,9 +58,13 @@ impl Signature {
///
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ValueType {
/// 32-bit signed or unsigned integer.
I32,
/// 64-bit signed or unsigned integer.
I64,
/// 32-bit IEEE 754-2008 floating point number.
F32,
/// 64-bit IEEE 754-2008 floating point number.
F64,
}
@ -99,10 +107,12 @@ impl GlobalDescriptor {
}
}
/// Returns [`ValueType`] of the requested global.
pub fn value_type(&self) -> ValueType {
self.value_type
}
/// Returns whether the requested global mutable.
pub fn is_mutable(&self) -> bool {
self.mutable
}
@ -127,10 +137,12 @@ impl TableDescriptor {
}
}
/// Returns initial size of the requested table.
pub fn initial(&self) -> u32 {
self.initial
}
/// Returns maximum size of the requested table.
pub fn maximum(&self) -> Option<u32> {
self.maximum
}
@ -155,10 +167,12 @@ impl MemoryDescriptor {
}
}
/// Returns initial size (in pages) of the requested memory.
pub fn initial(&self) -> u32 {
self.initial
}
/// Returns maximum size (in pages) of the requested memory.
pub fn maximum(&self) -> Option<u32> {
self.maximum
}