diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index d8ce4aa6451c..e0749b8a10d8 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -168,6 +168,7 @@ #include "JSMapInlines.h" #include "JSMapIteratorInlines.h" #include "JSMicrotask.h" +#include "MicrotaskQueueInlines.h" #include "JSModuleEnvironmentInlines.h" #include "JSModuleLoaderInlines.h" #include "JSModuleNamespaceObjectInlines.h" @@ -3329,6 +3330,12 @@ void JSGlobalObject::bumpGlobalLexicalBindingEpoch(VM& vm) void JSGlobalObject::queueMicrotask(JSFunction* job, JSValue argument0, JSValue argument1, JSValue argument2, JSValue argument3) { QueuedTask task { nullptr, this, job, argument0, argument1, argument2, argument3 }; + + if (m_contextMicrotaskQueue) { + m_contextMicrotaskQueue->enqueue(WTFMove(task)); + return; + } + if (globalObjectMethodTable()->queueMicrotaskToEventLoop) { globalObjectMethodTable()->queueMicrotaskToEventLoop(*this, WTFMove(task)); return; @@ -3336,6 +3343,41 @@ void JSGlobalObject::queueMicrotask(JSFunction* job, JSValue argument0, JSValue vm().queueMicrotask(WTFMove(task)); } +void JSGlobalObject::createContextMicrotaskQueue() +{ + ASSERT(!m_contextMicrotaskQueue); + m_contextMicrotaskQueue = makeUnique(vm()); +} + +void JSGlobalObject::drainMicrotasks() +{ + if (!m_contextMicrotaskQueue) + return; + + auto& vm = this->vm(); + + if (vm.executionForbidden()) [[unlikely]] + m_contextMicrotaskQueue->clear(); + else { + do { + m_contextMicrotaskQueue->performMicrotaskCheckpoint(vm, + [&](QueuedTask& task) ALWAYS_INLINE_LAMBDA { + if (RefPtr dispatcher = task.dispatcher()) + return dispatcher->run(task); + + runJSMicrotask(task.globalObject(), task.identifier(), task.job(), task.arguments()); + return QueuedTask::Result::Executed; + }); + if (vm.hasPendingTerminationException()) [[unlikely]] + return; + vm.didExhaustMicrotaskQueue(); + if (vm.hasPendingTerminationException()) [[unlikely]] + return; + } while (!m_contextMicrotaskQueue->isEmpty()); + } + vm.finalizeSynchronousJSExecution(); +} + void JSGlobalObject::reportUncaughtExceptionAtEventLoop(JSGlobalObject*, Exception* exception) { dataLogLn("Uncaught Exception at run loop: ", exception->value()); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index 01c02c781865..6dc0463943b2 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -614,7 +614,9 @@ class JSGlobalObject : public JSSegmentedVariableObject { WeakPtr m_consoleClient; std::optional m_stackTraceLimit; Weak m_executableForCachedFunctionExecutableForFunctionConstructor; - + + std::unique_ptr m_contextMicrotaskQueue; + // Added for "bun test" double overridenDateNow { -1 }; @@ -1139,6 +1141,9 @@ class JSGlobalObject : public JSSegmentedVariableObject { JS_EXPORT_PRIVATE void queueMicrotask(JSFunction* job, JSValue, JSValue, JSValue, JSValue); + JS_EXPORT_PRIVATE void createContextMicrotaskQueue(); + JS_EXPORT_PRIVATE void drainMicrotasks(); + static void reportViolationForUnsafeEval(const JSGlobalObject*, const String&) { } bool evalEnabled() const { return m_evalEnabled; } diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h index 16783e87aa8a..1c3d7a15c30e 100644 --- a/Source/JavaScriptCore/runtime/VM.h +++ b/Source/JavaScriptCore/runtime/VM.h @@ -886,6 +886,7 @@ class VM : public ThreadSafeRefCountedWithSuppressingSaferCPPChecking, publi DrainMicrotaskDelayScope drainMicrotaskDelayScope() { return DrainMicrotaskDelayScope { *this }; } JS_EXPORT_PRIVATE void drainMicrotasks(); + JS_EXPORT_PRIVATE void didExhaustMicrotaskQueue(); void setOnEachMicrotaskTick(WTF::Function&& func) { m_onEachMicrotaskTick = WTFMove(func); } void callOnEachMicrotaskTick() { @@ -1060,7 +1061,6 @@ class VM : public ThreadSafeRefCountedWithSuppressingSaferCPPChecking, publi void primitiveGigacageDisabled(); void callPromiseRejectionCallback(Strong&); - void didExhaustMicrotaskQueue(); #if ENABLE(GC_VALIDATION) const ClassInfo* m_initializingObjectClass { nullptr };