Rust bindings for the Fizzy WebAssembly interpreter.
Fizzy is a small, fast, deterministic interpreter implementing the WebAssembly
1.0 (MVP) specification. fizzyx wraps Fizzy's C API in a safe, ergonomic Rust
interface inspired by wasmi and wasmtime.
| Crate | Description |
|---|---|
fizzyx |
Safe, high-level API (Engine, Module, Linker, Instance, Func, …). |
fizzyx-sys |
Low-level unsafe FFI bindings generated with bindgen. |
Most users only need fizzyx.
use fizzyx::{Engine, Linker, Module, Val};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let wat = r#"(module (func (export "add") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add))"#;
let wasm = wat::parse_str(wat)?;
let engine = Engine::default();
let module = Module::new(&wasm)?;
let linker = Linker::new(&engine);
let mut instance = linker.instantiate(&module)?;
let add = instance.get_func("add").expect("missing export");
let mut results = [Val::I32(0)];
add.call(&mut instance, &[Val::I32(1), Val::I32(2)], &mut results)?;
assert_eq!(results[0], Val::I32(3));
Ok(())
}Host functions are defined on the Linker before instantiation:
use fizzyx::{FuncType, Val, ValType};
linker.func_new(
"env",
"mul",
FuncType::new([ValType::I32, ValType::I32], [ValType::I32]),
|params, results| {
let a = params[0].i32().unwrap();
let b = params[1].i32().unwrap();
results[0] = Val::I32(a * b);
},
)?;The API follows Fizzy's own model instead of forcing an exact match:
- At most one result. Fizzy targets WebAssembly 1.0, so functions return zero or one value.
- No
Store. Fizzy instances are self-contained — they own their memory and globals — so there is no separate store. Methods that mutate an instance take&mut Instancedirectly. - Instantiate-time imports. Imports are resolved by
module::namewhen a module is instantiated through aLinker. Only function imports are currently supported; instantiating a module that imports a memory, table, or global fails. - No fuel/metering. Fizzy's metered execution context is not yet exposed.
fizzyx-sys builds Fizzy from source, vendored as a git submodule. After cloning:
git submodule update --init --recursive
cargo buildRequirements:
- A C++17 toolchain and CMake (≥ 3.15) to build Fizzy.
- libclang for
bindgen.
Fizzy stores its byte buffers as std::basic_string<uint8_t>. Modern libc++
(≥ 19, e.g. the Xcode 16 SDK) no longer provides std::char_traits<unsigned char>,
so the build script force-includes a small shim
(crates/fizzyx-sys/shim/char_traits_shim.hpp)
that supplies it. The shim is a no-op on libstdc++.
Fizzy's repository root ships its own Cargo.toml, which makes Cargo treat the
submodule as a foreign package and exclude it from published tarballs. So
fizzyx-sys builds and publishes from a committed, Cargo.toml-free mirror of the
sources it needs at crates/fizzyx-sys/fizzy-vendored/.
build.rs regenerates that mirror from the submodule on every build, so you never
edit it by hand.
To update Fizzy:
git -C crates/fizzyx-sys/fizzy checkout <new-commit> # bump the submodule
cargo build -p fizzyx-sys # regenerates fizzy-vendored/
git add crates/fizzyx-sys/fizzy crates/fizzyx-sys/fizzy-vendored
git commit -m "update Fizzy"CI fails if fizzy-vendored/ is out of sync with the submodule.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option. The vendored Fizzy sources are licensed under Apache-2.0.