You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[js-api] Ambiguity in WebAssembly.instantiate(bytes) if WebAssembly.Module is a thenable #2034
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:
Asynchronously compile the bytes into a WebAssembly module, let promiseOfModule be the result.
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:
SpiderMonkey & JavaScriptCore: Seem to ignore the then property on the prototype, resolving with the Module object regardless.
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.
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.
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.
@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.
Activity
eqrion commentedon Mar 25, 2026
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
thencheck in these cases. I think there is some emerging precedent to avoidthenablesin 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.LeszekSwirski commentedon Mar 25, 2026
As far as I'm aware, the Thenable curtailment proposal doesn't remove Thenables entirely, but rather makes the lookup of
thenslightly more secure (prevent it from triggering side effects if in the middle of other spec operations). IIUC it would still eventually call the userthen.eqrion commentedon Mar 25, 2026
@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.