diff --git a/deps/v8/include/v8-microtask-dispatch.h b/deps/v8/include/v8-microtask-dispatch.h index 58a6251f3e3..5557ea581a5 100644 --- a/deps/v8/include/v8-microtask-dispatch.h +++ b/deps/v8/include/v8-microtask-dispatch.h @@ -36,6 +36,16 @@ V8_EXPORT void SetMicrotaskDispatchCallback(Isolate* isolate, V8_EXPORT void RunDispatchedMicrotask(Isolate* isolate, DispatchedMicrotask* task); +// If the next job in the isolate's default microtask queue is a promise +// reaction job with non-undefined CPED, takes it off the queue and runs it on +// the current stack, as RunDispatchedMicrotask does, and returns true. +// Otherwise returns false and leaves the queue alone. Lets an embedder that is +// running a dispatched job keep running the dispatchable jobs right behind it +// without switching stacks for each one. Only call it while the job that was +// dispatched is still nested inside the microtask loop, so the order of jobs +// is unchanged. +V8_EXPORT bool RunNextDispatchableMicrotask(Isolate* isolate); + } // namespace v8 // Unmangled entry points, so an addon can find them with dlsym() and still @@ -45,6 +55,7 @@ V8_EXPORT void v8_qualia_SetMicrotaskDispatchCallback( v8::Isolate* isolate, v8::MicrotaskDispatchCallback callback, void* data); V8_EXPORT void v8_qualia_RunDispatchedMicrotask(v8::Isolate* isolate, v8::DispatchedMicrotask* task); +V8_EXPORT bool v8_qualia_RunNextDispatchableMicrotask(v8::Isolate* isolate); } #endif // INCLUDE_V8_MICROTASK_DISPATCH_H_ diff --git a/deps/v8/src/runtime/runtime-promise.cc b/deps/v8/src/runtime/runtime-promise.cc index 2e7a7b36f57..c33f1b198d6 100644 --- a/deps/v8/src/runtime/runtime-promise.cc +++ b/deps/v8/src/runtime/runtime-promise.cc @@ -13,6 +13,7 @@ #include "src/objects/heap-object-inl.h" #include "src/objects/js-promise-inl.h" #include "src/objects/microtask-inl.h" +#include "src/objects/promise-inl.h" #include "src/objects/objects-inl.h" #include "src/objects/oddball-inl.h" #include "src/runtime/runtime-utils.h" @@ -188,6 +189,40 @@ void RunDispatchedMicrotask(Isolate* v8_isolate, DispatchedMicrotask* task) { } } +bool RunNextDispatchableMicrotask(Isolate* v8_isolate) { + internal::Isolate* isolate = reinterpret_cast(v8_isolate); + internal::MicrotaskQueue* queue = isolate->default_microtask_queue(); + if (queue == nullptr || queue->size() == 0) return false; + internal::HandleScope handle_scope(isolate); + internal::Microtask front = queue->get(0); + if (!front.IsPromiseFulfillReactionJobTask() && + !front.IsPromiseRejectReactionJobTask()) { + return false; + } + if (internal::PromiseReactionJobTask::cast(front) + .continuation_preserved_embedder_data() + .IsUndefined(isolate)) { + return false; + } + internal::Handle microtask(front, isolate); + // Take it off the front of the queue the way the RunMicrotasks builtin does + // (that loop re-reads start and size before every job, so the drain this is + // nested in carries on with the job after it). + internal::Address base = reinterpret_cast(queue); + intptr_t* start = reinterpret_cast( + base + internal::MicrotaskQueue::kStartOffset); + intptr_t* size = reinterpret_cast( + base + internal::MicrotaskQueue::kSizeOffset); + *start = (*start + 1) % queue->capacity(); + *size -= 1; + std::unique_ptr private_queue = + internal::MicrotaskQueue::New(isolate); + private_queue->EnqueueMicrotask(*microtask); + RunDispatchedMicrotask(v8_isolate, + new DispatchedMicrotask{private_queue.release()}); + return true; +} + } // namespace v8 extern "C" { @@ -200,6 +235,10 @@ void v8_qualia_RunDispatchedMicrotask(v8::Isolate* isolate, v8::DispatchedMicrotask* task) { v8::RunDispatchedMicrotask(isolate, task); } + +bool v8_qualia_RunNextDispatchableMicrotask(v8::Isolate* isolate) { + return v8::RunNextDispatchableMicrotask(isolate); +} } namespace v8 {