summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/cli/Cargo.toml3
-rw-r--r--crates/cli/README.md2
-rw-r--r--crates/cli/src/args.rs9
-rw-r--r--crates/cli/src/bin.rs10
-rw-r--r--crates/parser/Cargo.toml3
-rw-r--r--crates/parser/README.md19
-rw-r--r--crates/tinywasm/Cargo.toml8
-rw-r--r--crates/tinywasm/src/instance.rs22
-rw-r--r--crates/tinywasm/src/interpreter/num_helpers.rs12
-rw-r--r--crates/tinywasm/src/interpreter/simd/utils.rs8
-rw-r--r--crates/tinywasm/src/lib.rs45
-rw-r--r--crates/tinywasm/tests/internal_refs.rs2
-rw-r--r--crates/types/Cargo.toml3
-rw-r--r--crates/types/README.md4
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.