diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/cli/Cargo.toml | 3 | ||||
| -rw-r--r-- | crates/cli/README.md | 2 | ||||
| -rw-r--r-- | crates/cli/src/args.rs | 9 | ||||
| -rw-r--r-- | crates/cli/src/bin.rs | 10 | ||||
| -rw-r--r-- | crates/parser/Cargo.toml | 3 | ||||
| -rw-r--r-- | crates/parser/README.md | 19 | ||||
| -rw-r--r-- | crates/tinywasm/Cargo.toml | 8 | ||||
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 22 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/num_helpers.rs | 12 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/simd/utils.rs | 8 | ||||
| -rw-r--r-- | crates/tinywasm/src/lib.rs | 45 | ||||
| -rw-r--r-- | crates/tinywasm/tests/internal_refs.rs | 2 | ||||
| -rw-r--r-- | crates/types/Cargo.toml | 3 | ||||
| -rw-r--r-- | crates/types/README.md | 4 |
14 files changed, 90 insertions, 60 deletions
diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index caf541b..8472afb 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name="tinywasm-cli" version.workspace=true -description="TinyWasm CLI" +description="Command-line interface for TinyWasm" edition.workspace=true license.workspace=true authors.workspace=true @@ -9,6 +9,7 @@ repository.workspace=true rust-version.workspace=true keywords.workspace=true categories=["wasm"] +readme="README.md" [[bin]] name="tinywasm-cli" diff --git a/crates/cli/README.md b/crates/cli/README.md index 70b2cff..aa4d0c8 100644 --- a/crates/cli/README.md +++ b/crates/cli/README.md @@ -8,4 +8,6 @@ It is recommended to use the library directly instead of the CLI. ```bash $ cargo install tinywasm-cli $ tinywasm-cli --help +$ tinywasm-cli run ./module.wasm +$ tinywasm-cli run ./module.wasm -f add -a i32:1 -a i32:2 ``` diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 1fec2d5..0333c92 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -17,8 +17,11 @@ impl From<WasmArg> for WasmValue { impl FromStr for WasmArg { type Err = String; fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> { - let [ty, val]: [&str; 2] = - s.split(':').collect::<Vec<_>>().try_into().map_err(|e| format!("invalid arguments: {e:?}"))?; + let [ty, val]: [&str; 2] = s + .split(':') + .collect::<Vec<_>>() + .try_into() + .map_err(|_e| "invalid argument format; expected type:value".to_string())?; let arg: WasmValue = match ty { "i32" => val.parse::<i32>().map_err(|e| format!("invalid argument value for i32: {e:?}"))?.into(), @@ -26,7 +29,7 @@ impl FromStr for WasmArg { "f32" => val.parse::<f32>().map_err(|e| format!("invalid argument value for f32: {e:?}"))?.into(), "f64" => val.parse::<f64>().map_err(|e| format!("invalid argument value for f64: {e:?}"))?.into(), "v128" => val.parse::<i128>().map_err(|e| format!("invalid argument value for v128: {e:?}"))?.into(), - t => return Err(format!("Invalid arg type: {t}")), + t => return Err(format!("invalid arg type `{t}`; expected one of i32, i64, f32, f64, v128")), }; Ok(WasmArg(arg)) diff --git a/crates/cli/src/bin.rs b/crates/cli/src/bin.rs index 9b283c3..7e32fed 100644 --- a/crates/cli/src/bin.rs +++ b/crates/cli/src/bin.rs @@ -19,7 +19,7 @@ struct TinyWasmCli { #[argh(subcommand)] nested: TinyWasmSubcommand, - /// log level + /// log level: trace, debug, info, warn, or error #[argh(option, short = 'l', default = "\"info\".to_string()")] log_level: String, } @@ -53,15 +53,15 @@ struct Run { #[argh(positional)] wasm_file: String, - /// function to run + /// exported function to run; omit to only instantiate and run start #[argh(option, short = 'f')] func: Option<String>, - /// arguments to pass to the wasm file + /// arguments passed to the function in type:value form #[argh(option, short = 'a')] args: Vec<WasmArg>, - /// engine to use + /// engine to use (currently only `main`) #[argh(option, short = 'e', default = "Engine::Main")] engine: Engine, } @@ -74,7 +74,7 @@ fn main() -> Result<()> { "warn" => log::LevelFilter::Warn, "error" => log::LevelFilter::Error, "info" => log::LevelFilter::Info, - _ => log::LevelFilter::Info, + other => return Err(eyre::eyre!("invalid log level `{other}`; expected trace, debug, info, warn, or error")), }; pretty_env_logger::formatted_builder().filter_level(level).init(); diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 26bdd51..1c228c4 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -1,7 +1,7 @@ [package] name="tinywasm-parser" version.workspace=true -description="TinyWasm parser" +description="Parser and lowering pipeline for TinyWasm" edition.workspace=true license.workspace=true authors.workspace=true @@ -9,6 +9,7 @@ repository.workspace=true rust-version.workspace=true keywords.workspace=true categories.workspace=true +readme="README.md" [dependencies] wasmparser={workspace=true, features=["validate", "features", "simd"]} diff --git a/crates/parser/README.md b/crates/parser/README.md index c7eef8d..91145f1 100644 --- a/crates/parser/README.md +++ b/crates/parser/README.md @@ -1,20 +1,29 @@ # `tinywasm-parser` -This crate provides a compiler that can convert WebAssembly modules into a `tinywasm` modules. +This crate provides the parser and lowering pipeline that converts WebAssembly binaries into `tinywasm` modules. ## Features - `std`: Enables the use of `std` and `std::io` for parsing from files and streams. - `log`: Enables logging of the parsing process using the `log` crate. +- `parallel`: Enables multithreaded parsing and validation when `std` is available. ## Usage ```rust -use tinywasm_parser::Parser; +use tinywasm_parser::{Parser, ParserOptions}; + let bytes = include_bytes!("./file.wasm"); let parser = Parser::new(); -let module = parser.parse_module_bytes(bytes).unwrap(); -let module = parser.parse_module_file("path/to/file.wasm").unwrap(); -let module = parser.parse_module_stream(&mut stream).unwrap(); +let module = parser.parse_module_bytes(bytes)?; + +let parser = Parser::with_options(ParserOptions::default().with_rewrite_optimization(false)); +let module = parser.parse_module_bytes(bytes)?; + +let module = parser.parse_module_file("path/to/file.wasm")?; +let mut stream = std::fs::File::open("path/to/file.wasm")?; +let module = parser.parse_module_stream(&mut stream)?; ``` + +If you just want the default configuration, the top-level `parse_bytes`, `parse_file`, and `parse_stream` helpers are thin wrappers around `Parser::new()`. diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index 0eb939e..2f80695 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -16,7 +16,7 @@ name="tinywasm" path="src/lib.rs" [package.metadata.docs.rs] -features=["std", "parser", "archive", "log", "canonicalize_nans", "debug", "guest_debug"] +features=["std", "parser", "archive", "log", "canonicalize-nans", "debug", "guest-debug"] rustdoc-args=["--cfg", "docsrs"] [dependencies] @@ -38,7 +38,7 @@ serde_json.workspace=true serde.workspace=true [features] -default=["std", "parser", "log", "archive", "canonicalize_nans", "debug", "parallel-parser"] +default=["std", "parser", "log", "archive", "canonicalize-nans", "debug", "parallel-parser"] log=["dep:log", "tinywasm-parser?/log", "tinywasm-types/log"] std=["tinywasm-parser?/std", "tinywasm-types/std"] @@ -53,13 +53,13 @@ parallel-parser=["parser", "tinywasm-parser?/parallel"] archive=["tinywasm-types/archive"] # canonicalize all NaN values to a single representation -canonicalize_nans=[] +canonicalize-nans=[] # derive Debug for runtime/types structs debug=["tinywasm-types/debug"] # expose module-internal by-index inspection APIs for non-exported entities (for testing and debugging) -guest_debug=[] +guest-debug=[] # enable x86-specific SIMD intrinsics in Value128 (uses unsafe code) # note: for x86 backend selection, compile with x86-64-v3 target features diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 2ec7916..8ef572d 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -260,7 +260,7 @@ impl ModuleInstance { } #[inline] - #[cfg(feature = "guest_debug")] + #[cfg(feature = "guest-debug")] fn index_addr<T: Copy>(slice: &[T], idx: u32, kind: &str) -> Result<T> { match slice.get(idx as usize) { Some(addr) => Ok(*addr), @@ -316,8 +316,8 @@ impl ModuleInstance { /// normal export boundary. It is mainly intended for tooling and /// introspection. Calling private functions can change behavior in ways the /// module author did not expose as part of the public API. - #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))] - #[cfg(feature = "guest_debug")] + #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] + #[cfg(feature = "guest-debug")] pub fn func_by_index(&self, store: &Store, func_index: FuncAddr) -> Result<Function> { self.validate_store(store)?; let func_addr = Self::index_addr(&self.0.func_addrs, func_index, "function")?; @@ -343,8 +343,8 @@ impl ModuleInstance { } /// Get a typed function by its module-local index. - #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))] - #[cfg(feature = "guest_debug")] + #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] + #[cfg(feature = "guest-debug")] pub fn func_typed_by_index< P: IntoWasmValueTuple + WasmTypesFromTuple, R: FromWasmValueTuple + WasmTypesFromTuple, @@ -396,8 +396,8 @@ impl ModuleInstance { /// normal export boundary. It is mainly intended for tooling and /// inspection. Mutating a private memory can change module behavior in ways /// that are not part of the module's public API. - #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))] - #[cfg(feature = "guest_debug")] + #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] + #[cfg(feature = "guest-debug")] pub fn memory_by_index(&self, memory_index: MemAddr) -> Result<Memory> { Ok(Memory::from_store_addr(self.0.store_id, Self::index_addr(&self.0.mem_addrs, memory_index, "memory")?)) } @@ -416,8 +416,8 @@ impl ModuleInstance { /// normal export boundary. It is mainly intended for tooling and /// inspection. Mutating a private table can change module behavior in ways /// that are not part of the module's public API. - #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))] - #[cfg(feature = "guest_debug")] + #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] + #[cfg(feature = "guest-debug")] pub fn table_by_index(&self, table_index: TableAddr) -> Result<Table> { Ok(Table::from_store_addr(self.0.store_id, Self::index_addr(&self.0.table_addrs, table_index, "table")?)) } @@ -446,8 +446,8 @@ impl ModuleInstance { /// normal export boundary. It is mainly intended for tooling and /// inspection. Mutating a private global can change module behavior in ways /// that are not part of the module's public API. - #[cfg_attr(docsrs, doc(cfg(feature = "guest_debug")))] - #[cfg(feature = "guest_debug")] + #[cfg_attr(docsrs, doc(cfg(feature = "guest-debug")))] + #[cfg(feature = "guest-debug")] pub fn global_by_index(&self, global_index: GlobalAddr) -> Result<Global> { Ok(Global::from_store_addr(self.0.store_id, Self::index_addr(&self.0.global_addrs, global_index, "global")?)) } diff --git a/crates/tinywasm/src/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs index 9651ce0..c88d7e4 100644 --- a/crates/tinywasm/src/interpreter/num_helpers.rs +++ b/crates/tinywasm/src/interpreter/num_helpers.rs @@ -67,9 +67,9 @@ macro_rules! impl_wasm_float_ops { #[inline] fn tw_nearest(self) -> Self { match self { - #[cfg(not(feature = "canonicalize_nans"))] + #[cfg(not(feature = "canonicalize-nans"))] x if x.is_nan() => x, // preserve NaN - #[cfg(feature = "canonicalize_nans")] + #[cfg(feature = "canonicalize-nans")] x if x.is_nan() => Self::NAN, // Do not preserve NaN x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros x if (0.0..=0.5).contains(&x) => 0.0, @@ -95,9 +95,9 @@ macro_rules! impl_wasm_float_ops { Some(core::cmp::Ordering::Less) => self, Some(core::cmp::Ordering::Greater) => other, Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { self } else { other }, - #[cfg(not(feature = "canonicalize_nans"))] + #[cfg(not(feature = "canonicalize-nans"))] None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - #[cfg(feature = "canonicalize_nans")] + #[cfg(feature = "canonicalize-nans")] None => Self::NAN, // Do not preserve NaN } } @@ -110,9 +110,9 @@ macro_rules! impl_wasm_float_ops { Some(core::cmp::Ordering::Greater) => self, Some(core::cmp::Ordering::Less) => other, Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { other } else { self }, - #[cfg(not(feature = "canonicalize_nans"))] + #[cfg(not(feature = "canonicalize-nans"))] None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - #[cfg(feature = "canonicalize_nans")] + #[cfg(feature = "canonicalize-nans")] None => Self::NAN, // Do not preserve NaN } } diff --git a/crates/tinywasm/src/interpreter/simd/utils.rs b/crates/tinywasm/src/interpreter/simd/utils.rs index 6feabe1..e02bac3 100644 --- a/crates/tinywasm/src/interpreter/simd/utils.rs +++ b/crates/tinywasm/src/interpreter/simd/utils.rs @@ -28,24 +28,24 @@ impl Value128 { } pub(super) const fn canonicalize_simd_f32_nan(x: f32) -> f32 { - #[cfg(feature = "canonicalize_nans")] + #[cfg(feature = "canonicalize-nans")] if x.is_nan() { f32::NAN } else { x } - #[cfg(not(feature = "canonicalize_nans"))] + #[cfg(not(feature = "canonicalize-nans"))] x } pub(super) const fn canonicalize_simd_f64_nan(x: f64) -> f64 { - #[cfg(feature = "canonicalize_nans")] + #[cfg(feature = "canonicalize-nans")] if x.is_nan() { f64::NAN } else { x } - #[cfg(not(feature = "canonicalize_nans"))] + #[cfg(not(feature = "canonicalize-nans"))] x } diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index b4dad9e..4b6b360 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -7,26 +7,33 @@ #![cfg_attr(not(feature = "simd-x86"), forbid(unsafe_code))] #![cfg_attr(feature = "simd-x86", deny(unsafe_code))] -//! A tiny WebAssembly Runtime written in Rust -//! -//! `TinyWasm` provides a minimal WebAssembly runtime for executing WebAssembly modules. -//! It currently supports all features of the WebAssembly MVP specification and is -//! designed to be easy to use and integrate in other projects. +//! `tinywasm` provides a small, portable WebAssembly interpreter with support for +//! the WebAssembly MVP, WebAssembly 2.0, and a growing set of newer proposals. +//! It is designed to stay lightweight while still being practical to embed in +//! applications, tools, and `no_std + alloc` environments. //! //! ## 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 pre-parsing of archives. This is enabled by default. -//!- **`guest_debug`**\ -//! Enables module-internal by-index inspection APIs (`*_by_index`). +//! - **`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 all these features disabled, `TinyWasm` only depends on `core`, `alloc` and `libm`. -//! By disabling `std`, you can use `TinyWasm` in `no_std` environments. This requires +//! 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))] @@ -64,6 +71,10 @@ //! # Ok::<(), tinywasm::Error>(()) //! ``` //! +//! For non-default runtime behavior, construct a [`Store`] with a custom [`Engine`] +//! and [`engine::Config`] to control stack sizing, fuel accounting, memory backends, +//! and trap-on-OOM behavior. +//! //! For more examples, see the [`examples`](https://github.com/explodingcamera/tinywasm/tree/main/examples) directory. //! //! ## Imports diff --git a/crates/tinywasm/tests/internal_refs.rs b/crates/tinywasm/tests/internal_refs.rs index eb0a7c2..9e96d4c 100644 --- a/crates/tinywasm/tests/internal_refs.rs +++ b/crates/tinywasm/tests/internal_refs.rs @@ -3,7 +3,7 @@ use tinywasm::types::{FuncRef, WasmValue}; use tinywasm::{ExternItem, ModuleInstance, Store}; #[test] -#[cfg(feature = "guest_debug")] +#[cfg(feature = "guest-debug")] fn private_items_are_accessible_by_index() -> Result<()> { let wasm = wat::parse_str( r#" diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 77e0be7..9f2dc8c 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -1,7 +1,7 @@ [package] name="tinywasm-types" version.workspace=true -description="TinyWasm types" +description="Shared runtime, module, and archive types for TinyWasm" edition.workspace=true license.workspace=true authors.workspace=true @@ -9,6 +9,7 @@ repository.workspace=true rust-version.workspace=true keywords.workspace=true categories.workspace=true +readme="README.md" [dependencies] log={workspace=true, optional=true} diff --git a/crates/types/README.md b/crates/types/README.md index 5a4431e..315a340 100644 --- a/crates/types/README.md +++ b/crates/types/README.md @@ -1,3 +1,5 @@ # `tinywasm-types` -This crate contains the types used by the [`tinywasm`](https://crates.io/crates/tinywasm) crate. It is also used by the [`tinywasm-parser`](https://crates.io/crates/tinywasm-parser) crate to parse WebAssembly binaries. +This crate contains the shared module, instruction, value, and archive types used by [`tinywasm`](https://crates.io/crates/tinywasm) and [`tinywasm-parser`](https://crates.io/crates/tinywasm-parser). + +Most users should depend on `tinywasm` directly. This crate is useful when you need to work with parsed modules, serialized `twasm` archives, or shared type definitions without pulling in the runtime. |
