0.3.0 — five new proposals, W3C testsuite runner, 13 bug fixes
Second Maven Central release. Adds five new WebAssembly proposals on top of 0.1.1's MVP + first-generation post-MVP set, ships the W3C testsuite runner, fixes a dozen real interpreter bugs surfaced along the way, and brings full strict-validation conformance across the wasm-3.0 binary format. Same zero-runtime-dependency surface, same three-platform cross-build (JVM / Scala.js / Scala Native).
Install
libraryDependencies ++= Seq(
"io.github.edadma" %%% "wasm" % "0.3.0",
"io.github.edadma" %%% "wasm-wasi" % "0.3.0", // optional — only if you want the WASI shim
)| Coordinate | What you get |
|---|---|
io.github.edadma:wasm:0.3.0 |
The interpreter — Runtime.instantiate, ModuleInstance |
io.github.edadma:wasm-wasi:0.3.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.1.1
Five new proposals
- Exception handling — legacy form (Phase 7.E):
try/catch tagidx/catch_all/throw/rethrow/delegateplus a new Section 13 (Tag) and import/export kind 0x04. Uncaught throws surface asWasmError.UncaughtException(tagIdx, args). - Exception handling — modern try_table form: opcode 0x1F with all four catch shapes, plus the
exnrefvalue type (wire byte 0x69) andthrow_ref. Both EH forms coexist; the validator and runtime share one tag-dispatch path. - Tail-call proposal:
return_callandreturn_call_indirect, with proper stack-frame replacement (not just a "do a regular call and return" shim) so deeply tail-recursive programs run in constant stack. - Relaxed SIMD (Phase 8.F): all 20 sub-opcodes (0x100..0x113) —
i32x4.relaxed_dot_i8x16_i7x16_add_sand friends. Implementation matches the deterministic-where-possible side of the spec. - Threads / atomics proposal: 66 atomic ops covering load / store / rmw (add, sub, and, or, xor, xchg, cmpxchg) at every width (8 / 16 / 32 / 64) for both i32 and i64, plus
memory.atomic.wait32/wait64/notify/fenceand shared memory (limits flag bit 0x02). Single-thread semantics:waiton a matching value traps as "would block".
Spec proposal landings on the imports/globals side
- Imported globals (kind 0x03):
GlobalImportin the module model, resolved at instantiation from a newHostGlobalvalue onHostModule.globals: Map[String, HostGlobal]. - Extended-const proposal:
i32.add/i32.sub/i32.mul/i64.add/i64.sub/i64.mulare now legal inside a constant expression. The parser flattens the wire-format stack sequence into an expression tree; validator and runtime walk it recursively. - Relaxed const-expr (wasm-3.0 GC-proposal relaxation):
global.get Nin a const-expr can reference any earlier-defined immutable global, not just imports. Active data and element segment offsets accept the same forms.
W3C testsuite runner
A new JVM-only integrated runner consumes wast2json JSON manifests + .wasm blobs and dispatches assert_return / assert_trap / assert_invalid / assert_malformed against Runtime.instantiate + inst.invoke. The curated slice covers 142 manifests / ~53,000 assertions — the full SIMD proposal, bulk memory + tables + element segments, EH and tail-call proposals, binary-format and UTF-8 edge cases. 129 manifests fully green; 13 pinned in KnownFailures with documented feature gaps. See Spec compliance for the full table.
Thirteen interpreter bugs fixed (each surfaced by the runner)
i32.trunc_f64_sover-rejected values strictly between-2^31and-2^31 - 1(off-by-one range check).MemArg.offsetwasInt, so a wasm u32 offset of0xFFFFFFFFwas stored as Java-1and the OOB trap silently wrapped to low memory. Widened toLong.alignimmediate validation was missing for plain load / store (only the atomic path enforced it).if-without-else accepted mismatched params/results (the implicit empty else-branch needsstartTypes == endTypes).v128.constwasn't accepted in const expressions ((global v128 (v128.const ...))failed to parse).- Untyped
select(0x1B) rejected v128 operands — SIMD treats v128 as a numtype for select. i8x16.popcntwas completely unimplemented.i16x8.q15mulr_sat_swas completely unimplemented (only the relaxed-SIMD variant was present).try_tablecatch labels counted with the try_table on the label stack — off-by-one in both the validator and the runtime's throw-dispatch.- Export-section validation was missing entirely — duplicate names silently shadowed each other, and out-of-range idx for any kind instantiated.
- Name-field UTF-8 validation was missing —
new String(bytes, "UTF-8")silently replaced bad bytes with U+FFFD. Now strict RFC 3629. - Seven small binary-format strictness rules — LEB128 minimal-encoding range checks (u32 / s32 / s64), section ID range, section order + uniqueness (Tag and DataCount have non-monotonic numeric IDs), section size mismatch, custom-section name overruns size, too-many-locals u32 overflow.
- Imported globals + extended-const + relaxed const-expr were not surfaced — three spec features that travel together, now landing as one drop with full validator + runtime support.
Other additions
- Tracer hooks for opcode dispatch, function-frame transitions, throws, and traps. No-op default keeps zero overhead for the common case.
Numbers
- 870 tests on the JVM (648 interpreter + 208 WASI + 14 CLI), all green.
- 648 + 208 also green on Scala.js (Node 20+) and Scala Native (0.5.11).
- 142 W3C manifests in the spec runner / ~53,000 assertions / 51,084 passing.
- Deterministic across all three platforms: every
i32/i64/f32/f64opcode produces bit-identical results, including IEEE-754 edge cases.
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:
mkdir -p /tmp/sandbox
echo "Hello from the host filesystem" > /tmp/sandbox/hello.txt
sbt 'cliJVM/run --preopen /tmp/sandbox:/sandbox \
wasi/shared/src/test/resources/fixtures/real_rust_fileread.wasm'
# Hello from the host filesystemDocumentation
Full docs at https://edadma.github.io/wasm/:
- Getting Started — install + run your first module
- Concepts — the validator, host imports, traps and errors
- WASI — the 29 syscalls implemented and the three preopen flavours
- CLI —
--preopen,--invoke,--args, dispatch rules - Reference — supported opcodes, binary sections, error variants
- Spec compliance — W3C testsuite slice, what the runner caught, what's pinned
License
ISC.