zig-js

WebAssembly wg-1.0 spec suite (packed runner)

On this page 5

This document describes the self-contained upstream spec runner (zig build wasm-spec) and its checked-in artifacts. A second, complementary harness — the live-WABT corpus evaluator (zig build wasm-spec-eval + tools/wasm-spec.ts) — is documented in wasm.md. Both pin the same upstream corpus; they differ in how NaN-boundary assertions are scored (see below).

Pin

  • Upstream: WebAssembly/spec tag wg-1.0 (pure MVP surface), commit 977f97014c962f7bd1291fcc6d28b41a924882bf.
  • Converter: checksum-pinned WABT 1.0.39 wat2wasm via Home-executed tools/wasm-spec/gen.ts. The generator normalizes modernized elem/data text forms and packs every binary module into tests/wasm/spec/modules.bin with a manifest.json directive index.

The wasm-spec-wg1 submodule is intentionally this frozen MVP fixture, not a checkout of upstream main. Core 2 and proposal corpora are separate, exact-pin profiles in the WebAssembly matrix. Stable Core 3 has its own wasm-spec-wg3 submodule at official tag wg-3.0; #366 tracks its terminal gate. Keeping the baselines separate makes published command counts reproducible. Newer upstream main snapshots run through the separate non-release shadow profile, with their own exact inventories instead of moving either stable gitlink.

Running

zig build wasm-spec                       # full suite, prints per-file + total
zig build wasm-spec -Dwasm-spec-filter=linking   # only matching files
zig build wasm-spec -Dwasm-spec-out=tests/wasm/spec/inventory.json

WASM_SPEC_DIR overrides the artifact directory. For CI compatibility the step also accepts -Dwasm-spec-inventory=<path> (alias of -Dwasm-spec-out) and -Dwast2json=<path> (unused — the packed artifacts already embed converter output, so no converter is needed at run time). The runner re-executes itself once per .wast file (WASM_SPEC_WORKER=<file>) for crash isolation: a worker that dies or exceeds the 120 s watchdog is recorded as a crash entry instead of aborting the inventory. Every directive executes through the real JavaScript WebAssembly API — no test-only engine hooks.

Current inventory

Checked in at tests/wasm/spec/inventory.json:

filespassfailskipcrash
7318,79104790

All 479 skips are classified — there are no hidden exclusions:

countclassrationale
430assert_malformed quote/text modulesThe wat text format is outside the binary runtime; a binary-only engine cannot express these.
40NaN payload/sign expectationsSetToNaN is implementation-defined at the JS Number boundary; the argument's NaN pattern cannot cross the JS API, so the expectation is unobservable. The eval harness in wasm.md scores these exactly through a test-only bit-exact path.
4generator rejectsWABT itself cannot parse these generated fragments (data.wast:315, data.wast:323, elem.wast:281, elem.wast:289).
5Core 2.0 policyThe engine deliberately implements Core 2.0 semantics (matching V8): transactional-instantiation visibility at linking.wast:236/248/342/354 and br_table polymorphic-bottom typing at unreached-invalid.wast:538. Each is locked by a dedicated exec/validate unit test.

Error-text aliases

The WebAssembly JS API fixes only the class of each failure (CompileError/LinkError/RuntimeError); diagnostic text is implementation-defined. The runner therefore carries a small, explicit alias table (6 compile-wording and 2 link-wording groups) mapping the reference interpreter's expected strings onto this engine's deterministic diagnostics; the error class always matches, and every alias was verified against the specific directives it unlocks. Notably the 32 "segment does not fit" directives surface as RuntimeError, matching V8.

Regenerating the artifacts

Only needed when the pin changes:

~/Code/Home/lang/zig-out/bin/home-tool run tools/wasm-spec/fetch.ts /tmp/spec-src /tmp/wabt
~/Code/Home/lang/zig-out/bin/home-tool run tools/wasm-spec/gen.ts \
  /tmp/spec-src/test/core tests/wasm/spec /tmp/wabt/bin/wat2wasm
zig build wasm-spec -Dwasm-spec-out=tests/wasm/spec/inventory.json

The fetcher verifies the release SHA-256 before extraction and verifies the converter reports version 1.0.39. The upstream macOS archive carries a Homebrew OpenSSL install name; on Pantry-managed hosts the fetcher relocates that install name to the installed Pantry libcrypto.3.dylib before the version check.

Commit all three regenerated files (manifest.json, modules.bin, inventory.json) together. The checked-in artifacts are the source of truth; CI and local runs never touch the network.