wasmtime-mojo#
Mojo FFI bindings for the Wasmtime WebAssembly runtime.
Provides high-level RAII wrappers around the Wasmtime C API, enabling direct execution of WebAssembly modules from Mojo without requiring Python interop. The library loads libwasmtime.so dynamically at runtime via Mojo's DLHandle.
How it works#
The package mirrors Wasmtime's C struct layouts in Mojo using UInt64 fields for correct 8-byte alignment (avoiding SIMD alignment issues), then exposes typed wrappers on top:
libwasmtime.so (C API)
└─ _lib.mojo — DLHandle loader + raw FFI function wrappers
└─ _types.mojo — C struct definitions (WasmtimeVal, WasmtimeExtern, …)
└─ engine.mojo — RAII Engine (compilation settings, module caching)
└─ store.mojo — RAII Store (runtime state isolation) + Context
└─ module.mojo — RAII Module (compile, serialize, deserialize)
└─ linker.mojo — RAII Linker (host imports, instantiation)
└─ instance.mojo — Free-standing helpers (export lookup, calls, memory, globals)
The shared library is discovered automatically from NIX_LDFLAGS (set by the Nix dev shell), making it work seamlessly in Nix-based environments.
Project structure#
wasmtime-mojo/
└── src/
└── wasmtime_mojo/
├── __init__.mojo # Package root — re-exports all public API
├── engine.mojo # Engine: compilation settings, optional file-based module cache
├── store.mojo # Store + Context: runtime state isolation
├── module.mojo # Module: compile from WASM bytes, serialize/deserialize
├── linker.mojo # Linker: define host function imports, instantiate modules
├── instance.mojo # Export lookup, function calls, memory & global access helpers
├── _types.mojo # C struct type definitions matching Wasmtime memory layout
└── _lib.mojo # DLHandle loader + thin typed wrappers over the C API
API overview#
Core types#
| Type | Description |
|---|---|
Engine |
Top-level compilation environment. Supports optional file-based module caching (Engine(cache=True)) for fast reloads across processes. |
Store |
Unit of isolation — holds all runtime state (memories, globals, tables). Provides a context() handle for runtime operations. |
Module |
A compiled WebAssembly module. Created from raw .wasm bytes. Supports serialize()/deserialize_file() for pre-compiled .cwasm files. |
Linker |
Defines host-provided imports and instantiates modules. Use define_func() to register host callbacks, then instantiate() to produce an instance. |
Instance helpers#
| Function | Description |
|---|---|
instance_get_func |
Look up an exported function by name |
instance_get_memory |
Look up an exported memory by name |
instance_get_global |
Look up an exported global by name |
func_call |
Call a WASM function with List[WasmtimeVal] args and results |
func_call_i32 / func_call_i64 / func_call_f32 / func_call_f64 |
Typed single-return-value call helpers |
func_call_0 |
Call a WASM function that returns no values |
global_get_i32 / global_get_i64 |
Read typed values from WASM globals |
memory_read_bytes / memory_write_bytes |
Read/write byte slices in linear memory |
memory_read_i32_le / memory_write_i32_le |
Little-endian i32 memory access |
memory_read_i64_le / memory_write_i64_le |
Little-endian i64 memory access |
Value constructors#
WasmtimeVal provides static constructors for building typed WASM values:
WasmtimeVal.from_i32(value)/WasmtimeVal.from_i64(value)WasmtimeVal.from_f32(value)/WasmtimeVal.from_f64(value)
Usage#
from wasmtime_mojo import Engine, Store, Module, Linker
from wasmtime_mojo import instance_get_func, func_call_i32, WasmtimeVal
# Set up the runtime
var engine = Engine(cache=True)
var store = Store(engine.ptr())
var linker = Linker(engine.ptr())
# Compile a WASM module from bytes
var wasm_bytes = read_wasm_file("module.wasm")
var module = Module(engine.ptr(), wasm_bytes)
# Instantiate (linker resolves imports)
var instance = linker.instantiate(store.context(), module.ptr())
# Call an exported function
var add = instance_get_func(store.context(), instance, "add_int32")
var result = func_call_i32(
store.context(),
add,
List[WasmtimeVal](WasmtimeVal.from_i32(40), WasmtimeVal.from_i32(2)),
)
print(result) # 42
To define host function imports before instantiation:
from wasmtime_mojo import WASM_I32, WASM_I64
fn my_host_func(
env: UnsafePointer[NoneType],
caller: UnsafePointer[NoneType],
args: UnsafePointer[WasmtimeVal],
nargs: Int,
results: UnsafePointer[WasmtimeVal],
nresults: Int,
) -> UnsafePointer[NoneType]:
results[] = WasmtimeVal.from_i64(args[].get_i32().cast[DType.int64]())
return UnsafePointer[NoneType]() # null = success (no trap)
linker.define_func(
"env",
"widen_i32",
List[UInt8](WASM_I32), # parameter types
List[UInt8](WASM_I64), # result types
my_host_func,
)
Prerequisites#
Enter the dev shell (requires Nix):
nix develop .#wasm-mojo
This provides mojo and libwasmtime with the shared library path exported in NIX_LDFLAGS.