Skip to content

globals: complete standard WebAssembly namespace surface #2599

Description

@andrewtdiz

Summary

PR #705 closed #76 by landing a useful WebAssembly host PoC, but the shipped API is still a Perry-specific synchronous subset. Current Node exposes the standard global WebAssembly namespace object, with constructor classes, async compile/instantiate methods, streaming methods, and typed error constructors. Perry currently recognizes a few WebAssembly.* call shapes syntactically instead of exposing a Node-compatible namespace value.

This is observable to packages that feature-detect globalThis.WebAssembly, destructure methods/classes, construct WebAssembly.Module / WebAssembly.Instance, catch WebAssembly.CompileError, or rely on the standard Promise-returning instantiate() shape used by wasm-pack/emscripten glue.

Expected Node behavior

Node's WebAssembly guide says Node provides the necessary APIs via the global WebAssembly object: https://nodejs.org/en/learn/getting-started/nodejs-with-webassembly

A local Node v25.9.0 probe shows the namespace shape:

typeof WebAssembly object
global identity true
keys CompileError,Exception,Global,Instance,JSTag,LinkError,Memory,Module,RuntimeError,SuspendError,Suspending,Table,Tag,compile,compileStreaming,instantiate,instantiateStreaming,promising,validate
Module proto function constructor
Instance proto function constructor,exports

Probe:

console.log("typeof WebAssembly", typeof WebAssembly);
console.log("global identity", globalThis.WebAssembly === WebAssembly);
console.log("keys", Object.getOwnPropertyNames(WebAssembly).sort().join(","));
console.log("Module proto", typeof WebAssembly.Module, Object.getOwnPropertyNames(WebAssembly.Module.prototype).sort().join(","));
console.log("Instance proto", typeof WebAssembly.Instance, Object.getOwnPropertyNames(WebAssembly.Instance.prototype).sort().join(","));

Node's standard WebAssembly.instantiate(bufferSource[, importObject]) returns a Promise for raw bytes and supports new WebAssembly.Module(bytes) plus new WebAssembly.Instance(module, imports) for synchronous construction.

Current Perry evidence

Source inspection against origin/main:

  • crates/perry-runtime/src/webassembly.rs explicitly documents the current surface as MVP API (Perry-specific, not standard).
  • The same comment says the PoC exposes only three synchronous top-level namespace builtins: WebAssembly.validate(bytes), WebAssembly.instantiate(bytes) returning an opaque handle, and WebAssembly.callExport(handle, name, ...).
  • crates/perry-hir/src/lower/expr_call/module_static.rs only recognizes validate, instantiate, and the nonstandard callExport when the receiver is the identifier WebAssembly.
  • crates/perry-runtime/src/object/global_this.rs::GLOBAL_THIS_BUILTIN_NAMESPACES does not include WebAssembly, so globalThis.WebAssembly is not populated through the native global singleton path.
  • crates/perry-codegen/src/expr/helpers.rs::is_global_this_builtin_name() also omits WebAssembly, so globalThis.WebAssembly reads do not route to a populated namespace object.
  • test-files/test_wasm_add.ts only covers the PoC flow: WebAssembly.validate(bytes), sync WebAssembly.instantiate(bytes), and inst.exports.add(...); it does not cover globalThis.WebAssembly, constructors, error classes, Promise shape, or imports object behavior.

I did not run a Perry binary because this checkout does not have target/release/perry; this issue is based on committed origin/main source, current Node docs, and a local Node v25.9.0 probe.

Suggested test surface

Add parity tests for the standard namespace shape separately from the existing PoC test:

console.log(typeof globalThis.WebAssembly === "object");
console.log(globalThis.WebAssembly === WebAssembly);
for (const k of [
  "validate",
  "compile",
  "instantiate",
  "compileStreaming",
  "instantiateStreaming",
  "Module",
  "Instance",
  "Memory",
  "Table",
  "Global",
  "CompileError",
  "LinkError",
  "RuntimeError",
]) {
  console.log(k, typeof WebAssembly[k]);
}

Add behavior tests for:

  • WebAssembly.instantiate(bytes) returning a Promise resolving to { module, instance } for raw bytes.
  • new WebAssembly.Module(bytes) and new WebAssembly.Instance(module, imports).
  • instance.exports.<name> working without compile-time-only local tagging assumptions.
  • invalid bytes throwing/rejecting with WebAssembly.CompileError.
  • importObject plumbing for numeric host imports once supported.

Scope / non-goals

This issue is a completion follow-up for the standard Node/global WebAssembly API shape after #76/#705. It should not remove the existing PoC helper path unless maintainers decide to deprecate it; WebAssembly.callExport can remain Perry-specific if useful, but it should not be the only way to call exports.

Streaming methods can initially throw or reject with a clear unsupported error if Perry lacks Response body streaming, but the namespace and function/class presence should match Node closely enough for feature detection.

Duplicate search

Checked existing issues and PRs with:

  • WebAssembly global
  • WebAssembly validate instantiate globalThis
  • WebAssembly Module Instance Memory Table
  • WebAssembly instantiate validate wasmi

The hits were closed #76 and merged #705, both of which explicitly shipped the PoC subset and list the standard async surface, imports, streaming methods, and host passthrough as follow-up work. I did not find an open tracker for the Node-compatible namespace object and standard constructor/method surface.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions