Skip to content

feat(stdlib): WasmValue + wasm_export_call typed wasm-exports binding (closes #455)#467

Merged
hyperpolymath merged 1 commit into
mainfrom
stdlib/wasm-export-call-455
May 30, 2026
Merged

feat(stdlib): WasmValue + wasm_export_call typed wasm-exports binding (closes #455)#467
hyperpolymath merged 1 commit into
mainfrom
stdlib/wasm-export-call-455

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Tier 1 #5 of the AS bindings top-50 roadmap (#446). Owner's #455-decided Option B: generic `wasm_export_call(exports, name, args: [WasmValue]) -> WasmValue` with `WasmValue` as a tagged scalar carrier covering all four wasm numeric kinds (i32, i64, f32, f64).

Encoding decision

`WasmValue` lands as an OPAQUE `pub extern type` rather than a true AS sum type. Rationale: the JS interop boundary needs a hand-written marshaller pairing `wv_i32(42) -> { kind: "i32", v: 42 }` with the export-call dispatch `__as_wasm_export_call(exports, name, args)`. Mirrors the existing `WasmExports` opaque pattern.

A true sum-type variant on top of this opaque base ships in a follow-up once `json.affine`-style tagged-variant codegen lands for the Deno-ESM backend. Per #455 "weaker static safety acknowledged trade-off; typed wrappers will land as a follow-up sub-issue once usage patterns crystallise."

What this PR ships

`stdlib/Deno.affine` (+87 lines)

  • `pub extern type WasmValue`
  • Constructors: `wv_i32` / `wv_i64` / `wv_f32` / `wv_f64`
  • Accessors: `wv_as_int` / `wv_as_float` / `wv_kind`
  • `wasm_export_call(exports, name, args: [WasmValue]) -> WasmValue`
  • Worked example: `addI32ViaWasm(bytes, a, b)` in the docstring

`lib/codegen_deno.ml` (+47 lines)

  • JS prelude: 8 `_as_wv*` / `__as_wasm_export_call` helpers
  • `deno_builtins` dispatch table: 8 entries
  • BigInt for i64 (preserves precision beyond 2^53); `Math.fround` for f32
  • Return wraps as f64 by default (lossless for any numeric); i64 returns detected via `typeof result === "bigint"` and wrapped as i64

What's deferred (per #455 implementation-scope breakdown)

Each independently shippable now that the Deno.affine surface is in place; will file follow-up tracking issues after merge:

  • Zig FFI implementation for the native backend
  • Idris2 ABI pattern doc (Zig=APIs/FFIs, Idris2=ABIs convention)
  • `examples/wasm-exports-demo.affine` end-to-end demo
  • Smoke-test through the Zig FFI

Owner-directive compliance

Test plan

  • CI build job (`opam exec -- dune build`) green
  • CI `dune runtest` green
  • CI `tools/run_codegen_deno_tests.sh` green (existing wasm tests stay unaffected)
  • Manual smoke: load a tiny wasm module emitting an `add(i32, i32) -> i32` export; call via `wasm_export_call` with `[wv_i32(2), wv_i32(3)]`; verify `wv_as_int(result)` returns 5. Defer to follow-up PR with example file + harness.

Refs

🤖 Generated with Claude Code

…closes #455)

Tier 1 #5 of the AS bindings top-50 roadmap (#446). Owner's #455-decided
Option B: generic `wasm_export_call(exports, name, args: [WasmValue]) ->
WasmValue` with WasmValue as a tagged scalar carrier covering all four
wasm numeric kinds (i32, i64, f32, f64).

Future-proof: covers any wasm signature including i64, multi-typed args,
future spec additions. No binding change required as wasm evolves. Tiny
addition vs Option A's ~30 per-signature variants. Typed wrappers can
be layered on top of this generic as ergonomic helpers in a follow-up
sub-issue.

## Encoding decision

WasmValue lands as an OPAQUE `pub extern type` rather than a true AS sum
type. Rationale: the JS interop boundary needs a hand-written marshaller
that pairs `wv_i32(42) -> { kind: "i32", v: 42 }` with the export-call
dispatch `__as_wasm_export_call(exports, name, args)`. Mirrors the
existing `WasmExports` opaque pattern in stdlib/Deno.affine. A true
sum-type variant on top of this opaque base ships in a follow-up once
json.affine-style tagged-variant codegen lands for the Deno-ESM backend.

## What this PR ships

- `stdlib/Deno.affine`: +87 lines
  * `pub extern type WasmValue`
  * `wv_i32` / `wv_i64` / `wv_f32` / `wv_f64` constructors
  * `wv_as_int` / `wv_as_float` / `wv_kind` accessors
  * `wasm_export_call(exports, name, args: [WasmValue]) -> WasmValue`
  * Worked example: `addI32ViaWasm(bytes, a, b)` in the docstring

- `lib/codegen_deno.ml`: +47 lines
  * JS prelude: 8 `__as_wv_*` / `__as_wasm_export_call` helpers
  * `deno_builtins` dispatch table: 8 entries
  * BigInt for i64 (preserves precision beyond 2^53); `Math.fround` for f32
  * Return wraps as f64 by default (lossless for any numeric); i64 returns
    detected via `typeof result === "bigint"` and wrapped as i64

## What's deferred (per owner's #455 implementation-scope breakdown)

- Zig FFI implementation for the native backend — separate PR
- Idris2 ABI pattern doc (Zig=APIs/FFIs, Idris2=ABIs convention) — separate PR
- `examples/wasm-exports-demo.affine` end-to-end demo — separate PR
- Smoke-test through the Zig FFI — separate PR

Each of these is independently shippable now that the Deno.affine surface
is in place. Will file follow-up tracking issues after this lands.

## Owner-directive compliance

- Adds 8 externs in the WebAssembly section adjacent to existing `wasmCall`.
- Adds 8 codegen dispatch entries in the existing `let () = ...` block.
- Pure additive change; no existing surface modified.
- Owner Option B confirmed in #455 comment 2026-05-30 13:18Z.

## Refs

- closes #455 (Tier 1 #5, scope: Deno.affine + JS codegen)
- #446 — AS bindings top-50 umbrella
- `project_affinescript_bindings_top50_roadmap.md` — memory tracker
- `stdlib/Deno.affine:155-176` — existing `wasmCall` / `wasmInstance`
  surface for context

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 30, 2026 15:32
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 83 issues detected

Severity Count
🔴 Critical 4
🟠 High 11
🟡 Medium 68

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action ons/checkout@v6\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action land/setup-deno@v2\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in affine-vscode-publish.yml",
    "type": "unknown",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "unknown",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "unknown",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "unknown",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

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.

[#446 Tier 1 #5] WASM-exports calling pattern (recommended kickoff)

1 participant