@phron/image-wasm32-wasip1 0.0.0-stage → 0.1.0
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/README.md +2 -2
- package/package.json +64 -4
- package/phron-image.wasip1-browser.js +1404 -0
- package/phron-image.wasip1-deferred.d.ts +83 -0
- package/phron-image.wasip1-deferred.js +1865 -0
- package/phron-image.wasip1.cjs +1626 -0
- package/phron-image.wasip1.d.cts +76 -0
- package/phron-image.wasm32-wasip1.wasm +0 -0
- package/phron-image.wasm32-wasip1.wasm.d.ts +5 -0
|
@@ -0,0 +1,1865 @@
|
|
|
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 './phron-image.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 './phron-image.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
|
+
// A real, referenced timer rather than a zero-delay macrotask, for the same
|
|
492
|
+
// reason the async-work wait uses one: this polls the addon instead of
|
|
493
|
+
// interleaving with the @emnapi/core dispatch, so a zero-delay turn would spin
|
|
494
|
+
// the loop instead of yielding it.
|
|
495
|
+
const __WASM_RUNTIME_WORK_POLL_INTERVAL_MS = 1
|
|
496
|
+
// Arrivals it takes before the poll paces on the host's timers alone. One
|
|
497
|
+
// proves nothing: a timer armed before the host's timers stopped still fires.
|
|
498
|
+
const __WASM_RUNTIME_WORK_POLL_TRUSTED_ARRIVALS = 2
|
|
499
|
+
// How long a parked turn's own timer must already have been due before a
|
|
500
|
+
// backup that runs calls it dropped. Slack, not a deadline: a timer is due
|
|
501
|
+
// against the event loop's clock, which is read once per iteration, while
|
|
502
|
+
// these are `Date.now()` readings taken part-way through one, so the two
|
|
503
|
+
// drift apart by however long the loop has been inside the current iteration.
|
|
504
|
+
const __WASM_RUNTIME_WORK_POLL_STALL_MS = 50
|
|
505
|
+
// How long a backup itself waits. What is left of it after the slack and one
|
|
506
|
+
// interval — 149 ms — has to cover the *two* poll turns that can separate a
|
|
507
|
+
// parked turn from the last backup armed while the host's timers still
|
|
508
|
+
// worked, so the ceiling on a single turn is half of it. See the invariant on
|
|
509
|
+
// `__armWasmRuntimePollStallBackup`.
|
|
510
|
+
const __WASM_RUNTIME_WORK_POLL_BACKUP_MS = 200
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Pacing state for one runtime-work poll.
|
|
514
|
+
*
|
|
515
|
+
* Per poll, never per module: whether the host's timers arrive is not a
|
|
516
|
+
* property of the module. A host can lose its timers between two disposals,
|
|
517
|
+
* and in the deferred shape every instance shares this module — one healthy
|
|
518
|
+
* instance must not disarm the fallback for the next one.
|
|
519
|
+
*/
|
|
520
|
+
function __createWasmRuntimePollPace() {
|
|
521
|
+
return {
|
|
522
|
+
// Timers armed by *this* poll that have actually arrived.
|
|
523
|
+
arrivals: 0,
|
|
524
|
+
// The turn waiting on a timer alone *right now* — undefined whenever no
|
|
525
|
+
// turn is parked — and when that turn's own timer came due.
|
|
526
|
+
settleTurn: undefined,
|
|
527
|
+
turnTimerDueAt: 0,
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* The backup that ends a turn whose timer is never going to arrive.
|
|
533
|
+
*
|
|
534
|
+
* Once the poll paces on the timer alone it has nothing left to fall back on
|
|
535
|
+
* if the host's timers stop mid-poll: the turn that armed the dead timer is
|
|
536
|
+
* the turn that parks, and a parked poll schedules nothing that could notice.
|
|
537
|
+
* So every turn arms one of these before it yields, and each one compares due
|
|
538
|
+
* times instead of measuring how long the parked turn has been waiting.
|
|
539
|
+
*
|
|
540
|
+
* Invariant: a parked turn is ended by the newest backup that was armed while
|
|
541
|
+
* the host's timers still worked, and a backup ends a turn only when that
|
|
542
|
+
* turn's own timer was already due a whole window before the backup itself.
|
|
543
|
+
* Neither half turns on how far apart the arms happen to fall — what bounds
|
|
544
|
+
* the rescue is how far back that newest live backup is:
|
|
545
|
+
*
|
|
546
|
+
* - *Ends it.* Hosts run timers in due order, so a backup that runs while a
|
|
547
|
+
* turn due a whole window earlier is still parked proves that turn's timer
|
|
548
|
+
* was dropped rather than merely late. That same comparison is what leaves a
|
|
549
|
+
* healthy host alone: there the turn's timer has already run and cleared
|
|
550
|
+
* `settleTurn` before any backup due after it can look.
|
|
551
|
+
* - *Two turns back, not one.* A turn that ended does not prove its own timer
|
|
552
|
+
* arrived: until `…_TRUSTED_ARRIVALS` is reached every turn arms both
|
|
553
|
+
* primitives and the macrotask wins, so such a turn can end with its own
|
|
554
|
+
* timer — and the backup armed one line before it — already dead. The
|
|
555
|
+
* arrival that then flips the poll onto the timer alone can itself be a
|
|
556
|
+
* timer armed before the host's timers died. So the turn that parks can sit
|
|
557
|
+
* two turns past the last live arm, and the newest live backup is due
|
|
558
|
+
* `…_BACKUP_MS` less *two* turn lengths after that turn's own timer.
|
|
559
|
+
* Arming on every turn is what holds it to two, rather than however far back
|
|
560
|
+
* a throttle last let one through.
|
|
561
|
+
* - *Ceiling.* Coverage therefore holds while two consecutive poll turns fit
|
|
562
|
+
* inside `…_BACKUP_MS` less the slack and one interval: 149 ms, so 74 ms
|
|
563
|
+
* per turn (measured: a 74 ms turn is still rescued, a 75 ms one parks).
|
|
564
|
+
* Past that the turn stays parked and the disposal promise never settles.
|
|
565
|
+
* The bound is deliberate: reaching it takes a host that drops timers
|
|
566
|
+
* mid-poll *and* keeps every poll turn busy for more than 74 ms, and neither
|
|
567
|
+
* Node nor WebContainer — the hosts that run the threaded artifact — does
|
|
568
|
+
* the second.
|
|
569
|
+
*
|
|
570
|
+
* The poll then goes back to arming both primitives until two fresh arrivals
|
|
571
|
+
* prove the timers again. A host that stops running the timers it has
|
|
572
|
+
* *already* accepted leaves nothing to fire, and the disposal promise stays
|
|
573
|
+
* pending rather than wedging the thread — the same outcome as a blocking
|
|
574
|
+
* closure that never returns. Unreferenced wherever the host allows it: the
|
|
575
|
+
* poll's own turn timers are what keep the loop alive, never these.
|
|
576
|
+
*/
|
|
577
|
+
function __armWasmRuntimePollStallBackup(__pace) {
|
|
578
|
+
const __setTimer = globalThis.setTimeout
|
|
579
|
+
if (typeof __setTimer !== 'function') {
|
|
580
|
+
// Nothing to back up: `__scheduleTimer` is on the macrotask channel
|
|
581
|
+
// already, and that one cannot park.
|
|
582
|
+
return
|
|
583
|
+
}
|
|
584
|
+
// Read before arming, so this never claims to be due earlier than the timer
|
|
585
|
+
// actually is: a backup ends a turn only when it is provably due after it.
|
|
586
|
+
const __dueAt = Date.now() + __WASM_RUNTIME_WORK_POLL_BACKUP_MS
|
|
587
|
+
let __handle
|
|
588
|
+
try {
|
|
589
|
+
__handle = __setTimer(() => {
|
|
590
|
+
const __settleTurn = __pace.settleTurn
|
|
591
|
+
if (
|
|
592
|
+
!__settleTurn ||
|
|
593
|
+
__pace.turnTimerDueAt > __dueAt - __WASM_RUNTIME_WORK_POLL_STALL_MS
|
|
594
|
+
) {
|
|
595
|
+
// No turn is parked, or the parked one's timer came due too close to
|
|
596
|
+
// this backup to call it dropped — it may still arrive, and the turn
|
|
597
|
+
// that armed it armed a backup due a whole window after *that*.
|
|
598
|
+
return
|
|
599
|
+
}
|
|
600
|
+
__pace.arrivals = 0
|
|
601
|
+
__pace.settleTurn = undefined
|
|
602
|
+
__settleTurn()
|
|
603
|
+
}, __WASM_RUNTIME_WORK_POLL_BACKUP_MS)
|
|
604
|
+
} catch {
|
|
605
|
+
return
|
|
606
|
+
}
|
|
607
|
+
if (__handle && typeof __handle.unref === 'function') {
|
|
608
|
+
try {
|
|
609
|
+
__handle.unref()
|
|
610
|
+
} catch {}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* One turn of the runtime-work poll.
|
|
616
|
+
*
|
|
617
|
+
* `__scheduleTimer` falls back to the macrotask scheduler when `setTimeout` is
|
|
618
|
+
* missing or throws, but not when it is present, returns a handle and never
|
|
619
|
+
* fires — fake timers in a test suite that disposes from an `afterEach`, or a
|
|
620
|
+
* host whose timers belong to an IO context that is already gone. That host
|
|
621
|
+
* would park this poll forever, and the poll is unbounded, so nothing would
|
|
622
|
+
* ever call `…_finish`.
|
|
623
|
+
*
|
|
624
|
+
* Arm both primitives until timers armed by this poll have arrived twice, and
|
|
625
|
+
* let whichever lands first end the turn; the loser resolves nothing. A host
|
|
626
|
+
* with working timers therefore pays the double arming for the first turn or
|
|
627
|
+
* two — the macrotask wins the race, but the timers behind it still arrive and
|
|
628
|
+
* are counted — and paces on the timer alone from then on, instead of spinning
|
|
629
|
+
* the loop on a zero-delay queue. A host whose timers never arrive keeps both,
|
|
630
|
+
* and the macrotask is what keeps the poll moving. A host whose timers stop
|
|
631
|
+
* after proving themselves is caught by `__armWasmRuntimePollStallBackup`,
|
|
632
|
+
* which ends the parked turn and puts this poll back on both.
|
|
633
|
+
*/
|
|
634
|
+
function __yieldWasmRuntimePollTurn(__pace) {
|
|
635
|
+
// Armed before the turn yields, and by every turn: what rescues a parked
|
|
636
|
+
// turn has to have been armed while the host's timers still worked, and the
|
|
637
|
+
// turn that parks is the one whose own timer is already dead.
|
|
638
|
+
__armWasmRuntimePollStallBackup(__pace)
|
|
639
|
+
return new Promise((resolve) => {
|
|
640
|
+
let __settled = false
|
|
641
|
+
const __settle = () => {
|
|
642
|
+
if (__settled) {
|
|
643
|
+
return
|
|
644
|
+
}
|
|
645
|
+
__settled = true
|
|
646
|
+
if (__pace.settleTurn === __settle) {
|
|
647
|
+
// Nothing is parked any more: a backup running later must not read a
|
|
648
|
+
// due time this turn has already answered.
|
|
649
|
+
__pace.settleTurn = undefined
|
|
650
|
+
}
|
|
651
|
+
resolve()
|
|
652
|
+
}
|
|
653
|
+
__scheduleTimer(() => {
|
|
654
|
+
__pace.arrivals++
|
|
655
|
+
__settle()
|
|
656
|
+
}, __WASM_RUNTIME_WORK_POLL_INTERVAL_MS)
|
|
657
|
+
// Read next to the arming it describes; see
|
|
658
|
+
// `__armWasmRuntimePollStallBackup` for what the two due times mean.
|
|
659
|
+
const __turnTimerDueAt = Date.now() + __WASM_RUNTIME_WORK_POLL_INTERVAL_MS
|
|
660
|
+
if (__pace.arrivals < __WASM_RUNTIME_WORK_POLL_TRUSTED_ARRIVALS) {
|
|
661
|
+
__scheduleMacrotask(__settle)
|
|
662
|
+
return
|
|
663
|
+
}
|
|
664
|
+
// Paced by the timer alone from here; the backup is what ends this turn if
|
|
665
|
+
// the timer never arrives.
|
|
666
|
+
__pace.settleTurn = __settle
|
|
667
|
+
__pace.turnTimerDueAt = __turnTimerDueAt
|
|
668
|
+
})
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Yield event-loop turns until the addon reports its runtime work finished.
|
|
673
|
+
*
|
|
674
|
+
* The window between `napi_prepare_wasm_env_cleanup_begin` and
|
|
675
|
+
* `…_finish` — the turns are the entire point of splitting the barrier, because
|
|
676
|
+
* on a threaded artifact the work `…_finish` joins can itself be waiting for a
|
|
677
|
+
* JavaScript turn from this very thread.
|
|
678
|
+
*
|
|
679
|
+
* Unbounded, for the same reason the async-work wait above is: giving up means
|
|
680
|
+
* calling `…_finish`, which joins on this thread, and the work it would join is
|
|
681
|
+
* the work waiting for a turn from this thread — so a bound does not end the
|
|
682
|
+
* wait, it only moves it somewhere the JavaScript thread can no longer be
|
|
683
|
+
* reached. A blocking closure that never returns keeps the disposal promise
|
|
684
|
+
* pending instead. The host contract is in `crates/async-runtime/README.md`: a
|
|
685
|
+
* blocking closure must never wait on a JavaScript turn.
|
|
686
|
+
*/
|
|
687
|
+
async function __pollWasmRuntimeWork(__workPending) {
|
|
688
|
+
const __pace = __createWasmRuntimePollPace()
|
|
689
|
+
for (;;) {
|
|
690
|
+
await __yieldWasmRuntimePollTurn(__pace)
|
|
691
|
+
try {
|
|
692
|
+
if (!__workPending()) {
|
|
693
|
+
return
|
|
694
|
+
}
|
|
695
|
+
} catch {
|
|
696
|
+
// A trap is the only way this fails, and a trapped instance has no
|
|
697
|
+
// reachable work left. Stop polling and finish.
|
|
698
|
+
return
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// How often to re-read `napi_wasm_async_work_pending` while waiting. The wait
|
|
704
|
+
// ends when the addon reports zero, so this only decides how promptly disposal
|
|
705
|
+
// notices — not how long it waits.
|
|
706
|
+
const __WASI_ASYNC_WORK_POLL_INTERVAL_MS = 1
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Settles this instance's outstanding `napi_async_work` before the teardown
|
|
710
|
+
* that would strand it.
|
|
711
|
+
*
|
|
712
|
+
* The same hole the eager loaders had, and the same fix: the environment
|
|
713
|
+
* cleanup barrier covers promise settlements queued on the threadsafe-function
|
|
714
|
+
* queue and says nothing about `napi_async_work`, so destroying the context
|
|
715
|
+
* with a work still outstanding leaves its completion callback with nowhere to
|
|
716
|
+
* run and its promise unsettled forever.
|
|
717
|
+
*
|
|
718
|
+
* This flavor is threadless, so `compute` runs on the JavaScript thread inside
|
|
719
|
+
* the macrotask that dequeued it: while this is running, any outstanding work
|
|
720
|
+
* is queued rather than executing, and `napi_wasm_cancel_pending_async_work`
|
|
721
|
+
* can take all of it. The poll is still what decides when the drain is done —
|
|
722
|
+
* cancellation delivers those completions from a later macrotask, not
|
|
723
|
+
* synchronously.
|
|
724
|
+
*
|
|
725
|
+
* Per instance, from that instance's own exports: two instances of this loader
|
|
726
|
+
* have separate registries and must not wait on each other.
|
|
727
|
+
*
|
|
728
|
+
* Both exports are optional, so an addon built against a napi crate that
|
|
729
|
+
* predates them keeps the previous behavior.
|
|
730
|
+
*
|
|
731
|
+
* Returns nothing when there is nothing outstanding, which keeps disposal
|
|
732
|
+
* synchronous in the common case. No deadline: giving up would destroy the
|
|
733
|
+
* environment with a completion callback still owed.
|
|
734
|
+
*/
|
|
735
|
+
function __drainInstanceAsyncWork(__instance) {
|
|
736
|
+
const __exports = __instance?.exports
|
|
737
|
+
const __pending = __exports?.napi_wasm_async_work_pending
|
|
738
|
+
const __cancelPending = __exports?.napi_wasm_cancel_pending_async_work
|
|
739
|
+
if (typeof __pending !== 'function' || typeof __cancelPending !== 'function') {
|
|
740
|
+
return
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const __readPending = () => {
|
|
744
|
+
try {
|
|
745
|
+
return __pending()
|
|
746
|
+
} catch (__error) {
|
|
747
|
+
// A trap is the only way this call fails: it reads a counter and cannot
|
|
748
|
+
// allocate or call back into JavaScript. A trapped instance can no longer
|
|
749
|
+
// run anything, so its outstanding work is unreachable by definition and
|
|
750
|
+
// best-effort is the honest answer. Anything else means the export is not
|
|
751
|
+
// what this loader thinks it is — a defect worth surfacing.
|
|
752
|
+
if (__error instanceof globalThis.WebAssembly.RuntimeError) {
|
|
753
|
+
return 0
|
|
754
|
+
}
|
|
755
|
+
throw __error
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (!__readPending()) {
|
|
760
|
+
return
|
|
761
|
+
}
|
|
762
|
+
try {
|
|
763
|
+
__cancelPending()
|
|
764
|
+
} catch {
|
|
765
|
+
// Cancellation only bounds the wait. Failing it means waiting for the queue
|
|
766
|
+
// to run instead, which the poll below already does.
|
|
767
|
+
}
|
|
768
|
+
if (!__readPending()) {
|
|
769
|
+
return
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return (async () => {
|
|
773
|
+
while (__readPending()) {
|
|
774
|
+
await new Promise((__resolve) => {
|
|
775
|
+
__scheduleTimer(__resolve, __WASI_ASYNC_WORK_POLL_INTERVAL_MS)
|
|
776
|
+
})
|
|
777
|
+
}
|
|
778
|
+
})()
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function __createLifecycleReentryError(__operation) {
|
|
782
|
+
const __error = new Error(
|
|
783
|
+
__operation +
|
|
784
|
+
'() cannot run while an emnapi Context.destroy() call is still active; await the original cleanup promise instead.',
|
|
785
|
+
)
|
|
786
|
+
__error.code = 'ERR_NAPI_WASI_LIFECYCLE_REENTRY'
|
|
787
|
+
return __error
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const __managedEmnapiContextDestroyers = new Set()
|
|
791
|
+
let __managedCleanupProcess
|
|
792
|
+
let __managedBeforeExitListener
|
|
793
|
+
let __managedDestroyPromise
|
|
794
|
+
let __managedDestroyersInFlight
|
|
795
|
+
let __managedBeforeExitRegistrationRetryCount = 0
|
|
796
|
+
let __managedBeforeExitRegistrationRetryScheduled = false
|
|
797
|
+
let __moduleLifecycleDestroyDepth = 0
|
|
798
|
+
|
|
799
|
+
function __removeManagedEmnapiCleanupListeners() {
|
|
800
|
+
const __process = __managedCleanupProcess
|
|
801
|
+
const __beforeExitListener = __managedBeforeExitListener
|
|
802
|
+
__managedCleanupProcess = undefined
|
|
803
|
+
__managedBeforeExitListener = undefined
|
|
804
|
+
__managedBeforeExitRegistrationRetryCount = 0
|
|
805
|
+
if (__process && __beforeExitListener) {
|
|
806
|
+
try {
|
|
807
|
+
__process.removeListener('beforeExit', __beforeExitListener)
|
|
808
|
+
} catch {}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function __scheduleManagedBeforeExitListenerRegistration() {
|
|
813
|
+
if (
|
|
814
|
+
!__managedCleanupProcess ||
|
|
815
|
+
__managedBeforeExitListener ||
|
|
816
|
+
__managedEmnapiContextDestroyers.size === 0 ||
|
|
817
|
+
__managedBeforeExitRegistrationRetryScheduled ||
|
|
818
|
+
__managedBeforeExitRegistrationRetryCount >= 3
|
|
819
|
+
) {
|
|
820
|
+
return
|
|
821
|
+
}
|
|
822
|
+
__managedBeforeExitRegistrationRetryScheduled = true
|
|
823
|
+
__managedBeforeExitRegistrationRetryCount++
|
|
824
|
+
queueMicrotask(() => {
|
|
825
|
+
__managedBeforeExitRegistrationRetryScheduled = false
|
|
826
|
+
if (
|
|
827
|
+
!__managedCleanupProcess ||
|
|
828
|
+
__managedBeforeExitListener ||
|
|
829
|
+
__managedEmnapiContextDestroyers.size === 0
|
|
830
|
+
) {
|
|
831
|
+
return
|
|
832
|
+
}
|
|
833
|
+
try {
|
|
834
|
+
__registerManagedBeforeExitListener()
|
|
835
|
+
} catch {}
|
|
836
|
+
})
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function __registerManagedBeforeExitListener() {
|
|
840
|
+
if (!__managedCleanupProcess || __managedBeforeExitListener) {
|
|
841
|
+
return
|
|
842
|
+
}
|
|
843
|
+
try {
|
|
844
|
+
__managedCleanupProcess.once(
|
|
845
|
+
'beforeExit',
|
|
846
|
+
__destroyManagedEmnapiContextsBeforeExit,
|
|
847
|
+
)
|
|
848
|
+
} catch (error) {
|
|
849
|
+
__scheduleManagedBeforeExitListenerRegistration()
|
|
850
|
+
throw error
|
|
851
|
+
}
|
|
852
|
+
__managedBeforeExitListener = __destroyManagedEmnapiContextsBeforeExit
|
|
853
|
+
__managedBeforeExitRegistrationRetryCount = 0
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function __settleManagedEmnapiContextDestroy(__promise) {
|
|
857
|
+
if (__managedDestroyPromise === __promise) {
|
|
858
|
+
__managedDestroyPromise = undefined
|
|
859
|
+
__managedDestroyersInFlight = undefined
|
|
860
|
+
}
|
|
861
|
+
if (__managedEmnapiContextDestroyers.size === 0) {
|
|
862
|
+
__removeManagedEmnapiCleanupListeners()
|
|
863
|
+
return
|
|
864
|
+
}
|
|
865
|
+
try {
|
|
866
|
+
__registerManagedBeforeExitListener()
|
|
867
|
+
} catch {}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function __destroyManagedEmnapiContexts(__excludedDestroyers) {
|
|
871
|
+
if (__managedDestroyPromise) {
|
|
872
|
+
return __managedDestroyPromise
|
|
873
|
+
}
|
|
874
|
+
const __destroyers = Array.from(__managedEmnapiContextDestroyers).filter(
|
|
875
|
+
(__destroy) => !__excludedDestroyers?.has(__destroy),
|
|
876
|
+
)
|
|
877
|
+
if (__destroyers.length === 0) {
|
|
878
|
+
return Promise.resolve()
|
|
879
|
+
}
|
|
880
|
+
let __resolveDestroy
|
|
881
|
+
let __rejectDestroy
|
|
882
|
+
const __promise = new Promise((resolve, reject) => {
|
|
883
|
+
__resolveDestroy = resolve
|
|
884
|
+
__rejectDestroy = reject
|
|
885
|
+
})
|
|
886
|
+
__managedDestroyPromise = __promise
|
|
887
|
+
__managedDestroyersInFlight = new Set(__destroyers)
|
|
888
|
+
void Promise.all(
|
|
889
|
+
__destroyers.map((__destroy) => {
|
|
890
|
+
try {
|
|
891
|
+
return Promise.resolve(__destroy()).then(
|
|
892
|
+
() => ({ failed: false }),
|
|
893
|
+
(error) => ({ failed: true, error }),
|
|
894
|
+
)
|
|
895
|
+
} catch (error) {
|
|
896
|
+
return { failed: true, error }
|
|
897
|
+
}
|
|
898
|
+
}),
|
|
899
|
+
).then((__results) => {
|
|
900
|
+
let __primaryError
|
|
901
|
+
let __failed = false
|
|
902
|
+
for (const __result of __results) {
|
|
903
|
+
if (!__result.failed) {
|
|
904
|
+
continue
|
|
905
|
+
}
|
|
906
|
+
if (!__failed) {
|
|
907
|
+
__failed = true
|
|
908
|
+
__primaryError = __result.error
|
|
909
|
+
} else {
|
|
910
|
+
__attachCleanupError(__primaryError, __result.error)
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (__failed) {
|
|
914
|
+
__rejectDestroy(__primaryError)
|
|
915
|
+
} else {
|
|
916
|
+
__resolveDestroy()
|
|
917
|
+
}
|
|
918
|
+
}, __rejectDestroy)
|
|
919
|
+
void __promise.then(
|
|
920
|
+
() => {
|
|
921
|
+
__settleManagedEmnapiContextDestroy(__promise)
|
|
922
|
+
},
|
|
923
|
+
() => {
|
|
924
|
+
__settleManagedEmnapiContextDestroy(__promise)
|
|
925
|
+
},
|
|
926
|
+
)
|
|
927
|
+
return __promise
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
async function __drainManagedEmnapiContexts(__excludedDestroyers) {
|
|
931
|
+
const __attemptedDestroyers = new Set(__excludedDestroyers)
|
|
932
|
+
let __primaryError
|
|
933
|
+
let __failed = false
|
|
934
|
+
while (true) {
|
|
935
|
+
let __promise = __managedDestroyPromise
|
|
936
|
+
let __destroyers = __managedDestroyersInFlight
|
|
937
|
+
if (!__promise) {
|
|
938
|
+
__promise = __destroyManagedEmnapiContexts(__attemptedDestroyers)
|
|
939
|
+
__destroyers = __managedDestroyersInFlight
|
|
940
|
+
if (!__destroyers) {
|
|
941
|
+
break
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
for (const __destroy of __destroyers) {
|
|
945
|
+
__attemptedDestroyers.add(__destroy)
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
await __promise
|
|
949
|
+
} catch (error) {
|
|
950
|
+
if (!__failed) {
|
|
951
|
+
__failed = true
|
|
952
|
+
__primaryError = error
|
|
953
|
+
} else {
|
|
954
|
+
__attachCleanupError(__primaryError, error)
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (__failed) {
|
|
959
|
+
throw __primaryError
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function __destroyManagedEmnapiContextsBeforeExit() {
|
|
964
|
+
// A once listener is consumed before Node invokes it, including when another
|
|
965
|
+
// cleanup batch is still pending.
|
|
966
|
+
__managedBeforeExitListener = undefined
|
|
967
|
+
if (__managedDestroyPromise) {
|
|
968
|
+
return
|
|
969
|
+
}
|
|
970
|
+
void __destroyManagedEmnapiContexts().catch((error) => {
|
|
971
|
+
queueMicrotask(() => {
|
|
972
|
+
throw error
|
|
973
|
+
})
|
|
974
|
+
})
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function __registerManagedEmnapiContext(__process, __destroy) {
|
|
978
|
+
__managedEmnapiContextDestroyers.add(__destroy)
|
|
979
|
+
if (
|
|
980
|
+
!__managedCleanupProcess &&
|
|
981
|
+
__process &&
|
|
982
|
+
typeof __process.once === 'function' &&
|
|
983
|
+
typeof __process.removeListener === 'function'
|
|
984
|
+
) {
|
|
985
|
+
__managedCleanupProcess = __process
|
|
986
|
+
}
|
|
987
|
+
let __registered = true
|
|
988
|
+
return () => {
|
|
989
|
+
if (!__registered) {
|
|
990
|
+
return
|
|
991
|
+
}
|
|
992
|
+
__registered = false
|
|
993
|
+
__managedEmnapiContextDestroyers.delete(__destroy)
|
|
994
|
+
if (__managedEmnapiContextDestroyers.size === 0) {
|
|
995
|
+
__removeManagedEmnapiCleanupListeners()
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
async function __createManagedEmnapiContext(
|
|
1001
|
+
__prepareEnvCleanup,
|
|
1002
|
+
__isPreparingEnvCleanup,
|
|
1003
|
+
) {
|
|
1004
|
+
const __process =
|
|
1005
|
+
typeof process === 'object' && process !== null ? process : undefined
|
|
1006
|
+
const __finishAutoDestroyCapture =
|
|
1007
|
+
__captureEmnapiAutoDestroyListener(__process)
|
|
1008
|
+
let __emnapiContext
|
|
1009
|
+
let __contextInitializationError
|
|
1010
|
+
let __contextInitializationFailed = false
|
|
1011
|
+
try {
|
|
1012
|
+
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
|
1013
|
+
__emnapiCreateContext({ autoDestroy: false }),
|
|
1014
|
+
__prepareEnvCleanup,
|
|
1015
|
+
__isPreparingEnvCleanup,
|
|
1016
|
+
)
|
|
1017
|
+
// emnapi 2.x still registers an unconditional process.once('beforeExit')
|
|
1018
|
+
// auto-destroy listener on Node hosts, and suppressDestroy() only
|
|
1019
|
+
// neutralizes its callback without removing it. This loader must stay
|
|
1020
|
+
// side-effect free per instance, so the listener is captured and removed;
|
|
1021
|
+
// suppressDestroy() remains the safety net when removal is unavailable.
|
|
1022
|
+
__emnapiContext.suppressDestroy()
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
__contextInitializationError = error
|
|
1025
|
+
__contextInitializationFailed = true
|
|
1026
|
+
} finally {
|
|
1027
|
+
// Remove only the exact emnapi callback captured above.
|
|
1028
|
+
__finishAutoDestroyCapture?.()
|
|
1029
|
+
}
|
|
1030
|
+
if (__emnapiContext === undefined) {
|
|
1031
|
+
throw __contextInitializationError
|
|
1032
|
+
}
|
|
1033
|
+
let __disposed = false
|
|
1034
|
+
let __destroying = false
|
|
1035
|
+
let __destroyPromise
|
|
1036
|
+
let __cleanupRegistered = false
|
|
1037
|
+
let __unregisterCleanup
|
|
1038
|
+
const __destroy = (__blocksModuleLifecycle = false) => {
|
|
1039
|
+
if (__disposed) {
|
|
1040
|
+
return
|
|
1041
|
+
}
|
|
1042
|
+
if (__destroying) {
|
|
1043
|
+
throw __createLifecycleReentryError('dispose')
|
|
1044
|
+
}
|
|
1045
|
+
if (__destroyPromise) {
|
|
1046
|
+
return __destroyPromise
|
|
1047
|
+
}
|
|
1048
|
+
__destroying = true
|
|
1049
|
+
let __result
|
|
1050
|
+
const __finishDestroyInvocation = () => {
|
|
1051
|
+
__destroying = false
|
|
1052
|
+
}
|
|
1053
|
+
const __finishModuleLifecycleDestroy = () => {
|
|
1054
|
+
if (__blocksModuleLifecycle) {
|
|
1055
|
+
__blocksModuleLifecycle = false
|
|
1056
|
+
__moduleLifecycleDestroyDepth--
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
if (__blocksModuleLifecycle) {
|
|
1060
|
+
__moduleLifecycleDestroyDepth++
|
|
1061
|
+
}
|
|
1062
|
+
try {
|
|
1063
|
+
// Context.destroy() disables JS before cleanup hooks run, so settle
|
|
1064
|
+
// runtime-owned promises while this environment can still call JS.
|
|
1065
|
+
__prepareEnvCleanup?.()
|
|
1066
|
+
if (__isPreparingEnvCleanup?.()) {
|
|
1067
|
+
// Reached from inside the barrier, so `Context.destroy()` below would
|
|
1068
|
+
// hit the wrapper's in-flight no-op. Recording that as a completed
|
|
1069
|
+
// destroy is what makes the frame that *did* start the barrier skip the
|
|
1070
|
+
// real one afterwards, leaving the context retained with its cleanup
|
|
1071
|
+
// hooks unrun. Refuse instead: nothing is flagged, the context stays
|
|
1072
|
+
// registered for managed beforeExit cleanup, and a later destroy still
|
|
1073
|
+
// works. dispose() coalesces reentrancy before it can get here, so this
|
|
1074
|
+
// is the backstop for any other caller that manages to. A handshake
|
|
1075
|
+
// parked between the two halves of the barrier does not reach here —
|
|
1076
|
+
// `__prepareEnvCleanup` closes one rather than skipping it.
|
|
1077
|
+
throw __createLifecycleReentryError('dispose')
|
|
1078
|
+
}
|
|
1079
|
+
__result = __emnapiContext.destroy()
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
__finishDestroyInvocation()
|
|
1082
|
+
__finishModuleLifecycleDestroy()
|
|
1083
|
+
throw error
|
|
1084
|
+
}
|
|
1085
|
+
let __then
|
|
1086
|
+
try {
|
|
1087
|
+
if (
|
|
1088
|
+
__result !== null &&
|
|
1089
|
+
(typeof __result === 'object' || typeof __result === 'function')
|
|
1090
|
+
) {
|
|
1091
|
+
__then = __result.then
|
|
1092
|
+
}
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
__finishDestroyInvocation()
|
|
1095
|
+
__finishModuleLifecycleDestroy()
|
|
1096
|
+
throw error
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof __then === 'function') {
|
|
1099
|
+
let __resolveResult
|
|
1100
|
+
let __rejectResult
|
|
1101
|
+
const __resultPromise = new Promise((resolve, reject) => {
|
|
1102
|
+
__resolveResult = resolve
|
|
1103
|
+
__rejectResult = reject
|
|
1104
|
+
})
|
|
1105
|
+
const __promise = __resultPromise.then(
|
|
1106
|
+
(value) => {
|
|
1107
|
+
__finishDestroyInvocation()
|
|
1108
|
+
__finishModuleLifecycleDestroy()
|
|
1109
|
+
__disposed = true
|
|
1110
|
+
__destroyPromise = undefined
|
|
1111
|
+
__unregisterCleanup?.()
|
|
1112
|
+
return value
|
|
1113
|
+
},
|
|
1114
|
+
(error) => {
|
|
1115
|
+
__finishDestroyInvocation()
|
|
1116
|
+
__finishModuleLifecycleDestroy()
|
|
1117
|
+
__destroyPromise = undefined
|
|
1118
|
+
throw error
|
|
1119
|
+
},
|
|
1120
|
+
)
|
|
1121
|
+
__destroyPromise = __promise
|
|
1122
|
+
try {
|
|
1123
|
+
Reflect.apply(__then, __result, [__resolveResult, __rejectResult])
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
__rejectResult(error)
|
|
1126
|
+
}
|
|
1127
|
+
return __promise
|
|
1128
|
+
}
|
|
1129
|
+
__finishDestroyInvocation()
|
|
1130
|
+
__finishModuleLifecycleDestroy()
|
|
1131
|
+
__disposed = true
|
|
1132
|
+
__unregisterCleanup?.()
|
|
1133
|
+
}
|
|
1134
|
+
const __destroyForModuleLifecycle = () => __destroy(true)
|
|
1135
|
+
const __registerCleanup = (
|
|
1136
|
+
__beforeExitDestroy = __destroyForModuleLifecycle,
|
|
1137
|
+
) => {
|
|
1138
|
+
if (__cleanupRegistered || __disposed) {
|
|
1139
|
+
return
|
|
1140
|
+
}
|
|
1141
|
+
__unregisterCleanup = __registerManagedEmnapiContext(
|
|
1142
|
+
__process,
|
|
1143
|
+
__beforeExitDestroy,
|
|
1144
|
+
)
|
|
1145
|
+
__cleanupRegistered = true
|
|
1146
|
+
__registerManagedBeforeExitListener()
|
|
1147
|
+
}
|
|
1148
|
+
if (__contextInitializationFailed) {
|
|
1149
|
+
let __registrationError
|
|
1150
|
+
let __registrationFailed = false
|
|
1151
|
+
try {
|
|
1152
|
+
__registerCleanup()
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
__attachCleanupError(__contextInitializationError, error)
|
|
1155
|
+
__registrationError = error
|
|
1156
|
+
__registrationFailed = true
|
|
1157
|
+
}
|
|
1158
|
+
try {
|
|
1159
|
+
await __destroyForModuleLifecycle()
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
__attachCleanupError(
|
|
1162
|
+
__registrationFailed
|
|
1163
|
+
? __registrationError
|
|
1164
|
+
: __contextInitializationError,
|
|
1165
|
+
error,
|
|
1166
|
+
)
|
|
1167
|
+
try {
|
|
1168
|
+
__registerManagedBeforeExitListener()
|
|
1169
|
+
} catch {}
|
|
1170
|
+
}
|
|
1171
|
+
throw __contextInitializationError
|
|
1172
|
+
}
|
|
1173
|
+
return {
|
|
1174
|
+
context: __emnapiContext,
|
|
1175
|
+
destroy: __destroy,
|
|
1176
|
+
destroyForModuleLifecycle: __destroyForModuleLifecycle,
|
|
1177
|
+
registerCleanup: __registerCleanup,
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
async function __createInstance(
|
|
1182
|
+
__wasmInput,
|
|
1183
|
+
__options,
|
|
1184
|
+
__beforeExitDestroy,
|
|
1185
|
+
__onManagedDestroyer,
|
|
1186
|
+
) {
|
|
1187
|
+
const __module = await __resolveModule(__wasmInput)
|
|
1188
|
+
const __emnapiModule = await __normalizeModuleForEmnapi(__module)
|
|
1189
|
+
const __wasi = new __WASI({
|
|
1190
|
+
version: 'preview1',
|
|
1191
|
+
})
|
|
1192
|
+
// The wasm module is linked with `--import-memory`, so a Memory must be
|
|
1193
|
+
// provided. It is resolved here in function scope (workerd bans global scope
|
|
1194
|
+
// allocation) and is never shared (no threads, no SharedArrayBuffer).
|
|
1195
|
+
// Resolve it before the emnapi context so a rejected option bag or a host
|
|
1196
|
+
// memory-limit failure cannot leak a context that never reaches
|
|
1197
|
+
// instantiation.
|
|
1198
|
+
const __wasmMemory = __resolveInstanceMemory(__options)
|
|
1199
|
+
let __lifecycleState = 'pending'
|
|
1200
|
+
let __destroyEmnapiContext
|
|
1201
|
+
let __destroyOwnedContext
|
|
1202
|
+
let __destroyManagedOwnedContext
|
|
1203
|
+
let __napiInstance
|
|
1204
|
+
let __wasmEnvCleanupRan = false
|
|
1205
|
+
let __wasmEnvCleanupPrepared = false
|
|
1206
|
+
let __wasmEnvCleanupPreparing = false
|
|
1207
|
+
// The closer for a barrier parked between `…_begin` and `…_finish`, set only
|
|
1208
|
+
// while that window is open. `__wasmEnvCleanupPreparing` cannot tell that
|
|
1209
|
+
// apart from a purely synchronous frame, which must not be re-entered and
|
|
1210
|
+
// which nothing outside it can finish; this window spans real event-loop
|
|
1211
|
+
// turns, so a caller that cannot yield — the managed beforeExit teardown —
|
|
1212
|
+
// can land inside it, and can close it. See `__prepareEnvCleanup`.
|
|
1213
|
+
let __finishParkedEnvCleanup
|
|
1214
|
+
// Raised while a caller that can still yield is driving the barrier, so the
|
|
1215
|
+
// queue it leaves behind is expected rather than lost.
|
|
1216
|
+
let __wasmEnvCleanupYielding = false
|
|
1217
|
+
let __wasmEnvSettlementLossReported = false
|
|
1218
|
+
let __wasmEnvCleanupDrained = false
|
|
1219
|
+
let __wasmEnvCleanupDrainPromise
|
|
1220
|
+
const __isPreparingEnvCleanup = () => __wasmEnvCleanupPreparing
|
|
1221
|
+
/**
|
|
1222
|
+
* Say so when the barrier leaves settlements queued and nothing is left that
|
|
1223
|
+
* could deliver them.
|
|
1224
|
+
*
|
|
1225
|
+
* Only `dispose()` and the initialization rollback yield the event-loop turns
|
|
1226
|
+
* @emnapi/core needs to dispatch its queue. Every other caller of the barrier
|
|
1227
|
+
* destroys in the same turn — a raw `Context.destroy()`, the managed
|
|
1228
|
+
* beforeExit teardown — and `Context.destroy()` runs the threadsafe
|
|
1229
|
+
* function's cleanup hook, which drains that queue with a null env and
|
|
1230
|
+
* discards it. The promises those settlements were for then hang forever,
|
|
1231
|
+
* silently.
|
|
1232
|
+
*
|
|
1233
|
+
* Loud, once, and never throwing: this runs from inside `Context.destroy()`,
|
|
1234
|
+
* where throwing would take the whole teardown down with it. Destroying
|
|
1235
|
+
* anyway is still the right trade — the queue is already unreachable by then.
|
|
1236
|
+
*/
|
|
1237
|
+
const __reportUnreachedSettlements = () => {
|
|
1238
|
+
if (__wasmEnvCleanupYielding || __wasmEnvSettlementLossReported) {
|
|
1239
|
+
return
|
|
1240
|
+
}
|
|
1241
|
+
const __pending = __napiInstance?.exports.napi_wasm_env_cleanup_pending
|
|
1242
|
+
if (typeof __pending !== 'function') {
|
|
1243
|
+
return
|
|
1244
|
+
}
|
|
1245
|
+
let __queued
|
|
1246
|
+
try {
|
|
1247
|
+
__queued = __pending()
|
|
1248
|
+
} catch {
|
|
1249
|
+
return
|
|
1250
|
+
}
|
|
1251
|
+
if (!__queued) {
|
|
1252
|
+
return
|
|
1253
|
+
}
|
|
1254
|
+
__wasmEnvSettlementLossReported = true
|
|
1255
|
+
try {
|
|
1256
|
+
const __consoleHost = globalThis.console
|
|
1257
|
+
if (__consoleHost && typeof __consoleHost.error === 'function') {
|
|
1258
|
+
__consoleHost.error(
|
|
1259
|
+
'napi-rs: the wasm environment is being destroyed with ' +
|
|
1260
|
+
__queued +
|
|
1261
|
+
' queued promise settlement(s). Context.destroy() discards them, so those promises never settle. Dispose the instance instead: only dispose() yields the event-loop turns the settlements need.',
|
|
1262
|
+
)
|
|
1263
|
+
}
|
|
1264
|
+
} catch {}
|
|
1265
|
+
}
|
|
1266
|
+
const __prepareEnvCleanup = () => {
|
|
1267
|
+
if (__wasmEnvCleanupPrepared) {
|
|
1268
|
+
return
|
|
1269
|
+
}
|
|
1270
|
+
// A handshake parked between its two halves is one this frame can close,
|
|
1271
|
+
// and must: every caller of this is about to destroy the context, and the
|
|
1272
|
+
// turns the poll is waiting for will not come. Closing it runs `…_finish`,
|
|
1273
|
+
// which is the call that joins, so this degrades to exactly the single
|
|
1274
|
+
// call below. Leaving it open destroys the context with the barrier still
|
|
1275
|
+
// raised and the runtime never joined.
|
|
1276
|
+
const __finishParked = __finishParkedEnvCleanup
|
|
1277
|
+
if (__finishParked !== undefined) {
|
|
1278
|
+
__finishParked()
|
|
1279
|
+
__reportUnreachedSettlements()
|
|
1280
|
+
return
|
|
1281
|
+
}
|
|
1282
|
+
if (__wasmEnvCleanupPreparing) {
|
|
1283
|
+
return
|
|
1284
|
+
}
|
|
1285
|
+
const __prepareWasmEnvCleanup =
|
|
1286
|
+
__napiInstance?.exports.napi_prepare_wasm_env_cleanup
|
|
1287
|
+
if (typeof __prepareWasmEnvCleanup === 'function') {
|
|
1288
|
+
// The addon settles the promises it cancels synchronously, under a
|
|
1289
|
+
// non-reentrant lifecycle mutex: anything a promise hook calls from in
|
|
1290
|
+
// here must not reach this export again.
|
|
1291
|
+
__wasmEnvCleanupPreparing = true
|
|
1292
|
+
try {
|
|
1293
|
+
__prepareWasmEnvCleanup()
|
|
1294
|
+
} finally {
|
|
1295
|
+
__wasmEnvCleanupPreparing = false
|
|
1296
|
+
}
|
|
1297
|
+
__wasmEnvCleanupRan = true
|
|
1298
|
+
__reportUnreachedSettlements()
|
|
1299
|
+
}
|
|
1300
|
+
__wasmEnvCleanupPrepared = true
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* The barrier for the callers that can yield: `__prepareEnvCleanup` with real
|
|
1304
|
+
* event-loop turns in the middle.
|
|
1305
|
+
*
|
|
1306
|
+
* `napi_prepare_wasm_env_cleanup` waits — it returns only once the addon's
|
|
1307
|
+
* async runtime has quiesced, and the work it waits for can itself be waiting
|
|
1308
|
+
* for a JavaScript turn from this thread. The addon's two-phase form splits
|
|
1309
|
+
* that: `…_begin` stops the runtime without joining and reports whether
|
|
1310
|
+
* anything is still live, `napi_wasm_runtime_work_pending` answers that again
|
|
1311
|
+
* without blocking, and `…_finish` joins.
|
|
1312
|
+
*
|
|
1313
|
+
* The poll is unbounded — see `__pollWasmRuntimeWork` — but a caller that
|
|
1314
|
+
* cannot yield closes the handshake itself rather than waiting for it, so
|
|
1315
|
+
* `…_finish` still runs on every teardown path. Feature-detected like every
|
|
1316
|
+
* other export here, and returns nothing whenever the handshake finished
|
|
1317
|
+
* without yielding.
|
|
1318
|
+
*/
|
|
1319
|
+
const __prepareEnvCleanupWithTurns = () => {
|
|
1320
|
+
if (__wasmEnvCleanupPrepared || __wasmEnvCleanupPreparing) {
|
|
1321
|
+
return
|
|
1322
|
+
}
|
|
1323
|
+
const __exports = __napiInstance?.exports
|
|
1324
|
+
const __begin = __exports?.napi_prepare_wasm_env_cleanup_begin
|
|
1325
|
+
const __finish = __exports?.napi_prepare_wasm_env_cleanup_finish
|
|
1326
|
+
if (typeof __begin !== 'function' || typeof __finish !== 'function') {
|
|
1327
|
+
// No split to use. The settlement drain still follows this, so the queue
|
|
1328
|
+
// the single call leaves behind is expected rather than lost.
|
|
1329
|
+
__wasmEnvCleanupYielding = true
|
|
1330
|
+
try {
|
|
1331
|
+
__prepareEnvCleanup()
|
|
1332
|
+
} finally {
|
|
1333
|
+
__wasmEnvCleanupYielding = false
|
|
1334
|
+
}
|
|
1335
|
+
return
|
|
1336
|
+
}
|
|
1337
|
+
const __workPending = __exports?.napi_wasm_runtime_work_pending
|
|
1338
|
+
// The in-flight flag stays raised across the turns below, so a `destroy()`
|
|
1339
|
+
// from one of the JavaScript handlers they run is the same no-op it is
|
|
1340
|
+
// inside the single call: the barrier is up and the runtime is
|
|
1341
|
+
// mid-teardown, and destroying between the halves would strand exactly what
|
|
1342
|
+
// this delivers.
|
|
1343
|
+
__wasmEnvCleanupPreparing = true
|
|
1344
|
+
let __live
|
|
1345
|
+
try {
|
|
1346
|
+
__live = __begin()
|
|
1347
|
+
} catch (__error) {
|
|
1348
|
+
__wasmEnvCleanupPreparing = false
|
|
1349
|
+
throw __error
|
|
1350
|
+
}
|
|
1351
|
+
__wasmEnvCleanupRan = true
|
|
1352
|
+
const __finishEnvCleanup = () => {
|
|
1353
|
+
if (__wasmEnvCleanupPrepared) {
|
|
1354
|
+
// Already closed by a caller that could not yield. `…_finish` is
|
|
1355
|
+
// idempotent, but the flags it lowers are not.
|
|
1356
|
+
return
|
|
1357
|
+
}
|
|
1358
|
+
__finishParkedEnvCleanup = undefined
|
|
1359
|
+
try {
|
|
1360
|
+
__finish()
|
|
1361
|
+
} finally {
|
|
1362
|
+
__wasmEnvCleanupPreparing = false
|
|
1363
|
+
}
|
|
1364
|
+
__wasmEnvCleanupPrepared = true
|
|
1365
|
+
}
|
|
1366
|
+
if (!__live || typeof __workPending !== 'function') {
|
|
1367
|
+
__finishEnvCleanup()
|
|
1368
|
+
return
|
|
1369
|
+
}
|
|
1370
|
+
// Publish the closer before yielding: from here until `__finishEnvCleanup`
|
|
1371
|
+
// runs, a caller that cannot yield is entitled to end this handshake.
|
|
1372
|
+
__finishParkedEnvCleanup = __finishEnvCleanup
|
|
1373
|
+
return __pollWasmRuntimeWork(__workPending).then(
|
|
1374
|
+
__finishEnvCleanup,
|
|
1375
|
+
__finishEnvCleanup,
|
|
1376
|
+
)
|
|
1377
|
+
}
|
|
1378
|
+
// The barrier + settlement drain, hoisted out of the context destroyer so the
|
|
1379
|
+
// drain can yield without widening the destroyer's reentry window. Both
|
|
1380
|
+
// yielding paths run it — dispose() and the initialization-failure rollback.
|
|
1381
|
+
// The destroyer still runs the barrier itself (idempotently) for the one path
|
|
1382
|
+
// that cannot yield: managed beforeExit cleanup of an instance whose rollback
|
|
1383
|
+
// is being retried.
|
|
1384
|
+
//
|
|
1385
|
+
// "Drained" is recorded only once a wait has actually finished. Scheduling a
|
|
1386
|
+
// macrotask can fail — a host-provided or patched `setImmediate` that throws
|
|
1387
|
+
// is enough — and dispose() stays retryable after it rejects, so marking the
|
|
1388
|
+
// drain complete up front would make the retry skip it and destroy the context
|
|
1389
|
+
// with the barrier's settlements still queued.
|
|
1390
|
+
const __drainAfterEnvCleanup = () => {
|
|
1391
|
+
if (!__wasmEnvCleanupRan) {
|
|
1392
|
+
return
|
|
1393
|
+
}
|
|
1394
|
+
const __drained = __drainWasmEnvCleanup(__napiInstance)
|
|
1395
|
+
if (!__drained || typeof __drained.then !== 'function') {
|
|
1396
|
+
__wasmEnvCleanupDrained = true
|
|
1397
|
+
return
|
|
1398
|
+
}
|
|
1399
|
+
return __drained.then((__value) => {
|
|
1400
|
+
__wasmEnvCleanupDrained = true
|
|
1401
|
+
return __value
|
|
1402
|
+
})
|
|
1403
|
+
}
|
|
1404
|
+
const __prepareForDisposal = () => {
|
|
1405
|
+
if (__wasmEnvCleanupDrained) {
|
|
1406
|
+
return
|
|
1407
|
+
}
|
|
1408
|
+
if (__wasmEnvCleanupDrainPromise) {
|
|
1409
|
+
return __wasmEnvCleanupDrainPromise
|
|
1410
|
+
}
|
|
1411
|
+
// The barrier itself can yield now, so the memo below has to cover it too:
|
|
1412
|
+
// a reentrant caller must join this handshake rather than start a second
|
|
1413
|
+
// one while the first is parked between the two halves.
|
|
1414
|
+
const __prepared = __prepareEnvCleanupWithTurns()
|
|
1415
|
+
const __settled =
|
|
1416
|
+
__prepared && typeof __prepared.then === 'function'
|
|
1417
|
+
? __prepared.then(__drainAfterEnvCleanup)
|
|
1418
|
+
: __drainAfterEnvCleanup()
|
|
1419
|
+
if (!__settled || typeof __settled.then !== 'function') {
|
|
1420
|
+
return
|
|
1421
|
+
}
|
|
1422
|
+
const __tracked = __settled.then(
|
|
1423
|
+
(__value) => {
|
|
1424
|
+
__wasmEnvCleanupDrainPromise = undefined
|
|
1425
|
+
return __value
|
|
1426
|
+
},
|
|
1427
|
+
(__error) => {
|
|
1428
|
+
__wasmEnvCleanupDrainPromise = undefined
|
|
1429
|
+
throw __error
|
|
1430
|
+
},
|
|
1431
|
+
)
|
|
1432
|
+
__wasmEnvCleanupDrainPromise = __tracked
|
|
1433
|
+
return __tracked
|
|
1434
|
+
}
|
|
1435
|
+
let __disposed = false
|
|
1436
|
+
const __runInstanceDisposal = async () => {
|
|
1437
|
+
if (__lifecycleState !== 'failed') {
|
|
1438
|
+
__lifecycleState = 'disposal'
|
|
1439
|
+
}
|
|
1440
|
+
// Outstanding async work first, while the environment is still completely
|
|
1441
|
+
// live: its completion callbacks run addon code, and the barrier and
|
|
1442
|
+
// `Context.destroy()` below each take that away. Undefined unless work is
|
|
1443
|
+
// outstanding.
|
|
1444
|
+
const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
|
|
1445
|
+
if (__asyncWorkDrained) {
|
|
1446
|
+
await __asyncWorkDrained
|
|
1447
|
+
}
|
|
1448
|
+
// Then settle what the barrier cancelled, before the environment stops
|
|
1449
|
+
// accepting JavaScript calls. Undefined unless something is queued, so
|
|
1450
|
+
// an idle disposal is not delayed by a single turn.
|
|
1451
|
+
const __drained = __prepareForDisposal()
|
|
1452
|
+
if (__drained) {
|
|
1453
|
+
await __drained
|
|
1454
|
+
}
|
|
1455
|
+
const __result = await (__beforeExitDestroy
|
|
1456
|
+
? __destroyManagedOwnedContext()
|
|
1457
|
+
: __destroyOwnedContext())
|
|
1458
|
+
// Only a completed destroy retires the instance; a throw above leaves
|
|
1459
|
+
// the counter untouched so a retried dispose() cannot double-decrement.
|
|
1460
|
+
if (!__disposed) {
|
|
1461
|
+
__disposed = true
|
|
1462
|
+
__liveInstances -= 1
|
|
1463
|
+
}
|
|
1464
|
+
return __result
|
|
1465
|
+
}
|
|
1466
|
+
let __instanceDisposePromise
|
|
1467
|
+
/**
|
|
1468
|
+
* The disposal frame runs the barrier, and the barrier settles the promises it
|
|
1469
|
+
* cancels synchronously — so a promise hook firing inside it can call this
|
|
1470
|
+
* same instance's dispose() again while the first call is still in its drain.
|
|
1471
|
+
* That nested call finds the barrier flagged in flight, prepares nothing,
|
|
1472
|
+
* drains nothing, and falls straight through to the context destroyer, whose
|
|
1473
|
+
* `Context.destroy()` hits the wrapper's in-flight no-op. It would record a
|
|
1474
|
+
* destruction that never happened, and the outer frame would then skip the
|
|
1475
|
+
* real one: both disposals resolve, no cleanup hook runs, the context stays
|
|
1476
|
+
* retained.
|
|
1477
|
+
*
|
|
1478
|
+
* Memoize before any of that starts, exactly like the eager loaders'
|
|
1479
|
+
* `__disposeWasiBinding`, so there is only ever one disposal frame per
|
|
1480
|
+
* instance and a reentrant caller awaits it instead of racing it. Cleared on
|
|
1481
|
+
* rejection: a drain that failed has to stay retryable.
|
|
1482
|
+
*/
|
|
1483
|
+
const __disposeInstance = () => {
|
|
1484
|
+
if (__instanceDisposePromise) {
|
|
1485
|
+
return __instanceDisposePromise
|
|
1486
|
+
}
|
|
1487
|
+
let __resolveDispose
|
|
1488
|
+
let __rejectDispose
|
|
1489
|
+
const __disposePromise = new Promise((__resolve, __reject) => {
|
|
1490
|
+
__resolveDispose = __resolve
|
|
1491
|
+
__rejectDispose = __reject
|
|
1492
|
+
})
|
|
1493
|
+
__instanceDisposePromise = __disposePromise
|
|
1494
|
+
__runInstanceDisposal().then(__resolveDispose, (__error) => {
|
|
1495
|
+
__instanceDisposePromise = undefined
|
|
1496
|
+
__rejectDispose(__error)
|
|
1497
|
+
})
|
|
1498
|
+
return __disposePromise
|
|
1499
|
+
}
|
|
1500
|
+
const __destroyBeforeExit = __beforeExitDestroy
|
|
1501
|
+
? async () => {
|
|
1502
|
+
if (__lifecycleState === 'failed') {
|
|
1503
|
+
await __destroyManagedOwnedContext()
|
|
1504
|
+
return
|
|
1505
|
+
}
|
|
1506
|
+
__lifecycleState = 'disposal'
|
|
1507
|
+
try {
|
|
1508
|
+
await __beforeExitDestroy()
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
if (__lifecycleState !== 'failed') {
|
|
1511
|
+
throw error
|
|
1512
|
+
}
|
|
1513
|
+
// The singleton's initialization rejection is already observable
|
|
1514
|
+
// through instantiate() and dispose(). Managed beforeExit cleanup
|
|
1515
|
+
// owns only context destruction, including retrying a failed rollback.
|
|
1516
|
+
await __destroyManagedOwnedContext()
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
: undefined
|
|
1520
|
+
const {
|
|
1521
|
+
context: __emnapiContext,
|
|
1522
|
+
destroy,
|
|
1523
|
+
destroyForModuleLifecycle,
|
|
1524
|
+
registerCleanup: __registerCleanup,
|
|
1525
|
+
} = await __createManagedEmnapiContext(
|
|
1526
|
+
__prepareEnvCleanup,
|
|
1527
|
+
__isPreparingEnvCleanup,
|
|
1528
|
+
)
|
|
1529
|
+
__destroyEmnapiContext = destroy
|
|
1530
|
+
__destroyOwnedContext = () => __destroyEmnapiContext()
|
|
1531
|
+
__destroyManagedOwnedContext = destroyForModuleLifecycle
|
|
1532
|
+
try {
|
|
1533
|
+
if (__destroyBeforeExit) {
|
|
1534
|
+
__onManagedDestroyer(__destroyBeforeExit)
|
|
1535
|
+
await __registerCleanup(__destroyBeforeExit)
|
|
1536
|
+
}
|
|
1537
|
+
let __napiModule
|
|
1538
|
+
;({
|
|
1539
|
+
instance: __napiInstance,
|
|
1540
|
+
napiModule: __napiModule,
|
|
1541
|
+
} = await __emnapiInstantiateNapiModule(__emnapiModule, {
|
|
1542
|
+
context: __emnapiContext,
|
|
1543
|
+
asyncWorkPoolSize: 0,
|
|
1544
|
+
plugins: [__emnapiAsyncWorkPlugin, __emnapiTSFNPlugin],
|
|
1545
|
+
wasi: __wasi,
|
|
1546
|
+
overwriteImports(importObject) {
|
|
1547
|
+
importObject.env = {
|
|
1548
|
+
...importObject.env,
|
|
1549
|
+
...importObject.napi,
|
|
1550
|
+
...importObject.emnapi,
|
|
1551
|
+
memory: __wasmMemory,
|
|
1552
|
+
}
|
|
1553
|
+
return importObject
|
|
1554
|
+
},
|
|
1555
|
+
beforeInit({ instance }) {
|
|
1556
|
+
__napiInstance = instance
|
|
1557
|
+
for (const name of Object.keys(instance.exports)) {
|
|
1558
|
+
if (name.startsWith('__napi_register__')) {
|
|
1559
|
+
instance.exports[name]()
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
},
|
|
1563
|
+
}))
|
|
1564
|
+
// `instantiate()` and `createInstance().exports` hand out this object; a
|
|
1565
|
+
// named module export does not travel with it. After the instance host
|
|
1566
|
+
// install, which hands the same object to addon-provided registration
|
|
1567
|
+
// functions that may put anything on it, and inside this `try`, so a
|
|
1568
|
+
// claimed name flips `__lifecycleState` to 'failed' and tears the instance
|
|
1569
|
+
// down rather than escaping a half-built one.
|
|
1570
|
+
__napiStampBindingTarget(__napiModule.exports, __napiBindingTarget)
|
|
1571
|
+
if (__lifecycleState === 'pending') {
|
|
1572
|
+
__lifecycleState = 'succeeded'
|
|
1573
|
+
}
|
|
1574
|
+
__createdInstances += 1
|
|
1575
|
+
__liveInstances += 1
|
|
1576
|
+
return {
|
|
1577
|
+
exports: __napiModule.exports,
|
|
1578
|
+
get memory() {
|
|
1579
|
+
return __wasmMemory
|
|
1580
|
+
},
|
|
1581
|
+
get memoryBytes() {
|
|
1582
|
+
// The Memory outlives the environment, so this stays readable after a
|
|
1583
|
+
// FAILED dispose() (which leaves the instance undisposed and
|
|
1584
|
+
// retryable). It reports 0 only once disposal has actually completed.
|
|
1585
|
+
return __disposed ? 0 : __wasmMemory.buffer.byteLength
|
|
1586
|
+
},
|
|
1587
|
+
get disposed() {
|
|
1588
|
+
return __disposed
|
|
1589
|
+
},
|
|
1590
|
+
dispose: __disposeInstance,
|
|
1591
|
+
}
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
__lifecycleState = 'failed'
|
|
1594
|
+
// Instantiation can fail *after* registration has run, and registration runs
|
|
1595
|
+
// with a live environment: a module-init hook can start async work and then
|
|
1596
|
+
// return an error, and the promise it created may already have escaped into
|
|
1597
|
+
// JavaScript. Settle what the barrier cancels before the context is
|
|
1598
|
+
// destroyed, exactly like dispose() does; destroying without yielding
|
|
1599
|
+
// discards the queue with a null env. Undefined unless something is queued,
|
|
1600
|
+
// so a failure before beforeInit costs no extra turn.
|
|
1601
|
+
let __settlementsUnreached = false
|
|
1602
|
+
try {
|
|
1603
|
+
// Registration can start async work too, and this path destroys the same
|
|
1604
|
+
// environment its completions need. A drain that cannot finish leaves the
|
|
1605
|
+
// work outstanding, so it counts as settlements unreached and stops the
|
|
1606
|
+
// rollback short of destroying, exactly like a failed barrier drain.
|
|
1607
|
+
const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
|
|
1608
|
+
if (__asyncWorkDrained) {
|
|
1609
|
+
await __asyncWorkDrained
|
|
1610
|
+
}
|
|
1611
|
+
const __drained = __prepareForDisposal()
|
|
1612
|
+
if (__drained) {
|
|
1613
|
+
await __drained
|
|
1614
|
+
}
|
|
1615
|
+
} catch (drainError) {
|
|
1616
|
+
__attachCleanupError(error, drainError)
|
|
1617
|
+
__settlementsUnreached = true
|
|
1618
|
+
}
|
|
1619
|
+
let __registrationError
|
|
1620
|
+
let __registrationFailed = false
|
|
1621
|
+
if (!__beforeExitDestroy) {
|
|
1622
|
+
try {
|
|
1623
|
+
// Independent instances are caller-owned while pending and after
|
|
1624
|
+
// success. Register only failed rollback so cleanup remains retryable.
|
|
1625
|
+
await __registerCleanup()
|
|
1626
|
+
} catch (registrationError) {
|
|
1627
|
+
__attachCleanupError(error, registrationError)
|
|
1628
|
+
__registrationError = registrationError
|
|
1629
|
+
__registrationFailed = true
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
if (__settlementsUnreached) {
|
|
1633
|
+
// The barrier or the drain did not finish, so the settlements it queued
|
|
1634
|
+
// are still in the threadsafe-function queue. Destroying now runs the
|
|
1635
|
+
// cleanup hook that drains that queue with a null env and discards it,
|
|
1636
|
+
// stranding a promise that already escaped into JavaScript — with nothing
|
|
1637
|
+
// left that could ever settle it. dispose() refuses to destroy for exactly
|
|
1638
|
+
// this reason (a rejected drain there never reaches the destroy), so this
|
|
1639
|
+
// path refuses too.
|
|
1640
|
+
//
|
|
1641
|
+
// Nothing leaks. The registration just above — and, for the singleton, the
|
|
1642
|
+
// one made before instantiation — leaves this context in
|
|
1643
|
+
// `__managedEmnapiContextDestroyers`, so beforeExit destroys it and
|
|
1644
|
+
// dispose() can retry it. Only the destruction is deferred, and the turns
|
|
1645
|
+
// that pass in the meantime are exactly what the queue needed.
|
|
1646
|
+
try {
|
|
1647
|
+
__registerManagedBeforeExitListener()
|
|
1648
|
+
} catch {}
|
|
1649
|
+
throw error
|
|
1650
|
+
}
|
|
1651
|
+
try {
|
|
1652
|
+
await __destroyManagedOwnedContext()
|
|
1653
|
+
} catch (disposeError) {
|
|
1654
|
+
// Initialization is the primary failure. Preserve it even if cleanup
|
|
1655
|
+
// also fails, while retaining the cleanup error when the value is
|
|
1656
|
+
// extensible and has no existing cause.
|
|
1657
|
+
__attachCleanupError(
|
|
1658
|
+
__registrationFailed ? __registrationError : error,
|
|
1659
|
+
disposeError,
|
|
1660
|
+
)
|
|
1661
|
+
try {
|
|
1662
|
+
__registerManagedBeforeExitListener()
|
|
1663
|
+
} catch {}
|
|
1664
|
+
}
|
|
1665
|
+
throw error
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
/**
|
|
1670
|
+
* Create an independent instance. Call and await dispose() when the instance
|
|
1671
|
+
* is no longer needed so emnapi cleanup hooks run deterministically.
|
|
1672
|
+
*
|
|
1673
|
+
* The optional second argument selects this instance's linear memory: either
|
|
1674
|
+
* `memory` (an unshared, single-use WebAssembly.Memory you allocated) or
|
|
1675
|
+
* `initialMemoryPages` / `maximumMemoryPages`, never both. Omitted, the
|
|
1676
|
+
* loader allocates WASM_MEMORY.initialPages..WASM_MEMORY.maximumPages.
|
|
1677
|
+
*
|
|
1678
|
+
* A provided Memory must come from this loader's own realm: the WASI and
|
|
1679
|
+
* emnapi layers underneath identify one with a realm-local `instanceof`, so a
|
|
1680
|
+
* Memory built in a `node:vm` context or another frame is rejected. Every
|
|
1681
|
+
* Memory an instance runs on is single-use, the loader-allocated one included:
|
|
1682
|
+
* `instance.memory` cannot be recycled into a second `createInstance()`.
|
|
1683
|
+
*/
|
|
1684
|
+
export async function createInstance(__wasmInput, __options) {
|
|
1685
|
+
return __createInstance(__wasmInput, __options)
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
let __defaultModulePromise
|
|
1689
|
+
let __defaultInstancePromise
|
|
1690
|
+
let __defaultDisposePromise
|
|
1691
|
+
let __defaultDisposalStarted = false
|
|
1692
|
+
const __defaultManagedDestroyers = new WeakMap()
|
|
1693
|
+
let __moduleDisposePromise
|
|
1694
|
+
|
|
1695
|
+
/**
|
|
1696
|
+
* Instantiate a module-local singleton. Concurrent and repeated calls
|
|
1697
|
+
* with the same module share one instance and one Memory allocation.
|
|
1698
|
+
*/
|
|
1699
|
+
export function instantiate(__wasmInput) {
|
|
1700
|
+
const __modulePromise = __resolveModule(__wasmInput)
|
|
1701
|
+
if (__moduleLifecycleDestroyDepth !== 0) {
|
|
1702
|
+
void __modulePromise.catch(() => {})
|
|
1703
|
+
return Promise.reject(__createLifecycleReentryError('instantiate'))
|
|
1704
|
+
}
|
|
1705
|
+
if (__moduleDisposePromise) {
|
|
1706
|
+
void __modulePromise.catch(() => {})
|
|
1707
|
+
return __moduleDisposePromise.then(() => instantiate(__modulePromise))
|
|
1708
|
+
}
|
|
1709
|
+
if (__defaultDisposalStarted) {
|
|
1710
|
+
// Observe rejected input immediately, but preserve lifecycle ordering and
|
|
1711
|
+
// error precedence by instantiating only after disposal succeeds. A failed
|
|
1712
|
+
// disposal retains the old instance only so its cleanup can be retried.
|
|
1713
|
+
void __modulePromise.catch(() => {})
|
|
1714
|
+
const __disposePromise = __defaultDisposePromise ?? dispose()
|
|
1715
|
+
return __disposePromise.then(() => instantiate(__modulePromise))
|
|
1716
|
+
}
|
|
1717
|
+
if (!__defaultInstancePromise) {
|
|
1718
|
+
__defaultModulePromise = __modulePromise
|
|
1719
|
+
const __instancePromise = __modulePromise.then((__module) =>
|
|
1720
|
+
__createInstance(
|
|
1721
|
+
__module,
|
|
1722
|
+
undefined,
|
|
1723
|
+
__disposeDefaultInstance,
|
|
1724
|
+
(__managedDestroyer) => {
|
|
1725
|
+
__defaultManagedDestroyers.set(
|
|
1726
|
+
__instancePromise,
|
|
1727
|
+
__managedDestroyer,
|
|
1728
|
+
)
|
|
1729
|
+
},
|
|
1730
|
+
),
|
|
1731
|
+
)
|
|
1732
|
+
__defaultInstancePromise = __instancePromise
|
|
1733
|
+
void __instancePromise.catch(() => {
|
|
1734
|
+
if (__defaultInstancePromise === __instancePromise) {
|
|
1735
|
+
__defaultInstancePromise = undefined
|
|
1736
|
+
__defaultModulePromise = undefined
|
|
1737
|
+
}
|
|
1738
|
+
})
|
|
1739
|
+
return __instancePromise.then((__instance) => __instance.exports)
|
|
1740
|
+
}
|
|
1741
|
+
const __defaultModulePromiseForCall = __defaultModulePromise
|
|
1742
|
+
const __defaultInstancePromiseForCall = __defaultInstancePromise
|
|
1743
|
+
return Promise.all([__defaultModulePromiseForCall, __modulePromise]).then(
|
|
1744
|
+
async ([__defaultModule, __module]) => {
|
|
1745
|
+
if (__defaultModule !== __module) {
|
|
1746
|
+
throw new Error(
|
|
1747
|
+
'instantiate() already owns a different WebAssembly.Module; call dispose() first or use createInstance() for independent instances.',
|
|
1748
|
+
)
|
|
1749
|
+
}
|
|
1750
|
+
return (await __defaultInstancePromiseForCall).exports
|
|
1751
|
+
},
|
|
1752
|
+
)
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
async function __disposeDefaultInstance(__onDestroy) {
|
|
1756
|
+
if (__defaultDisposePromise) {
|
|
1757
|
+
return __defaultDisposePromise
|
|
1758
|
+
}
|
|
1759
|
+
const __instancePromise = __defaultInstancePromise
|
|
1760
|
+
if (!__instancePromise) {
|
|
1761
|
+
__defaultDisposalStarted = false
|
|
1762
|
+
return
|
|
1763
|
+
}
|
|
1764
|
+
__defaultDisposalStarted = true
|
|
1765
|
+
const __disposePromise = (async () => {
|
|
1766
|
+
let __instance
|
|
1767
|
+
try {
|
|
1768
|
+
__instance = await __instancePromise
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
const __managedDestroyer =
|
|
1771
|
+
__defaultManagedDestroyers.get(__instancePromise)
|
|
1772
|
+
if (__managedDestroyer) {
|
|
1773
|
+
__onDestroy?.(__managedDestroyer)
|
|
1774
|
+
}
|
|
1775
|
+
__defaultManagedDestroyers.delete(__instancePromise)
|
|
1776
|
+
throw error
|
|
1777
|
+
}
|
|
1778
|
+
const __managedDestroyer =
|
|
1779
|
+
__defaultManagedDestroyers.get(__instancePromise)
|
|
1780
|
+
if (__managedDestroyer) {
|
|
1781
|
+
__onDestroy?.(__managedDestroyer)
|
|
1782
|
+
}
|
|
1783
|
+
await __instance.dispose()
|
|
1784
|
+
if (__defaultInstancePromise === __instancePromise) {
|
|
1785
|
+
__defaultInstancePromise = undefined
|
|
1786
|
+
__defaultModulePromise = undefined
|
|
1787
|
+
__defaultDisposalStarted = false
|
|
1788
|
+
}
|
|
1789
|
+
__defaultManagedDestroyers.delete(__instancePromise)
|
|
1790
|
+
})()
|
|
1791
|
+
__defaultDisposePromise = __disposePromise
|
|
1792
|
+
try {
|
|
1793
|
+
await __disposePromise
|
|
1794
|
+
} finally {
|
|
1795
|
+
if (__defaultDisposePromise === __disposePromise) {
|
|
1796
|
+
__defaultDisposePromise = undefined
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
async function __dispose() {
|
|
1802
|
+
let __defaultDisposeError
|
|
1803
|
+
let __defaultDisposeFailed = false
|
|
1804
|
+
let __attemptedDefaultDestroyer
|
|
1805
|
+
try {
|
|
1806
|
+
await __disposeDefaultInstance((__destroyer) => {
|
|
1807
|
+
__attemptedDefaultDestroyer = __destroyer
|
|
1808
|
+
})
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
__defaultDisposeError = error
|
|
1811
|
+
__defaultDisposeFailed = true
|
|
1812
|
+
}
|
|
1813
|
+
const __excludedDestroyers = new Set()
|
|
1814
|
+
if (__defaultDisposeFailed && __attemptedDefaultDestroyer) {
|
|
1815
|
+
__excludedDestroyers.add(__attemptedDefaultDestroyer)
|
|
1816
|
+
}
|
|
1817
|
+
try {
|
|
1818
|
+
await __drainManagedEmnapiContexts(__excludedDestroyers)
|
|
1819
|
+
} catch (error) {
|
|
1820
|
+
if (!__defaultDisposeFailed) {
|
|
1821
|
+
throw error
|
|
1822
|
+
}
|
|
1823
|
+
if (error !== __defaultDisposeError) {
|
|
1824
|
+
__attachCleanupError(__defaultDisposeError, error)
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (__defaultDisposeFailed) {
|
|
1828
|
+
throw __defaultDisposeError
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
/**
|
|
1833
|
+
* Dispose the singleton created by instantiate(). A later call may create a
|
|
1834
|
+
* fresh instance, including from a different module. This also retries cleanup
|
|
1835
|
+
* retained after a failed initialization rollback.
|
|
1836
|
+
*/
|
|
1837
|
+
export function dispose() {
|
|
1838
|
+
if (__moduleLifecycleDestroyDepth !== 0) {
|
|
1839
|
+
return Promise.reject(__createLifecycleReentryError('dispose'))
|
|
1840
|
+
}
|
|
1841
|
+
if (__moduleDisposePromise) {
|
|
1842
|
+
return __moduleDisposePromise
|
|
1843
|
+
}
|
|
1844
|
+
let __resolveDispose
|
|
1845
|
+
let __rejectDispose
|
|
1846
|
+
const __promise = new Promise((resolve, reject) => {
|
|
1847
|
+
__resolveDispose = resolve
|
|
1848
|
+
__rejectDispose = reject
|
|
1849
|
+
})
|
|
1850
|
+
__moduleDisposePromise = __promise
|
|
1851
|
+
void __dispose().then(__resolveDispose, __rejectDispose)
|
|
1852
|
+
void __promise.then(
|
|
1853
|
+
() => {
|
|
1854
|
+
if (__moduleDisposePromise === __promise) {
|
|
1855
|
+
__moduleDisposePromise = undefined
|
|
1856
|
+
}
|
|
1857
|
+
},
|
|
1858
|
+
() => {
|
|
1859
|
+
if (__moduleDisposePromise === __promise) {
|
|
1860
|
+
__moduleDisposePromise = undefined
|
|
1861
|
+
}
|
|
1862
|
+
},
|
|
1863
|
+
)
|
|
1864
|
+
return __promise
|
|
1865
|
+
}
|