mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-11 18:20:43 +09:00
LibJS: Make PromiseJob store callback as a HeapFunction
This is a speculative fix for a flake seen on CI where a JobCallback captured by a PromiseJob callback was GC'd prematurely.
This commit is contained in:
parent
43b0b3fa80
commit
41cc8e75f2
Notes:
sideshowbarker
2024-07-17 07:06:47 +09:00
Author: https://github.com/awesomekling
Commit: 41cc8e75f2
Pull-request: https://github.com/SerenityOS/serenity/pull/23714
6 changed files with 23 additions and 17 deletions
|
@ -296,6 +296,9 @@ struct TimeZoneMethods;
|
|||
struct PartialDurationRecord;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class HeapFunction;
|
||||
|
||||
template<typename T>
|
||||
requires(!IsLvalueReference<T>)
|
||||
class ThrowCompletionOr;
|
||||
|
|
|
@ -90,9 +90,9 @@ PromiseJob create_promise_reaction_job(VM& vm, PromiseReaction& reaction, Value
|
|||
{
|
||||
// 1. Let job be a new Job Abstract Closure with no parameters that captures reaction and argument and performs the following steps when called:
|
||||
// See run_reaction_job for "the following steps".
|
||||
auto job = [&vm, reaction = make_handle(&reaction), argument = make_handle(argument)] {
|
||||
auto job = create_heap_function(vm.heap(), [&vm, reaction = make_handle(&reaction), argument = make_handle(argument)] {
|
||||
return run_reaction_job(vm, *reaction.cell(), argument.value());
|
||||
};
|
||||
});
|
||||
|
||||
// 2. Let handlerRealm be null.
|
||||
Realm* handler_realm { nullptr };
|
||||
|
@ -115,7 +115,7 @@ PromiseJob create_promise_reaction_job(VM& vm, PromiseReaction& reaction, Value
|
|||
}
|
||||
|
||||
// 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }.
|
||||
return { move(job), handler_realm };
|
||||
return { job, handler_realm };
|
||||
}
|
||||
|
||||
// 27.2.2.2 NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ), https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob
|
||||
|
@ -164,12 +164,12 @@ PromiseJob create_promise_resolve_thenable_job(VM& vm, Promise& promise_to_resol
|
|||
|
||||
// 1. Let job be a new Job Abstract Closure with no parameters that captures promiseToResolve, thenable, and then and performs the following steps when called:
|
||||
// See run_resolve_thenable_job() for "the following steps".
|
||||
auto job = [&vm, promise_to_resolve = make_handle(promise_to_resolve), thenable = make_handle(thenable), then]() mutable {
|
||||
auto job = create_heap_function(vm.heap(), [&vm, promise_to_resolve = make_handle(promise_to_resolve), thenable = make_handle(thenable), then]() mutable {
|
||||
return run_resolve_thenable_job(vm, *promise_to_resolve.cell(), thenable.value(), then);
|
||||
};
|
||||
});
|
||||
|
||||
// 6. Return the Record { [[Job]]: job, [[Realm]]: thenRealm }.
|
||||
return { move(job), then_realm };
|
||||
return { job, then_realm };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
namespace JS {
|
||||
|
||||
struct PromiseJob {
|
||||
Function<ThrowCompletionOr<Value>()> job;
|
||||
NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>> job;
|
||||
GCPtr<Realm> realm;
|
||||
};
|
||||
|
||||
|
|
|
@ -86,8 +86,8 @@ VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
|
|||
enqueue_finalization_registry_cleanup_job(finalization_registry);
|
||||
};
|
||||
|
||||
host_enqueue_promise_job = [this](Function<ThrowCompletionOr<Value>()> job, Realm* realm) {
|
||||
enqueue_promise_job(move(job), realm);
|
||||
host_enqueue_promise_job = [this](NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>> job, Realm* realm) {
|
||||
enqueue_promise_job(job, realm);
|
||||
};
|
||||
|
||||
host_make_job_callback = [](FunctionObject& function_object) {
|
||||
|
@ -214,6 +214,9 @@ void VM::gather_roots(HashMap<Cell*, HeapRoot>& roots)
|
|||
gather_roots_from_execution_context_stack(m_execution_context_stack);
|
||||
for (auto& saved_stack : m_saved_execution_context_stacks)
|
||||
gather_roots_from_execution_context_stack(saved_stack);
|
||||
|
||||
for (auto& job : m_promise_jobs)
|
||||
roots.set(job, HeapRoot { .type = HeapRoot::Type::VM });
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> VM::named_evaluation_if_anonymous_function(ASTNode const& expression, DeprecatedFlyString const& name)
|
||||
|
@ -636,19 +639,19 @@ void VM::run_queued_promise_jobs()
|
|||
auto job = m_promise_jobs.take_first();
|
||||
dbgln_if(PROMISE_DEBUG, "Calling promise job function");
|
||||
|
||||
[[maybe_unused]] auto result = job();
|
||||
[[maybe_unused]] auto result = job->function()();
|
||||
}
|
||||
}
|
||||
|
||||
// 9.5.4 HostEnqueuePromiseJob ( job, realm ), https://tc39.es/ecma262/#sec-hostenqueuepromisejob
|
||||
void VM::enqueue_promise_job(Function<ThrowCompletionOr<Value>()> job, Realm*)
|
||||
void VM::enqueue_promise_job(NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>> job, Realm*)
|
||||
{
|
||||
// An implementation of HostEnqueuePromiseJob must conform to the requirements in 9.5 as well as the following:
|
||||
// - FIXME: If realm is not null, each time job is invoked the implementation must perform implementation-defined steps such that execution is prepared to evaluate ECMAScript code at the time of job's invocation.
|
||||
// - FIXME: Let scriptOrModule be GetActiveScriptOrModule() at the time HostEnqueuePromiseJob is invoked. If realm is not null, each time job is invoked the implementation must perform implementation-defined steps
|
||||
// such that scriptOrModule is the active script or module at the time of job's invocation.
|
||||
// - Jobs must run in the same order as the HostEnqueuePromiseJob invocations that scheduled them.
|
||||
m_promise_jobs.append(move(job));
|
||||
m_promise_jobs.append(job);
|
||||
}
|
||||
|
||||
void VM::run_queued_finalization_registry_cleanup_jobs()
|
||||
|
|
|
@ -210,7 +210,7 @@ public:
|
|||
CommonPropertyNames names;
|
||||
|
||||
void run_queued_promise_jobs();
|
||||
void enqueue_promise_job(Function<ThrowCompletionOr<Value>()> job, Realm*);
|
||||
void enqueue_promise_job(NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>> job, Realm*);
|
||||
|
||||
void run_queued_finalization_registry_cleanup_jobs();
|
||||
void enqueue_finalization_registry_cleanup_job(FinalizationRegistry&);
|
||||
|
@ -254,7 +254,7 @@ public:
|
|||
Function<void(Promise&, Promise::RejectionOperation)> host_promise_rejection_tracker;
|
||||
Function<ThrowCompletionOr<Value>(JobCallback&, Value, ReadonlySpan<Value>)> host_call_job_callback;
|
||||
Function<void(FinalizationRegistry&)> host_enqueue_finalization_registry_cleanup_job;
|
||||
Function<void(Function<ThrowCompletionOr<Value>()>, Realm*)> host_enqueue_promise_job;
|
||||
Function<void(NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>>, Realm*)> host_enqueue_promise_job;
|
||||
Function<JS::NonnullGCPtr<JobCallback>(FunctionObject&)> host_make_job_callback;
|
||||
Function<ThrowCompletionOr<void>(Realm&)> host_ensure_can_compile_strings;
|
||||
Function<ThrowCompletionOr<void>(Object&)> host_ensure_can_add_private_element;
|
||||
|
@ -300,7 +300,7 @@ private:
|
|||
// GlobalSymbolRegistry, https://tc39.es/ecma262/#table-globalsymbolregistry-record-fields
|
||||
HashMap<String, NonnullGCPtr<Symbol>> m_global_symbol_registry;
|
||||
|
||||
Vector<Function<ThrowCompletionOr<Value>()>> m_promise_jobs;
|
||||
Vector<NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>>> m_promise_jobs;
|
||||
|
||||
Vector<GCPtr<FinalizationRegistry>> m_finalization_registry_cleanup_jobs;
|
||||
|
||||
|
|
|
@ -249,7 +249,7 @@ ErrorOr<void> initialize_main_thread_vm()
|
|||
};
|
||||
|
||||
// 8.1.5.4.3 HostEnqueuePromiseJob(job, realm), https://html.spec.whatwg.org/multipage/webappapis.html#hostenqueuepromisejob
|
||||
s_main_thread_vm->host_enqueue_promise_job = [](Function<JS::ThrowCompletionOr<JS::Value>()> job, JS::Realm* realm) {
|
||||
s_main_thread_vm->host_enqueue_promise_job = [](JS::NonnullGCPtr<JS::HeapFunction<JS::ThrowCompletionOr<JS::Value>()>> job, JS::Realm* realm) {
|
||||
// 1. If realm is not null, then let job settings be the settings object for realm. Otherwise, let job settings be null.
|
||||
HTML::EnvironmentSettingsObject* job_settings { nullptr };
|
||||
if (realm)
|
||||
|
@ -296,7 +296,7 @@ ErrorOr<void> initialize_main_thread_vm()
|
|||
}
|
||||
|
||||
// 3. Let result be job().
|
||||
[[maybe_unused]] auto result = job();
|
||||
auto result = job->function()();
|
||||
|
||||
// 4. If job settings is not null, then clean up after running script with job settings.
|
||||
if (job_settings) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue