blob: ae66ec34f2669e6da2b11d2b5001f782001b2c24 [file] [edit]
/**
* @license
* Copyright 2023 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
mergeInto(LibraryManager.library, {
$promiseMap__deps: ['$HandleAllocator'],
$promiseMap: "new HandleAllocator();",
$getPromise__deps: ['$promiseMap'],
$getPromise: function(id) {
return promiseMap.get(id).promise;
},
$makePromise__deps: ['$promiseMap'],
$makePromise: function() {
var promiseInfo = {};
promiseInfo.promise = new Promise((resolve, reject) => {
promiseInfo.reject = reject;
promiseInfo.resolve = resolve;
});
promiseInfo.id = promiseMap.allocate(promiseInfo);
#if RUNTIME_DEBUG
dbg(`makePromise: ${promiseInfo.id}`);
#endif
return promiseInfo;
},
$idsToPromises__deps: ['$promiseMap', '$getPromise'],
$idsToPromises: function(idBuf, size) {
var promises = [];
for (var i = 0; i < size; i++) {
var id = {{{ makeGetValue('idBuf', `i*${POINTER_SIZE}`, 'i32') }}};
promises[i] = getPromise(id);
}
return promises;
},
emscripten_promise_create__deps: ['$makePromise'],
emscripten_promise_create: function() {
return makePromise().id;
},
emscripten_promise_destroy__deps: ['$promiseMap'],
emscripten_promise_destroy: function(id) {
#if RUNTIME_DEBUG
dbg(`emscripten_promise_destroy: ${id}`);
#endif
promiseMap.free(id);
},
emscripten_promise_resolve__deps: ['$promiseMap',
'$getPromise',
'emscripten_promise_destroy'],
emscripten_promise_resolve: function(id, result, value) {
#if RUNTIME_DEBUG
dbg(`emscripten_promise_resolve: ${id}`);
#endif
var info = promiseMap.get(id);
switch (result) {
case {{{ cDefs.EM_PROMISE_FULFILL }}}:
info.resolve(value);
return;
case {{{ cDefs.EM_PROMISE_MATCH }}}:
info.resolve(getPromise(value));
return;
case {{{ cDefs.EM_PROMISE_MATCH_RELEASE }}}:
info.resolve(getPromise(value));
_emscripten_promise_destroy(value);
return;
case {{{ cDefs.EM_PROMISE_REJECT }}}:
info.reject(value);
return;
}
#if ASSERTIONS
abort("unexpected promise callback result " + result);
#endif
},
$makePromiseCallback__deps: ['$getPromise',
'$POINTER_SIZE',
'emscripten_promise_destroy'],
$makePromiseCallback: function(callback, userData) {
return (value) => {
#if RUNTIME_DEBUG
dbg(`emscripten promise callback: ${value}`);
#endif
{{{ runtimeKeepalivePop() }}};
var stack = stackSave();
// Allocate space for the result value and initialize it to NULL.
var resultPtr = stackAlloc(POINTER_SIZE);
{{{ makeSetValue('resultPtr', 0, '0', '*') }}};
try {
var result =
{{{ makeDynCall('ippp', 'callback') }}}(resultPtr, userData, value);
var resultVal = {{{ makeGetValue('resultPtr', 0, '*') }}};
} catch (e) {
// If the thrown value is potentially a valid pointer, use it as the
// rejection reason. Otherwise use a null pointer as the reason. If we
// allow arbitrary objects to be thrown here, we will get a TypeError in
// MEMORY64 mode when they are later converted to void* rejection
// values.
#if MEMORY64
if (typeof e !== 'bigint') {
throw 0n;
}
#else
if (typeof e !== 'number') {
throw 0;
}
#endif
throw e;
} finally {
// Thrown errors will reject the promise, but at least we will restore
// the stack first.
stackRestore(stack);
}
switch (result) {
case {{{ cDefs.EM_PROMISE_FULFILL }}}:
return resultVal;
case {{{ cDefs.EM_PROMISE_MATCH }}}:
return getPromise(resultVal);
case {{{ cDefs.EM_PROMISE_MATCH_RELEASE }}}:
var ret = getPromise(resultVal);
_emscripten_promise_destroy(resultVal);
return ret;
case {{{ cDefs.EM_PROMISE_REJECT }}}:
throw resultVal;
}
#if ASSERTIONS
abort("unexpected promise callback result " + result);
#endif
};
},
emscripten_promise_then__deps: ['$promiseMap',
'$getPromise',
'$makePromiseCallback'],
emscripten_promise_then: function(id,
onFulfilled,
onRejected,
userData) {
#if RUNTIME_DEBUG
dbg(`emscripten_promise_then: ${id}`);
#endif
{{{ runtimeKeepalivePush() }}};
var promise = getPromise(id);
var newId = promiseMap.allocate({
promise: promise.then(makePromiseCallback(onFulfilled, userData),
makePromiseCallback(onRejected, userData))
});
#if RUNTIME_DEBUG
dbg(`emscripten_promise_then: -> ${newId}`);
#endif
return newId;
},
emscripten_promise_all__deps: ['$promiseMap', '$idsToPromises'],
emscripten_promise_all: function(idBuf, resultBuf, size) {
var promises = idsToPromises(idBuf, size);
#if RUNTIME_DEBUG
dbg(`emscripten_promise_all: ${promises}`);
#endif
var id = promiseMap.allocate({
promise: Promise.all(promises).then((results) => {
if (resultBuf) {
for (var i = 0; i < size; i++) {
var result = results[i];
{{{ makeSetValue('resultBuf', `i*${POINTER_SIZE}`, 'result', '*') }}};
}
}
return resultBuf;
})
});
#if RUNTIME_DEBUG
dbg(`create: ${id}`);
#endif
return id;
},
emscripten_promise_all_settled__deps: ['$promiseMap', '$idsToPromises'],
emscripten_promise_all_settled: function(idBuf, resultBuf, size) {
var promises = idsToPromises(idBuf, size);
#if RUNTIME_DEBUG
dbg(`emscripten_promise_all_settled: ${promises}`);
#endif
var id = promiseMap.allocate({
promise: Promise.allSettled(promises).then((results) => {
if (resultBuf) {
for (var i = 0; i < size; i++) {
var baseOffset = i * {{{ C_STRUCTS.em_settled_result_t.__size__ }}};
var resultOffset =
baseOffset + {{{ C_STRUCTS.em_settled_result_t.result }}};
var valueOffset =
baseOffset + {{{ C_STRUCTS.em_settled_result_t.value }}};
if (results[i].status === 'fulfilled') {
var fulfill = {{{ cDefs.EM_PROMISE_FULFILL }}};
{{{ makeSetValue('resultBuf', 'resultOffset', 'fulfill', 'i32') }}};
{{{ makeSetValue('resultBuf', 'valueOffset', 'results[i].value', '*') }}};
} else {
var reject = {{{ cDefs.EM_PROMISE_REJECT }}};
{{{ makeSetValue('resultBuf', 'resultOffset', 'reject', 'i32') }}};
// Closure can't type `reason` in some contexts.
var reason = /** @type {number} */ (results[i].reason);
{{{ makeSetValue('resultBuf', 'valueOffset', 'reason', '*') }}};
}
}
}
return resultBuf;
})
});
#if RUNTIME_DEBUG
dbg(`create: ${id}`);
#endif
return id;
},
emscripten_promise_any__deps: [
'$promiseMap', '$idsToPromises',
#if !SUPPORTS_PROMISE_ANY && !INCLUDE_FULL_LIBRARY
() => error("emscripten_promise_any used, but Promise.any is not supported by the current runtime configuration (run with EMCC_DEBUG=1 in the env for more details)"),
#endif
],
emscripten_promise_any: function(idBuf, errorBuf, size) {
var promises = idsToPromises(idBuf, size);
#if RUNTIME_DEBUG
dbg(`emscripten_promise_any: ${promises}`);
#endif
#if ASSERTIONS
assert(typeof Promise.any !== 'undefined', "Promise.any does not exist");
#endif
var id = promiseMap.allocate({
promise: Promise.any(promises).catch((err) => {
if (errorBuf) {
for (var i = 0; i < size; i++) {
{{{ makeSetValue('errorBuf', `i*${POINTER_SIZE}`, 'err.errors[i]', '*') }}};
}
}
throw errorBuf;
})
});
#if RUNTIME_DEBUG
dbg(`create: ${id}`);
#endif
return id;
},
emscripten_promise_race__deps: ['$promiseMap', '$idsToPromises'],
emscripten_promise_race: function(idBuf, size) {
var promises = idsToPromises(idBuf, size);
#if RUNTIME_DEBUG
dbg(`emscripten_promise_race: ${promises}`);
#endif
var id = promiseMap.allocate({
promise: Promise.race(promises)
});
#if RUNTIME_DEBUG
dbg(`create: ${id}`);
#endif
return id;
}
});