Skip to content

builtins.wasm: Support WAT, add a test#405

Merged
edolstra merged 1 commit intomainfrom
wasm-improvements
Apr 1, 2026
Merged

builtins.wasm: Support WAT, add a test#405
edolstra merged 1 commit intomainfrom
wasm-improvements

Conversation

@edolstra
Copy link
Copy Markdown
Collaborator

@edolstra edolstra commented Mar 30, 2026

Motivation

This adds support for executing Wasm modules in textual format, which is mostly useful for testing.

Context

Summary by CodeRabbit

  • New Features
    • Added WebAssembly Text (WAT) format support to builtins.wasm, enabling inline module definitions alongside existing binary module support.
    • Module specification now accepts either path or wat attributes (mutually exclusive) for flexible module input methods.
    • Improved error messages and logging to better reflect module sources and execution context.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

This PR extends the wasm builtin to accept WebAssembly modules in text (WAT) format alongside binary paths. The NixWasmInstancePre constructor was refactored to compile from raw module bytes through a new compile method, with a human-readable name field added for error reporting. New functional tests validate the feature using a recursive fibonacci implementation.

Changes

Cohort / File(s) Summary
Wasm builtin core implementation
src/libexpr/primops/wasm.cc
Refactored NixWasmInstancePre to accept raw WASM bytes via new compile(std::span<uint8_t>) method. Added NixWasmInstancePre(std::string_view wat) overload for WAT-to-WASM conversion. Extended prim_wasm to accept either path or wat attributes (mutually exclusive). Updated logging and error messages to use pre->name instead of removed wasmPath field.
Wasm functional test suite
tests/functional/meson.build, tests/functional/wasm.sh
Added wasm.sh to test suite. New test script conditionally executes when builtin is available, validating both WAT inline and binary WASM module execution with recursive fibonacci evaluation (expected result: 165580141).
Test WebAssembly module
tests/functional/fib.wat
New WAT module implementing recursive fibonacci function with host function imports (env.get_int, env.make_int) and required nix_wasm_init_v1 initialization hook.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Evaluator as Nix Evaluator
    participant PrimWasm as prim_wasm Handler
    participant WAT as WAT to WASM
    participant Compiler as WASM Compiler
    participant Instance as NixWasmInstancePre

    User->>Evaluator: builtins.wasm { wat = "...", function = "fib" }
    Evaluator->>PrimWasm: Process builtin call
    PrimWasm->>PrimWasm: Detect 'wat' attribute
    PrimWasm->>WAT: Convert WAT text to bytes
    WAT-->>PrimWasm: WASM bytecode
    PrimWasm->>Compiler: compile(bytes)
    Compiler->>Instance: Create NixWasmInstancePre(name)
    Instance-->>Compiler: Compiled module
    Compiler-->>PrimWasm: Ready instance
    PrimWasm->>Instance: Instantiate & invoke function
    Instance-->>PrimWasm: Function result
    PrimWasm-->>User: Return value
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #309: Introduces wasm builtin support; this PR extends that with WAT format support and refactors the instantiation path.
  • PR #359: Modifies NixWasmInstancePre and instantiation logic in wasm.cc; overlapping structural changes warrant cross-review.
  • PR #370: Refactors pre-instantiation and module instantiation flow in wasm.cc; concurrent changes to same functions need coordination.

Suggested reviewers

  • cole-h
  • grahamc
  • RossComputerGuy

Poem

🐰 A bunny hops through bytes and text,
WAT becomes WASM—what comes next?
Fibonacci bounces, swift and bright,
From inline WAT to module might!
Compile, instantiate, run with glee,
The wasm builtin wild and free! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'builtins.wasm: Support WAT, add a test' clearly and concisely summarizes the main changes: adding WAT format support to builtins.wasm and introducing a test for it, which aligns perfectly with the changeset across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wasm-improvements

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

@github-actions github-actions bot temporarily deployed to pull request March 30, 2026 14:20 Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/libexpr/primops/wasm.cc (2)

704-710: Document the non-WASI ABI alongside the new WAT example.

The example makes wat plus function look sufficient, but hand-written non-WASI modules also need the exported memory and nix_wasm_init_v1 hook. Calling that out here would save users from a confusing first failure.

