mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-05-01 20:17:13 +02:00
LibJS: Combine onFulfilled and onRejected into a single settled callback
The fulfilled and rejected closures in AsyncFunctionDriverWrapper::await were identical in structure, differing only in whether they resumed the async context with a NormalCompletion or ThrowCompletion. Since the callback has access to the current promise, we can check its state at reaction time to determine which completion to use. This allows us to allocate a single GC-tracked NativeFunction (m_on_settled) and register it for both the onFulfilled and onRejected slots in PerformPromiseThen, halving the number of GC allocations on this path.
This commit is contained in:
committed by
Andreas Kling
parent
d76330645a
commit
b34274a2a0
Notes:
github-actions[bot]
2026-03-16 16:19:43 +00:00
Author: https://github.com/shannonbooth Commit: https://github.com/LadybirdBrowser/ladybird/commit/b34274a2a0f Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8450
@@ -51,8 +51,18 @@ ThrowCompletionOr<void> AsyncFunctionDriverWrapper::await(JS::Value value)
|
||||
|
||||
// 3. Let fulfilledClosure be a new Abstract Closure with parameters (v) that captures asyncContext and performs the
|
||||
// following steps when called:
|
||||
auto fulfilled_closure = [this](VM& vm) -> ThrowCompletionOr<Value> {
|
||||
auto value = vm.argument(0);
|
||||
// 5. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures asyncContext and performs the
|
||||
// following steps when called:
|
||||
// OPTIMIZATION: onRejected and onFulfilled are identical other than the resumption value passed to continue_async_execution.
|
||||
// To avoid allocated two GC functions down this path, we combine both callbacks into one function.
|
||||
auto settled_closure = [this](VM& vm) -> ThrowCompletionOr<Value> {
|
||||
auto reason = vm.argument(0);
|
||||
|
||||
// The currently awaited promise is settled when this reaction runs, so we can use
|
||||
// its state to decide whether to resume with a normal or throw completion.
|
||||
VERIFY(m_current_promise);
|
||||
auto is_successful = m_current_promise->state() == Promise::State::Fulfilled;
|
||||
VERIFY(is_successful || m_current_promise->state() == Promise::State::Rejected);
|
||||
|
||||
// a. Let prevContext be the running execution context.
|
||||
auto& prev_context = vm.running_execution_context();
|
||||
@@ -61,9 +71,11 @@ ThrowCompletionOr<void> AsyncFunctionDriverWrapper::await(JS::Value value)
|
||||
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
|
||||
TRY(vm.push_execution_context(*m_suspended_execution_context, {}));
|
||||
|
||||
// d. Resume the suspended evaluation of asyncContext using NormalCompletion(v) as the result of the operation that
|
||||
// suspended it.
|
||||
continue_async_execution(vm, value, true);
|
||||
// 3.d. Resume the suspended evaluation of asyncContext using NormalCompletion(v) as the result of the operation that
|
||||
// suspended it.
|
||||
// 5.d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that
|
||||
// suspended it.
|
||||
continue_async_execution(vm, reason, is_successful);
|
||||
vm.pop_execution_context();
|
||||
|
||||
// e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and
|
||||
@@ -75,41 +87,13 @@ ThrowCompletionOr<void> AsyncFunctionDriverWrapper::await(JS::Value value)
|
||||
};
|
||||
|
||||
// 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »).
|
||||
if (!m_on_fulfilled)
|
||||
m_on_fulfilled = NativeFunction::create(realm, move(fulfilled_closure), 1);
|
||||
|
||||
// 5. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures asyncContext and performs the
|
||||
// following steps when called:
|
||||
auto rejected_closure = [this](VM& vm) -> ThrowCompletionOr<Value> {
|
||||
auto reason = vm.argument(0);
|
||||
|
||||
// a. Let prevContext be the running execution context.
|
||||
auto& prev_context = vm.running_execution_context();
|
||||
|
||||
// b. Suspend prevContext.
|
||||
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
|
||||
TRY(vm.push_execution_context(*m_suspended_execution_context, {}));
|
||||
|
||||
// d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that
|
||||
// suspended it.
|
||||
continue_async_execution(vm, reason, false);
|
||||
vm.pop_execution_context();
|
||||
|
||||
// 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.
|
||||
VERIFY(&vm.running_execution_context() == &prev_context);
|
||||
|
||||
// f. Return undefined.
|
||||
return js_undefined();
|
||||
};
|
||||
|
||||
// 6. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
|
||||
if (!m_on_rejected)
|
||||
m_on_rejected = NativeFunction::create(realm, move(rejected_closure), 1);
|
||||
if (!m_on_settled)
|
||||
m_on_settled = NativeFunction::create(realm, move(settled_closure), 1);
|
||||
|
||||
// 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
|
||||
m_current_promise = as<Promise>(promise_object);
|
||||
m_current_promise->perform_then(m_on_fulfilled, m_on_rejected, {});
|
||||
m_current_promise->perform_then(m_on_settled, m_on_settled, {});
|
||||
|
||||
// NOTE: None of these are necessary. 8-12 are handled by step d of the above lambdas.
|
||||
// 8. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the
|
||||
@@ -177,8 +161,7 @@ void AsyncFunctionDriverWrapper::visit_edges(Cell::Visitor& visitor)
|
||||
visitor.visit(m_current_promise);
|
||||
if (m_suspended_execution_context)
|
||||
m_suspended_execution_context->visit_edges(visitor);
|
||||
visitor.visit(m_on_fulfilled);
|
||||
visitor.visit(m_on_rejected);
|
||||
visitor.visit(m_on_settled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user