Add docs (#32)
This commit is contained in:
parent
c96735d6d6
commit
fa82dee676
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
19
src/table.rs
19
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<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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
14
src/types.rs
14
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<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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue