A slow and insecure runtime for WebAssembly
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.
moon install Milky2018/wasmoon/cmd/wasmoon
moon install Milky2018/wasmoon/cmd/wasmoon-toolsgit clone https://github.com/Milky2018/wasmoon.git
cd wasmoon
./install.shmoon add Milky2018/wasmoonpython3 scripts/smith_diff/run.py run --count 1000# Run with default _start function
wasmoon run hello.wat
# Call a specific function with arguments
wasmoon run examples/add.wat --invoke add --arg 5 --arg 3
# Run with interpreter (no JIT)
wasmoon run examples/add.wat --invoke add --arg 5 --arg 3 --no-jit
# Run WAST test scripts
wasmoon test spec/i32.wast
# Run component-model .wast tests (validation-only; requires wasm-tools)
python3 scripts/run_component_wast.py --dir component-spec --rec
# Explore compilation stages (IR, VCode, machine code)
wasmoon explore examples/add.wat --stage ir vcode mc# Validate a core Wasm module (WASM/WAT)
wasmoon-tools validate examples/add.wat
# Convert between WASM and WAT
wasmoon-tools wasm2wat examples/add.wasm -o examples/add.wat
wasmoon-tools wat2wasm examples/add.wat -o examples/add.wasm
# Parse WIT (text) and print a normalized representation
wasmoon-tools wit path/to/foo.wit
# Parse a directory as a WIT package (all *.wit files, sorted by filename),
# resolve it with `deps/`, and print a canonicalized output (toplevel-use removed,
# include flattened, and transitive interface imports injected).
wasmoon-tools wit path/to/pkgdir
# Emit the resolved WIT graph to a directory (root + deps/*.wit)
wasmoon-tools wit path/to/pkgdir --out-dir out
# Emit a JSON AST (stable, for scripting/debugging)
wasmoon-tools wit path/to/foo.wit --json
# Encode a WIT package as a component (type-only) binary / text
wasmoon-tools wit path/to/foo.wit --wasm -o foo.wasm
wasmoon-tools wit path/to/foo.wit --wat > foo.wat
# Alias for compatibility with wasm-tools' subcommand shape
wasmoon-tools component wit path/to/foo.wit --json///|
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)]")
}