2021-11-09 22:52:21 +02:00
/*
* Copyright ( c ) 2021 , Idan Horowitz < idan . horowitz @ serenityos . org >
2023-01-27 21:33:28 +00:00
* Copyright ( c ) 2021 - 2023 , Linus Groh < linusg @ serenityos . org >
2021-11-09 22:52:21 +02:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/TypeCasts.h>
2025-04-20 16:49:34 +12:00
# include <LibJS/Runtime/Agent.h>
2021-11-09 22:52:21 +02:00
# include <LibJS/Runtime/Completion.h>
# include <LibJS/Runtime/NativeFunction.h>
2022-10-02 10:59:22 +01:00
# include <LibJS/Runtime/Promise.h>
# include <LibJS/Runtime/PromiseCapability.h>
2021-11-09 22:52:21 +02:00
# include <LibJS/Runtime/PromiseConstructor.h>
# include <LibJS/Runtime/VM.h>
# include <LibJS/Runtime/Value.h>
namespace JS {
2025-07-19 10:41:08 -07:00
bool g_log_all_js_exceptions = false ;
2024-04-16 08:02:41 +02:00
2021-11-14 00:48:15 +00:00
Completion : : Completion ( ThrowCompletionOr < Value > const & throw_completion_or_value )
{
if ( throw_completion_or_value . is_throw_completion ( ) ) {
m_type = Type : : Throw ;
m_value = throw_completion_or_value . throw_completion ( ) . value ( ) ;
} else {
m_type = Type : : Normal ;
m_value = throw_completion_or_value . value ( ) ;
}
}
2021-11-09 22:52:21 +02:00
// 6.2.3.1 Await, https://tc39.es/ecma262/#await
2023-01-27 21:33:28 +00:00
// FIXME: This no longer matches the spec!
2022-08-21 20:38:35 +01:00
ThrowCompletionOr < Value > await ( VM & vm , Value value )
2021-11-09 22:52:21 +02:00
{
2022-08-21 20:38:35 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-11-09 22:52:21 +02:00
// 1. Let asyncContext be the running execution context.
2021-11-16 02:41:18 +03:30
// NOTE: This is not needed, as we don't suspend anything.
2021-11-09 22:52:21 +02:00
// 2. Let promise be ? PromiseResolve(%Promise%, value).
2023-04-13 00:47:15 +02:00
auto * promise_object = TRY ( promise_resolve ( vm , realm . intrinsics ( ) . promise_constructor ( ) , value ) ) ;
2021-11-09 22:52:21 +02:00
2024-04-07 16:32:33 -07:00
IGNORE_USE_IN_ESCAPING_LAMBDA Optional < bool > success ;
IGNORE_USE_IN_ESCAPING_LAMBDA Value result ;
2021-11-09 22:52:21 +02:00
// 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called:
2022-08-22 11:48:08 +01:00
auto fulfilled_closure = [ & success , & result ] ( VM & vm ) - > ThrowCompletionOr < Value > {
2021-11-09 22:52:21 +02:00
// a. Let prevContext be the running execution context.
// b. Suspend prevContext.
// FIXME: We don't have this concept yet.
// NOTE: Since we don't support context suspension, we exfiltrate the result to await()'s scope instead
success = true ;
result = vm . argument ( 0 ) ;
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
2021-11-16 02:41:18 +03:30
// NOTE: This is not done, because we're not suspending anything (see above).
2021-11-09 22:52:21 +02:00
// d. Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended it.
// e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.
// FIXME: We don't have this concept yet.
// f. Return undefined.
return js_undefined ( ) ;
} ;
2022-05-02 20:54:39 +02:00
// 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »).
2025-03-16 21:25:29 -05:00
auto on_fulfilled = NativeFunction : : create ( realm , move ( fulfilled_closure ) , 1 ) ;
2021-11-09 22:52:21 +02:00
// 5. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures asyncContext and performs the following steps when called:
2022-08-22 11:48:08 +01:00
auto rejected_closure = [ & success , & result ] ( VM & vm ) - > ThrowCompletionOr < Value > {
2021-11-09 22:52:21 +02:00
// a. Let prevContext be the running execution context.
// b. Suspend prevContext.
// FIXME: We don't have this concept yet.
// NOTE: Since we don't support context suspension, we exfiltrate the result to await()'s scope instead
success = false ;
result = vm . argument ( 0 ) ;
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
2021-11-16 02:41:18 +03:30
// NOTE: This is not done, because we're not suspending anything (see above).
2021-11-09 22:52:21 +02:00
// d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that suspended it.
// e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.
// FIXME: We don't have this concept yet.
// f. Return undefined.
return js_undefined ( ) ;
} ;
2022-05-02 20:54:39 +02:00
// 6. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
2025-03-16 21:25:29 -05:00
auto on_rejected = NativeFunction : : create ( realm , move ( rejected_closure ) , 1 ) ;
2021-11-09 22:52:21 +02:00
2022-05-02 20:54:39 +02:00
// 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
2025-01-21 09:12:05 -05:00
auto promise = as < Promise > ( promise_object ) ;
2021-11-16 02:41:18 +03:30
promise - > perform_then ( on_fulfilled , on_rejected , { } ) ;
// FIXME: Since we don't support context suspension, we attempt to "wait" for the promise to resolve
2025-04-09 17:02:30 +00:00
// by synchronously running all queued promise jobs.
2025-04-20 16:49:34 +12:00
if ( auto * agent = vm . agent ( ) ) {
2025-01-12 13:33:39 +13:00
// Embedder case (i.e. LibWeb). Runs all promise jobs by performing a microtask checkpoint.
2025-04-20 16:49:34 +12:00
agent - > spin_event_loop_until ( GC : : create_function ( vm . heap ( ) , [ success ] {
2022-09-08 00:13:39 +02:00
return success . has_value ( ) ;
2024-10-31 01:06:56 +13:00
} ) ) ;
2025-01-12 13:33:39 +13:00
} else {
// No embbedder, standalone LibJS implementation
vm . run_queued_promise_jobs ( ) ;
2022-09-08 00:13:39 +02:00
}
2021-11-09 22:52:21 +02:00
// 8. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
2021-11-16 02:41:18 +03:30
// NOTE: Since we don't push any EC, this step is not performed.
2021-11-09 22:52:21 +02:00
2022-05-02 20:54:39 +02:00
// 9. Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion Record completion, the following steps of the algorithm that invoked Await will be performed, with completion available.
// 10. Return NormalCompletion(unused).
2021-11-09 22:52:21 +02:00
// 11. NOTE: This returns to the evaluation of the operation that had most previously resumed evaluation of asyncContext.
2021-11-16 02:41:18 +03:30
// Make sure that the promise _actually_ resolved.
// Note that this is checked down the chain (result.is_empty()) anyway, but let's make the source of the issue more clear.
VERIFY ( success . has_value ( ) ) ;
if ( success . value ( ) )
2021-11-09 22:52:21 +02:00
return result ;
2021-11-16 02:41:18 +03:30
return throw_completion ( result ) ;
2021-11-09 22:52:21 +02:00
}
2024-04-16 08:02:41 +02:00
static void log_exception ( Value value )
{
if ( ! value . is_object ( ) ) {
dbgln ( " \033 [31;1mTHROW! \033 [0m {} " , value ) ;
return ;
}
auto & object = value . as_object ( ) ;
auto & vm = object . vm ( ) ;
dbgln ( " \033 [31;1mTHROW! \033 [0m {} " , object . get ( vm . names . message ) . value ( ) ) ;
vm . dump_backtrace ( ) ;
}
2023-01-27 21:33:28 +00:00
// 6.2.4.2 ThrowCompletion ( value ), https://tc39.es/ecma262/#sec-throwcompletion
2022-11-09 15:22:12 +01:00
Completion throw_completion ( Value value )
{
2024-04-16 08:02:41 +02:00
if ( g_log_all_js_exceptions )
log_exception ( value ) ;
2023-01-27 21:33:28 +00:00
// 1. Return Completion Record { [[Type]]: throw, [[Value]]: value, [[Target]]: empty }.
2024-05-10 09:28:48 +02:00
return { Completion : : Type : : Throw , value } ;
2022-11-09 15:22:12 +01:00
}
2025-06-28 03:32:38 -07:00
void set_log_all_js_exceptions ( bool const enabled )
{
g_log_all_js_exceptions = enabled ;
}
2021-11-09 22:52:21 +02:00
}