Also applies to: 723-729

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libexpr/primops/wasm.cc` around lines 704 - 710, Update the doc text in
src/libexpr/primops/wasm.cc around the WAT example to explicitly document the
non-WASI ABI requirements: state that when using `wat` + `function` for non-WASI
modules you must also export a linear `memory` and provide the
`nix_wasm_init_v1` initialization hook (and that `function` is not allowed for
WASI modules); add the same clarification to the other block around lines
723-729 so both examples mention the exported `memory` and `nix_wasm_init_v1`
hook alongside `wat`/`function` and the mutual-exclusion of `path`/`wat`.

693-694: Keep the selected module source in the outer trace.

Malformed wat or invalid path failures still get wrapped as just "while executing a Wasm module". Including the chosen source here (basename(path) or <inline wat>) would make the new compile-time failure path much easier to diagnose.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libexpr/primops/wasm.cc` around lines 693 - 694, The trace added before
rethrowing in wasm.cc currently uses e.addTrace(state.positions[pos], "while
executing a Wasm module"); change that to include the actual selected module
source (e.g. basename(path) when a file was used or "<inline wat>" when inline
WAT was provided) so the outer trace shows which module was being
compiled/executed; modify the code around e.addTrace/state.positions[pos] to
build a descriptive message like "while executing Wasm module: <module-source>"
using the existing path/wat variables and then call e.addTrace with that message
before throw.
tests/functional/wasm.sh (1)

10-17: Use a smaller input for this smoke test.

tests/functional/fib.wat is the naive recursive algorithm, so 40 makes this check exponentially expensive and you run it twice. A much smaller value still exercises WAT parsing and Wasm/Nix interop without stretching suite time on slower builders.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/functional/wasm.sh` around lines 10 - 17, The test uses the naive
recursive fib implementation with input 40 twice (once for WAT via builtins.wasm
{ wat = readFile ./fib.wat; function = "fib"; } and once for binary via path =
./fib.wasm), which is expensive; change the argument from 40 to a much smaller
number (e.g., 10 or 15) in both occurrences so the WAT parsing and Wasm/Nix
interop are exercised without long runtime while keeping the expected result
consistent for function "fib".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/libexpr/primops/wasm.cc`:
- Around line 704-710: Update the doc text in src/libexpr/primops/wasm.cc around
the WAT example to explicitly document the non-WASI ABI requirements: state that
when using `wat` + `function` for non-WASI modules you must also export a linear
`memory` and provide the `nix_wasm_init_v1` initialization hook (and that
`function` is not allowed for WASI modules); add the same clarification to the
other block around lines 723-729 so both examples mention the exported `memory`
and `nix_wasm_init_v1` hook alongside `wat`/`function` and the mutual-exclusion
of `path`/`wat`.
- Around line 693-694: The trace added before rethrowing in wasm.cc currently
uses e.addTrace(state.positions[pos], "while executing a Wasm module"); change
that to include the actual selected module source (e.g. basename(path) when a
file was used or "<inline wat>" when inline WAT was provided) so the outer trace
shows which module was being compiled/executed; modify the code around
e.addTrace/state.positions[pos] to build a descriptive message like "while
executing Wasm module: <module-source>" using the existing path/wat variables
and then call e.addTrace with that message before throw.

In `@tests/functional/wasm.sh`:
- Around line 10-17: The test uses the naive recursive fib implementation with
input 40 twice (once for WAT via builtins.wasm { wat = readFile ./fib.wat;
function = "fib"; } and once for binary via path = ./fib.wasm), which is
expensive; change the argument from 40 to a much smaller number (e.g., 10 or 15)
in both occurrences so the WAT parsing and Wasm/Nix interop are exercised
without long runtime while keeping the expected result consistent for function
"fib".

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ed2126a1-78bf-48e3-9df1-b2edda35ad98

📥 Commits

Reviewing files that changed from the base of the PR and between cb9989b and 52eb36a.

⛔ Files ignored due to path filters (1)
  • tests/functional/fib.wasm is excluded by !**/*.wasm
📒 Files selected for processing (4)
  • src/libexpr/primops/wasm.cc
  • tests/functional/fib.wat
  • tests/functional/meson.build
  • tests/functional/wasm.sh

@edolstra edolstra added this pull request to the merge queue Apr 1, 2026
Merged via the queue into main with commit 7f10286 Apr 1, 2026
28 checks passed
@edolstra edolstra deleted the wasm-improvements branch April 1, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants