From c7c26e8900e86f8b2116648e988b1e34c85f4077 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 24 Jul 2026 14:45:52 +0200 Subject: docs: improve rust docs Signed-off-by: Henry --- CHANGELOG.md | 2 +- README.md | 37 ++++++++------------- crates/parser/src/lib.rs | 10 ++++-- crates/tinywasm/src/engine.rs | 17 +++++++--- crates/tinywasm/src/func.rs | 26 +++++++++++++-- crates/tinywasm/src/instance.rs | 4 +-- crates/tinywasm/src/lib.rs | 58 ++++++++++++++++----------------- crates/tinywasm/src/reference.rs | 23 +++++++++++++ crates/tinywasm/src/store/memory/mod.rs | 6 ++-- crates/types/src/archive.rs | 5 ++- crates/types/src/instructions.rs | 7 ++-- crates/types/src/lib.rs | 6 ++-- 12 files changed, 127 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c104f8..37f697d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed the parser's `optimize_remove_nop` option and accessors. - Changed public `Instruction` variants and the `.twasm` format. Existing archives must be regenerated. - `TableType` limits now use `u64`. Use `TableType::new` or `TableType::new64` instead of struct literals. -- `ToWasmType` and `ToWasmTypes` now use associated constants, and `wasm_types` returns `Cow`. +- `ToWasmTypes` now uses an associated constant, and `wasm_types` returns `Cow`. - `LinearMemory` now uses a single `usize` address and changed its fixed-width read/write signatures. ## [0.9.1] - 2026-06-29 diff --git a/README.md b/README.md index 794d5dc..132ee49 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ```toml [dependencies] -tinywasm = "0.9" +tinywasm = "0.10" ``` ## Usage @@ -31,7 +31,7 @@ let mut store = Store::default(); let instance = ModuleInstance::instantiate(&mut store, &module, None)?; // Call an exported function with typed parameters -let func = instance.func::<(i32, i32), i32>(&mut store, "add")?; +let func = instance.func::<(i32, i32), i32>(&store, "add")?; let result = func.call(&mut store, (1, 2))?; assert_eq!(result, 3); @@ -41,24 +41,15 @@ See the [examples](./examples) directory and [documentation](https://docs.rs/tin ## Cargo Features -- **`std`**\ - Enables the use of `std` and `std::io` for parsing from files and streams. This is enabled by default. -- **`log`**\ - Enables logging using the `log` crate. This is enabled by default. -- **`parser`**\ - Enables the `tinywasm-parser` crate. This is enabled by default. -- **`archive`**\ - Enables serialization/deserialization of compiled modules to the internal `twasm` bytecode format. This is enabled by default. -- **`canonicalize-nans`**\ - Canonicalizes NaN values to a single representation. This is enabled by default. -- **`debug`**\ - Derives `Debug` for runtime types. This is enabled by default. -- **`parallel-parser`**\ - Parallelizes function parsing and validation across threads (requires `std`). This is enabled by default. -- **`guest-debug`**\ - Exposes module-internal by-index inspection APIs (`*_by_index`). -- **`simd-x86`**\ - Enables x86-specific SIMD intrinsics for `i8x16_swizzle` and `i8x16_shuffle` (uses `unsafe` code). +- **`std`:** Enables `std` and parsing from files and streams. Enabled by default. +- **`log`:** Enables integration with the `log` crate. Enabled by default. +- **`parser`:** Enables `tinywasm-parser` and top-level parse helpers. Enabled by default. +- **`archive`:** Enables serialization and deserialization of the internal `twasm` format. Enabled by default. +- **`canonicalize-nans`:** Canonicalizes NaN values. Enabled by default. +- **`debug`:** Derives `Debug` for runtime types. Enabled by default. +- **`parallel-parser`:** Parallelizes function parsing and validation when `std` is enabled. Enabled by default. +- **`guest-debug`:** Exposes module-internal by-index inspection APIs (`*_by_index`). +- **`simd-x86`:** Enables x86-specific SIMD intrinsics and uses `unsafe` internally. With default features disabled, `tinywasm` depends only on `core`, `alloc`, and `libm`[^libm], making it usable in `no_std + alloc` environments. @@ -68,13 +59,13 @@ Use `Engine` and `engine::Config` when you need non-default runtime settings suc ## Current Status -`tinywasm` passes the WebAssembly MVP and WebAssembly 2.0 core testsuites. WebAssembly 3.0 support is still in progress, and some newer proposal suites are tracked in-repo as experimental coverage rather than release guarantees; see [Supported Proposals](#supported-proposals) for details. +`tinywasm` passes the WebAssembly MVP and WebAssembly 2.0 core testsuites and supports the [Lime1](https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#lime1) interoperability target. WebAssembly 3.0 support is still in progress, and some newer proposal suites are tracked in-repo as experimental coverage rather than release guarantees; see [Supported Proposals](#supported-proposals) for details. -TinyWasm also has its own internal bytecode format, `twasm`. WebAssembly modules can be compiled to `twasm`, which stores TinyWasm's validated and optimized instruction representation for faster loading and reuse. +TinyWasm also has its own internal bytecode format, `twasm`. WebAssembly modules can be compiled to `twasm`, which stores TinyWasm's optimized instruction representation for faster loading and reuse. ## Safety -TinyWasm only uses safe Rust by default. The optional `simd-x86` feature enables x86-specific SIMD intrinsics and uses `unsafe` internally. WebAssembly input is validated by TinyWasm before execution and runs inside a sandbox: untrusted Wasm should not be able to access host memory, escape the sandbox, or cause undefined behavior in the runtime. +TinyWasm only uses safe Rust by default. The optional `simd-x86` feature enables x86-specific SIMD intrinsics and uses `unsafe` internally. WebAssembly input is validated by default and runs inside a sandbox: untrusted Wasm should not be able to access host memory, escape the sandbox, or cause undefined behavior in the runtime. Validation should only be disabled for trusted input. The internal `twasm` bytecode format is not currently validated as an untrusted input format. Malformed `twasm` may panic, but should not compromise memory safety or allow sandbox escape. Only run trusted `twasm` bytecode, or generate it through TinyWasm from Wasm input. diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 7936675..aa5e0b0 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -49,7 +49,10 @@ pub use tinywasm_types::Module; #[non_exhaustive] #[derive(Debug, Clone)] pub struct ParserOptions { - /// Whether to validate modules while parsing. + /// Whether to validate modules while parsing. Enabled by default. + /// + /// Disable this only for trusted input. Parsing without validation may produce + /// a module that violates runtime assumptions. pub validation: bool, /// Whether to optimize local memory allocation by skipping allocation of unused local memories. pub optimize_local_memory_allocation: bool, @@ -81,6 +84,9 @@ impl Default for ParserOptions { impl ParserOptions { /// Enable or disable WebAssembly validation. + /// + /// Disable this only for trusted input. Parsing without validation may produce + /// a module that violates runtime assumptions. pub const fn with_validation(mut self, enabled: bool) -> Self { self.validation = enabled; self @@ -337,7 +343,7 @@ pub fn parse_file(path: impl AsRef + Clone) -> Result Result { Parser::new().parse_module_stream(stream) } diff --git a/crates/tinywasm/src/engine.rs b/crates/tinywasm/src/engine.rs index dfb4d15..521eb80 100644 --- a/crates/tinywasm/src/engine.rs +++ b/crates/tinywasm/src/engine.rs @@ -92,7 +92,10 @@ impl StackConfig { /// /// let config = Config::new() /// .with_fuel_policy(FuelPolicy::Weighted) -/// .with_value_stack(StackConfig::dynamic(1024, 16 * 1024)) +/// .with_value_stack_32(StackConfig::dynamic(1024, 36 * 1024)) +/// .with_value_stack_64(StackConfig::dynamic(1024, 32 * 1024)) +/// .with_value_stack_128(StackConfig::dynamic(256, 4 * 1024)) +/// .with_call_stack(StackConfig::dynamic(64, 1024)) /// .with_memory_backend(MemoryBackend::paged(64 * 1024)) /// .with_trap_on_oom(true); /// @@ -103,23 +106,27 @@ impl StackConfig { #[non_exhaustive] pub struct Config { /// Configuration for the 32-bit value stack (i32, f32, ref values). + /// Defaults to `StackConfig::fixed(36 * 1024)`. pub value_stack_32: StackConfig, /// Configuration for the 64-bit value stack (i64, f64 values). + /// Defaults to `StackConfig::fixed(32 * 1024)`. pub value_stack_64: StackConfig, /// Configuration for the 128-bit value stack (v128 values). + /// Defaults to `StackConfig::fixed(4 * 1024)`. pub value_stack_128: StackConfig, - /// Configuration for the call stack. + /// Configuration for the call stack. Defaults to `StackConfig::fixed(1024)`. pub call_stack: StackConfig, - /// Fuel accounting policy used by budgeted execution. + /// Fuel accounting policy used by budgeted execution. Defaults to [`FuelPolicy::PerInstruction`]. pub fuel_policy: FuelPolicy, - /// Backend used for runtime memories. + /// Backend used for runtime memories. Defaults to [`MemoryBackend::vec`]. pub memory_backend: MemoryBackend, /// Whether memory and stack allocation failures should trap instead of degrading into normal operation failure modes. + /// Defaults to `false`. pub trap_on_oom: bool, } impl Config { - /// Create a new stack configuration with default settings. + /// Create a new interpreter configuration with default settings. pub fn new() -> Self { Self::default() } diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 9777a47..05767d5 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -47,7 +47,7 @@ impl Function { /// /// The returned handle keeps a mutable borrow of the [`Store`] until it /// completes. Use [`FuncExecution::resume_with_fuel`] (or - /// [`FuncExecution::resume_with_time_budget`] with `std`) to continue. + /// `resume_with_time_budget` with `std`) to continue. pub fn call_resumable<'store>( &self, store: &'store mut Store, @@ -540,6 +540,25 @@ impl FunctionTyped { /// Call a typed function and return a resumable execution handle. /// /// The handle keeps a mutable borrow of the [`Store`] until completion. + /// + /// ## Example + /// + /// ```rust + /// # fn main() -> tinywasm::Result<()> { + /// use tinywasm::{ExecProgress, ModuleInstance, Store}; + /// + /// let wasm = include_bytes!("../../../examples/wasm/add.wasm"); + /// let module = tinywasm::parse_bytes(wasm)?; + /// let mut store = Store::default(); + /// let instance = ModuleInstance::instantiate(&mut store, &module, None)?; + /// let add = instance.func::<(i32, i32), i32>(&store, "add")?; + /// + /// let mut execution = add.call_resumable(&mut store, (20, 22))?; + /// assert!(matches!(execution.resume_with_fuel(0)?, ExecProgress::Suspended)); + /// assert!(matches!(execution.resume_with_fuel(16)?, ExecProgress::Completed(42))); + /// # Ok(()) + /// # } + /// ``` pub fn call_resumable<'store>(&self, store: &'store mut Store, params: P) -> Result> { let wasm_values = params.into_wasm_values(); let execution = self.func.call_resumable(store, &wasm_values)?; @@ -574,7 +593,10 @@ impl<'store, R: FromWasmValues> FuncExecutionTyped<'store, R> { /// Describes the WebAssembly value types produced by a Rust value or tuple shape. pub trait ToWasmTypes { - /// Static WebAssembly types for shapes that do not require runtime concatenation. + /// Static WebAssembly types for this shape. + /// + /// Implementations that require runtime construction may set this to `None`, + /// but must then override [`Self::wasm_types`]. const WASM_TYPES: Option<&'static [WasmType]>; /// Return the flattened WebAssembly value types for this tuple shape. diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 8cff310..a727638 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -560,8 +560,8 @@ impl ModuleInstance { /// Get the start function of the module /// - /// Returns None if the module has no start function - /// If no start function is specified, also checks for a `_start` function in the exports + /// Returns `None` if the module has no start section. Exported functions named + /// `_start` are not treated as the module's start function. /// /// ## Example /// ```rust diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index a48d9d7..908f33b 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -9,42 +9,22 @@ //! `tinywasm` provides a small, portable WebAssembly interpreter with support for //! the WebAssembly MVP, WebAssembly 2.0, and a growing set of newer proposals. +//! It also supports the [Lime1](https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#lime1) +//! interoperability target. //! It is designed to stay lightweight while still being practical to embed in //! applications, tools, and `no_std + alloc` environments. -//! -//! ## Features -//! - **`std`**\ -//! Enables parsing from files and streams. Enabled by default. -//! - **`log`**\ -//! Enables integration with the `log` crate. Enabled by default. -//! - **`parser`**\ -//! Enables the bundled `tinywasm-parser` crate and top-level parse helpers. Enabled by default. -//! - **`archive`**\ -//! Enables serialization and deserialization of compiled modules in the internal `twasm` format. Enabled by default. -//! - **`canonicalize-nans`**\ -//! Canonicalizes NaN values to a single representation. Enabled by default. -//! - **`debug`**\ -//! Derives `Debug` for runtime types. Enabled by default. -//! - **`parallel-parser`**\ -//! Parallelizes function parsing and validation across threads when `std` is enabled. Enabled by default. -//! - **`guest-debug`**\ -//! Exposes module-internal by-index inspection APIs (`*_by_index`). -//! - **`simd-x86`**\ -//! Enables x86-specific SIMD intrinsics for selected operations and uses `unsafe` internally. -//! -//! With default features disabled, `tinywasm` only depends on `core`, `alloc`, and `libm`. -//! By disabling `std`, you can use `tinywasm` in `no_std` environments. This requires -//! a custom allocator and removes support for parsing from files and streams, but otherwise the API is the same. #![cfg_attr(docsrs, feature(doc_cfg))] //! -//! ## Getting Started -//! The easiest way to get started is to use the [`crate::parse_bytes`] function to load a +//! ## Getting started +//! +//! The easiest way to get started is to use the `parse_bytes` function to load a //! WebAssembly module from bytes. This will parse the module and validate it, returning //! a [`Module`] that can be used to instantiate the module. //! -//! //! ```rust +//! # #[cfg(feature = "parser")] +//! # fn main() -> tinywasm::Result<()> { //! use tinywasm::{ModuleInstance, Store}; //! //! // Load a module from bytes @@ -62,12 +42,15 @@ //! //! // # Get a typed handle to the exported "add" function //! // Alternatively, you can use `instance.func_untyped` to get an untyped handle -//! // that takes and returns [`WasmValue`]s -//! let func = instance.func::<(i32, i32), i32>(&mut store, "add")?; +//! // that takes and returns [`types::WasmValue`]s +//! let func = instance.func::<(i32, i32), i32>(&store, "add")?; //! let res = func.call(&mut store, (1, 2))?; //! //! assert_eq!(res, 3); -//! # Ok::<(), tinywasm::Error>(()) +//! # Ok(()) +//! # } +//! # #[cfg(not(feature = "parser"))] +//! # fn main() {} //! ``` //! //! For non-default runtime behavior, construct a [`Store`] with a custom [`Engine`] @@ -76,6 +59,21 @@ //! //! For more examples, see the [`examples`](https://github.com/explodingcamera/tinywasm/tree/main/examples) directory. //! +//! ## Cargo features +//! +//! - **`std`:** Enables `std` and parsing from files and streams. Enabled by default. +//! - **`log`:** Enables integration with the `log` crate. Enabled by default. +//! - **`parser`:** Enables `tinywasm-parser` and top-level parse helpers. Enabled by default. +//! - **`archive`:** Enables serialization and deserialization of the internal `twasm` format. Enabled by default. +//! - **`canonicalize-nans`:** Canonicalizes NaN values. Enabled by default. +//! - **`debug`:** Derives `Debug` for runtime types. Enabled by default. +//! - **`parallel-parser`:** Parallelizes function parsing and validation when `std` is enabled. Enabled by default. +//! - **`guest-debug`:** Exposes module-internal by-index inspection APIs (`*_by_index`). +//! - **`simd-x86`:** Enables x86-specific SIMD intrinsics and uses `unsafe` internally. +//! +//! With default features disabled, `tinywasm` depends only on `core`, `alloc`, and `libm`, +//! making it usable in `no_std + alloc` environments with a custom allocator. +//! //! ## Imports //! //! To provide imports to a module, you can use the [`Imports`] struct. diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index e46db51..004939f 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -167,6 +167,29 @@ impl Memory { /// Creates a cursor positioned at the start of this memory. /// /// Available with the `std` feature enabled. + /// + /// ## Example + /// + /// ```rust + /// # fn main() -> Result<(), Box> { + /// use std::io::{Read, Seek, SeekFrom, Write}; + /// 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))?; + /// let mut cursor = memory.cursor(&mut store)?; + /// + /// cursor.seek(SeekFrom::Start(2))?; + /// cursor.write_all(b"abc")?; + /// cursor.seek(SeekFrom::Start(0))?; + /// + /// let mut bytes = [0; 5]; + /// cursor.read_exact(&mut bytes)?; + /// assert_eq!(bytes, [0, 0, b'a', b'b', b'c']); + /// # Ok(()) + /// # } + /// ``` #[cfg(feature = "std")] pub fn cursor<'a>(&self, store: &'a mut Store) -> Result> { self.cursor_at(store, 0) diff --git a/crates/tinywasm/src/store/memory/mod.rs b/crates/tinywasm/src/store/memory/mod.rs index aeaf3ba..a3f0053 100644 --- a/crates/tinywasm/src/store/memory/mod.rs +++ b/crates/tinywasm/src/store/memory/mod.rs @@ -23,6 +23,7 @@ pub use {lazy::LazyLinearMemory, paged::PagedMemory, vec_memory::VecMemory}; /// This is a low-level trait that abstracts over the actual storage mechanism for linear memory. /// This will probably change in the future to allow more efficient implementations. /// See [`MemoryBackend`] for a higher-level interface to configuring memory storage. +/// The runtime passes slices of the exact indicated width to the fixed-width `write_*` methods. pub trait LinearMemory { /// Returns the current memory length in bytes. fn len(&self) -> usize; @@ -285,12 +286,13 @@ impl MemoryBackend { /// Uses sparse chunked storage for each memory instance. /// - /// `chunk_size` is the backend chunk size in bytes. It is independent from the Wasm page size. + /// `chunk_size` is the backend chunk size in bytes. It must be a non-zero power + /// of two and is independent from the Wasm page size. /// /// This generally makes growth cheaper than [`Self::vec`], but read and write operations do a /// little more work and may be slightly slower. pub fn paged(chunk_size: usize) -> Self { - assert!(chunk_size != 0, "chunk_size must be greater than zero"); + assert!(chunk_size.is_power_of_two(), "chunk_size must be a non-zero power of two"); Self(MemoryBackendInner::Paged { chunk_size }) } diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index 48dc53f..99238ab 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -45,7 +45,10 @@ impl Display for TwasmError { impl core::error::Error for TwasmError {} impl Module { - /// Creates a [`Module`] from a slice of bytes. + /// Creates a [`Module`] from internal `twasm` archive bytes. + /// + /// Archives are version-specific and are not validated as untrusted input. + /// Only load archives from a trusted source. pub fn try_from_twasm(wasm: &[u8]) -> Result { let len = validate_magic(wasm)?; postcard::from_bytes(&wasm[len..]).map_err(TwasmError::InvalidArchive) diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index a22279e..cec9d5b 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -116,10 +116,11 @@ pub enum BinOp128 { I64x2Mul, } -/// A WebAssembly Instruction +/// A TinyWasm bytecode instruction. /// -/// These are our own internal bytecode instructions so they may not match the spec exactly. -/// Wasm Bytecode can map to multiple of these instructions. +/// These instructions are an internal, version-specific representation and do not +/// map one-to-one to WebAssembly instructions. Their variants and serialized form +/// may change between TinyWasm releases. /// /// See #[rustfmt::skip] diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index fcac23c..0b4b54c 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -45,8 +45,8 @@ pub mod archive { /// A `TinyWasm` WebAssembly Module /// /// This is the internal representation of a WebAssembly module in `TinyWasm`. -/// [`Module`] are validated before being created, so they are guaranteed to be valid (as long as they were created by `TinyWasm`). -/// This means you should not trust a [`Module`] created by a third party to be valid. +/// Modules produced by the parser are validated by default, but validation can be +/// disabled for trusted input. Do not trust modules or archives from third parties. #[derive(Clone, Default, PartialEq)] #[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] @@ -76,7 +76,7 @@ pub struct ModuleInner { /// Corresponds to the `start` section of the original WebAssembly module. pub start_func: Option, - /// Optimized and validated WebAssembly functions + /// Optimized WebAssembly functions /// /// Contains data from to the `code`, `func`, and `type` sections of the original WebAssembly module. pub funcs: Box<[Arc]>, -- cgit v1.3.1