2021-11-09 20:52:21 +00: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 20:52:21 +00:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/TypeCasts.h>
# include <LibJS/Runtime/Completion.h>
# include <LibJS/Runtime/NativeFunction.h>
2022-10-02 09:59:22 +00:00
# include <LibJS/Runtime/Promise.h>
# include <LibJS/Runtime/PromiseCapability.h>
2021-11-09 20:52:21 +00:00
# include <LibJS/Runtime/PromiseConstructor.h>
# include <LibJS/Runtime/VM.h>
# include <LibJS/Runtime/Value.h>
namespace JS {
2024-04-16 06:02:41 +00:00
bool g_log_all_js_exceptions = false ;
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 20:52:21 +00: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 19:38:35 +00:00
ThrowCompletionOr < Value > await ( VM & vm , Value value )
2021-11-09 20:52:21 +00:00
{
2022-08-21 19:38:35 +00:00
auto & realm = * vm . current_realm ( ) ;
2021-11-09 20:52:21 +00:00
// 1. Let asyncContext be the running execution context.
2021-11-15 23:11:18 +00:00
// NOTE: This is not needed, as we don't suspend anything.
2021-11-09 20:52:21 +00:00
// 2. Let promise be ? PromiseResolve(%Promise%, value).
2023-04-12 22:47:15 +00:00
auto * promise_object = TRY ( promise_resolve ( vm , realm . intrinsics ( ) . promise_constructor ( ) , value ) ) ;
2021-11-09 20:52:21 +00:00
2024-04-07 23:32:33 +00:00
IGNORE_USE_IN_ESCAPING_LAMBDA Optional < bool > success ;
IGNORE_USE_IN_ESCAPING_LAMBDA Value result ;
2021-11-09 20:52:21 +00: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 10:48:08 +00:00
auto fulfilled_closure = [ & success , & result ] ( VM & vm ) - > ThrowCompletionOr < Value > {
2021-11-09 20:52:21 +00: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-15 23:11:18 +00:00
// NOTE: This is not done, because we're not suspending anything (see above).
2021-11-09 20:52:21 +00: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 18:54:39 +00:00
// 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »).
2022-12-13 20:49:50 +00:00
auto on_fulfilled = NativeFunction : : create ( realm , move ( fulfilled_closure ) , 1 , " " ) ;
2021-11-09 20:52:21 +00: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 10:48:08 +00:00
auto rejected_closure = [ & success , & result ] ( VM & vm ) - > ThrowCompletionOr < Value > {
2021-11-09 20:52:21 +00: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-15 23:11:18 +00:00
// NOTE: This is not done, because we're not suspending anything (see above).
2021-11-09 20:52:21 +00: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 18:54:39 +00:00
// 6. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
2022-12-13 20:49:50 +00:00
auto on_rejected = NativeFunction : : create ( realm , move ( rejected_closure ) , 1 , " " ) ;
2021-11-09 20:52:21 +00:00
2022-05-02 18:54:39 +00:00
// 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
2022-12-13 20:49:50 +00:00
auto promise = verify_cast < Promise > ( promise_object ) ;
2021-11-15 23:11:18 +00:00
promise - > perform_then ( on_fulfilled , on_rejected , { } ) ;
// FIXME: Since we don't support context suspension, we attempt to "wait" for the promise to resolve
// by letting the event loop spin until our promise is no longer pending, and then synchronously
// running all queued promise jobs.
// Note: This is not used by LibJS itself, and is performed for the embedder (i.e. LibWeb).
2022-09-07 22:13:39 +00:00
if ( auto * custom_data = vm . custom_data ( ) ) {
2024-11-14 15:01:23 +00:00
custom_data - > spin_event_loop_until ( GC : : create_function ( vm . heap ( ) , [ success ] {
2022-09-07 22:13:39 +00:00
return success . has_value ( ) ;
2024-10-30 12:06:56 +00:00
} ) ) ;
2022-09-07 22:13:39 +00:00
}
2021-11-09 20:52:21 +00: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-15 23:11:18 +00:00
// NOTE: Since we don't push any EC, this step is not performed.
2021-11-09 20:52:21 +00:00
2022-05-02 18:54:39 +00: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 20:52:21 +00:00
// 11. NOTE: This returns to the evaluation of the operation that had most previously resumed evaluation of asyncContext.
2021-11-15 23:11:18 +00:00
2021-11-09 20:52:21 +00:00
vm . run_queued_promise_jobs ( ) ;
2021-11-15 23:11:18 +00:00
// 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 20:52:21 +00:00
return result ;
2021-11-15 23:11:18 +00:00
return throw_completion ( result ) ;
2021-11-09 20:52:21 +00:00
}
2024-04-16 06:02:41 +00: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 14:22:12 +00:00
Completion throw_completion ( Value value )
{
2024-04-16 06:02:41 +00: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 07:28:48 +00:00
return { Completion : : Type : : Throw , value } ;
2022-11-09 14:22:12 +00:00
}
2021-11-09 20:52:21 +00:00
}