Skip to content

Commit 3110b49

Browse files
fix(codegen-deno): WASI stub in __as_wasmInstance — instantiate affinescript-produced wasm (#534)
## What The Deno-ESM backend's `wasmInstance` lowering (`__as_wasmInstance` in `lib/codegen_deno.ml`) instantiated with **no import object**: ```js new WebAssembly.Instance(new WebAssembly.Module(bytes)).exports; ``` But every affinescript-produced wasm imports `wasi_snapshot_preview1.fd_write` **unconditionally** (even a trivial pure-integer module with no print/panic path), so the import-less call throws `WebAssembly.Instance(): Imports argument must be present and must be an object`. Net effect: the Deno-ESM backend could not load *any* affinescript-compiled wasm. The Node backend (`codegen_node.ml:251-273`) already wires this stub — this gives the Deno-ESM backend the same one line: ```diff const __as_wasmInstance = (bytes) => - new WebAssembly.Instance(new WebAssembly.Module(bytes)).exports; + new WebAssembly.Instance(new WebAssembly.Module(bytes), + { wasi_snapshot_preview1: { fd_write: () => 0 } }).exports; ``` ## Regression test `tests/codegen-deno/wasm_wasi_instance.{affine,harness.mjs}` — the harness hand-builds an 86-byte wasm that **imports** `wasi_snapshot_preview1.fd_write` (declared, never called) and exports `add(i32,i32)->i32`, then drives it through the `wasmInstance` lowering. It passes **only** when the stub is present (without it, instantiation throws). Mirrors the existing `wasm_call` fixture (#414), which used an import-free module and so never exercised this path. ## Validation Built from source on this branch (`dune build`), `./tools/run_codegen_deno_tests.sh` green including the new fixture; `wasm_wasi_instance.harness.mjs OK`. Separately confirmed the bytes instantiate **with** the stub (`add(2,3)=5`) and throw **without** it. ## Why it matters Unblocks the Deno-ESM differential-test path downstream — e.g. `hyperpolymath/idaptik`'s evacuation of its ~87 `*_diff.ts` parity harnesses to AffineScript, which load affinescript-compiled co-processor wasm via this backend. (Verified end-to-end there: a from-source build with this fix runs an AffineScript harness GREEN 1309/1309.) ## Caveat ⚠️ Commit is **DCO signed-off but not GPG-verified** (no signing key in the remote execution container). Re-sign locally before merge if the branch rule requires it. https://claude.ai/code/session_01PhqGcxCqkMdJtR6NWq56Hx --- _Generated by [Claude Code](https://claude.ai/code/session_01PhqGcxCqkMdJtR6NWq56Hx)_ Signed-off-by: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com>
1 parent f3ef056 commit 3110b49

3 files changed

Lines changed: 58 additions & 1 deletion

File tree

lib/codegen_deno.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ const __as_walkRecursive = (root) => {
191191
};
192192
const __as_regexMatch = (s, pat) => new RegExp(pat).test(String(s));
193193
const __as_wasmInstance = (bytes) =>
194-
new WebAssembly.Instance(new WebAssembly.Module(bytes)).exports;
194+
new WebAssembly.Instance(new WebAssembly.Module(bytes),
195+
{ wasi_snapshot_preview1: { fd_write: () => 0 } }).exports;
195196
const __as_wasmCall = (exports, name, args) => Number(exports[name](...(args || [])));
196197
// ---- WasmValue (Deno.affine #455Tier 1 #5, Option B) ----
197198
// Opaque tagged value crossing the AS/JS boundary as `{ kind, v }`.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// Regression for the Deno-ESM WASI-stub fix: `__as_wasmInstance`
3+
// (lib/codegen_deno.ml) must be able to instantiate a wasm that imports
4+
// `wasi_snapshot_preview1.fd_write` — which EVERY affinescript-compiled wasm
5+
// does, unconditionally — and not only an import-free module. Before the fix
6+
// the shim instantiated with no import object and threw "Imports argument must
7+
// be present and must be an object", so the deno-esm backend could not load any
8+
// affinescript-produced wasm. Companion to wasm_call.affine (closes #414); this
9+
// fixture exercises the wasmInstance lowering against a WASI-importing module.
10+
11+
use Deno::{ Bytes, WasmExports, wasmInstance, wasmCall };
12+
13+
pub fn addViaWasiWasm(bytes: Bytes, a: Float, b: Float) -> Float {
14+
let exports = wasmInstance(bytes);
15+
wasmCall(exports, "add", [a, b])
16+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// Node-ESM harness for the WASI-stub regression (wasm_wasi_instance.affine).
3+
//
4+
// Instantiates an 86-byte wasm module that IMPORTS
5+
// `wasi_snapshot_preview1.fd_write` (declared, never called) and exports
6+
// `add(i32,i32)->i32` — the same import shape every affinescript-compiled wasm
7+
// carries. `addViaWasiWasm` lowers to `__as_wasmInstance(bytes)` followed by
8+
// `__as_wasmCall(...)`; the call succeeds only when `__as_wasmInstance` supplies
9+
// the `{ wasi_snapshot_preview1: { fd_write: () => 0 } }` import object. Before
10+
// the fix this threw "Imports argument must be present and must be an object".
11+
12+
import assert from "node:assert/strict";
13+
import { addViaWasiWasm } from "./wasm_wasi_instance.deno.js";
14+
15+
// (module
16+
// (import "wasi_snapshot_preview1" "fd_write"
17+
// (func (param i32 i32 i32 i32) (result i32))) ;; imported, never called
18+
// (func (export "add") (param i32 i32) (result i32)
19+
// local.get 0 local.get 1 i32.add))
20+
const wasmBytes = new Uint8Array([
21+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, // magic + version
22+
0x01, 0x0f, 0x02, // type section: 2 types
23+
0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, // (i32,i32,i32,i32)->i32 (fd_write)
24+
0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, // (i32,i32)->i32 (add)
25+
0x02, 0x23, 0x01, // import section: 1 import
26+
0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, // "wasi_snap...
27+
0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, // ...shot_previ...
28+
0x65, 0x77, 0x31, // ...ew1"
29+
0x08, 0x66, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, // "fd_write"
30+
0x00, 0x00, // kind=func, type 0
31+
0x03, 0x02, 0x01, 0x01, // function section: func 1 of type 1
32+
0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x01, // export "add" -> func index 1
33+
0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b, // code: local.get 0/1, i32.add
34+
]);
35+
36+
assert.equal(addViaWasiWasm(wasmBytes, 2, 3), 5, "addViaWasiWasm(2,3) == 5 (WASI-importing module instantiates)");
37+
assert.equal(addViaWasiWasm(wasmBytes, -1, 1), 0, "addViaWasiWasm(-1,1) == 0");
38+
assert.equal(addViaWasiWasm(wasmBytes, 100, 200), 300, "addViaWasiWasm(100,200) == 300");
39+
40+
console.log("wasm_wasi_instance.harness.mjs OK");

0 commit comments

Comments
 (0)