feat(stdlib): WasmValue + wasm_export_call typed wasm-exports binding (closes #455)#467
Merged
Merged
Conversation
…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>
🔍 Hypatia Security ScanFindings: 83 issues detected
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)
`lib/codegen_deno.ml` (+47 lines)
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:
Owner-directive compliance
Test plan
Refs
🤖 Generated with Claude Code