Skip to content

Commit 2aa1f31

Browse files
docs: add more doctests, reduce minumum fuel usage
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent b99b643 commit 2aa1f31

9 files changed

Lines changed: 242 additions & 21 deletions

File tree

crates/tinywasm/src/engine.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ pub use crate::store::{LazyLinearMemory, LinearMemory, MemoryBackend, PagedMemor
44
/// Global configuration for the WebAssembly interpreter
55
///
66
/// Can be cheaply cloned and shared across multiple executions and threads.
7+
///
8+
/// ## Example
9+
/// ```rust
10+
/// use tinywasm::engine::{Config, StackConfig};
11+
/// use tinywasm::{Engine, Store};
12+
///
13+
/// let config = Config::new()
14+
/// .with_value_stack(StackConfig::dynamic(1024, 16 * 1024))
15+
/// .with_call_stack(StackConfig::fixed(256));
16+
/// let engine = Engine::new(config);
17+
/// let store = Store::new(engine);
18+
/// # _ = store;
19+
/// ```
720
#[derive(Clone, Default)]
821
#[cfg_attr(feature = "debug", derive(Debug))]
922
pub struct Engine {
@@ -72,6 +85,19 @@ impl StackConfig {
7285
}
7386

7487
/// Configuration for the WebAssembly interpreter
88+
///
89+
/// ## Example
90+
/// ```rust
91+
/// use tinywasm::engine::{Config, FuelPolicy, MemoryBackend, StackConfig};
92+
///
93+
/// let config = Config::new()
94+
/// .with_fuel_policy(FuelPolicy::Weighted)
95+
/// .with_value_stack(StackConfig::dynamic(1024, 16 * 1024))
96+
/// .with_memory_backend(MemoryBackend::paged(64 * 1024))
97+
/// .with_trap_on_oom(true);
98+
///
99+
/// assert!(matches!(config.fuel_policy(), FuelPolicy::Weighted));
100+
/// ```
75101
#[derive(Clone)]
76102
#[cfg_attr(feature = "debug", derive(Debug))]
77103
#[non_exhaustive]

crates/tinywasm/src/func.rs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,37 @@ impl HostFunction {
120120
}
121121

122122
/// Create a new untyped host function import.
123+
///
124+
/// ## Example
125+
/// ```rust
126+
/// # fn main() -> tinywasm::Result<()> {
127+
/// # use tinywasm::{FuncContext, HostFunction, Imports, ModuleInstance, Store};
128+
/// # use tinywasm::types::{FuncType, WasmType, WasmValue};
129+
/// # let wasm = wat::parse_str(r#"
130+
/// # (module
131+
/// # (import "host" "add_one" (func $add_one (param i32) (result i32)))
132+
/// # (func (export "call") (param i32) (result i32)
133+
/// # local.get 0
134+
/// # call $add_one))
135+
/// # "#).expect("valid wat");
136+
/// # let module = tinywasm::parse_bytes(&wasm)?;
137+
/// let mut store = Store::default();
138+
/// let ty = FuncType::new(&[WasmType::I32], &[WasmType::I32]);
139+
/// let add_one = HostFunction::from_untyped(&mut store, &ty, |_ctx: FuncContext<'_>, args| {
140+
/// let WasmValue::I32(value) = args[0] else {
141+
/// return Err(tinywasm::Error::Other("expected i32".into()));
142+
/// };
143+
/// Ok(vec![WasmValue::I32(value + 1)])
144+
/// });
145+
///
146+
/// let mut imports = Imports::new();
147+
/// imports.define("host", "add_one", add_one);
148+
/// # let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports))?;
149+
/// # let call = instance.func::<i32, i32>(&store, "call")?;
150+
/// # assert_eq!(call.call(&mut store, 41)?, 42);
151+
/// # Ok(())
152+
/// # }
153+
/// ```
123154
pub fn from_untyped(
124155
store: &mut Store,
125156
ty: &FuncType,
@@ -150,6 +181,30 @@ impl HostFunction {
150181
}
151182

152183
/// Create a new typed host function import.
184+
///
185+
/// ## Example
186+
/// ```rust
187+
/// # fn main() -> tinywasm::Result<()> {
188+
/// # use tinywasm::{HostFunction, Imports, ModuleInstance, Store};
189+
/// # let wasm = wat::parse_str(r#"
190+
/// # (module
191+
/// # (import "host" "add_one" (func $add_one (param i32) (result i32)))
192+
/// # (func (export "call") (param i32) (result i32)
193+
/// # local.get 0
194+
/// # call $add_one))
195+
/// # "#).expect("valid wat");
196+
/// # let module = tinywasm::parse_bytes(&wasm)?;
197+
/// let mut store = Store::default();
198+
/// let add_one = HostFunction::from(&mut store, |_ctx, value: i32| Ok(value + 1));
199+
///
200+
/// let mut imports = Imports::new();
201+
/// imports.define("host", "add_one", add_one);
202+
/// # let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports))?;
203+
/// # let call = instance.func::<i32, i32>(&store, "call")?;
204+
/// # assert_eq!(call.call(&mut store, 41)?, 42);
205+
/// # Ok(())
206+
/// # }
207+
/// ```
153208
pub fn from<P, R>(store: &mut Store, func: impl Fn(FuncContext<'_>, P) -> Result<R> + 'static) -> Function
154209
where
155210
P: FromWasmValues + ToWasmTypes,
@@ -604,14 +659,10 @@ impl_tuple!(impl_tuple_traits);
604659
/// # let mut store = Store::default();
605660
/// # let instance = ModuleInstance::instantiate(&mut store, &module, None)?;
606661
///
607-
/// type Params = WasmTupleChain<
608-
/// (i32, i32, i32, i32, i32, i32),
609-
/// (i32, i32, i32, i32, i32, i32, i32),
610-
/// >;
611-
/// type Results = WasmTupleChain<
612-
/// (i32, i32, i32, i32, i32, i32),
613-
/// (i32, i32, i32, i32, i32, i32, i32),
614-
/// >;
662+
/// type Params =
663+
/// WasmTupleChain<(i32, i32, i32, i32, i32, i32), (i32, i32, i32, i32, i32, i32, i32)>;
664+
/// type Results =
665+
/// WasmTupleChain<(i32, i32, i32, i32, i32, i32), (i32, i32, i32, i32, i32, i32, i32)>;
615666
///
616667
/// let echo13 = instance.func::<Params, Results>(&store, "echo13")?;
617668
/// let result = echo13.call(&mut store, ((1, 2, 3, 4, 5, 6), (7, 8, 9, 10, 11, 12, 13)).into())?;

crates/tinywasm/src/imports.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,24 +67,30 @@ impl From<&Import> for ExternName {
6767
/// ```rust
6868
/// # use log;
6969
/// # fn main() -> tinywasm::Result<()> {
70+
/// use tinywasm::types::{GlobalType, MemoryType, TableType, WasmType, WasmValue};
7071
/// use tinywasm::{Global, HostFunction, Imports, Memory, ModuleInstance, Store, Table};
71-
/// use tinywasm::types::{WasmType, TableType, MemoryType, WasmValue};
7272
/// # let wasm = wat::parse_str("(module)").expect("valid wat");
7373
/// # let module = tinywasm::parse_bytes(&wasm)?;
7474
/// # let mut store = Store::default();
7575
/// # let my_other_instance = ModuleInstance::instantiate(&mut store, &module, None)?;
7676
/// let mut imports = Imports::new();
7777
///
78-
/// // function args can be either a single
79-
/// // value that implements `TryFrom<WasmValue>` or a tuple of them
8078
/// let print_i32 = HostFunction::from(&mut store, |_ctx: tinywasm::FuncContext<'_>, arg: i32| {
8179
/// log::debug!("print_i32: {}", arg);
8280
/// Ok(())
8381
/// });
8482
///
85-
/// let table = Table::new(&mut store, TableType::new(WasmType::RefFunc, 10, Some(20)), WasmValue::default_for(WasmType::RefFunc))?;
86-
/// let memory = Memory::new(&mut store, MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)))?;
87-
/// let global_i32 = Global::new(&mut store, tinywasm::types::GlobalType::default().with_ty(WasmType::I32), WasmValue::I32(666))?;
83+
/// let table = Table::new(
84+
/// &mut store,
85+
/// TableType::new(WasmType::RefFunc, 10, Some(20)),
86+
/// WasmValue::default_for(WasmType::RefFunc),
87+
/// )?;
88+
/// let memory = Memory::new(
89+
/// &mut store,
90+
/// MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)),
91+
/// )?;
92+
/// let global_i32 =
93+
/// Global::new(&mut store, GlobalType::default().with_ty(WasmType::I32), WasmValue::I32(666))?;
8894
///
8995
/// imports
9096
/// .define("my_module", "print_i32", print_i32)

crates/tinywasm/src/instance.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,30 @@ impl ModuleInstance {
193193

194194
/// Instantiate the module in the given store (without running the start function)
195195
///
196+
/// ## Example
197+
/// ```rust
198+
/// # fn main() -> tinywasm::Result<()> {
199+
/// # use tinywasm::{ModuleInstance, Store};
200+
/// # let wasm = wat::parse_str(r#"
201+
/// # (module
202+
/// # (global $g (mut i32) (i32.const 0))
203+
/// # (func $start
204+
/// # i32.const 42
205+
/// # global.set $g)
206+
/// # (start $start)
207+
/// # (export "g" (global $g)))
208+
/// # "#).expect("valid wat");
209+
/// # let module = tinywasm::parse_bytes(&wasm)?;
210+
/// let mut store = Store::default();
211+
/// let instance = ModuleInstance::instantiate_no_start(&mut store, &module, None)?;
212+
///
213+
/// assert_eq!(instance.global_get(&store, "g")?, 0.into());
214+
/// instance.start(&mut store)?;
215+
/// assert_eq!(instance.global_get(&store, "g")?, 42.into());
216+
/// # Ok(())
217+
/// # }
218+
/// ```
219+
///
196220
/// See <https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation>
197221
pub fn instantiate_no_start(store: &mut Store, module: &Module, imports: Option<Imports>) -> Result<Self> {
198222
let idx = store.next_module_instance_idx();
@@ -251,6 +275,33 @@ impl ModuleInstance {
251275
}
252276

253277
/// Returns an iterator over all exported extern values for this instance.
278+
///
279+
/// ## Example
280+
/// ```rust
281+
/// # fn main() -> tinywasm::Result<()> {
282+
/// # use tinywasm::{ExternItem, ModuleInstance, Store};
283+
/// # let wasm = wat::parse_str(r#"
284+
/// # (module
285+
/// # (func (export "f"))
286+
/// # (memory (export "mem") 1))
287+
/// # "#).expect("valid wat");
288+
/// # let module = tinywasm::parse_bytes(&wasm)?;
289+
/// # let mut store = Store::default();
290+
/// let instance = ModuleInstance::instantiate(&mut store, &module, None)?;
291+
///
292+
/// let mut saw_func = false;
293+
/// let mut saw_memory = false;
294+
/// for (name, item) in instance.exports() {
295+
/// match (name, item) {
296+
/// ("f", ExternItem::Func(_)) => saw_func = true,
297+
/// ("mem", ExternItem::Memory(_)) => saw_memory = true,
298+
/// _ => {}
299+
/// }
300+
/// }
301+
/// assert!(saw_func && saw_memory);
302+
/// # Ok(())
303+
/// # }
304+
/// ```
254305
pub fn exports(&self) -> impl Iterator<Item = (&str, ExternItem)> + '_ {
255306
self.0.exports.iter().map(move |export| {
256307
let item = match export.kind {
@@ -302,6 +353,26 @@ impl ModuleInstance {
302353
}
303354

304355
/// Get any exported extern value by name.
356+
///
357+
/// ## Example
358+
/// ```rust
359+
/// # fn main() -> tinywasm::Result<()> {
360+
/// # use tinywasm::{ExternItem, ModuleInstance, Store};
361+
/// # let wasm = wat::parse_str(r#"
362+
/// # (module
363+
/// # (global (export "answer") i32 (i32.const 42)))
364+
/// # "#).expect("valid wat");
365+
/// # let module = tinywasm::parse_bytes(&wasm)?;
366+
/// # let mut store = Store::default();
367+
/// let instance = ModuleInstance::instantiate(&mut store, &module, None)?;
368+
///
369+
/// let ExternItem::Global(global) = instance.extern_item("answer")? else {
370+
/// panic!("expected global export");
371+
/// };
372+
/// assert_eq!(global.get(&store)?, 42.into());
373+
/// # Ok(())
374+
/// # }
375+
/// ```
305376
pub fn extern_item(&self, name: &str) -> Result<ExternItem> {
306377
match self.require_export(name)? {
307378
ExternVal::Func(addr) => {
@@ -530,6 +601,23 @@ impl ModuleInstance {
530601
/// Returns None if the module has no start function
531602
/// If no start function is specified, also checks for a `_start` function in the exports
532603
///
604+
/// ## Example
605+
/// ```rust
606+
/// # fn main() -> tinywasm::Result<()> {
607+
/// # use tinywasm::{ModuleInstance, Store};
608+
/// # let wasm = wat::parse_str(r#"
609+
/// # (module
610+
/// # (func (export "_start"))
611+
/// # )
612+
/// # "#).expect("valid wat");
613+
/// # let module = tinywasm::parse_bytes(&wasm)?;
614+
/// # let mut store = Store::default();
615+
/// let instance = ModuleInstance::instantiate_no_start(&mut store, &module, None)?;
616+
/// assert!(instance.start_func(&store)?.is_some());
617+
/// # Ok(())
618+
/// # }
619+
/// ```
620+
///
533621
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#start-function>
534622
pub fn start_func(&self, store: &Store) -> Result<Option<Function>> {
535623
self.validate_store(store)?;
@@ -564,6 +652,29 @@ impl ModuleInstance {
564652
///
565653
/// Returns `None` if the module has no start function
566654
///
655+
/// ## Example
656+
/// ```rust
657+
/// # fn main() -> tinywasm::Result<()> {
658+
/// # use tinywasm::{ModuleInstance, Store};
659+
/// # let wasm = wat::parse_str(r#"
660+
/// # (module
661+
/// # (global $g (mut i32) (i32.const 0))
662+
/// # (func (export "_start")
663+
/// # i32.const 7
664+
/// # global.set $g)
665+
/// # (export "g" (global $g)))
666+
/// # "#).expect("valid wat");
667+
/// # let module = tinywasm::parse_bytes(&wasm)?;
668+
/// let mut store = Store::default();
669+
/// let instance = ModuleInstance::instantiate_no_start(&mut store, &module, None)?;
670+
///
671+
/// assert_eq!(instance.global_get(&store, "g")?, 0.into());
672+
/// assert_eq!(instance.start(&mut store)?, Some(()));
673+
/// assert_eq!(instance.global_get(&store, "g")?, 7.into());
674+
/// # Ok(())
675+
/// # }
676+
/// ```
677+
///
567678
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#syntax-start>
568679
pub fn start(&self, store: &mut Store) -> Result<Option<()>> {
569680
match self.start_func(store)? {

crates/tinywasm/src/interpreter/executor.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,7 +1568,7 @@ impl<'store> Executor<'store, false> {
15681568
}
15691569

15701570
loop {
1571-
for _ in 0..1024 {
1571+
for _ in 0..128 {
15721572
if self.exec()?.is_some() {
15731573
return Ok(ExecState::Completed);
15741574
}
@@ -1590,13 +1590,13 @@ impl<'store> Executor<'store, true> {
15901590
}
15911591

15921592
loop {
1593-
for _ in 0..1024 {
1593+
for _ in 0..128 {
15941594
if self.exec()?.is_some() {
15951595
return Ok(ExecState::Completed);
15961596
}
15971597
}
15981598

1599-
self.store.execution_fuel = self.store.execution_fuel.saturating_sub(1024_u32);
1599+
self.store.execution_fuel = self.store.execution_fuel.saturating_sub(128);
16001600
if self.store.execution_fuel == 0 {
16011601
return Ok(ExecState::Suspended(self.cf));
16021602
}

crates/tinywasm/src/reference.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@ impl StoreItem {
3333
}
3434

3535
/// A memory instance in a store.
36+
///
37+
/// ## Example
38+
/// ```rust
39+
/// # fn main() -> tinywasm::Result<()> {
40+
/// use tinywasm::types::MemoryType;
41+
/// use tinywasm::{Memory, Store};
42+
///
43+
/// let mut store = Store::default();
44+
/// let memory = Memory::new(&mut store, MemoryType::default().with_page_count_initial(1))?;
45+
///
46+
/// memory.copy_from_slice(&mut store, 0, b"hi")?;
47+
/// assert_eq!(memory.read_vec(&store, 0, 2)?, b"hi");
48+
/// assert_eq!(memory.page_count(&store)?, 1);
49+
/// memory.grow(&mut store, 1)?;
50+
/// assert_eq!(memory.page_count(&store)?, 2);
51+
/// # Ok(())
52+
/// # }
53+
/// ```
3654
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
3755
#[cfg_attr(feature = "debug", derive(Debug))]
3856
pub struct Memory(pub(crate) StoreItem);

crates/tinywasm/src/store/mod.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,19 @@ static STORE_ID: AtomicUsize = AtomicUsize::new(0);
2424

2525
/// Global state that can be manipulated by WebAssembly programs
2626
///
27-
/// Data should only be addressable by the module that owns it
28-
///
2927
/// Note that the state doesn't do any garbage collection - so it will grow
3028
/// indefinitely if you keep adding modules to it. When calling temporary
31-
/// functions, you should create a new store and then drop it when you're done (e.g. in a request handler)
29+
/// functions, you should create a new store and then drop it when you're done (e.g. in a request handler).
30+
///
31+
/// ## Example
32+
/// ```rust
33+
/// use tinywasm::engine::{Config, StackConfig};
34+
/// use tinywasm::{Engine, Store};
35+
///
36+
/// let engine = Engine::new(Config::new().with_call_stack(StackConfig::dynamic(64, 512)));
37+
/// let store = Store::new(engine);
38+
/// # _ = store;
39+
/// ```
3240
///
3341
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#store>
3442
pub struct Store {

examples/wasm-rust.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use tinywasm::{FuncContext, HostFunction, Imports, ModuleInstance, Store};
1919
/// `rustup target add wasm32-unknown-unknown`.
2020
/// <https://github.com/WebAssembly/wabt>
2121
/// <https://github.com/WebAssembly/binaryen>
22-
///
2322
fn main() -> Result<()> {
2423
pretty_env_logger::init();
2524

rustfmt.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
unstable_features=true
2+
format_code_in_doc_comments=true
13
max_width=120
24
use_small_heuristics="Max"

0 commit comments

Comments
 (0)