diff --git a/src/global.rs b/src/global.rs index e58e543..697b734 100644 --- a/src/global.rs +++ b/src/global.rs @@ -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); @@ -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() } diff --git a/src/host.rs b/src/host.rs index 4fd46b7..6e2385a 100644 --- a/src/host.rs +++ b/src/host.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs index 4f919e0..9802ff9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/memory.rs b/src/memory.rs index e2c6882..e6dcd98 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -88,7 +88,20 @@ impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref> } 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) -> Result { 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 { 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, 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 { let mut buffer = self.buffer.borrow_mut(); let old_size = buffer.len() as u32; diff --git a/src/module.rs b/src/module.rs index 5419a76..b7b3ed7 100644 --- a/src/module.rs +++ b/src/module.rs @@ -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(self, state: &mut E) -> Result { 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(); + } +} diff --git a/src/table.rs b/src/table.rs index 4c47da3..53dcc10 100644 --- a/src/table.rs +++ b/src/table.rs @@ -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) -> Result { 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 { 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); diff --git a/src/tests/host.rs b/src/tests/host.rs index 02cb86b..211df56 100644 --- a/src/tests/host.rs +++ b/src/tests/host.rs @@ -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( diff --git a/src/tests/mod.rs b/src/tests/mod.rs index c202910..e07c3a8 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,3 +1,6 @@ +use wabt; +use {Module}; + mod host; mod wasm; @@ -13,3 +16,8 @@ fn assert_error_properties() { assert_sync::(); assert_std_err_impl::(); } + +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") +} diff --git a/src/types.rs b/src/types.rs index 419a67c..b05e636 100644 --- a/src/types.rs +++ b/src/types.rs @@ -18,6 +18,8 @@ pub struct Signature { } impl Signature { + /// Creates new signature with givens + /// parameter types and optional return type. pub fn new>>( params: C, return_type: Option @@ -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 { 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 { 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 { self.maximum }