Skip to content
File

Blob: src/workerd/io/wasm-instantiate-shim.js

javascript108 lines
1// Copyright (c) 2026 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4// This file contains a shim for WebAssembly.Instance and WebAssembly.instantiate. Currently, the
5// runtime does not support instantiateStreaming, but if this ever changes, we will need to add a
6// shim for that too. V8's `SetWasmInstanceCallback` was considered as an alternative, but does
7// not quite work since it runs BEFORE instantiation, when the operations we want to do must happen
8// after.
9 
10(function (registerShutdown) {
11 const {
12 Instance: originalInstance,
13 instantiate: originalInstantiate,
14 Module,
15 Memory,
16 Global,
17 } = WebAssembly;
18 
19 // Finds the first imported or exported memory with type kind 'memory'.
20 function findMemory(instance, importObj, module) {
21 const importedMemory = Module.imports(module).find(
22 ({ kind }) => kind === 'memory'
23 );
24 if (importedMemory) {
25 // instantiation was already successful => importObj is known to
26 // have the right shape, and checkAndRegisterShutdown is wrapped
27 // in try-catch in case this fails anyway.
28 const value = importObj[importedMemory.module][importedMemory.name];
29 return value instanceof Memory ? value : undefined;
30 }
31 const exportedMemory = Module.exports(module).find(
32 ({ kind }) => kind === 'memory'
33 );
34 if (exportedMemory) return instance.exports[exportedMemory.name];
35 return undefined;
36 }
37 
38 function checkAndRegisterShutdown(instance, imports, module) {
39 const exports = instance.exports;
40 if (!exports) return;
41 const terminatedGlobal = exports['__instance_terminated'];
42 const signalGlobal = exports['__instance_signal'];
43 const hasTerminated = terminatedGlobal instanceof Global;
44 const hasSignal = signalGlobal instanceof Global;
45 // Register if at least one is present.
46 if (!hasTerminated && !hasSignal) return;
47 const memory = findMemory(instance, imports, module);
48 if (memory) {
49 // Pass -1 for whichever offset is absent. The C++ side interprets -1 as "no address".
50 registerShutdown(
51 instance,
52 memory,
53 hasSignal ? signalGlobal.value : -1,
54 hasTerminated ? terminatedGlobal.value : -1
55 );
56 }
57 }
58 
59 // WebAssembly.instantiate has two overloads:
60 // instantiate(bytes, imports?, compileOptions?) -> Promise<{module, instance}>
61 // instantiate(module, imports?, compileOptions?) -> Promise<Instance>
62 // NOTE: Exactly 1 argument defined to match WebAssembly.instantiate length
63 WebAssembly.instantiate = function instantiate(moduleOrBytes) {
64 const importObj = arguments[1];
65 return Reflect.apply(originalInstantiate, this, arguments).then(
66 function (result) {
67 // never fail instantiation for failed registration
68 try {
69 // Called with bytes: result is {module, instance}.
70 // Called with a Module: result is just the Instance.
71 const instance = result.instance || result;
72 const module = result.module || moduleOrBytes;
73 checkAndRegisterShutdown(instance, importObj, module);
74 } catch {}
75 return result;
76 }
77 );
78 };
79 
80 // new WebAssembly.Instance(module, imports?)
81 // Forward all arguments and new.target so subclassing works correctly.
82 // NOTE: Exactly 1 argument defined to match WebAssembly.Instance length
83 function Instance(module) {
84 const instance = Reflect.construct(
85 originalInstance,
86 arguments,
87 new.target || originalInstance
88 );
89 // never fail instantiation for failed registration
90 try {
91 checkAndRegisterShutdown(instance, arguments[1], module);
92 } catch {}
93 return instance;
94 }
95 // Point the shim's prototype at the original so instanceof checks continue to work.
96 Instance.prototype = originalInstance.prototype;
97 Object.defineProperty(Instance.prototype, 'constructor', {
98 value: Instance,
99 writable: true,
100 configurable: true,
101 });
102 Object.defineProperty(WebAssembly, 'Instance', {
103 value: Instance,
104 writable: true,
105 configurable: true,
106 });
107});