@petrea/wasm 0.0.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +33 -0
- package/binding.wasip1-browser.js +990 -0
- package/binding.wasip1-deferred.d.ts +83 -0
- package/binding.wasip1-deferred.js +1488 -0
- package/binding.wasip1.cjs +1209 -0
- package/binding.wasip1.d.cts +82 -0
- package/binding.wasm32-wasip1.wasm +0 -0
- package/binding.wasm32-wasip1.wasm.d.ts +2 -0
- package/package.json +44 -6
- package/README.md +0 -3
- package/index.js +0 -1
|
@@ -0,0 +1,1488 @@
|
|
|
1
|
+
import {
|
|
2
|
+
emnapiAsyncWorkPlugin as __emnapiAsyncWorkPlugin,
|
|
3
|
+
emnapiTSFNPlugin as __emnapiTSFNPlugin,
|
|
4
|
+
instantiateNapiModule as __emnapiInstantiateNapiModule,
|
|
5
|
+
WASI as __WASI,
|
|
6
|
+
} from '@napi-rs/wasm-runtime'
|
|
7
|
+
import { createContext as __emnapiCreateContext } from '@emnapi/runtime'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export const WASM_MEMORY = Object.freeze({
|
|
11
|
+
initialPages: 1024,
|
|
12
|
+
maximumPages: 65536,
|
|
13
|
+
pageBytes: 65536,
|
|
14
|
+
initialBytes: 1024 * 65536,
|
|
15
|
+
maximumBytes: 65536 * 65536,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
let __createdInstances = 0
|
|
19
|
+
let __liveInstances = 0
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Counters for instances created by THIS module evaluation, not process-wide:
|
|
23
|
+
* a second bundled copy of this loader keeps its own. Only successfully
|
|
24
|
+
* created instances are counted, and `liveInstances` drops when an instance's
|
|
25
|
+
* `dispose()` resolves.
|
|
26
|
+
*
|
|
27
|
+
* `declaredInitialMemoryBytes` is declared address space, not a host's
|
|
28
|
+
* committed-memory metric; pair it with host telemetry rather than treating it
|
|
29
|
+
* as a quota.
|
|
30
|
+
*/
|
|
31
|
+
export function getDeferredRuntimeStats() {
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
createdInstances: __createdInstances,
|
|
34
|
+
liveInstances: __liveInstances,
|
|
35
|
+
declaredInitialMemoryBytes: WASM_MEMORY.initialBytes,
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const __arrayBufferByteLengthGetter = Object.getOwnPropertyDescriptor(
|
|
40
|
+
ArrayBuffer.prototype,
|
|
41
|
+
'byteLength',
|
|
42
|
+
).get
|
|
43
|
+
const __memoryBufferGetter = Object.getOwnPropertyDescriptor(
|
|
44
|
+
WebAssembly.Memory.prototype,
|
|
45
|
+
'buffer',
|
|
46
|
+
).get
|
|
47
|
+
// One managed initialization per Memory, success or failure: an attempt that
|
|
48
|
+
// throws may already have written into linear memory, so the bytes are not a
|
|
49
|
+
// clean slate for a second instance. Module-local, like the counters above.
|
|
50
|
+
const __claimedMemories = new WeakSet()
|
|
51
|
+
|
|
52
|
+
function __resolveInstanceMemory(__options) {
|
|
53
|
+
const __provided = __options == null ? undefined : __options.memory
|
|
54
|
+
if (__provided === undefined || __provided === null) {
|
|
55
|
+
// Page counts are handed to the engine unvalidated: it already rejects a
|
|
56
|
+
// negative, over-4GiB or below-maximum value with a precise message, and a
|
|
57
|
+
// second set of bounds here would only drift from it.
|
|
58
|
+
const __allocated = new WebAssembly.Memory({
|
|
59
|
+
initial:
|
|
60
|
+
__options != null && __options.initialMemoryPages !== undefined
|
|
61
|
+
? __options.initialMemoryPages
|
|
62
|
+
: WASM_MEMORY.initialPages,
|
|
63
|
+
maximum:
|
|
64
|
+
__options != null && __options.maximumMemoryPages !== undefined
|
|
65
|
+
? __options.maximumMemoryPages
|
|
66
|
+
: WASM_MEMORY.maximumPages,
|
|
67
|
+
})
|
|
68
|
+
// Claimed like a caller-provided one. The handle publishes it as
|
|
69
|
+
// `instance.memory`, so handing it back to `createInstance()` is as easy
|
|
70
|
+
// as passing your own twice, and it would put two live instances on one
|
|
71
|
+
// linear memory: each initialization rewrites the emnapi/WASI state the
|
|
72
|
+
// other is still running on.
|
|
73
|
+
__claimedMemories.add(__allocated)
|
|
74
|
+
return __allocated
|
|
75
|
+
}
|
|
76
|
+
if (
|
|
77
|
+
__options.initialMemoryPages !== undefined ||
|
|
78
|
+
__options.maximumMemoryPages !== undefined
|
|
79
|
+
) {
|
|
80
|
+
throw new TypeError(
|
|
81
|
+
'Pass either memory or initialMemoryPages/maximumMemoryPages, not both',
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
let __buffer
|
|
85
|
+
try {
|
|
86
|
+
// Brand check: the getter throws for anything that is not a genuine
|
|
87
|
+
// WebAssembly.Memory, including a cross-realm look-alike object.
|
|
88
|
+
__buffer = Reflect.apply(__memoryBufferGetter, __provided, [])
|
|
89
|
+
} catch {
|
|
90
|
+
throw new TypeError('memory must be an unshared WebAssembly.Memory')
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
// Throws for a SharedArrayBuffer. This loader has no threads, and shared
|
|
94
|
+
// growth does not detach: external views handed to the addon would
|
|
95
|
+
// silently outlive the bytes they describe.
|
|
96
|
+
Reflect.apply(__arrayBufferByteLengthGetter, __buffer, [])
|
|
97
|
+
} catch {
|
|
98
|
+
throw new TypeError(
|
|
99
|
+
'The deferred loader requires an unshared WebAssembly.Memory',
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
// The intrinsic getters above accept a genuine Memory from ANY realm, but
|
|
103
|
+
// the loader's dependencies do not: `WASI.setMemory` in
|
|
104
|
+
// `@napi-rs/wasm-runtime` and emnapi identify a Memory with a realm-local
|
|
105
|
+
// `instanceof`. A Memory built in another realm (a `node:vm` context, a
|
|
106
|
+
// same-origin iframe) would pass every check here and only fail deep inside
|
|
107
|
+
// initialization. Reject it up front, and before the claim below, so the
|
|
108
|
+
// caller keeps it usable in the realm that made it.
|
|
109
|
+
if (!(__provided instanceof WebAssembly.Memory)) {
|
|
110
|
+
throw new TypeError(
|
|
111
|
+
'memory must be a WebAssembly.Memory created in the same realm as this loader',
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
if (__claimedMemories.has(__provided)) {
|
|
115
|
+
throw new TypeError(
|
|
116
|
+
'This WebAssembly.Memory has already been used for a deferred initialization attempt and cannot be reused, including after a failed initialization or a disposal',
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
// Last step, after every check: a rejected option bag must leave the Memory
|
|
120
|
+
// unclaimed, or a caller could not fix the call and retry with it.
|
|
121
|
+
__claimedMemories.add(__provided)
|
|
122
|
+
return __provided
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const __napiBindingTarget = 'wasm32-wasip1'
|
|
126
|
+
function __napiStampBindingTarget(exportsObject, target) {
|
|
127
|
+
if (
|
|
128
|
+
Object.prototype.hasOwnProperty.call(exportsObject, '__napiBindingTarget')
|
|
129
|
+
) {
|
|
130
|
+
if (exportsObject.__napiBindingTarget === target) {
|
|
131
|
+
// Already ours: the root entry aliases the object it loaded, so a WASI
|
|
132
|
+
// fallback candidate — or a `NAPI_RS_NATIVE_LIBRARY_PATH` override that
|
|
133
|
+
// is a generated loader — arrives already stamped with this same value.
|
|
134
|
+
return target
|
|
135
|
+
}
|
|
136
|
+
const error = new Error(
|
|
137
|
+
'`__napiBindingTarget` is reserved by the generated binding loader, but the loaded binding already exports it. Rename the export, e.g. #[napi(js_name = "...")].',
|
|
138
|
+
)
|
|
139
|
+
error.code = 'ERR_NAPI_BINDING_TARGET_CONFLICT'
|
|
140
|
+
throw error
|
|
141
|
+
}
|
|
142
|
+
if (!Object.isExtensible(exportsObject)) {
|
|
143
|
+
// A `#[napi(module_exports)]` hook may seal or freeze this object
|
|
144
|
+
// (`Object::seal` / `Object::freeze`). Reporting the artifact is metadata,
|
|
145
|
+
// never a reason to fail an otherwise successful load, so the stamp is
|
|
146
|
+
// skipped. What a consumer still sees then follows the entry point: the
|
|
147
|
+
// browser and deferred loaders declare `__napiBindingTarget` at module
|
|
148
|
+
// level and go on reporting it, while the CommonJS entries hand back this
|
|
149
|
+
// very object as `module.exports`, so there the value is absent.
|
|
150
|
+
return target
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
// [[Define]], not [[Set]]: an ordinary assignment walks the prototype
|
|
154
|
+
// chain, so an inherited accessor could swallow the value or throw and
|
|
155
|
+
// fail an otherwise successful load. The descriptor is what a successful
|
|
156
|
+
// assignment would have produced.
|
|
157
|
+
Object.defineProperty(exportsObject, '__napiBindingTarget', {
|
|
158
|
+
configurable: true,
|
|
159
|
+
enumerable: true,
|
|
160
|
+
value: target,
|
|
161
|
+
writable: true,
|
|
162
|
+
})
|
|
163
|
+
} catch {
|
|
164
|
+
// Same rule as the non-extensible skip above: reporting the artifact is
|
|
165
|
+
// metadata, never a reason to fail an otherwise successful load. An exotic
|
|
166
|
+
// object (a Proxy whose defineProperty trap refuses) is skipped, not
|
|
167
|
+
// thrown over.
|
|
168
|
+
}
|
|
169
|
+
// The CommonJS loaders assign this return value so `cjs-module-lexer` — and
|
|
170
|
+
// therefore Node's CJS -> ESM named export detection — can see
|
|
171
|
+
// `__napiBindingTarget` statically.
|
|
172
|
+
return target
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Deferred, workerd-safe instantiation: no top-level I/O, no compile-from-bytes.
|
|
177
|
+
* Accepts ONLY a precompiled WebAssembly.Module, or a Promise resolving to one
|
|
178
|
+
* (e.g. `import mod from './binding.wasm32-wasip1.wasm'` under a CompiledWasm
|
|
179
|
+
* module rule / wrangler module import). Byte buffers, URLs and Response
|
|
180
|
+
* objects are rejected: they require dynamic Wasm compilation, which
|
|
181
|
+
* Cloudflare Workers disallows.
|
|
182
|
+
*/
|
|
183
|
+
async function __resolveModule(__wasmInput) {
|
|
184
|
+
const __module = await __wasmInput
|
|
185
|
+
// Brand check, not `instanceof`: `WebAssembly.Module.imports` throws unless
|
|
186
|
+
// its argument is a genuine WebAssembly.Module, so prototype-spoofed byte
|
|
187
|
+
// buffers are rejected while cross-realm Module instances are accepted.
|
|
188
|
+
try {
|
|
189
|
+
WebAssembly.Module.imports(__module)
|
|
190
|
+
} catch {
|
|
191
|
+
throw new TypeError(
|
|
192
|
+
"instantiate() and createInstance() expect a precompiled WebAssembly.Module (or a Promise resolving to one), " +
|
|
193
|
+
"e.g. import mod from './binding.wasm32-wasip1.wasm' under a CompiledWasm module rule / wrangler module import. " +
|
|
194
|
+
"Byte buffers, URLs and Response objects require dynamic Wasm compilation, which Cloudflare Workers disallows.",
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
return __module
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let __normalizedModules
|
|
201
|
+
|
|
202
|
+
function __rememberNormalizedModule(__module, __normalizedModule) {
|
|
203
|
+
if (!__normalizedModules) {
|
|
204
|
+
__normalizedModules = new WeakMap()
|
|
205
|
+
}
|
|
206
|
+
__normalizedModules.set(__module, __normalizedModule)
|
|
207
|
+
return __normalizedModule
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function __normalizeModuleForEmnapi(__module) {
|
|
211
|
+
if (__module instanceof WebAssembly.Module) {
|
|
212
|
+
return __module
|
|
213
|
+
}
|
|
214
|
+
if (__normalizedModules) {
|
|
215
|
+
const __normalizedModule = __normalizedModules.get(__module)
|
|
216
|
+
if (__normalizedModule) {
|
|
217
|
+
return __normalizedModule
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// @emnapi/core currently performs realm-local `instanceof` checks after
|
|
221
|
+
// accepting the module. Structured cloning preserves compiled code without
|
|
222
|
+
// compiling bytes and produces a Module owned by the current realm.
|
|
223
|
+
if (typeof structuredClone === 'function') {
|
|
224
|
+
try {
|
|
225
|
+
const __normalizedModule = structuredClone(__module)
|
|
226
|
+
if (__normalizedModule instanceof WebAssembly.Module) {
|
|
227
|
+
return __rememberNormalizedModule(__module, __normalizedModule)
|
|
228
|
+
}
|
|
229
|
+
} catch {}
|
|
230
|
+
}
|
|
231
|
+
// MessageChannel uses the same structured-clone semantics and covers older
|
|
232
|
+
// browser/Node hosts that expose it but not the structuredClone function.
|
|
233
|
+
if (typeof MessageChannel === 'function') {
|
|
234
|
+
let __channel
|
|
235
|
+
try {
|
|
236
|
+
__channel = new MessageChannel()
|
|
237
|
+
const __normalizedModule = await new Promise((resolve, reject) => {
|
|
238
|
+
__channel.port1.onmessage = (event) => resolve(event.data)
|
|
239
|
+
__channel.port1.onmessageerror = () =>
|
|
240
|
+
reject(new TypeError('Failed to clone WebAssembly.Module'))
|
|
241
|
+
try {
|
|
242
|
+
__channel.port2.postMessage(__module)
|
|
243
|
+
} catch (error) {
|
|
244
|
+
reject(error)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
if (__normalizedModule instanceof WebAssembly.Module) {
|
|
248
|
+
return __rememberNormalizedModule(__module, __normalizedModule)
|
|
249
|
+
}
|
|
250
|
+
} catch {
|
|
251
|
+
} finally {
|
|
252
|
+
try {
|
|
253
|
+
__channel?.port1.close()
|
|
254
|
+
} catch {}
|
|
255
|
+
try {
|
|
256
|
+
__channel?.port2.close()
|
|
257
|
+
} catch {}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Last-resort compatibility for genuine, extensible foreign Modules.
|
|
261
|
+
try {
|
|
262
|
+
Object.setPrototypeOf(__module, WebAssembly.Module.prototype)
|
|
263
|
+
} catch {}
|
|
264
|
+
if (__module instanceof WebAssembly.Module) {
|
|
265
|
+
return __module
|
|
266
|
+
}
|
|
267
|
+
throw new TypeError(
|
|
268
|
+
'This host cannot normalize a cross-realm WebAssembly.Module; ' +
|
|
269
|
+
'provide structuredClone or MessageChannel support.',
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function __wrapEmnapiContextDestroyForSettlement(
|
|
274
|
+
context,
|
|
275
|
+
prepareEnvCleanup,
|
|
276
|
+
isPreparingEnvCleanup,
|
|
277
|
+
) {
|
|
278
|
+
let destroy
|
|
279
|
+
try {
|
|
280
|
+
destroy = context.destroy
|
|
281
|
+
} catch {
|
|
282
|
+
return context
|
|
283
|
+
}
|
|
284
|
+
if (typeof destroy !== 'function') {
|
|
285
|
+
return context
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
Object.defineProperty(context, 'destroy', {
|
|
289
|
+
configurable: true,
|
|
290
|
+
enumerable: false,
|
|
291
|
+
writable: true,
|
|
292
|
+
value: function () {
|
|
293
|
+
// Reentered from a promise hook that fired inside the barrier: the
|
|
294
|
+
// frame running it destroys as soon as it returns.
|
|
295
|
+
if (isPreparingEnvCleanup?.()) {
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
prepareEnvCleanup?.()
|
|
299
|
+
return Reflect.apply(destroy, this, arguments)
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
} catch {}
|
|
303
|
+
return context
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function __captureEmnapiAutoDestroyListener(__process) {
|
|
307
|
+
if (
|
|
308
|
+
!__process ||
|
|
309
|
+
typeof __process.prependListener !== 'function' ||
|
|
310
|
+
typeof __process.removeListener !== 'function'
|
|
311
|
+
) {
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
let __autoDestroyListener
|
|
315
|
+
const __captureListener = (__event, __listener) => {
|
|
316
|
+
if (__event === 'beforeExit' && __autoDestroyListener === undefined) {
|
|
317
|
+
__autoDestroyListener = __listener
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
// Run before existing newListener hooks so a hook that registers its own
|
|
322
|
+
// beforeExit listener cannot be mistaken for emnapi's registration.
|
|
323
|
+
__process.prependListener('newListener', __captureListener)
|
|
324
|
+
} catch {
|
|
325
|
+
return
|
|
326
|
+
}
|
|
327
|
+
return () => {
|
|
328
|
+
try {
|
|
329
|
+
__process.removeListener('newListener', __captureListener)
|
|
330
|
+
} catch {}
|
|
331
|
+
if (__autoDestroyListener !== undefined) {
|
|
332
|
+
try {
|
|
333
|
+
__process.removeListener('beforeExit', __autoDestroyListener)
|
|
334
|
+
} catch {}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function __attachCleanupError(__error, __cleanupError) {
|
|
340
|
+
try {
|
|
341
|
+
if (
|
|
342
|
+
__error &&
|
|
343
|
+
(typeof __error === 'object' || typeof __error === 'function') &&
|
|
344
|
+
__error.cause === undefined
|
|
345
|
+
) {
|
|
346
|
+
__error.cause = __cleanupError
|
|
347
|
+
}
|
|
348
|
+
} catch {}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Mirror the primitive @emnapi/core schedules its threadsafe-function dispatch
|
|
352
|
+
// on, so the drain turns below interleave with that dispatch instead of racing
|
|
353
|
+
// ahead of it on a faster queue.
|
|
354
|
+
const __scheduleMacrotask = (function () {
|
|
355
|
+
if (typeof setImmediate === 'function') {
|
|
356
|
+
return function (__callback) {
|
|
357
|
+
setImmediate(__callback)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const __MessageChannel = globalThis.MessageChannel
|
|
361
|
+
if (typeof __MessageChannel === 'function') {
|
|
362
|
+
return function (__callback) {
|
|
363
|
+
const __channel = new __MessageChannel()
|
|
364
|
+
__channel.port1.onmessage = function () {
|
|
365
|
+
__channel.port1.onmessage = null
|
|
366
|
+
try {
|
|
367
|
+
__channel.port1.close()
|
|
368
|
+
} catch {}
|
|
369
|
+
try {
|
|
370
|
+
__channel.port2.close()
|
|
371
|
+
} catch {}
|
|
372
|
+
__callback()
|
|
373
|
+
}
|
|
374
|
+
__channel.port2.postMessage(null)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return function (__callback) {
|
|
378
|
+
setTimeout(__callback, 0)
|
|
379
|
+
}
|
|
380
|
+
})()
|
|
381
|
+
|
|
382
|
+
// Turns to wait for while the addon still reports queued settlements. Reaching
|
|
383
|
+
// zero is the only success. A counter still nonzero at this bound rejects the
|
|
384
|
+
// disposal as retryable (`ERR_NAPI_WASI_CLEANUP_PENDING`) rather than
|
|
385
|
+
// destroying the context over a still-queued settlement — the wait stays
|
|
386
|
+
// bounded either way.
|
|
387
|
+
const __WASM_ENV_CLEANUP_DRAIN_TURNS = 128
|
|
388
|
+
// Without `napi_wasm_env_cleanup_pending` the queue is not observable. Fall
|
|
389
|
+
// back to the number of turns @emnapi/core needs to coalesce and dispatch a
|
|
390
|
+
// call made on this thread (two), plus a margin.
|
|
391
|
+
const __WASM_ENV_CLEANUP_BLIND_DRAIN_TURNS = 4
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* `napi_prepare_wasm_env_cleanup` only *queues* the promise settlements of the
|
|
395
|
+
* tasks it cancelled: `napi_call_threadsafe_function` appends to the
|
|
396
|
+
* threadsafe-function queue, and @emnapi/core dispatches that queue from a
|
|
397
|
+
* macrotask — two coalescing turns later, even for a call made on this very
|
|
398
|
+
* thread. `Context.destroy()` then runs the threadsafe function's cleanup hook,
|
|
399
|
+
* which drains the queue with a null env and *discards* whatever is still in it.
|
|
400
|
+
*
|
|
401
|
+
* So destroying without yielding first strands exactly the promises the barrier
|
|
402
|
+
* exists to settle. Yield real event-loop turns until the addon reports the
|
|
403
|
+
* queue empty; microtask checkpoints cannot help, no number of them lets a
|
|
404
|
+
* macrotask run.
|
|
405
|
+
*
|
|
406
|
+
* Returns nothing when there is nothing to wait for, which keeps disposal
|
|
407
|
+
* synchronous in the common case.
|
|
408
|
+
*/
|
|
409
|
+
function __drainWasmEnvCleanup(__instance) {
|
|
410
|
+
const __pending = __instance?.exports.napi_wasm_env_cleanup_pending
|
|
411
|
+
const __observable = typeof __pending === 'function'
|
|
412
|
+
if (__observable) {
|
|
413
|
+
let __queued
|
|
414
|
+
try {
|
|
415
|
+
__queued = __pending()
|
|
416
|
+
} catch {
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
if (!__queued) {
|
|
420
|
+
return
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const __limit = __observable
|
|
424
|
+
? __WASM_ENV_CLEANUP_DRAIN_TURNS
|
|
425
|
+
: __WASM_ENV_CLEANUP_BLIND_DRAIN_TURNS
|
|
426
|
+
return (async () => {
|
|
427
|
+
let __queued = 0
|
|
428
|
+
for (let __turn = 0; __turn < __limit; __turn++) {
|
|
429
|
+
await new Promise((resolve) => {
|
|
430
|
+
__scheduleMacrotask(resolve)
|
|
431
|
+
})
|
|
432
|
+
if (!__observable) {
|
|
433
|
+
continue
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
__queued = __pending()
|
|
437
|
+
} catch {
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
if (!__queued) {
|
|
441
|
+
return
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (!__observable) {
|
|
445
|
+
// Blind wait: without `napi_wasm_env_cleanup_pending` the bound IS the
|
|
446
|
+
// contract — there is nothing to consult, so finishing the turns is
|
|
447
|
+
// finishing the drain.
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
// The counter is still nonzero after every turn the bound allows. The wait
|
|
451
|
+
// stays bounded — but claiming success here would be indistinguishable from
|
|
452
|
+
// the stranding this drain exists to prevent: disposal would go on to
|
|
453
|
+
// destroy the context, whose cleanup hook discards the still-queued
|
|
454
|
+
// settlement with a null env, and the promise it was for hangs forever.
|
|
455
|
+
// Reject instead, as a retryable cleanup failure: `__prepareForDisposal`
|
|
456
|
+
// leaves its drained flag unset, dispose() (and the instantiation-failure
|
|
457
|
+
// path) decline to destroy, and a later dispose() runs the drain again — by
|
|
458
|
+
// which time the queue has usually been delivered. A counter that is
|
|
459
|
+
// somehow stuck nonzero therefore costs each attempt at most another
|
|
460
|
+
// bounded wait and a rejection, never a stranded promise; the managed
|
|
461
|
+
// beforeExit destroyer still reclaims the context.
|
|
462
|
+
const __drainError = new Error(
|
|
463
|
+
'the wasm environment still reports ' +
|
|
464
|
+
__queued +
|
|
465
|
+
' queued settlement(s) after ' +
|
|
466
|
+
__limit +
|
|
467
|
+
' event-loop turns; the context was not destroyed - retry dispose() to wait for the queue again',
|
|
468
|
+
)
|
|
469
|
+
__drainError.code = 'ERR_NAPI_WASI_CLEANUP_PENDING'
|
|
470
|
+
throw __drainError
|
|
471
|
+
})()
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// A real, *referenced* timer for the async-work wait below, which polls the
|
|
475
|
+
// addon rather than interleaving with the @emnapi/core dispatch: a zero-delay
|
|
476
|
+
// macrotask there would spin the loop instead of yielding it. Falls back to the
|
|
477
|
+
// macrotask scheduler on a host without timers.
|
|
478
|
+
function __scheduleTimer(__callback, __delay) {
|
|
479
|
+
const __setTimer = globalThis.setTimeout
|
|
480
|
+
if (typeof __setTimer !== 'function') {
|
|
481
|
+
__scheduleMacrotask(__callback)
|
|
482
|
+
return
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
__setTimer(__callback, __delay)
|
|
486
|
+
} catch {
|
|
487
|
+
__scheduleMacrotask(__callback)
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// How often to re-read `napi_wasm_async_work_pending` while waiting. The wait
|
|
492
|
+
// ends when the addon reports zero, so this only decides how promptly disposal
|
|
493
|
+
// notices — not how long it waits.
|
|
494
|
+
const __WASI_ASYNC_WORK_POLL_INTERVAL_MS = 1
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Settles this instance's outstanding `napi_async_work` before the teardown
|
|
498
|
+
* that would strand it.
|
|
499
|
+
*
|
|
500
|
+
* The same hole the eager loaders had, and the same fix: the environment
|
|
501
|
+
* cleanup barrier covers promise settlements queued on the threadsafe-function
|
|
502
|
+
* queue and says nothing about `napi_async_work`, so destroying the context
|
|
503
|
+
* with a work still outstanding leaves its completion callback with nowhere to
|
|
504
|
+
* run and its promise unsettled forever.
|
|
505
|
+
*
|
|
506
|
+
* This flavor is threadless, so `compute` runs on the JavaScript thread inside
|
|
507
|
+
* the macrotask that dequeued it: while this is running, any outstanding work
|
|
508
|
+
* is queued rather than executing, and `napi_wasm_cancel_pending_async_work`
|
|
509
|
+
* can take all of it. The poll is still what decides when the drain is done —
|
|
510
|
+
* cancellation delivers those completions from a later macrotask, not
|
|
511
|
+
* synchronously.
|
|
512
|
+
*
|
|
513
|
+
* Per instance, from that instance's own exports: two instances of this loader
|
|
514
|
+
* have separate registries and must not wait on each other.
|
|
515
|
+
*
|
|
516
|
+
* Both exports are optional, so an addon built against a napi crate that
|
|
517
|
+
* predates them keeps the previous behavior.
|
|
518
|
+
*
|
|
519
|
+
* Returns nothing when there is nothing outstanding, which keeps disposal
|
|
520
|
+
* synchronous in the common case. No deadline: giving up would destroy the
|
|
521
|
+
* environment with a completion callback still owed.
|
|
522
|
+
*/
|
|
523
|
+
function __drainInstanceAsyncWork(__instance) {
|
|
524
|
+
const __exports = __instance?.exports
|
|
525
|
+
const __pending = __exports?.napi_wasm_async_work_pending
|
|
526
|
+
const __cancelPending = __exports?.napi_wasm_cancel_pending_async_work
|
|
527
|
+
if (typeof __pending !== 'function' || typeof __cancelPending !== 'function') {
|
|
528
|
+
return
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const __readPending = () => {
|
|
532
|
+
try {
|
|
533
|
+
return __pending()
|
|
534
|
+
} catch (__error) {
|
|
535
|
+
// A trap is the only way this call fails: it reads a counter and cannot
|
|
536
|
+
// allocate or call back into JavaScript. A trapped instance can no longer
|
|
537
|
+
// run anything, so its outstanding work is unreachable by definition and
|
|
538
|
+
// best-effort is the honest answer. Anything else means the export is not
|
|
539
|
+
// what this loader thinks it is — a defect worth surfacing.
|
|
540
|
+
if (__error instanceof globalThis.WebAssembly.RuntimeError) {
|
|
541
|
+
return 0
|
|
542
|
+
}
|
|
543
|
+
throw __error
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (!__readPending()) {
|
|
548
|
+
return
|
|
549
|
+
}
|
|
550
|
+
try {
|
|
551
|
+
__cancelPending()
|
|
552
|
+
} catch {
|
|
553
|
+
// Cancellation only bounds the wait. Failing it means waiting for the queue
|
|
554
|
+
// to run instead, which the poll below already does.
|
|
555
|
+
}
|
|
556
|
+
if (!__readPending()) {
|
|
557
|
+
return
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return (async () => {
|
|
561
|
+
while (__readPending()) {
|
|
562
|
+
await new Promise((__resolve) => {
|
|
563
|
+
__scheduleTimer(__resolve, __WASI_ASYNC_WORK_POLL_INTERVAL_MS)
|
|
564
|
+
})
|
|
565
|
+
}
|
|
566
|
+
})()
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function __createLifecycleReentryError(__operation) {
|
|
570
|
+
const __error = new Error(
|
|
571
|
+
__operation +
|
|
572
|
+
'() cannot run while an emnapi Context.destroy() call is still active; await the original cleanup promise instead.',
|
|
573
|
+
)
|
|
574
|
+
__error.code = 'ERR_NAPI_WASI_LIFECYCLE_REENTRY'
|
|
575
|
+
return __error
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const __managedEmnapiContextDestroyers = new Set()
|
|
579
|
+
let __managedCleanupProcess
|
|
580
|
+
let __managedBeforeExitListener
|
|
581
|
+
let __managedDestroyPromise
|
|
582
|
+
let __managedDestroyersInFlight
|
|
583
|
+
let __managedBeforeExitRegistrationRetryCount = 0
|
|
584
|
+
let __managedBeforeExitRegistrationRetryScheduled = false
|
|
585
|
+
let __moduleLifecycleDestroyDepth = 0
|
|
586
|
+
|
|
587
|
+
function __removeManagedEmnapiCleanupListeners() {
|
|
588
|
+
const __process = __managedCleanupProcess
|
|
589
|
+
const __beforeExitListener = __managedBeforeExitListener
|
|
590
|
+
__managedCleanupProcess = undefined
|
|
591
|
+
__managedBeforeExitListener = undefined
|
|
592
|
+
__managedBeforeExitRegistrationRetryCount = 0
|
|
593
|
+
if (__process && __beforeExitListener) {
|
|
594
|
+
try {
|
|
595
|
+
__process.removeListener('beforeExit', __beforeExitListener)
|
|
596
|
+
} catch {}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function __scheduleManagedBeforeExitListenerRegistration() {
|
|
601
|
+
if (
|
|
602
|
+
!__managedCleanupProcess ||
|
|
603
|
+
__managedBeforeExitListener ||
|
|
604
|
+
__managedEmnapiContextDestroyers.size === 0 ||
|
|
605
|
+
__managedBeforeExitRegistrationRetryScheduled ||
|
|
606
|
+
__managedBeforeExitRegistrationRetryCount >= 3
|
|
607
|
+
) {
|
|
608
|
+
return
|
|
609
|
+
}
|
|
610
|
+
__managedBeforeExitRegistrationRetryScheduled = true
|
|
611
|
+
__managedBeforeExitRegistrationRetryCount++
|
|
612
|
+
queueMicrotask(() => {
|
|
613
|
+
__managedBeforeExitRegistrationRetryScheduled = false
|
|
614
|
+
if (
|
|
615
|
+
!__managedCleanupProcess ||
|
|
616
|
+
__managedBeforeExitListener ||
|
|
617
|
+
__managedEmnapiContextDestroyers.size === 0
|
|
618
|
+
) {
|
|
619
|
+
return
|
|
620
|
+
}
|
|
621
|
+
try {
|
|
622
|
+
__registerManagedBeforeExitListener()
|
|
623
|
+
} catch {}
|
|
624
|
+
})
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function __registerManagedBeforeExitListener() {
|
|
628
|
+
if (!__managedCleanupProcess || __managedBeforeExitListener) {
|
|
629
|
+
return
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
__managedCleanupProcess.once(
|
|
633
|
+
'beforeExit',
|
|
634
|
+
__destroyManagedEmnapiContextsBeforeExit,
|
|
635
|
+
)
|
|
636
|
+
} catch (error) {
|
|
637
|
+
__scheduleManagedBeforeExitListenerRegistration()
|
|
638
|
+
throw error
|
|
639
|
+
}
|
|
640
|
+
__managedBeforeExitListener = __destroyManagedEmnapiContextsBeforeExit
|
|
641
|
+
__managedBeforeExitRegistrationRetryCount = 0
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function __settleManagedEmnapiContextDestroy(__promise) {
|
|
645
|
+
if (__managedDestroyPromise === __promise) {
|
|
646
|
+
__managedDestroyPromise = undefined
|
|
647
|
+
__managedDestroyersInFlight = undefined
|
|
648
|
+
}
|
|
649
|
+
if (__managedEmnapiContextDestroyers.size === 0) {
|
|
650
|
+
__removeManagedEmnapiCleanupListeners()
|
|
651
|
+
return
|
|
652
|
+
}
|
|
653
|
+
try {
|
|
654
|
+
__registerManagedBeforeExitListener()
|
|
655
|
+
} catch {}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function __destroyManagedEmnapiContexts(__excludedDestroyers) {
|
|
659
|
+
if (__managedDestroyPromise) {
|
|
660
|
+
return __managedDestroyPromise
|
|
661
|
+
}
|
|
662
|
+
const __destroyers = Array.from(__managedEmnapiContextDestroyers).filter(
|
|
663
|
+
(__destroy) => !__excludedDestroyers?.has(__destroy),
|
|
664
|
+
)
|
|
665
|
+
if (__destroyers.length === 0) {
|
|
666
|
+
return Promise.resolve()
|
|
667
|
+
}
|
|
668
|
+
let __resolveDestroy
|
|
669
|
+
let __rejectDestroy
|
|
670
|
+
const __promise = new Promise((resolve, reject) => {
|
|
671
|
+
__resolveDestroy = resolve
|
|
672
|
+
__rejectDestroy = reject
|
|
673
|
+
})
|
|
674
|
+
__managedDestroyPromise = __promise
|
|
675
|
+
__managedDestroyersInFlight = new Set(__destroyers)
|
|
676
|
+
void Promise.all(
|
|
677
|
+
__destroyers.map((__destroy) => {
|
|
678
|
+
try {
|
|
679
|
+
return Promise.resolve(__destroy()).then(
|
|
680
|
+
() => ({ failed: false }),
|
|
681
|
+
(error) => ({ failed: true, error }),
|
|
682
|
+
)
|
|
683
|
+
} catch (error) {
|
|
684
|
+
return { failed: true, error }
|
|
685
|
+
}
|
|
686
|
+
}),
|
|
687
|
+
).then((__results) => {
|
|
688
|
+
let __primaryError
|
|
689
|
+
let __failed = false
|
|
690
|
+
for (const __result of __results) {
|
|
691
|
+
if (!__result.failed) {
|
|
692
|
+
continue
|
|
693
|
+
}
|
|
694
|
+
if (!__failed) {
|
|
695
|
+
__failed = true
|
|
696
|
+
__primaryError = __result.error
|
|
697
|
+
} else {
|
|
698
|
+
__attachCleanupError(__primaryError, __result.error)
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (__failed) {
|
|
702
|
+
__rejectDestroy(__primaryError)
|
|
703
|
+
} else {
|
|
704
|
+
__resolveDestroy()
|
|
705
|
+
}
|
|
706
|
+
}, __rejectDestroy)
|
|
707
|
+
void __promise.then(
|
|
708
|
+
() => {
|
|
709
|
+
__settleManagedEmnapiContextDestroy(__promise)
|
|
710
|
+
},
|
|
711
|
+
() => {
|
|
712
|
+
__settleManagedEmnapiContextDestroy(__promise)
|
|
713
|
+
},
|
|
714
|
+
)
|
|
715
|
+
return __promise
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
async function __drainManagedEmnapiContexts(__excludedDestroyers) {
|
|
719
|
+
const __attemptedDestroyers = new Set(__excludedDestroyers)
|
|
720
|
+
let __primaryError
|
|
721
|
+
let __failed = false
|
|
722
|
+
while (true) {
|
|
723
|
+
let __promise = __managedDestroyPromise
|
|
724
|
+
let __destroyers = __managedDestroyersInFlight
|
|
725
|
+
if (!__promise) {
|
|
726
|
+
__promise = __destroyManagedEmnapiContexts(__attemptedDestroyers)
|
|
727
|
+
__destroyers = __managedDestroyersInFlight
|
|
728
|
+
if (!__destroyers) {
|
|
729
|
+
break
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
for (const __destroy of __destroyers) {
|
|
733
|
+
__attemptedDestroyers.add(__destroy)
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
736
|
+
await __promise
|
|
737
|
+
} catch (error) {
|
|
738
|
+
if (!__failed) {
|
|
739
|
+
__failed = true
|
|
740
|
+
__primaryError = error
|
|
741
|
+
} else {
|
|
742
|
+
__attachCleanupError(__primaryError, error)
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (__failed) {
|
|
747
|
+
throw __primaryError
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function __destroyManagedEmnapiContextsBeforeExit() {
|
|
752
|
+
// A once listener is consumed before Node invokes it, including when another
|
|
753
|
+
// cleanup batch is still pending.
|
|
754
|
+
__managedBeforeExitListener = undefined
|
|
755
|
+
if (__managedDestroyPromise) {
|
|
756
|
+
return
|
|
757
|
+
}
|
|
758
|
+
void __destroyManagedEmnapiContexts().catch((error) => {
|
|
759
|
+
queueMicrotask(() => {
|
|
760
|
+
throw error
|
|
761
|
+
})
|
|
762
|
+
})
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function __registerManagedEmnapiContext(__process, __destroy) {
|
|
766
|
+
__managedEmnapiContextDestroyers.add(__destroy)
|
|
767
|
+
if (
|
|
768
|
+
!__managedCleanupProcess &&
|
|
769
|
+
__process &&
|
|
770
|
+
typeof __process.once === 'function' &&
|
|
771
|
+
typeof __process.removeListener === 'function'
|
|
772
|
+
) {
|
|
773
|
+
__managedCleanupProcess = __process
|
|
774
|
+
}
|
|
775
|
+
let __registered = true
|
|
776
|
+
return () => {
|
|
777
|
+
if (!__registered) {
|
|
778
|
+
return
|
|
779
|
+
}
|
|
780
|
+
__registered = false
|
|
781
|
+
__managedEmnapiContextDestroyers.delete(__destroy)
|
|
782
|
+
if (__managedEmnapiContextDestroyers.size === 0) {
|
|
783
|
+
__removeManagedEmnapiCleanupListeners()
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
async function __createManagedEmnapiContext(
|
|
789
|
+
__prepareEnvCleanup,
|
|
790
|
+
__isPreparingEnvCleanup,
|
|
791
|
+
) {
|
|
792
|
+
const __process =
|
|
793
|
+
typeof process === 'object' && process !== null ? process : undefined
|
|
794
|
+
const __finishAutoDestroyCapture =
|
|
795
|
+
__captureEmnapiAutoDestroyListener(__process)
|
|
796
|
+
let __emnapiContext
|
|
797
|
+
let __contextInitializationError
|
|
798
|
+
let __contextInitializationFailed = false
|
|
799
|
+
try {
|
|
800
|
+
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
|
801
|
+
__emnapiCreateContext({ autoDestroy: false }),
|
|
802
|
+
__prepareEnvCleanup,
|
|
803
|
+
__isPreparingEnvCleanup,
|
|
804
|
+
)
|
|
805
|
+
// emnapi 2.x still registers an unconditional process.once('beforeExit')
|
|
806
|
+
// auto-destroy listener on Node hosts, and suppressDestroy() only
|
|
807
|
+
// neutralizes its callback without removing it. This loader must stay
|
|
808
|
+
// side-effect free per instance, so the listener is captured and removed;
|
|
809
|
+
// suppressDestroy() remains the safety net when removal is unavailable.
|
|
810
|
+
__emnapiContext.suppressDestroy()
|
|
811
|
+
} catch (error) {
|
|
812
|
+
__contextInitializationError = error
|
|
813
|
+
__contextInitializationFailed = true
|
|
814
|
+
} finally {
|
|
815
|
+
// Remove only the exact emnapi callback captured above.
|
|
816
|
+
__finishAutoDestroyCapture?.()
|
|
817
|
+
}
|
|
818
|
+
if (__emnapiContext === undefined) {
|
|
819
|
+
throw __contextInitializationError
|
|
820
|
+
}
|
|
821
|
+
let __disposed = false
|
|
822
|
+
let __destroying = false
|
|
823
|
+
let __destroyPromise
|
|
824
|
+
let __cleanupRegistered = false
|
|
825
|
+
let __unregisterCleanup
|
|
826
|
+
const __destroy = (__blocksModuleLifecycle = false) => {
|
|
827
|
+
if (__disposed) {
|
|
828
|
+
return
|
|
829
|
+
}
|
|
830
|
+
if (__destroying) {
|
|
831
|
+
throw __createLifecycleReentryError('dispose')
|
|
832
|
+
}
|
|
833
|
+
if (__destroyPromise) {
|
|
834
|
+
return __destroyPromise
|
|
835
|
+
}
|
|
836
|
+
__destroying = true
|
|
837
|
+
let __result
|
|
838
|
+
const __finishDestroyInvocation = () => {
|
|
839
|
+
__destroying = false
|
|
840
|
+
}
|
|
841
|
+
const __finishModuleLifecycleDestroy = () => {
|
|
842
|
+
if (__blocksModuleLifecycle) {
|
|
843
|
+
__blocksModuleLifecycle = false
|
|
844
|
+
__moduleLifecycleDestroyDepth--
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (__blocksModuleLifecycle) {
|
|
848
|
+
__moduleLifecycleDestroyDepth++
|
|
849
|
+
}
|
|
850
|
+
try {
|
|
851
|
+
// Context.destroy() disables JS before cleanup hooks run, so settle
|
|
852
|
+
// runtime-owned promises while this environment can still call JS.
|
|
853
|
+
__prepareEnvCleanup?.()
|
|
854
|
+
if (__isPreparingEnvCleanup?.()) {
|
|
855
|
+
// Reached from inside the barrier, so `Context.destroy()` below would
|
|
856
|
+
// hit the wrapper's in-flight no-op. Recording that as a completed
|
|
857
|
+
// destroy is what makes the frame that *did* start the barrier skip the
|
|
858
|
+
// real one afterwards, leaving the context retained with its cleanup
|
|
859
|
+
// hooks unrun. Refuse instead: nothing is flagged, the context stays
|
|
860
|
+
// registered for managed beforeExit cleanup, and a later destroy still
|
|
861
|
+
// works. dispose() coalesces reentrancy before it can get here, so this
|
|
862
|
+
// is the backstop for any other caller that manages to.
|
|
863
|
+
throw __createLifecycleReentryError('dispose')
|
|
864
|
+
}
|
|
865
|
+
__result = __emnapiContext.destroy()
|
|
866
|
+
} catch (error) {
|
|
867
|
+
__finishDestroyInvocation()
|
|
868
|
+
__finishModuleLifecycleDestroy()
|
|
869
|
+
throw error
|
|
870
|
+
}
|
|
871
|
+
let __then
|
|
872
|
+
try {
|
|
873
|
+
if (
|
|
874
|
+
__result !== null &&
|
|
875
|
+
(typeof __result === 'object' || typeof __result === 'function')
|
|
876
|
+
) {
|
|
877
|
+
__then = __result.then
|
|
878
|
+
}
|
|
879
|
+
} catch (error) {
|
|
880
|
+
__finishDestroyInvocation()
|
|
881
|
+
__finishModuleLifecycleDestroy()
|
|
882
|
+
throw error
|
|
883
|
+
}
|
|
884
|
+
if (typeof __then === 'function') {
|
|
885
|
+
let __resolveResult
|
|
886
|
+
let __rejectResult
|
|
887
|
+
const __resultPromise = new Promise((resolve, reject) => {
|
|
888
|
+
__resolveResult = resolve
|
|
889
|
+
__rejectResult = reject
|
|
890
|
+
})
|
|
891
|
+
const __promise = __resultPromise.then(
|
|
892
|
+
(value) => {
|
|
893
|
+
__finishDestroyInvocation()
|
|
894
|
+
__finishModuleLifecycleDestroy()
|
|
895
|
+
__disposed = true
|
|
896
|
+
__destroyPromise = undefined
|
|
897
|
+
__unregisterCleanup?.()
|
|
898
|
+
return value
|
|
899
|
+
},
|
|
900
|
+
(error) => {
|
|
901
|
+
__finishDestroyInvocation()
|
|
902
|
+
__finishModuleLifecycleDestroy()
|
|
903
|
+
__destroyPromise = undefined
|
|
904
|
+
throw error
|
|
905
|
+
},
|
|
906
|
+
)
|
|
907
|
+
__destroyPromise = __promise
|
|
908
|
+
try {
|
|
909
|
+
Reflect.apply(__then, __result, [__resolveResult, __rejectResult])
|
|
910
|
+
} catch (error) {
|
|
911
|
+
__rejectResult(error)
|
|
912
|
+
}
|
|
913
|
+
return __promise
|
|
914
|
+
}
|
|
915
|
+
__finishDestroyInvocation()
|
|
916
|
+
__finishModuleLifecycleDestroy()
|
|
917
|
+
__disposed = true
|
|
918
|
+
__unregisterCleanup?.()
|
|
919
|
+
}
|
|
920
|
+
const __destroyForModuleLifecycle = () => __destroy(true)
|
|
921
|
+
const __registerCleanup = (
|
|
922
|
+
__beforeExitDestroy = __destroyForModuleLifecycle,
|
|
923
|
+
) => {
|
|
924
|
+
if (__cleanupRegistered || __disposed) {
|
|
925
|
+
return
|
|
926
|
+
}
|
|
927
|
+
__unregisterCleanup = __registerManagedEmnapiContext(
|
|
928
|
+
__process,
|
|
929
|
+
__beforeExitDestroy,
|
|
930
|
+
)
|
|
931
|
+
__cleanupRegistered = true
|
|
932
|
+
__registerManagedBeforeExitListener()
|
|
933
|
+
}
|
|
934
|
+
if (__contextInitializationFailed) {
|
|
935
|
+
let __registrationError
|
|
936
|
+
let __registrationFailed = false
|
|
937
|
+
try {
|
|
938
|
+
__registerCleanup()
|
|
939
|
+
} catch (error) {
|
|
940
|
+
__attachCleanupError(__contextInitializationError, error)
|
|
941
|
+
__registrationError = error
|
|
942
|
+
__registrationFailed = true
|
|
943
|
+
}
|
|
944
|
+
try {
|
|
945
|
+
await __destroyForModuleLifecycle()
|
|
946
|
+
} catch (error) {
|
|
947
|
+
__attachCleanupError(
|
|
948
|
+
__registrationFailed
|
|
949
|
+
? __registrationError
|
|
950
|
+
: __contextInitializationError,
|
|
951
|
+
error,
|
|
952
|
+
)
|
|
953
|
+
try {
|
|
954
|
+
__registerManagedBeforeExitListener()
|
|
955
|
+
} catch {}
|
|
956
|
+
}
|
|
957
|
+
throw __contextInitializationError
|
|
958
|
+
}
|
|
959
|
+
return {
|
|
960
|
+
context: __emnapiContext,
|
|
961
|
+
destroy: __destroy,
|
|
962
|
+
destroyForModuleLifecycle: __destroyForModuleLifecycle,
|
|
963
|
+
registerCleanup: __registerCleanup,
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
async function __createInstance(
|
|
968
|
+
__wasmInput,
|
|
969
|
+
__options,
|
|
970
|
+
__beforeExitDestroy,
|
|
971
|
+
__onManagedDestroyer,
|
|
972
|
+
) {
|
|
973
|
+
const __module = await __resolveModule(__wasmInput)
|
|
974
|
+
const __emnapiModule = await __normalizeModuleForEmnapi(__module)
|
|
975
|
+
const __wasi = new __WASI({
|
|
976
|
+
version: 'preview1',
|
|
977
|
+
})
|
|
978
|
+
// The wasm module is linked with `--import-memory`, so a Memory must be
|
|
979
|
+
// provided. It is resolved here in function scope (workerd bans global scope
|
|
980
|
+
// allocation) and is never shared (no threads, no SharedArrayBuffer).
|
|
981
|
+
// Resolve it before the emnapi context so a rejected option bag or a host
|
|
982
|
+
// memory-limit failure cannot leak a context that never reaches
|
|
983
|
+
// instantiation.
|
|
984
|
+
const __wasmMemory = __resolveInstanceMemory(__options)
|
|
985
|
+
let __lifecycleState = 'pending'
|
|
986
|
+
let __destroyEmnapiContext
|
|
987
|
+
let __destroyOwnedContext
|
|
988
|
+
let __destroyManagedOwnedContext
|
|
989
|
+
let __napiInstance
|
|
990
|
+
let __wasmEnvCleanupRan = false
|
|
991
|
+
let __wasmEnvCleanupPrepared = false
|
|
992
|
+
let __wasmEnvCleanupPreparing = false
|
|
993
|
+
let __wasmEnvCleanupDrained = false
|
|
994
|
+
let __wasmEnvCleanupDrainPromise
|
|
995
|
+
const __isPreparingEnvCleanup = () => __wasmEnvCleanupPreparing
|
|
996
|
+
const __prepareEnvCleanup = () => {
|
|
997
|
+
if (__wasmEnvCleanupPrepared || __wasmEnvCleanupPreparing) {
|
|
998
|
+
return
|
|
999
|
+
}
|
|
1000
|
+
const __prepareWasmEnvCleanup =
|
|
1001
|
+
__napiInstance?.exports.napi_prepare_wasm_env_cleanup
|
|
1002
|
+
if (typeof __prepareWasmEnvCleanup === 'function') {
|
|
1003
|
+
// The addon settles the promises it cancels synchronously, under a
|
|
1004
|
+
// non-reentrant lifecycle mutex: anything a promise hook calls from in
|
|
1005
|
+
// here must not reach this export again.
|
|
1006
|
+
__wasmEnvCleanupPreparing = true
|
|
1007
|
+
try {
|
|
1008
|
+
__prepareWasmEnvCleanup()
|
|
1009
|
+
} finally {
|
|
1010
|
+
__wasmEnvCleanupPreparing = false
|
|
1011
|
+
}
|
|
1012
|
+
__wasmEnvCleanupRan = true
|
|
1013
|
+
}
|
|
1014
|
+
__wasmEnvCleanupPrepared = true
|
|
1015
|
+
}
|
|
1016
|
+
// The barrier + settlement drain, hoisted out of the context destroyer so the
|
|
1017
|
+
// drain can yield without widening the destroyer's reentry window. Both
|
|
1018
|
+
// yielding paths run it — dispose() and the initialization-failure rollback.
|
|
1019
|
+
// The destroyer still runs the barrier itself (idempotently) for the one path
|
|
1020
|
+
// that cannot yield: managed beforeExit cleanup of an instance whose rollback
|
|
1021
|
+
// is being retried.
|
|
1022
|
+
//
|
|
1023
|
+
// "Drained" is recorded only once a wait has actually finished. Scheduling a
|
|
1024
|
+
// macrotask can fail — a host-provided or patched `setImmediate` that throws
|
|
1025
|
+
// is enough — and dispose() stays retryable after it rejects, so marking the
|
|
1026
|
+
// drain complete up front would make the retry skip it and destroy the context
|
|
1027
|
+
// with the barrier's settlements still queued.
|
|
1028
|
+
const __prepareForDisposal = () => {
|
|
1029
|
+
if (__wasmEnvCleanupDrained) {
|
|
1030
|
+
return
|
|
1031
|
+
}
|
|
1032
|
+
if (__wasmEnvCleanupDrainPromise) {
|
|
1033
|
+
return __wasmEnvCleanupDrainPromise
|
|
1034
|
+
}
|
|
1035
|
+
__prepareEnvCleanup()
|
|
1036
|
+
if (!__wasmEnvCleanupRan) {
|
|
1037
|
+
return
|
|
1038
|
+
}
|
|
1039
|
+
const __drained = __drainWasmEnvCleanup(__napiInstance)
|
|
1040
|
+
if (!__drained || typeof __drained.then !== 'function') {
|
|
1041
|
+
__wasmEnvCleanupDrained = true
|
|
1042
|
+
return
|
|
1043
|
+
}
|
|
1044
|
+
const __tracked = __drained.then(
|
|
1045
|
+
(__value) => {
|
|
1046
|
+
__wasmEnvCleanupDrained = true
|
|
1047
|
+
__wasmEnvCleanupDrainPromise = undefined
|
|
1048
|
+
return __value
|
|
1049
|
+
},
|
|
1050
|
+
(__error) => {
|
|
1051
|
+
__wasmEnvCleanupDrainPromise = undefined
|
|
1052
|
+
throw __error
|
|
1053
|
+
},
|
|
1054
|
+
)
|
|
1055
|
+
__wasmEnvCleanupDrainPromise = __tracked
|
|
1056
|
+
return __tracked
|
|
1057
|
+
}
|
|
1058
|
+
let __disposed = false
|
|
1059
|
+
const __runInstanceDisposal = async () => {
|
|
1060
|
+
if (__lifecycleState !== 'failed') {
|
|
1061
|
+
__lifecycleState = 'disposal'
|
|
1062
|
+
}
|
|
1063
|
+
// Outstanding async work first, while the environment is still completely
|
|
1064
|
+
// live: its completion callbacks run addon code, and the barrier and
|
|
1065
|
+
// `Context.destroy()` below each take that away. Undefined unless work is
|
|
1066
|
+
// outstanding.
|
|
1067
|
+
const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
|
|
1068
|
+
if (__asyncWorkDrained) {
|
|
1069
|
+
await __asyncWorkDrained
|
|
1070
|
+
}
|
|
1071
|
+
// Then settle what the barrier cancelled, before the environment stops
|
|
1072
|
+
// accepting JavaScript calls. Undefined unless something is queued, so
|
|
1073
|
+
// an idle disposal is not delayed by a single turn.
|
|
1074
|
+
const __drained = __prepareForDisposal()
|
|
1075
|
+
if (__drained) {
|
|
1076
|
+
await __drained
|
|
1077
|
+
}
|
|
1078
|
+
const __result = await (__beforeExitDestroy
|
|
1079
|
+
? __destroyManagedOwnedContext()
|
|
1080
|
+
: __destroyOwnedContext())
|
|
1081
|
+
// Only a completed destroy retires the instance; a throw above leaves
|
|
1082
|
+
// the counter untouched so a retried dispose() cannot double-decrement.
|
|
1083
|
+
if (!__disposed) {
|
|
1084
|
+
__disposed = true
|
|
1085
|
+
__liveInstances -= 1
|
|
1086
|
+
}
|
|
1087
|
+
return __result
|
|
1088
|
+
}
|
|
1089
|
+
let __instanceDisposePromise
|
|
1090
|
+
/**
|
|
1091
|
+
* The disposal frame runs the barrier, and the barrier settles the promises it
|
|
1092
|
+
* cancels synchronously — so a promise hook firing inside it can call this
|
|
1093
|
+
* same instance's dispose() again while the first call is still in its drain.
|
|
1094
|
+
* That nested call finds the barrier flagged in flight, prepares nothing,
|
|
1095
|
+
* drains nothing, and falls straight through to the context destroyer, whose
|
|
1096
|
+
* `Context.destroy()` hits the wrapper's in-flight no-op. It would record a
|
|
1097
|
+
* destruction that never happened, and the outer frame would then skip the
|
|
1098
|
+
* real one: both disposals resolve, no cleanup hook runs, the context stays
|
|
1099
|
+
* retained.
|
|
1100
|
+
*
|
|
1101
|
+
* Memoize before any of that starts, exactly like the eager loaders'
|
|
1102
|
+
* `__disposeWasiBinding`, so there is only ever one disposal frame per
|
|
1103
|
+
* instance and a reentrant caller awaits it instead of racing it. Cleared on
|
|
1104
|
+
* rejection: a drain that failed has to stay retryable.
|
|
1105
|
+
*/
|
|
1106
|
+
const __disposeInstance = () => {
|
|
1107
|
+
if (__instanceDisposePromise) {
|
|
1108
|
+
return __instanceDisposePromise
|
|
1109
|
+
}
|
|
1110
|
+
let __resolveDispose
|
|
1111
|
+
let __rejectDispose
|
|
1112
|
+
const __disposePromise = new Promise((__resolve, __reject) => {
|
|
1113
|
+
__resolveDispose = __resolve
|
|
1114
|
+
__rejectDispose = __reject
|
|
1115
|
+
})
|
|
1116
|
+
__instanceDisposePromise = __disposePromise
|
|
1117
|
+
__runInstanceDisposal().then(__resolveDispose, (__error) => {
|
|
1118
|
+
__instanceDisposePromise = undefined
|
|
1119
|
+
__rejectDispose(__error)
|
|
1120
|
+
})
|
|
1121
|
+
return __disposePromise
|
|
1122
|
+
}
|
|
1123
|
+
const __destroyBeforeExit = __beforeExitDestroy
|
|
1124
|
+
? async () => {
|
|
1125
|
+
if (__lifecycleState === 'failed') {
|
|
1126
|
+
await __destroyManagedOwnedContext()
|
|
1127
|
+
return
|
|
1128
|
+
}
|
|
1129
|
+
__lifecycleState = 'disposal'
|
|
1130
|
+
try {
|
|
1131
|
+
await __beforeExitDestroy()
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
if (__lifecycleState !== 'failed') {
|
|
1134
|
+
throw error
|
|
1135
|
+
}
|
|
1136
|
+
// The singleton's initialization rejection is already observable
|
|
1137
|
+
// through instantiate() and dispose(). Managed beforeExit cleanup
|
|
1138
|
+
// owns only context destruction, including retrying a failed rollback.
|
|
1139
|
+
await __destroyManagedOwnedContext()
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
: undefined
|
|
1143
|
+
const {
|
|
1144
|
+
context: __emnapiContext,
|
|
1145
|
+
destroy,
|
|
1146
|
+
destroyForModuleLifecycle,
|
|
1147
|
+
registerCleanup: __registerCleanup,
|
|
1148
|
+
} = await __createManagedEmnapiContext(
|
|
1149
|
+
__prepareEnvCleanup,
|
|
1150
|
+
__isPreparingEnvCleanup,
|
|
1151
|
+
)
|
|
1152
|
+
__destroyEmnapiContext = destroy
|
|
1153
|
+
__destroyOwnedContext = () => __destroyEmnapiContext()
|
|
1154
|
+
__destroyManagedOwnedContext = destroyForModuleLifecycle
|
|
1155
|
+
try {
|
|
1156
|
+
if (__destroyBeforeExit) {
|
|
1157
|
+
__onManagedDestroyer(__destroyBeforeExit)
|
|
1158
|
+
await __registerCleanup(__destroyBeforeExit)
|
|
1159
|
+
}
|
|
1160
|
+
let __napiModule
|
|
1161
|
+
;({
|
|
1162
|
+
instance: __napiInstance,
|
|
1163
|
+
napiModule: __napiModule,
|
|
1164
|
+
} = await __emnapiInstantiateNapiModule(__emnapiModule, {
|
|
1165
|
+
context: __emnapiContext,
|
|
1166
|
+
asyncWorkPoolSize: 0,
|
|
1167
|
+
plugins: [__emnapiAsyncWorkPlugin, __emnapiTSFNPlugin],
|
|
1168
|
+
wasi: __wasi,
|
|
1169
|
+
overwriteImports(importObject) {
|
|
1170
|
+
importObject.env = {
|
|
1171
|
+
...importObject.env,
|
|
1172
|
+
...importObject.napi,
|
|
1173
|
+
...importObject.emnapi,
|
|
1174
|
+
memory: __wasmMemory,
|
|
1175
|
+
}
|
|
1176
|
+
return importObject
|
|
1177
|
+
},
|
|
1178
|
+
beforeInit({ instance }) {
|
|
1179
|
+
__napiInstance = instance
|
|
1180
|
+
for (const name of Object.keys(instance.exports)) {
|
|
1181
|
+
if (name.startsWith('__napi_register__')) {
|
|
1182
|
+
instance.exports[name]()
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
}))
|
|
1187
|
+
// `instantiate()` and `createInstance().exports` hand out this object; a
|
|
1188
|
+
// named module export does not travel with it. After the instance host
|
|
1189
|
+
// install, which hands the same object to addon-provided registration
|
|
1190
|
+
// functions that may put anything on it, and inside this `try`, so a
|
|
1191
|
+
// claimed name flips `__lifecycleState` to 'failed' and tears the instance
|
|
1192
|
+
// down rather than escaping a half-built one.
|
|
1193
|
+
__napiStampBindingTarget(__napiModule.exports, __napiBindingTarget)
|
|
1194
|
+
if (__lifecycleState === 'pending') {
|
|
1195
|
+
__lifecycleState = 'succeeded'
|
|
1196
|
+
}
|
|
1197
|
+
__createdInstances += 1
|
|
1198
|
+
__liveInstances += 1
|
|
1199
|
+
return {
|
|
1200
|
+
exports: __napiModule.exports,
|
|
1201
|
+
get memory() {
|
|
1202
|
+
return __wasmMemory
|
|
1203
|
+
},
|
|
1204
|
+
get memoryBytes() {
|
|
1205
|
+
// The Memory outlives the environment, so this stays readable after a
|
|
1206
|
+
// FAILED dispose() (which leaves the instance undisposed and
|
|
1207
|
+
// retryable). It reports 0 only once disposal has actually completed.
|
|
1208
|
+
return __disposed ? 0 : __wasmMemory.buffer.byteLength
|
|
1209
|
+
},
|
|
1210
|
+
get disposed() {
|
|
1211
|
+
return __disposed
|
|
1212
|
+
},
|
|
1213
|
+
dispose: __disposeInstance,
|
|
1214
|
+
}
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
__lifecycleState = 'failed'
|
|
1217
|
+
// Instantiation can fail *after* registration has run, and registration runs
|
|
1218
|
+
// with a live environment: a module-init hook can start async work and then
|
|
1219
|
+
// return an error, and the promise it created may already have escaped into
|
|
1220
|
+
// JavaScript. Settle what the barrier cancels before the context is
|
|
1221
|
+
// destroyed, exactly like dispose() does; destroying without yielding
|
|
1222
|
+
// discards the queue with a null env. Undefined unless something is queued,
|
|
1223
|
+
// so a failure before beforeInit costs no extra turn.
|
|
1224
|
+
let __settlementsUnreached = false
|
|
1225
|
+
try {
|
|
1226
|
+
// Registration can start async work too, and this path destroys the same
|
|
1227
|
+
// environment its completions need. A drain that cannot finish leaves the
|
|
1228
|
+
// work outstanding, so it counts as settlements unreached and stops the
|
|
1229
|
+
// rollback short of destroying, exactly like a failed barrier drain.
|
|
1230
|
+
const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
|
|
1231
|
+
if (__asyncWorkDrained) {
|
|
1232
|
+
await __asyncWorkDrained
|
|
1233
|
+
}
|
|
1234
|
+
const __drained = __prepareForDisposal()
|
|
1235
|
+
if (__drained) {
|
|
1236
|
+
await __drained
|
|
1237
|
+
}
|
|
1238
|
+
} catch (drainError) {
|
|
1239
|
+
__attachCleanupError(error, drainError)
|
|
1240
|
+
__settlementsUnreached = true
|
|
1241
|
+
}
|
|
1242
|
+
let __registrationError
|
|
1243
|
+
let __registrationFailed = false
|
|
1244
|
+
if (!__beforeExitDestroy) {
|
|
1245
|
+
try {
|
|
1246
|
+
// Independent instances are caller-owned while pending and after
|
|
1247
|
+
// success. Register only failed rollback so cleanup remains retryable.
|
|
1248
|
+
await __registerCleanup()
|
|
1249
|
+
} catch (registrationError) {
|
|
1250
|
+
__attachCleanupError(error, registrationError)
|
|
1251
|
+
__registrationError = registrationError
|
|
1252
|
+
__registrationFailed = true
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
if (__settlementsUnreached) {
|
|
1256
|
+
// The barrier or the drain did not finish, so the settlements it queued
|
|
1257
|
+
// are still in the threadsafe-function queue. Destroying now runs the
|
|
1258
|
+
// cleanup hook that drains that queue with a null env and discards it,
|
|
1259
|
+
// stranding a promise that already escaped into JavaScript — with nothing
|
|
1260
|
+
// left that could ever settle it. dispose() refuses to destroy for exactly
|
|
1261
|
+
// this reason (a rejected drain there never reaches the destroy), so this
|
|
1262
|
+
// path refuses too.
|
|
1263
|
+
//
|
|
1264
|
+
// Nothing leaks. The registration just above — and, for the singleton, the
|
|
1265
|
+
// one made before instantiation — leaves this context in
|
|
1266
|
+
// `__managedEmnapiContextDestroyers`, so beforeExit destroys it and
|
|
1267
|
+
// dispose() can retry it. Only the destruction is deferred, and the turns
|
|
1268
|
+
// that pass in the meantime are exactly what the queue needed.
|
|
1269
|
+
try {
|
|
1270
|
+
__registerManagedBeforeExitListener()
|
|
1271
|
+
} catch {}
|
|
1272
|
+
throw error
|
|
1273
|
+
}
|
|
1274
|
+
try {
|
|
1275
|
+
await __destroyManagedOwnedContext()
|
|
1276
|
+
} catch (disposeError) {
|
|
1277
|
+
// Initialization is the primary failure. Preserve it even if cleanup
|
|
1278
|
+
// also fails, while retaining the cleanup error when the value is
|
|
1279
|
+
// extensible and has no existing cause.
|
|
1280
|
+
__attachCleanupError(
|
|
1281
|
+
__registrationFailed ? __registrationError : error,
|
|
1282
|
+
disposeError,
|
|
1283
|
+
)
|
|
1284
|
+
try {
|
|
1285
|
+
__registerManagedBeforeExitListener()
|
|
1286
|
+
} catch {}
|
|
1287
|
+
}
|
|
1288
|
+
throw error
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Create an independent instance. Call and await dispose() when the instance
|
|
1294
|
+
* is no longer needed so emnapi cleanup hooks run deterministically.
|
|
1295
|
+
*
|
|
1296
|
+
* The optional second argument selects this instance's linear memory: either
|
|
1297
|
+
* `memory` (an unshared, single-use WebAssembly.Memory you allocated) or
|
|
1298
|
+
* `initialMemoryPages` / `maximumMemoryPages`, never both. Omitted, the
|
|
1299
|
+
* loader allocates WASM_MEMORY.initialPages..WASM_MEMORY.maximumPages.
|
|
1300
|
+
*
|
|
1301
|
+
* A provided Memory must come from this loader's own realm: the WASI and
|
|
1302
|
+
* emnapi layers underneath identify one with a realm-local `instanceof`, so a
|
|
1303
|
+
* Memory built in a `node:vm` context or another frame is rejected. Every
|
|
1304
|
+
* Memory an instance runs on is single-use, the loader-allocated one included:
|
|
1305
|
+
* `instance.memory` cannot be recycled into a second `createInstance()`.
|
|
1306
|
+
*/
|
|
1307
|
+
export async function createInstance(__wasmInput, __options) {
|
|
1308
|
+
return __createInstance(__wasmInput, __options)
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
let __defaultModulePromise
|
|
1312
|
+
let __defaultInstancePromise
|
|
1313
|
+
let __defaultDisposePromise
|
|
1314
|
+
let __defaultDisposalStarted = false
|
|
1315
|
+
const __defaultManagedDestroyers = new WeakMap()
|
|
1316
|
+
let __moduleDisposePromise
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* Instantiate a module-local singleton. Concurrent and repeated calls
|
|
1320
|
+
* with the same module share one instance and one Memory allocation.
|
|
1321
|
+
*/
|
|
1322
|
+
export function instantiate(__wasmInput) {
|
|
1323
|
+
const __modulePromise = __resolveModule(__wasmInput)
|
|
1324
|
+
if (__moduleLifecycleDestroyDepth !== 0) {
|
|
1325
|
+
void __modulePromise.catch(() => {})
|
|
1326
|
+
return Promise.reject(__createLifecycleReentryError('instantiate'))
|
|
1327
|
+
}
|
|
1328
|
+
if (__moduleDisposePromise) {
|
|
1329
|
+
void __modulePromise.catch(() => {})
|
|
1330
|
+
return __moduleDisposePromise.then(() => instantiate(__modulePromise))
|
|
1331
|
+
}
|
|
1332
|
+
if (__defaultDisposalStarted) {
|
|
1333
|
+
// Observe rejected input immediately, but preserve lifecycle ordering and
|
|
1334
|
+
// error precedence by instantiating only after disposal succeeds. A failed
|
|
1335
|
+
// disposal retains the old instance only so its cleanup can be retried.
|
|
1336
|
+
void __modulePromise.catch(() => {})
|
|
1337
|
+
const __disposePromise = __defaultDisposePromise ?? dispose()
|
|
1338
|
+
return __disposePromise.then(() => instantiate(__modulePromise))
|
|
1339
|
+
}
|
|
1340
|
+
if (!__defaultInstancePromise) {
|
|
1341
|
+
__defaultModulePromise = __modulePromise
|
|
1342
|
+
const __instancePromise = __modulePromise.then((__module) =>
|
|
1343
|
+
__createInstance(
|
|
1344
|
+
__module,
|
|
1345
|
+
undefined,
|
|
1346
|
+
__disposeDefaultInstance,
|
|
1347
|
+
(__managedDestroyer) => {
|
|
1348
|
+
__defaultManagedDestroyers.set(
|
|
1349
|
+
__instancePromise,
|
|
1350
|
+
__managedDestroyer,
|
|
1351
|
+
)
|
|
1352
|
+
},
|
|
1353
|
+
),
|
|
1354
|
+
)
|
|
1355
|
+
__defaultInstancePromise = __instancePromise
|
|
1356
|
+
void __instancePromise.catch(() => {
|
|
1357
|
+
if (__defaultInstancePromise === __instancePromise) {
|
|
1358
|
+
__defaultInstancePromise = undefined
|
|
1359
|
+
__defaultModulePromise = undefined
|
|
1360
|
+
}
|
|
1361
|
+
})
|
|
1362
|
+
return __instancePromise.then((__instance) => __instance.exports)
|
|
1363
|
+
}
|
|
1364
|
+
const __defaultModulePromiseForCall = __defaultModulePromise
|
|
1365
|
+
const __defaultInstancePromiseForCall = __defaultInstancePromise
|
|
1366
|
+
return Promise.all([__defaultModulePromiseForCall, __modulePromise]).then(
|
|
1367
|
+
async ([__defaultModule, __module]) => {
|
|
1368
|
+
if (__defaultModule !== __module) {
|
|
1369
|
+
throw new Error(
|
|
1370
|
+
'instantiate() already owns a different WebAssembly.Module; call dispose() first or use createInstance() for independent instances.',
|
|
1371
|
+
)
|
|
1372
|
+
}
|
|
1373
|
+
return (await __defaultInstancePromiseForCall).exports
|
|
1374
|
+
},
|
|
1375
|
+
)
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
async function __disposeDefaultInstance(__onDestroy) {
|
|
1379
|
+
if (__defaultDisposePromise) {
|
|
1380
|
+
return __defaultDisposePromise
|
|
1381
|
+
}
|
|
1382
|
+
const __instancePromise = __defaultInstancePromise
|
|
1383
|
+
if (!__instancePromise) {
|
|
1384
|
+
__defaultDisposalStarted = false
|
|
1385
|
+
return
|
|
1386
|
+
}
|
|
1387
|
+
__defaultDisposalStarted = true
|
|
1388
|
+
const __disposePromise = (async () => {
|
|
1389
|
+
let __instance
|
|
1390
|
+
try {
|
|
1391
|
+
__instance = await __instancePromise
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
const __managedDestroyer =
|
|
1394
|
+
__defaultManagedDestroyers.get(__instancePromise)
|
|
1395
|
+
if (__managedDestroyer) {
|
|
1396
|
+
__onDestroy?.(__managedDestroyer)
|
|
1397
|
+
}
|
|
1398
|
+
__defaultManagedDestroyers.delete(__instancePromise)
|
|
1399
|
+
throw error
|
|
1400
|
+
}
|
|
1401
|
+
const __managedDestroyer =
|
|
1402
|
+
__defaultManagedDestroyers.get(__instancePromise)
|
|
1403
|
+
if (__managedDestroyer) {
|
|
1404
|
+
__onDestroy?.(__managedDestroyer)
|
|
1405
|
+
}
|
|
1406
|
+
await __instance.dispose()
|
|
1407
|
+
if (__defaultInstancePromise === __instancePromise) {
|
|
1408
|
+
__defaultInstancePromise = undefined
|
|
1409
|
+
__defaultModulePromise = undefined
|
|
1410
|
+
__defaultDisposalStarted = false
|
|
1411
|
+
}
|
|
1412
|
+
__defaultManagedDestroyers.delete(__instancePromise)
|
|
1413
|
+
})()
|
|
1414
|
+
__defaultDisposePromise = __disposePromise
|
|
1415
|
+
try {
|
|
1416
|
+
await __disposePromise
|
|
1417
|
+
} finally {
|
|
1418
|
+
if (__defaultDisposePromise === __disposePromise) {
|
|
1419
|
+
__defaultDisposePromise = undefined
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
async function __dispose() {
|
|
1425
|
+
let __defaultDisposeError
|
|
1426
|
+
let __defaultDisposeFailed = false
|
|
1427
|
+
let __attemptedDefaultDestroyer
|
|
1428
|
+
try {
|
|
1429
|
+
await __disposeDefaultInstance((__destroyer) => {
|
|
1430
|
+
__attemptedDefaultDestroyer = __destroyer
|
|
1431
|
+
})
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
__defaultDisposeError = error
|
|
1434
|
+
__defaultDisposeFailed = true
|
|
1435
|
+
}
|
|
1436
|
+
const __excludedDestroyers = new Set()
|
|
1437
|
+
if (__defaultDisposeFailed && __attemptedDefaultDestroyer) {
|
|
1438
|
+
__excludedDestroyers.add(__attemptedDefaultDestroyer)
|
|
1439
|
+
}
|
|
1440
|
+
try {
|
|
1441
|
+
await __drainManagedEmnapiContexts(__excludedDestroyers)
|
|
1442
|
+
} catch (error) {
|
|
1443
|
+
if (!__defaultDisposeFailed) {
|
|
1444
|
+
throw error
|
|
1445
|
+
}
|
|
1446
|
+
if (error !== __defaultDisposeError) {
|
|
1447
|
+
__attachCleanupError(__defaultDisposeError, error)
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
if (__defaultDisposeFailed) {
|
|
1451
|
+
throw __defaultDisposeError
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* Dispose the singleton created by instantiate(). A later call may create a
|
|
1457
|
+
* fresh instance, including from a different module. This also retries cleanup
|
|
1458
|
+
* retained after a failed initialization rollback.
|
|
1459
|
+
*/
|
|
1460
|
+
export function dispose() {
|
|
1461
|
+
if (__moduleLifecycleDestroyDepth !== 0) {
|
|
1462
|
+
return Promise.reject(__createLifecycleReentryError('dispose'))
|
|
1463
|
+
}
|
|
1464
|
+
if (__moduleDisposePromise) {
|
|
1465
|
+
return __moduleDisposePromise
|
|
1466
|
+
}
|
|
1467
|
+
let __resolveDispose
|
|
1468
|
+
let __rejectDispose
|
|
1469
|
+
const __promise = new Promise((resolve, reject) => {
|
|
1470
|
+
__resolveDispose = resolve
|
|
1471
|
+
__rejectDispose = reject
|
|
1472
|
+
})
|
|
1473
|
+
__moduleDisposePromise = __promise
|
|
1474
|
+
void __dispose().then(__resolveDispose, __rejectDispose)
|
|
1475
|
+
void __promise.then(
|
|
1476
|
+
() => {
|
|
1477
|
+
if (__moduleDisposePromise === __promise) {
|
|
1478
|
+
__moduleDisposePromise = undefined
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
() => {
|
|
1482
|
+
if (__moduleDisposePromise === __promise) {
|
|
1483
|
+
__moduleDisposePromise = undefined
|
|
1484
|
+
}
|
|
1485
|
+
},
|
|
1486
|
+
)
|
|
1487
|
+
return __promise
|
|
1488
|
+
}
|