summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/tinywasm/src/engine.rs26
-rw-r--r--crates/tinywasm/src/func.rs67
-rw-r--r--crates/tinywasm/src/imports.rs18
-rw-r--r--crates/tinywasm/src/instance.rs111
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs6
-rw-r--r--crates/tinywasm/src/reference.rs18
-rw-r--r--crates/tinywasm/src/store/mod.rs14
-rw-r--r--examples/wasm-rust.rs1
-rw-r--r--rustfmt.toml2
9 files changed, 242 insertions, 21 deletions
diff --git a/crates/tinywasm/src/engine.rs b/crates/tinywasm/src/engine.rs
index 652a07c..dfb4d15 100644
--- a/crates/tinywasm/src/engine.rs
+++ b/crates/tinywasm/src/engine.rs
@@ -4,6 +4,19 @@ pub use crate::store::{LazyLinearMemory, LinearMemory, MemoryBackend, PagedMemor
/// Global configuration for the WebAssembly interpreter
///
/// Can be cheaply cloned and shared across multiple executions and threads.
+///
+/// ## Example
+/// ```rust
+/// use tinywasm::engine::{Config, StackConfig};
+/// use tinywasm::{Engine, Store};
+///
+/// let config = Config::new()
+/// .with_value_stack(StackConfig::dynamic(1024, 16 * 1024))
+/// .with_call_stack(StackConfig::fixed(256));
+/// let engine = Engine::new(config);
+/// let store = Store::new(engine);
+/// # _ = store;
+/// ```
#[derive(Clone, Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Engine {
@@ -72,6 +85,19 @@ impl StackConfig {
}
/// Configuration for the WebAssembly interpreter
+///
+/// ## Example
+/// ```rust
+/// use tinywasm::engine::{Config, FuelPolicy, MemoryBackend, StackConfig};
+///
+/// let config = Config::new()
+/// .with_fuel_policy(FuelPolicy::Weighted)
+/// .with_value_stack(StackConfig::dynamic(1024, 16 * 1024))
+/// .with_memory_backend(MemoryBackend::paged(64 * 1024))
+/// .with_trap_on_oom(true);
+///
+/// assert!(matches!(config.fuel_policy(), FuelPolicy::Weighted));
+/// ```
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[non_exhaustive]
diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs
index fa8e755..e3d1d2a 100644
--- a/crates/tinywasm/src/func.rs
+++ b/crates/tinywasm/src/func.rs
@@ -120,6 +120,37 @@ impl HostFunction {
}
/// Create a new untyped host function import.
+ ///
+ /// ## Example
+ /// ```rust
+ /// # fn main() -> tinywasm::Result<()> {
+ /// # use tinywasm::{FuncContext, HostFunction, Imports, ModuleInstance, Store};
+ /// # use tinywasm::types::{FuncType, WasmType, WasmValue};
+ /// # let wasm = wat::parse_str(r#"
+ /// # (module
+ /// # (import "host" "add_one" (func $add_one (param i32) (result i32)))
+ /// # (func (export "call") (param i32) (result i32)
+ /// # local.get 0
+ /// # call $add_one))
+ /// # "#).expect("valid wat");
+ /// # let module = tinywasm::parse_bytes(&wasm)?;
+ /// let mut store = Store::default();
+ /// let ty = FuncType::new(&[WasmType::I32], &[WasmType::I32]);
+ /// let add_one = HostFunction::from_untyped(&mut store, &ty, |_ctx: FuncContext<'_>, args| {
+ /// let WasmValue::I32(value) = args[0] else {
+ /// return Err(tinywasm::Error::Other("expected i32".into()));
+ /// };
+ /// Ok(vec![WasmValue::I32(value + 1)])
+ /// });
+ ///
+ /// let mut imports = Imports::new();
+ /// imports.define("host", "add_one", add_one);
+ /// # let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports))?;
+ /// # let call = instance.func::<i32, i32>(&store, "call")?;
+ /// # assert_eq!(call.call(&mut store, 41)?, 42);
+ /// # Ok(())
+ /// # }
+ /// ```
pub fn from_untyped(
store: &mut Store,
ty: &FuncType,
@@ -150,6 +181,30 @@ impl HostFunction {
}
/// Create a new typed host function import.
+ ///
+ /// ## Example
+ /// ```rust
+ /// # fn main() -> tinywasm::Result<()> {
+ /// # use tinywasm::{HostFunction, Imports, ModuleInstance, Store};
+ /// # let wasm = wat::parse_str(r#"
+ /// # (module
+ /// # (import "host" "add_one" (func $add_one (param i32) (result i32)))
+ /// # (func (export "call") (param i32) (result i32)
+ /// # local.get 0
+ /// # call $add_one))
+ /// # "#).expect("valid wat");
+ /// # let module = tinywasm::parse_bytes(&wasm)?;
+ /// let mut store = Store::default();
+ /// let add_one = HostFunction::from(&mut store, |_ctx, value: i32| Ok(value + 1));
+ ///
+ /// let mut imports = Imports::new();
+ /// imports.define("host", "add_one", add_one);
+ /// # let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports))?;
+ /// # let call = instance.func::<i32, i32>(&store, "call")?;
+ /// # assert_eq!(call.call(&mut store, 41)?, 42);
+ /// # Ok(())
+ /// # }
+ /// ```
pub fn from<P, R>(store: &mut Store, func: impl Fn(FuncContext<'_>, P) -> Result<R> + 'static) -> Function
where
P: FromWasmValues + ToWasmTypes,
@@ -604,14 +659,10 @@ impl_tuple!(impl_tuple_traits);
/// # let mut store = Store::default();
/// # let instance = ModuleInstance::instantiate(&mut store, &module, None)?;
///
-/// type Params = WasmTupleChain<
-/// (i32, i32, i32, i32, i32, i32),
-/// (i32, i32, i32, i32, i32, i32, i32),
-/// >;
-/// type Results = WasmTupleChain<
-/// (i32, i32, i32, i32, i32, i32),
-/// (i32, i32, i32, i32, i32, i32, i32),
-/// >;
+/// type Params =
+/// WasmTupleChain<(i32, i32, i32, i32, i32, i32), (i32, i32, i32, i32, i32, i32, i32)>;
+/// type Results =
+/// WasmTupleChain<(i32, i32, i32, i32, i32, i32), (i32, i32, i32, i32, i32, i32, i32)>;
///
/// let echo13 = instance.func::<Params, Results>(&store, "echo13")?;
/// let result = echo13.call(&mut store, ((1, 2, 3, 4, 5, 6), (7, 8, 9, 10, 11, 12, 13)).into())?;
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index e9f1029..900b8bd 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -67,24 +67,30 @@ impl From<&Import> for ExternName {
/// ```rust
/// # use log;
/// # fn main() -> tinywasm::Result<()> {
+/// use tinywasm::types::{GlobalType, MemoryType, TableType, WasmType, WasmValue};
/// use tinywasm::{Global, HostFunction, Imports, Memory, ModuleInstance, Store, Table};
-/// use tinywasm::types::{WasmType, TableType, MemoryType, WasmValue};
/// # let wasm = wat::parse_str("(module)").expect("valid wat");
/// # let module = tinywasm::parse_bytes(&wasm)?;
/// # let mut store = Store::default();
/// # let my_other_instance = ModuleInstance::instantiate(&mut store, &module, None)?;
/// let mut imports = Imports::new();
///
-/// // function args can be either a single
-/// // value that implements `TryFrom<WasmValue>` or a tuple of them
/// let print_i32 = HostFunction::from(&mut store, |_ctx: tinywasm::FuncContext<'_>, arg: i32| {
/// log::debug!("print_i32: {}", arg);
/// Ok(())
/// });
///
-/// let table = Table::new(&mut store, TableType::new(WasmType::RefFunc, 10, Some(20)), WasmValue::default_for(WasmType::RefFunc))?;
-/// let memory = Memory::new(&mut store, MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)))?;
-/// let global_i32 = Global::new(&mut store, tinywasm::types::GlobalType::default().with_ty(WasmType::I32), WasmValue::I32(666))?;
+/// let table = Table::new(
+/// &mut store,
+/// TableType::new(WasmType::RefFunc, 10, Some(20)),
+/// WasmValue::default_for(WasmType::RefFunc),
+/// )?;
+/// let memory = Memory::new(
+/// &mut store,
+/// MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)),
+/// )?;
+/// let global_i32 =
+/// Global::new(&mut store, GlobalType::default().with_ty(WasmType::I32), WasmValue::I32(666))?;
///
/// imports
/// .define("my_module", "print_i32", print_i32)
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index b050f0b..179f0c7 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -193,6 +193,30 @@ impl ModuleInstance {
/// Instantiate the module in the given store (without running the start function)
///
+ /// ## Example
+ /// ```rust
+ /// # fn main() -> tinywasm::Result<()> {
+ /// # use tinywasm::{ModuleInstance, Store};
+ /// # let wasm = wat::parse_str(r#"
+ /// # (module
+ /// # (global $g (mut i32) (i32.const 0))
+ /// # (func $start
+ /// # i32.const 42
+ /// # global.set $g)
+ /// # (start $start)
+ /// # (export "g" (global $g)))
+ /// # "#).expect("valid wat");
+ /// # let module = tinywasm::parse_bytes(&wasm)?;
+ /// let mut store = Store::default();
+ /// let instance = ModuleInstance::instantiate_no_start(&mut store, &module, None)?;
+ ///
+ /// assert_eq!(instance.global_get(&store, "g")?, 0.into());
+ /// instance.start(&mut store)?;
+ /// assert_eq!(instance.global_get(&store, "g")?, 42.into());
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
/// See <https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation>
pub fn instantiate_no_start(store: &mut Store, module: &Module, imports: Option<Imports>) -> Result<Self> {
let idx = store.next_module_instance_idx();
@@ -251,6 +275,33 @@ impl ModuleInstance {
}
/// Returns an iterator over all exported extern values for this instance.
+ ///
+ /// ## Example
+ /// ```rust
+ /// # fn main() -> tinywasm::Result<()> {
+ /// # use tinywasm::{ExternItem, ModuleInstance, Store};
+ /// # let wasm = wat::parse_str(r#"
+ /// # (module
+ /// # (func (export "f"))
+ /// # (memory (export "mem") 1))
+ /// # "#).expect("valid wat");
+ /// # let module = tinywasm::parse_bytes(&wasm)?;
+ /// # let mut store = Store::default();
+ /// let instance = ModuleInstance::instantiate(&mut store, &module, None)?;
+ ///
+ /// let mut saw_func = false;
+ /// let mut saw_memory = false;
+ /// for (name, item) in instance.exports() {
+ /// match (name, item) {
+ /// ("f", ExternItem::Func(_)) => saw_func = true,
+ /// ("mem", ExternItem::Memory(_)) => saw_memory = true,
+ /// _ => {}
+ /// }
+ /// }
+ /// assert!(saw_func && saw_memory);
+ /// # Ok(())
+ /// # }
+ /// ```
pub fn exports(&self) -> impl Iterator<Item = (&str, ExternItem)> + '_ {
self.0.exports.iter().map(move |export| {
let item = match export.kind {
@@ -302,6 +353,26 @@ impl ModuleInstance {
}
/// Get any exported extern value by name.
+ ///
+ /// ## Example
+ /// ```rust
+ /// # fn main() -> tinywasm::Result<()> {
+ /// # use tinywasm::{ExternItem, ModuleInstance, Store};
+ /// # let wasm = wat::parse_str(r#"
+ /// # (module
+ /// # (global (export "answer") i32 (i32.const 42)))
+ /// # "#).expect("valid wat");
+ /// # let module = tinywasm::parse_bytes(&wasm)?;
+ /// # let mut store = Store::default();
+ /// let instance = ModuleInstance::instantiate(&mut store, &module, None)?;
+ ///
+ /// let ExternItem::Global(global) = instance.extern_item("answer")? else {
+ /// panic!("expected global export");
+ /// };
+ /// assert_eq!(global.get(&store)?, 42.into());
+ /// # Ok(())
+ /// # }
+ /// ```
pub fn extern_item(&self, name: &str) -> Result<ExternItem> {
match self.require_export(name)? {
ExternVal::Func(addr) => {
@@ -530,6 +601,23 @@ impl ModuleInstance {
/// Returns None if the module has no start function
/// If no start function is specified, also checks for a `_start` function in the exports
///
+ /// ## Example
+ /// ```rust
+ /// # fn main() -> tinywasm::Result<()> {
+ /// # use tinywasm::{ModuleInstance, Store};
+ /// # let wasm = wat::parse_str(r#"
+ /// # (module
+ /// # (func (export "_start"))
+ /// # )
+ /// # "#).expect("valid wat");
+ /// # let module = tinywasm::parse_bytes(&wasm)?;
+ /// # let mut store = Store::default();
+ /// let instance = ModuleInstance::instantiate_no_start(&mut store, &module, None)?;
+ /// assert!(instance.start_func(&store)?.is_some());
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#start-function>
pub fn start_func(&self, store: &Store) -> Result<Option<Function>> {
self.validate_store(store)?;
@@ -564,6 +652,29 @@ impl ModuleInstance {
///
/// Returns `None` if the module has no start function
///
+ /// ## Example
+ /// ```rust
+ /// # fn main() -> tinywasm::Result<()> {
+ /// # use tinywasm::{ModuleInstance, Store};
+ /// # let wasm = wat::parse_str(r#"
+ /// # (module
+ /// # (global $g (mut i32) (i32.const 0))
+ /// # (func (export "_start")
+ /// # i32.const 7
+ /// # global.set $g)
+ /// # (export "g" (global $g)))
+ /// # "#).expect("valid wat");
+ /// # let module = tinywasm::parse_bytes(&wasm)?;
+ /// let mut store = Store::default();
+ /// let instance = ModuleInstance::instantiate_no_start(&mut store, &module, None)?;
+ ///
+ /// assert_eq!(instance.global_get(&store, "g")?, 0.into());
+ /// assert_eq!(instance.start(&mut store)?, Some(()));
+ /// assert_eq!(instance.global_get(&store, "g")?, 7.into());
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#syntax-start>
pub fn start(&self, store: &mut Store) -> Result<Option<()>> {
match self.start_func(store)? {
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index 4c941c8..f4e1352 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -1568,7 +1568,7 @@ impl<'store> Executor<'store, false> {
}
loop {
- for _ in 0..1024 {
+ for _ in 0..128 {
if self.exec()?.is_some() {
return Ok(ExecState::Completed);
}
@@ -1590,13 +1590,13 @@ impl<'store> Executor<'store, true> {
}
loop {
- for _ in 0..1024 {
+ for _ in 0..128 {
if self.exec()?.is_some() {
return Ok(ExecState::Completed);
}
}
- self.store.execution_fuel = self.store.execution_fuel.saturating_sub(1024_u32);
+ self.store.execution_fuel = self.store.execution_fuel.saturating_sub(128);
if self.store.execution_fuel == 0 {
return Ok(ExecState::Suspended(self.cf));
}
diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs
index 8f02317..8541062 100644
--- a/crates/tinywasm/src/reference.rs
+++ b/crates/tinywasm/src/reference.rs
@@ -33,6 +33,24 @@ impl StoreItem {
}
/// A memory instance in a store.
+///
+/// ## Example
+/// ```rust
+/// # fn main() -> tinywasm::Result<()> {
+/// use tinywasm::types::MemoryType;
+/// use tinywasm::{Memory, Store};
+///
+/// let mut store = Store::default();
+/// let memory = Memory::new(&mut store, MemoryType::default().with_page_count_initial(1))?;
+///
+/// memory.copy_from_slice(&mut store, 0, b"hi")?;
+/// assert_eq!(memory.read_vec(&store, 0, 2)?, b"hi");
+/// assert_eq!(memory.page_count(&store)?, 1);
+/// memory.grow(&mut store, 1)?;
+/// assert_eq!(memory.page_count(&store)?, 2);
+/// # Ok(())
+/// # }
+/// ```
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Memory(pub(crate) StoreItem);
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index a4d26e3..446c9ed 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -24,11 +24,19 @@ static STORE_ID: AtomicUsize = AtomicUsize::new(0);
/// Global state that can be manipulated by WebAssembly programs
///
-/// Data should only be addressable by the module that owns it
-///
/// Note that the state doesn't do any garbage collection - so it will grow
/// indefinitely if you keep adding modules to it. When calling temporary
-/// functions, you should create a new store and then drop it when you're done (e.g. in a request handler)
+/// functions, you should create a new store and then drop it when you're done (e.g. in a request handler).
+///
+/// ## Example
+/// ```rust
+/// use tinywasm::engine::{Config, StackConfig};
+/// use tinywasm::{Engine, Store};
+///
+/// let engine = Engine::new(Config::new().with_call_stack(StackConfig::dynamic(64, 512)));
+/// let store = Store::new(engine);
+/// # _ = store;
+/// ```
///
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#store>
pub struct Store {
diff --git a/examples/wasm-rust.rs b/examples/wasm-rust.rs
index 7240bef..e0b9968 100644
--- a/examples/wasm-rust.rs
+++ b/examples/wasm-rust.rs
@@ -19,7 +19,6 @@ use tinywasm::{FuncContext, HostFunction, Imports, ModuleInstance, Store};
/// `rustup target add wasm32-unknown-unknown`.
/// <https://github.com/WebAssembly/wabt>
/// <https://github.com/WebAssembly/binaryen>
-///
fn main() -> Result<()> {
pretty_env_logger::init();
diff --git a/rustfmt.toml b/rustfmt.toml
index 589b2d2..0fd4e90 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,2 +1,4 @@
+unstable_features=true
+format_code_in_doc_comments=true
max_width=120
use_small_heuristics="Max"