Third Maven Central release. Substantial drop on the wasm-3.0 conformance side — five spec manifests fully unlocked, the spec sweep passing count climbs by ~650, and the public host-import surface gains memories, tables, and live-cell mutable globals. CLI gets three new flags. Same zero-runtime-dependency profile, same three-platform cross-build (JVM / Scala.js / Scala Native).
Install
libraryDependencies ++= Seq(
"io.github.edadma" %%% "wasm" % "0.4.0",
"io.github.edadma" %%% "wasm-wasi" % "0.4.0", // optional — only if you want the WASI shim
)| Coordinate | What you get |
|---|---|
io.github.edadma:wasm:0.4.0 |
The interpreter — Runtime.instantiate, ModuleInstance |
io.github.edadma:wasm-wasi:0.4.0 |
The WASI Preview 1 host shim — depends on wasm |
The wasm-cli runner is built from this repo but not published; it's a runnable example, not a library.
What's new since 0.3.0
Compact-imports wire format
The wasm-3.0 testsuite emits a compact import-section encoding where a regular-looking import with field_name == "" and a kind byte of 0x7E (shared-kind across the group) or 0x7F (per-import-kind, sub-imports can mix) signals that the just-read mod_name is shared across a group of sub-imports. The 0x7E form is kind sub_count (field_name desc)*; the 0x7F form is sub_count (field_name kind desc)*. The outer count is the TOTAL imports across all groups — so the parser advances i by sub_count per compact group. Before 0.4.0 modules using the compact form failed at parse time with unknown import kind 0x7F.
Imported memories + tables
Parser silently skipped import kinds 0x01 (table) and 0x02 (memory) before 0.4.0; modules importing them then failed downstream when an instruction referenced a memidx or tableidx with "no memory" / "no table". Now:
MemoryImportandTableImportsurface in the module model alongsideGlobalImport.HostModule.memories: Map[String, Memory]andHostModule.tables: Map[String, RuntimeTable]are the new host-side maps. Both forward by reference — guest reads and writes hit the same backing array the host can inspect.- Limits checking at instantiation: host's current size ≥ module's declared min, host's max (if any) ≤ module's declared max (if any). Reftype match for tables; shared-vs-unshared match for memories.
Cross-module mutable-global sharing
Module storage for globals is now Array[GlobalCell] instead of Array[Value]. A GlobalCell is a tiny mutable holder; an imported mutable global installs the exporter's cell directly into the importing module's slot, so global.set from either side writes through the same storage — matching the wasm-3.0 spec's "imported mutable globals alias the exporter's storage" rule.
The host-import surface gains HostGlobal.live(vt, mut, cell) for sharing externally-owned cells; the existing HostGlobal(vt, mut, value) factory still works for the snapshot case (immutable globals + mutable globals the host doesn't need to observe).
New ModuleInstance accessors
Hosts forwarding one module's exports as another module's imports need richer introspection than globalValue / exportedMemory alone. Added:
exportedTable(name): Either[WasmError, RuntimeTable]exportedFunctionType(name): Either[WasmError, FuncType]exportedGlobalCell(name): Either[WasmError, GlobalCell]exportedGlobalMutability(name): Either[WasmError, Boolean]exportedMemoryNames/exportedTableNames/exportedGlobalNames(sorted enumeration helpers)
CLI flags
Three new flags on wasm-cli:
--trace— installsTracer.Countingand prints[trace] ops=N calls=N hostCalls=N throws=N traps=N maxDepth=Nto stderr after_start/main/--invokereturns.--validate-only— parses + validates the module and exits without instantiating. 0 onok, 1 with diagnostic. Useful as a CI lint step on generated wasm.--stdin <path>— redirects the guest's fd 0 to a host file. The file is read once and streamed byte-for-byte throughfd_readuntil exhausted.
WASI stdin reader
WasiContext gained a stdin: (Array[Byte], Int, Int) => Int field (POSIX-read shape). Default returns 0 bytes (EOF) so the historical "fd 0 returns EBADF" behaviour is gone — fd 0 now reads cleanly as empty input. WasiContext.stdinFromBytes(payload) builds a cursor-tracking reader from a byte array (used by the CLI's --stdin <path>).
Documentation
- README trimmed from ~285 lines to ~55 lines; detail moved into the juicer-rendered docs site.
- New
Development/section witharchitecture.md(sub-projects + source-tree layout) andtesting.md(unit suites, W3C testsuite runner, fixture regeneration).
Numbers
- 881 tests on the JVM (653 interpreter + 209 WASI + 19 CLI), all green.
- 653 + 209 also green on Scala.js (Node 20+) and Scala Native (0.5.11).
- 142 W3C manifests in the spec runner / ~53,000 assertions / 51,714 passing / 224 failing / 1,273 skipped.
- 133 of 142 manifests fully green (up from 129 in 0.3.0). 5 manifests fully unlocked:
names,exports,memory_grow,table_copy,table_grow. - 9 manifests pinned in
KnownFailures(down from 13 in 0.3.0). Residual causes are wasm-3.0 typed-function-references / GC reftype short forms (0x63/0x64) — a separate proposal we don't implement.
Cross-build matrix
| Target | Version | Runtime requirement |
|---|---|---|
| Scala | 3.8.3 | — |
| JVM | — | Java 17 or newer |
| Scala.js | 1.21.0 | Node.js 20+ |
| Scala Native | 0.5.11 | Clang |
Try it
git clone https://github.com/edadma/wasm
cd wasm
sbt 'cliJVM/run examples/hello.wasm'
# Hello, world!Or against a real WASI binary with a host preopen and the new --trace flag:
mkdir -p /tmp/sandbox
echo "Hello from the host filesystem" > /tmp/sandbox/hello.txt
sbt 'cliJVM/run --trace --preopen /tmp/sandbox:/sandbox \
wasi/shared/src/test/resources/fixtures/real_rust_fileread.wasm'
# Hello from the host filesystem
# [trace] ops=... calls=... hostCalls=... throws=... traps=... maxDepth=...Documentation
Full docs at https://edadma.github.io/wasm/:
- Getting Started — install + run your first module
- Concepts — the validator, host imports, traps and errors, the tracer
- WASI — the 29 syscalls implemented and the three preopen flavours
- CLI — every flag, dispatch rules, recipes
- Reference — supported opcodes, binary sections, error variants
- Spec compliance — W3C testsuite slice, what the runner caught, what's pinned
- Development — sub-project layout, test invocation, W3C testsuite + fixture regeneration
License
ISC.