Skip to content

[js-api] Ambiguity in WebAssembly.instantiate(bytes) if WebAssembly.Module is a thenable #2034

Open

Description

@backes

I believe the current JS-API spec underspecifies the behavior of WebAssembly.instantiate(bytes) when WebAssembly.Module has been modified to include a then method (making the module a "thenable").

Reproducer:

const bytes = new Uint8Array([0,97,115,109,1,0,0,0]);  // Minimal Wasm bytes.
WebAssembly.Module.prototype.then = resolve => resolve(17);
WebAssembly.instantiate(bytes).then(v => print(`Resolved to ${v} / ${v.instance}`), e => print(`Rejected: ${e}`));

(to be run in a d8, jsc, or sm shell; on the Web replace print by console.log).

The issue:
According to the JS-API spec, instantiate(bytes) performs the following steps:

  1. Asynchronously compile the bytes into a WebAssembly module, let promiseOfModule be the result.
  2. Instantiate that promiseOfModule.

The second step creates a new promise which reacts to the first promise.

At the end of the first step (when compilation finishes), the internal promise is resolved with the module object. According to the ECMAScript spec for promise resolution, if the resolution value (the Module) has a then method, that method is invoked to determine the final resolution.

In the reproducer above, the user-provided then resolves the promise to 17. Consequently, the next step, instantiate-a-promise-of-a-module receives 17 instead of a WebAssembly.Module.

Currently, the spec does not define what should happen if the promise resolves to a value that is not a WebAssembly.Module.

Current Implementation Behavior:

Proposal: The spec should explicitly handle this case. I propose we add a check to instantiate that rejects the promise with a TypeError if the promiseOfModule resolves to anything that is not a WebAssembly.Module.

Alternatively, if the intention is that WebAssembly.Module instances should never be treated as thenables during this process, the spec needs to clarify how the internal promise resolution bypasses the standard then check.

Activity

  1. eqrion commented on Mar 25, 2026

    @eqrion
    Collaborator

    Thanks for filing this, I agree this is underspecified.

    There's another odd case here too. Async instantiate a module: 6.1.4 resolves a promise with an Instance object. If that instance prototype also has a 'then' we need to run that to determine the final value. Which then gets returned here as the 'instance' field of the final record. I don't think there is ambiguity here because the value is just returned, but it's the same problem.

    I have a preference to avoid the standard then check in these cases. I think there is some emerging precedent to avoid thenables in internal spec operations where they serve no purpose and complicate the implementation (tc39/proposal-defer-import-eval#77) (thenable curtailment), although I don't know all the nuances here. I'm refreshing myself on the promises spec now.

  2. LeszekSwirski commented on Mar 25, 2026

    @LeszekSwirski

    I think there is some emerging precedent to avoid thenables in internal spec operations where they serve no purpose and complicate the implementation

    As far as I'm aware, the Thenable curtailment proposal doesn't remove Thenables entirely, but rather makes the lookup of then slightly more secure (prevent it from triggering side effects if in the middle of other spec operations). IIUC it would still eventually call the user then.

  3. eqrion commented on Mar 25, 2026

    @eqrion
    Collaborator

    @LeszekSwirski Yes you're correct. That wouldn't help here.

    One thing we could do would be to refactor the spec so that we don't create the intermediate promises.

    We could do this by inlining 'instantiate a promise of a module' (only used for this op anyways) into 'asynchronously compile a WebAssembly module' to get a new 'asyncronously compile and instantiate a WebAssembly module'. Which is how it seems all implementations actually implemented this previously.

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