A WebAssembly runtime written in MoonBit with JIT compilation support.
Warning: This project is primarily developed with AI assistance and has not been thoroughly audited. Do not use in production or security-sensitive environments.
Note: JIT optimization is actively improving. Performance depends on workload and platform; benchmark your target programs for an accurate comparison.
- JIT Compiler: AArch64 and amd64 native code generation with SSA-based IR
- Interpreter: Full WebAssembly 1.0 support as fallback
- WAT/WASM Parser: Parse both text and binary formats
- WASI Preview 1 Support: File I/O, environment variables, command-line arguments
- GC Proposal Support: i31/struct/array/ref operations in interpreter and JIT
- Component Model: Component parser/validator/runtime with component-spec runner support
- Required:
moonpython3
- Optional:
wasmtime(useful for differential/performance comparison workflows)
moon install Milky2018/wasmoon/cmd/wasmoon
moon install Milky2018/wasmoon/cmd/wasmoon-toolsIf your registry release does not expose these main packages yet, install from the Git repository directly:
moon install https://github.com/Milky2018/wasmoon.git cmd/wasmoon
moon install https://github.com/Milky2018/wasmoon.git cmd/wasmoon-toolsVerify binaries are on PATH:
wasmoon --help
wasmoon-tools --helpBy default these commands install binaries to ~/.moon/bin/ as:
wasmoon(runtime CLI)wasmoon-tools(utility CLI)
git clone https://github.com/Milky2018/wasmoon.git
cd wasmoon
./install.sh./install.sh uses moon install --path ... to install local binaries into
target/moon-install-bin/, then creates/updates two symlinks in repo root:
./wasmoon./wasmoon-tools
After code changes, re-run ./install.sh to refresh both symlinks.
moon add Milky2018/wasmoon# 1) Run with default _start
wasmoon run examples/add.wat
# 2) Invoke an export with arguments
wasmoon run examples/add.wat --invoke add --arg 5 --arg 3
# 3) Interpreter mode
wasmoon run examples/add.wat --invoke add --arg 5 --arg 3 --no-jit
# 4) WASI dirs/env/options
wasmoon run examples/hello_wasi.wat \
--dir . \
--env FOO=bar \
-S inherit-envFor detailed flags, run:
wasmoon run --help# run
wasmoon run examples/add.wat --invoke add --arg 1 --arg 2
wasmoon run --help
# test
wasmoon test spec/i32.wast
wasmoon test --help
# explore
wasmoon explore examples/add.wat --stage ir vcode mc
wasmoon explore --help
# component
wasmoon component path/to/component.wasm --validate
wasmoon component --help
# component-test
wasmoon component-test path/to/component-tests.json
wasmoon component-test --help
# wat
wasmoon wat examples/add.wat
wasmoon wat --help
# disasm
wasmoon disasm examples/stream.wasm
wasmoon disasm --helpQuick differential testing vs Wasmtime (wasm-smith):
python3 scripts/smith_diff/run.py run --count 1000Use these options when diagnosing JIT failures:
-D: debug logging--dump-on-trap: dump IR/VCode/MC for the trapping function-W: generate DWARF debug info for JIT code (better stack traces in LLDB)
Example:
wasmoon run examples/core_ed25519.wasm -D --dump-on-trap -WLLDB quick recipe:
lldb -- ./wasmoon run examples/core_ed25519.wasm
(lldb) run
(lldb) btwasmoon-tools provides common validation/conversion/WIT workflows:
# Validate a core Wasm module (WASM/WAT)
wasmoon-tools validate examples/add.wat
# Convert between WASM and WAT
wasmoon-tools wasm2wat examples/stream.wasm -o examples/stream.wat
wasmoon-tools wat2wasm examples/add.wat -o examples/add.wasm
# Parse WIT and print normalized text / JSON
wasmoon-tools wit path/to/foo.wit
wasmoon-tools wit path/to/foo.wit --json
# Resolve a directory package (with deps/) and emit graph
wasmoon-tools wit path/to/pkgdir
wasmoon-tools wit path/to/pkgdir --out-dir out
# Encode WIT package as component binary / text
wasmoon-tools wit path/to/foo.wit --wasm -o foo.wasm
wasmoon-tools wit path/to/foo.wit --wat > foo.wat
# Importize world flow
wasmoon-tools wit foo.wasm --importize --wat
wasmoon-tools wit path/to/pkgdir --importize-world my-world --wat
# Compatibility alias (wasm-tools-like shape)
wasmoon-tools component wit path/to/foo.wit --jsonWIT support is still evolving. Current wasmoon-tools wit supports parse/resolve
(deps/), JSON output, component encode/decode/importize workflows, and tested
non-scalar/resource-related cases. Some spec corners may still be unsupported.
moon check --target native
moon test --target native
./install.sh
python3 scripts/run_all_wast.py --dir spec --rec
python3 scripts/run_component_wast.py --dir component-spec --rec@jit.gc_setup(...) now requires VMContext + full function-table context for typed funcref/ref.func safety:
ctx_ptrfunc_type_indicesfunc_table_ptrnum_funcs
@jit.gc_teardown(...) also requires the same ctx_ptr.
GC runtime caches/heap references are now VMContext-local (no process-global GC cache state), aligned with Wasmtime-style per-store/per-context runtime isolation.
The API fails fast with GCSetupError when setup context is incomplete or inconsistent.
///|
test "basic add" {
let wat =
#|(module
#| (func (export "add") (param i32 i32) (result i32)
#| local.get 0
#| local.get 1
#| i32.add))
let mod = @wat.parse(wat)
let (store, instance) = @executor.instantiate_module(mod)
let result = @executor.call_exported_func(store, instance, "add", [
@types.Value::I32(5),
@types.Value::I32(3),
])
inspect(result, content="[I32(8)]")
}///|
test "memory" {
let wat =
#|(module
#| (memory (export "mem") 1)
#| (func (export "store") (param i32 i32)
#| local.get 0 local.get 1 i32.store)
#| (func (export "load") (param i32) (result i32)
#| local.get 0 i32.load))
let mod = @wat.parse(wat)
let (store, instance) = @executor.instantiate_module(mod)
@executor.call_exported_func(store, instance, "store", [
@types.Value::I32(0),
@types.Value::I32(42),
])
|> ignore
let result = @executor.call_exported_func(store, instance, "load", [
@types.Value::I32(0),
])
inspect(result, content="[I32(42)]")
}///|
test "cross-module" {
let linker = @runtime.Linker::new()
let mod_a =
#|(module (func (export "add") (param i32 i32) (result i32)
#| local.get 0 local.get 1 i32.add))
let mod_a = @wat.parse(mod_a)
let inst_a = @executor.instantiate_with_linker(linker, "math", mod_a)
linker.register("math", inst_a)
let mod_b =
#|(module
#| (import "math" "add" (func $add (param i32 i32) (result i32)))
#| (func (export "use_add") (param i32 i32) (result i32)
#| local.get 0 local.get 1 call $add))
let mod_b = @wat.parse(mod_b)
let inst_b = @executor.instantiate_with_linker(linker, "main", mod_b)
let result = @executor.call_exported_func(
linker.get_store(),
inst_b,
"use_add",
[@types.Value::I32(3), @types.Value::I32(5)],
)
inspect(result, content="[I32(8)]")
}///|
test "host function" {
let linker = @runtime.Linker::new()
// Register a host function that doubles an i32
linker.add_host_func(
"env",
"double",
fn(args) {
guard args[0] is @types.Value::I32(x) else { return [] }
[@types.Value::I32(x * 2)]
},
func_type={
params: [@types.ValueType::I32],
results: [@types.ValueType::I32],
},
)
let wat =
#|(module
#| (import "env" "double" (func $double (param i32) (result i32)))
#| (func (export "quadruple") (param i32) (result i32)
#| local.get 0 call $double call $double))
let mod = @wat.parse(wat)
let instance = @executor.instantiate_with_linker(linker, "main", mod)
let result = @executor.call_exported_func(
linker.get_store(),
instance,
"quadruple",
[@types.Value::I32(5)],
)
inspect(result, content="[I32(20)]")
}- WebAssembly 1.0 core specification
- JIT compiler (AArch64, amd64)
- Multi-value returns
- Reference types (funcref, externref)
- Tail calls
- Cross-module function calls
- WASI Preview 1 support
- GC proposal support (current implemented subset; experimental)
- Component Model support
- JIT optimizations (constant folding, dead code elimination, etc.)
- Edit
README.mbt.mdas the source of truth. README.mdis a symlink toREADME.mbt.md.
Apache-2.0