ladybird/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.h
Andreas Kling 3e1145ef07 LibJS: Synchronous await fast path when microtask queue is empty
When an async function is resumed from a microtask and hits another
await with a non-thenable value (primitive or already-settled native
promise), and the microtask queue is empty, we can resolve the await
synchronously without suspending. No other microtask can observe the
difference in execution order, making this optimization safe.

This avoids the overhead of creating a GC::Function for the microtask
job, enqueuing/dequeuing from the microtask queue, and the execution
context push/pop that comes with it.

A new VM host hook, host_promise_job_queue_is_empty, is added so both
the standalone js binary and LibWeb can provide the appropriate check
for their respective job queue implementations.
2026-03-16 19:15:03 -05:00

43 lines
1.2 KiB
C++

/*
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/GeneratorObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Promise.h>
namespace JS {
class AsyncFunctionDriverWrapper final : public Promise {
JS_OBJECT(AsyncFunctionDriverWrapper, Promise);
GC_DECLARE_ALLOCATOR(AsyncFunctionDriverWrapper);
public:
[[nodiscard]] static GC::Ref<Promise> create(Realm&, GeneratorObject*);
virtual ~AsyncFunctionDriverWrapper() override = default;
void visit_edges(Cell::Visitor&) override;
void continue_async_execution(VM&, Value, bool is_successful);
void schedule_resume(Value, bool is_fulfilled);
private:
AsyncFunctionDriverWrapper(Realm&, GC::Ref<GeneratorObject>, GC::Ref<Promise> top_level_promise);
ThrowCompletionOr<void> await(Value);
GC::Ref<GeneratorObject> m_generator_object;
GC::Ref<Promise> m_top_level_promise;
GC::Ptr<Promise> m_current_promise { nullptr };
OwnPtr<ExecutionContext> m_suspended_execution_context;
GC::Ptr<NativeFunction> m_on_settled;
bool m_is_initial_execution { true };
};
}