addToLibrary({
$runAndAbortIfError: (func) => {
try {
return func();
} catch (e) {
abort(e);
}
},
#if ASYNCIFY
$Asyncify__deps: ['$runAndAbortIfError', '$callUserCallback',
#if !MINIMAL_RUNTIME
'$runtimeKeepalivePush', '$runtimeKeepalivePop',
#endif
#if ASYNCIFY == 1
'malloc', 'free',
#endif
],
$Asyncify: {
#if ASYNCIFY == 1 && MEMORY64
rewindArguments: new Map(),
#endif
instrumentWasmImports(imports) {
#if EMBIND_GEN_MODE
return imports;
#endif
#if ASYNCIFY_DEBUG
dbg('asyncify instrumenting imports');
#endif
#if ASSERTIONS && ASYNCIFY == 2
assert('Suspending' in WebAssembly, 'JSPI not supported by current environment. Perhaps it needs to be enabled via flags?');
#endif
var importPattern = {{{ new RegExp(`^(${ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS.map(x => x.split('.')[1]).join('|').replace(/\*/g, '.*')})$`) }}};
for (let [x, original] of Object.entries(imports)) {
if (typeof original == 'function') {
let isAsyncifyImport = original.isAsync || importPattern.test(x);
#if ASYNCIFY == 2
// Wrap async imports with a suspending WebAssembly function.
if (isAsyncifyImport) {
#if ASYNCIFY_DEBUG
dbg('asyncify: suspendOnReturnedPromise for', x, original);
#endif
imports[x] = original = new WebAssembly.Suspending(original);
}
#endif
#if ASSERTIONS && ASYNCIFY != 2 // We cannot apply assertions with stack switching, as the imports must not be modified from suspender.suspendOnReturnedPromise TODO find a way
imports[x] = (...args) => {
var originalAsyncifyState = Asyncify.state;
try {
return original(...args);
} finally {
// Only asyncify-declared imports are allowed to change the
// state.
// Changing the state from normal to disabled is allowed (in any
// function) as that is what shutdown does (and we don't have an
// explicit list of shutdown imports).
var changedToDisabled =
originalAsyncifyState === Asyncify.State.Normal &&
Asyncify.state === Asyncify.State.Disabled;
// invoke_* functions are allowed to change the state if we do
// not ignore indirect calls.
var ignoredInvoke = x.startsWith('invoke_') &&
{{{ !ASYNCIFY_IGNORE_INDIRECT }}};
if (Asyncify.state !== originalAsyncifyState &&
!isAsyncifyImport &&
!changedToDisabled &&
!ignoredInvoke) {
throw new Error(`import ${x} was not in ASYNCIFY_IMPORTS, but changed the state`);
}
}
};
#if MAIN_MODULE
// The dynamic library loader needs to be able to read .sig
// properties, so that it knows function signatures when it adds
// them to the table.
imports[x].sig = original.sig;
#endif // MAIN_MODULE
#endif // ASSERTIONS
}
}
},
#if ASYNCIFY == 1 && MEMORY64
saveRewindArguments(func, passedArguments) {
return Asyncify.rewindArguments.set(func, Array.from(passedArguments));
},
restoreRewindArguments(func) {
#if ASSERTIONS
assert(Asyncify.rewindArguments.has(func));
#endif
return Asyncify.rewindArguments.get(func);
},
#endif
instrumentFunction(original) {
var wrapper = (...args) => {
#if ASYNCIFY_DEBUG >= 2
dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} try ${original}`);
#endif
#if ASYNCIFY == 1
Asyncify.exportCallStack.push(original);
try {
#endif
#if ASYNCIFY == 1 && MEMORY64
Asyncify.saveRewindArguments(original, args);
#endif
return original(...args);
#if ASYNCIFY == 1
} finally {
if (!ABORT) {
var top = Asyncify.exportCallStack.pop();
#if ASSERTIONS
assert(top === original);
#endif
#if ASYNCIFY_DEBUG >= 2
dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} finally ${original}`);
#endif
Asyncify.maybeStopUnwind();
}
}
#endif
};
#if ASYNCIFY == 1
Asyncify.funcWrappers.set(original, wrapper);
#endif
#if MAIN_MODULE || ASYNCIFY_LAZY_LOAD_CODE
wrapper.orig = original;
#endif
return wrapper;
},
instrumentWasmExports(exports) {
#if EMBIND_GEN_MODE
// Instrumenting is not needed when generating code.
return exports;
#endif
#if ASYNCIFY_DEBUG
dbg('asyncify instrumenting exports');
#endif
#if ASYNCIFY == 2
var exportPattern = {{{ new RegExp(`^(${ASYNCIFY_EXPORTS.join('|').replace(/\*/g, '.*')})$`) }}};
Asyncify.asyncExports = new Set();
#endif
var ret = {};
for (let [x, original] of Object.entries(exports)) {
if (typeof original == 'function') {
#if ASYNCIFY == 2
// Wrap all exports with a promising WebAssembly function.
let isAsyncifyExport = exportPattern.test(x);
if (isAsyncifyExport) {
Asyncify.asyncExports.add(original);
original = Asyncify.makeAsyncFunction(original);
}
#endif
var wrapper = Asyncify.instrumentFunction(original);
#if ASYNCIFY_LAZY_LOAD_CODE
original.exportName = x;
#endif
ret[x] = wrapper;
} else {
ret[x] = original;
}
}
#if ASYNCIFY_LAZY_LOAD_CODE
Asyncify.updateFunctionMapping(ret);
#endif
return ret;
},
#if ASYNCIFY == 1
//
// Original implementation of Asyncify.
//
State: {
Normal: 0,
Unwinding: 1,
Rewinding: 2,
Disabled: 3,
},
state: 0,
StackSize: {{{ ASYNCIFY_STACK_SIZE }}},
currData: null,
// The return value passed to wakeUp() in
// Asyncify.handleSleep((wakeUp) => {...}) is stored here,
// so we can return it later from the C function that called
// Asyncify.handleSleep() after rewinding finishes.
handleSleepReturnValue: 0,
// We must track which wasm exports are called into and
// exited, so that we know where the call stack began,
// which is where we must call to rewind it.
// This list contains the original Wasm exports.
exportCallStack: [],
callstackFuncToId: new Map(),
callStackIdToFunc: new Map(),
// Maps wasm functions to their corresponding wrapper function.
funcWrappers: new Map(),
callStackId: 0,
asyncPromiseHandlers: null, // { resolve, reject } pair for when *all* asynchronicity is done
sleepCallbacks: [], // functions to call every time we sleep
#if ASYNCIFY_LAZY_LOAD_CODE
updateFunctionMapping(newExports) {
#if ASYNCIFY_DEBUG
dbg('updateFunctionMapping', Asyncify.callStackIdToFunc);
#endif
#if ASSERTIONS
assert(!Asyncify.exportCallStack.length);
#endif
Asyncify.callStackIdToFunc.forEach((func, id) => {
#if ASSERTIONS
assert(func.exportName);
assert(newExports[func.exportName]);
assert(newExports[func.exportName].orig);
#endif
var newFunc = newExports[func.exportName].orig;
Asyncify.callStackIdToFunc.set(id, newFunc)
Asyncify.callstackFuncToId.set(newFunc, id);
#if MEMORY64
var args = Asyncify.rewindArguments.get(func);
if (args) {
Asyncify.rewindArguments.set(newFunc, args);
Asyncify.rewindArguments.delete(func);
}
#endif
});
},
#endif
getCallStackId(func) {
#if ASSERTIONS
assert(func);
#endif
if (!Asyncify.callstackFuncToId.has(func)) {
var id = Asyncify.callStackId++;
Asyncify.callstackFuncToId.set(func, id);
Asyncify.callStackIdToFunc.set(id, func);
}
return Asyncify.callstackFuncToId.get(func);
},
maybeStopUnwind() {
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY: maybe stop unwind', Asyncify.exportCallStack);
#endif
if (Asyncify.currData &&
Asyncify.state === Asyncify.State.Unwinding &&
Asyncify.exportCallStack.length === 0) {
// We just finished unwinding.
// Be sure to set the state before calling any other functions to avoid
// possible infinite recursion here (For example in debug pthread builds
// the dbg() function itself can call back into WebAssembly to get the
// current pthread_self() pointer).
Asyncify.state = Asyncify.State.Normal;
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY: stop unwind');
#endif
{{{ runtimeKeepalivePush(); }}}
// Keep the runtime alive so that a re-wind can be done later.
runAndAbortIfError(_asyncify_stop_unwind);
if (typeof Fibers != 'undefined') {
Fibers.trampoline();
}
}
},
whenDone() {
#if ASSERTIONS
assert(Asyncify.currData, 'Tried to wait for an async operation when none is in progress.');
assert(!Asyncify.asyncPromiseHandlers, 'Cannot have multiple async operations in flight at once');
#endif
return new Promise((resolve, reject) => {
Asyncify.asyncPromiseHandlers = { resolve, reject };
});
},
allocateData() {
// An asyncify data structure has three fields:
// 0 current stack pos
// 4 max stack pos
// 8 id of function at bottom of the call stack (callStackIdToFunc[id] == wasm func)
//
// The Asyncify ABI only interprets the first two fields, the rest is for the runtime.
// We also embed a stack in the same memory region here, right next to the structure.
// This struct is also defined as asyncify_data_t in emscripten/fiber.h
var ptr = _malloc({{{ C_STRUCTS.asyncify_data_s.__size__ }}} + Asyncify.StackSize);
Asyncify.setDataHeader(ptr, ptr + {{{ C_STRUCTS.asyncify_data_s.__size__ }}}, Asyncify.StackSize);
Asyncify.setDataRewindFunc(ptr);
return ptr;
},
setDataHeader(ptr, stack, stackSize) {
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_ptr, 'stack', '*') }}};
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_limit, 'stack + stackSize', '*') }}};
},
setDataRewindFunc(ptr) {
var bottomOfCallStack = Asyncify.exportCallStack[0];
#if ASYNCIFY_DEBUG >= 2
dbg(`ASYNCIFY: setDataRewindFunc(${ptr}), bottomOfCallStack is`, bottomOfCallStack, new Error().stack);
#endif
#if ASSERTIONS
assert(bottomOfCallStack, 'exportCallStack is empty');
#endif
var rewindId = Asyncify.getCallStackId(bottomOfCallStack);
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'rewindId', 'i32') }}};
},
getDataRewindFunc(ptr) {
var id = {{{ makeGetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'i32') }}};
var func = Asyncify.callStackIdToFunc.get(id);
#if ASSERTIONS
assert(func, `id ${id} not found in callStackIdToFunc`);
#endif
return func;
},
doRewind(ptr) {
var original = Asyncify.getDataRewindFunc(ptr);
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY: doRewind:', original);
#endif
var func = Asyncify.funcWrappers.get(original);
#if ASSERTIONS
assert(original);
assert(func);
#endif
// Once we have rewound and the stack we no longer need to artificially
// keep the runtime alive.
{{{ runtimeKeepalivePop(); }}}
#if MEMORY64
// When re-winding, the arguments to a function are ignored. For i32 arguments we
// can just call the function with no args at all since and the engine will produce zeros
// for all arguments. However, for i64 arguments we get `undefined cannot be converted to
return func(...Asyncify.restoreRewindArguments(original));
#else
return func();
#endif
},
handleSleep(startAsync) {
#if ASSERTIONS
assert(Asyncify.state !== Asyncify.State.Disabled, 'Asyncify cannot be done during or after the runtime exits');
#endif
if (ABORT) return;
#if ASYNCIFY_DEBUG
dbg(`ASYNCIFY: handleSleep ${Asyncify.state}`);
#endif
if (Asyncify.state === Asyncify.State.Normal) {
var reachedCallback = false;
var reachedAfterCallback = false;
startAsync((handleSleepReturnValue = 0) => {
#if ASSERTIONS
assert(!handleSleepReturnValue || typeof handleSleepReturnValue == 'number' || typeof handleSleepReturnValue == 'boolean');
#endif
if (ABORT) return;
Asyncify.handleSleepReturnValue = handleSleepReturnValue;
reachedCallback = true;
if (!reachedAfterCallback) {
return;
}
#if ASSERTIONS
assert(!Asyncify.exportCallStack.length, 'Waking up (starting to rewind) must be done from JS, without compiled code on the stack.');
#endif
#if ASYNCIFY_DEBUG
dbg(`ASYNCIFY: start rewind ${Asyncify.currData}`);
#endif
Asyncify.state = Asyncify.State.Rewinding;
runAndAbortIfError(() => _asyncify_start_rewind(Asyncify.currData));
if (typeof MainLoop != 'undefined' && MainLoop.func) {
MainLoop.resume();
}
var asyncWasmReturnValue, isError = false;
try {
asyncWasmReturnValue = Asyncify.doRewind(Asyncify.currData);
} catch (err) {
asyncWasmReturnValue = err;
isError = true;
}
var handled = false;
if (!Asyncify.currData) {
var asyncPromiseHandlers = Asyncify.asyncPromiseHandlers;
if (asyncPromiseHandlers) {
Asyncify.asyncPromiseHandlers = null;
(isError ? asyncPromiseHandlers.reject : asyncPromiseHandlers.resolve)(asyncWasmReturnValue);
handled = true;
}
}
if (isError && !handled) {
throw asyncWasmReturnValue;
}
});
reachedAfterCallback = true;
if (!reachedCallback) {
Asyncify.state = Asyncify.State.Unwinding;
Asyncify.currData = Asyncify.allocateData();
#if ASYNCIFY_DEBUG
dbg(`ASYNCIFY: start unwind ${Asyncify.currData}`);
#endif
if (typeof MainLoop != 'undefined' && MainLoop.func) {
MainLoop.pause();
}
runAndAbortIfError(() => _asyncify_start_unwind(Asyncify.currData));
}
} else if (Asyncify.state === Asyncify.State.Rewinding) {
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY: stop rewind');
#endif
Asyncify.state = Asyncify.State.Normal;
runAndAbortIfError(_asyncify_stop_rewind);
_free(Asyncify.currData);
Asyncify.currData = null;
Asyncify.sleepCallbacks.forEach(callUserCallback);
} else {
abort(`invalid state: ${Asyncify.state}`);
}
return Asyncify.handleSleepReturnValue;
},
handleAsync: (startAsync) => Asyncify.handleSleep((wakeUp) => {
startAsync().then(wakeUp);
}),
#elif ASYNCIFY == 2
asyncExports: null,
isAsyncExport(func) {
return Asyncify.asyncExports?.has(func);
},
handleAsync: async (startAsync) => {
{{{ runtimeKeepalivePush(); }}}
try {
return await startAsync();
} finally {
{{{ runtimeKeepalivePop(); }}}
}
},
handleSleep: (startAsync) => Asyncify.handleAsync(() => new Promise(startAsync)),
makeAsyncFunction(original) {
#if ASYNCIFY_DEBUG
dbg('asyncify: makeAsyncFunction for', original);
#endif
return WebAssembly.promising(original);
},
#endif
},
emscripten_sleep__deps: ['$safeSetTimeout'],
emscripten_sleep__async: true,
emscripten_sleep: (ms) => Asyncify.handleSleep((wakeUp) => safeSetTimeout(wakeUp, ms)),
emscripten_wget_data__deps: ['$asyncLoad', 'malloc'],
emscripten_wget_data__async: true,
emscripten_wget_data: (url, pbuffer, pnum, perror) => Asyncify.handleAsync(async () => {
try {
const byteArray = await asyncLoad(UTF8ToString(url));
var buffer = _malloc(byteArray.length);
HEAPU8.set(byteArray, buffer);
{{{ makeSetValue('pbuffer', 0, 'buffer', '*') }}};
{{{ makeSetValue('pnum', 0, 'byteArray.length', 'i32') }}};
{{{ makeSetValue('perror', 0, '0', 'i32') }}};
} catch (err) {
{{{ makeSetValue('perror', 0, '1', 'i32') }}};
}
}),
emscripten_scan_registers__deps: ['$safeSetTimeout'],
emscripten_scan_registers__async: true,
emscripten_scan_registers: (func) => {
return Asyncify.handleSleep((wakeUp) => {
safeSetTimeout(() => {
var stackBegin = Asyncify.currData + {{{ C_STRUCTS.asyncify_data_s.__size__ }}};
var stackEnd = {{{ makeGetValue('Asyncify.currData', 0, '*') }}};
{{{ makeDynCall('vpp', 'func') }}}(stackBegin, stackEnd);
wakeUp();
}, 0);
});
},
#if ASYNCIFY_LAZY_LOAD_CODE
emscripten_lazy_load_code__async: true,
emscripten_lazy_load_code: () => Asyncify.handleSleep((wakeUp) => {
wasmBinaryFile += '.lazy.wasm';
createWasm().then(() => wakeUp());
}),
#endif
_load_secondary_module__sig: 'v',
_load_secondary_module__async: true,
_load_secondary_module: async function() {
wasmExports['load_secondary_module_status'].value = 1;
var imports = {'primary': wasmExports};
var deferred = wasmBinaryFile.slice(0, -5) + '.deferred.wasm';
await instantiateAsync(null, deferred, imports);
},
$Fibers__deps: ['$Asyncify', 'emscripten_stack_set_limits', '$stackRestore'],
$Fibers: {
nextFiber: 0,
trampolineRunning: false,
trampoline() {
if (!Fibers.trampolineRunning && Fibers.nextFiber) {
Fibers.trampolineRunning = true;
do {
var fiber = Fibers.nextFiber;
Fibers.nextFiber = 0;
#if ASYNCIFY_DEBUG >= 2
dbg("ASYNCIFY/FIBER: trampoline jump into fiber", fiber, new Error().stack);
#endif
Fibers.finishContextSwitch(fiber);
} while (Fibers.nextFiber);
Fibers.trampolineRunning = false;
}
},
finishContextSwitch(newFiber) {
var stack_base = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_base, '*') }}};
var stack_max = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_limit, '*') }}};
_emscripten_stack_set_limits(stack_base, stack_max);
#if STACK_OVERFLOW_CHECK >= 2
___set_stack_limits(stack_base, stack_max);
#endif
stackRestore({{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, '*') }}});
var entryPoint = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, '*') }}};
if (entryPoint !== 0) {
#if STACK_OVERFLOW_CHECK
writeStackCookie();
#endif
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY/FIBER: entering fiber', newFiber, 'for the first time');
#endif
Asyncify.currData = null;
{{{ makeSetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, 0, '*') }}};
var userData = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.user_data, '*') }}};
{{{ makeDynCall('vp', 'entryPoint') }}}(userData);
} else {
var asyncifyData = newFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}};
Asyncify.currData = asyncifyData;
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY/FIBER: start rewind', asyncifyData, '(resuming fiber', newFiber, ')');
#endif
Asyncify.state = Asyncify.State.Rewinding;
_asyncify_start_rewind(asyncifyData);
Asyncify.doRewind(asyncifyData);
}
},
},
emscripten_fiber_swap__deps: ["$Asyncify", "$Fibers", '$stackSave'],
emscripten_fiber_swap__async: true,
emscripten_fiber_swap: (oldFiber, newFiber) => {
if (ABORT) return;
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY/FIBER: swap', oldFiber, '->', newFiber, 'state:', Asyncify.state);
#endif
if (Asyncify.state === Asyncify.State.Normal) {
Asyncify.state = Asyncify.State.Unwinding;
var asyncifyData = oldFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}};
Asyncify.setDataRewindFunc(asyncifyData);
Asyncify.currData = asyncifyData;
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY/FIBER: start unwind', asyncifyData);
#endif
_asyncify_start_unwind(asyncifyData);
var stackTop = stackSave();
{{{ makeSetValue('oldFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, 'stackTop', '*') }}};
Fibers.nextFiber = newFiber;
} else {
#if ASSERTIONS
assert(Asyncify.state === Asyncify.State.Rewinding);
#endif
#if ASYNCIFY_DEBUG
dbg('ASYNCIFY/FIBER: stop rewind');
#endif
Asyncify.state = Asyncify.State.Normal;
_asyncify_stop_rewind();
Asyncify.currData = null;
}
},
#else
emscripten_sleep: () => {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_sleep';
},
emscripten_wget: (url, file) => {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_wget';
},
emscripten_wget_data: (url, pbuffer, pnum, perror) => {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_wget_data';
},
emscripten_scan_registers: (func) => {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_scan_registers';
},
emscripten_fiber_swap: (oldFiber, newFiber) => {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_fiber_swap';
},
#endif
});
if (ASYNCIFY) {
extraLibraryFuncs.push('$Asyncify');
}