aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/bindings/JSNextTickQueue.cpp97
-rw-r--r--src/bun.js/bindings/JSNextTickQueue.h41
-rw-r--r--src/bun.js/bindings/Process.cpp98
-rw-r--r--src/bun.js/bindings/Process.lut.h2
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp124
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h5
-rw-r--r--src/bun.js/bindings/bindings.cpp3
-rw-r--r--src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h1
-rw-r--r--src/bun.js/bindings/webcore/DOMIsoSubspaces.h2
-rw-r--r--src/bun.js/event_loop.zig15
10 files changed, 309 insertions, 79 deletions
diff --git a/src/bun.js/bindings/JSNextTickQueue.cpp b/src/bun.js/bindings/JSNextTickQueue.cpp
new file mode 100644
index 000000000..8916ef6c8
--- /dev/null
+++ b/src/bun.js/bindings/JSNextTickQueue.cpp
@@ -0,0 +1,97 @@
+#include "root.h"
+
+#include "JavaScriptCore/JSCJSValueInlines.h"
+#include "JavaScriptCore/JSInternalPromise.h"
+#include "JavaScriptCore/LazyPropertyInlines.h"
+#include <JavaScriptCore/Weak.h>
+#include <JavaScriptCore/GetterSetter.h>
+
+#include "JSNextTickQueue.h"
+#include <JavaScriptCore/JSGlobalObject.h>
+#include <JavaScriptCore/Structure.h>
+#include <JavaScriptCore/JSInternalFieldObjectImplInlines.h>
+#include "ExtendedDOMClientIsoSubspaces.h"
+#include "ExtendedDOMIsoSubspaces.h"
+#include "BunClientData.h"
+
+namespace Bun {
+
+using namespace JSC;
+
+const JSC::ClassInfo JSNextTickQueue::s_info = { "JSNextTickQueue"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSNextTickQueue) };
+
+template<typename, JSC::SubspaceAccess mode>
+JSC::GCClient::IsoSubspace* JSNextTickQueue::subspaceFor(JSC::VM& vm)
+{
+ return WebCore::subspaceForImpl<JSNextTickQueue, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForJSNextTickQueue.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSNextTickQueue = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForJSNextTickQueue.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForJSNextTickQueue = std::forward<decltype(space)>(space); });
+}
+
+JSNextTickQueue* JSNextTickQueue::create(VM& vm, Structure* structure)
+{
+ JSNextTickQueue* mod = new (NotNull, allocateCell<JSNextTickQueue>(vm)) JSNextTickQueue(vm, structure);
+ return mod;
+}
+Structure* JSNextTickQueue::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+ return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
+}
+
+JSNextTickQueue::JSNextTickQueue(VM& vm, Structure* structure)
+ : Base(vm, structure)
+{
+}
+
+void JSNextTickQueue::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+}
+
+template<typename Visitor>
+void JSNextTickQueue::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ auto* thisObject = jsCast<JSNextTickQueue*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+}
+
+DEFINE_VISIT_CHILDREN(JSNextTickQueue);
+
+JSNextTickQueue* JSNextTickQueue::create(JSC::JSGlobalObject* globalObject)
+{
+ auto& vm = globalObject->vm();
+ auto* obj = create(vm, createStructure(vm, globalObject, jsNull()));
+ obj->finishCreation(vm);
+ return obj;
+}
+
+bool JSNextTickQueue::isEmpty()
+{
+ return !internalField(0) || internalField(0).get().asNumber() == 0;
+}
+
+void JSNextTickQueue::drain(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ bool mustResetContext = false;
+ if (isEmpty()) {
+ vm.drainMicrotasks();
+ mustResetContext = true;
+ }
+
+ if (!isEmpty()) {
+ if (mustResetContext) {
+ globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined());
+ }
+ auto* drainFn = internalField(2).get().getObject();
+
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ MarkedArgumentBuffer drainArgs;
+ JSC::call(globalObject, drainFn, drainArgs, "Failed to drain next tick queue"_s);
+ }
+}
+
+} \ No newline at end of file
diff --git a/src/bun.js/bindings/JSNextTickQueue.h b/src/bun.js/bindings/JSNextTickQueue.h
new file mode 100644
index 000000000..c3bd228cc
--- /dev/null
+++ b/src/bun.js/bindings/JSNextTickQueue.h
@@ -0,0 +1,41 @@
+#include "root.h"
+#include "headers-handwritten.h"
+
+#include "JavaScriptCore/JSCInlines.h"
+#include "BunClientData.h"
+#include <JavaScriptCore/JSInternalFieldObjectImpl.h>
+
+namespace Bun {
+using namespace JSC;
+
+class JSNextTickQueue : public JSC::JSInternalFieldObjectImpl<3> {
+public:
+ static constexpr unsigned numberOfInternalFields = 3;
+ using Base = JSC::JSInternalFieldObjectImpl<3>;
+
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm);
+
+ JS_EXPORT_PRIVATE static JSNextTickQueue* create(VM&, Structure*);
+ static JSNextTickQueue* create(JSC::JSGlobalObject* globalObject);
+ static JSNextTickQueue* createWithInitialValues(VM&, Structure*);
+ static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
+
+ static std::array<JSValue, numberOfInternalFields> initialValues()
+ {
+ return { {
+ jsNumber(-1),
+ jsUndefined(),
+ jsUndefined(),
+ } };
+ }
+
+ DECLARE_EXPORT_INFO;
+ DECLARE_VISIT_CHILDREN;
+
+ JSNextTickQueue(JSC::VM&, JSC::Structure*);
+ void finishCreation(JSC::VM&);
+
+ bool isEmpty();
+ void drain(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
+};
+} \ No newline at end of file
diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp
index 5c9c03dd2..252d00075 100644
--- a/src/bun.js/bindings/Process.cpp
+++ b/src/bun.js/bindings/Process.cpp
@@ -19,6 +19,7 @@
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
+#include "JSNextTickQueue.h"
#pragma mark - Node.js Process
@@ -160,63 +161,6 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionInternalGetWindowSize,
return JSC::JSValue::encode(jsBoolean(true));
}
-JSC_DEFINE_HOST_FUNCTION(Process_functionNextTick,
- (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = globalObject->vm();
- auto argCount = callFrame->argumentCount();
- if (argCount == 0) {
- auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
- JSC::throwTypeError(globalObject, scope, "nextTick requires 1 argument (a function)"_s);
- return JSC::JSValue::encode(JSC::JSValue {});
- }
-
- JSC::JSValue job = callFrame->uncheckedArgument(0);
-
- if (!job.isObject() || !job.getObject()->isCallable()) {
- auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
- JSC::throwTypeError(globalObject, scope, "nextTick expects a function"_s);
- return JSC::JSValue::encode(JSC::JSValue {});
- }
-
- Zig::GlobalObject* global = JSC::jsCast<Zig::GlobalObject*>(globalObject);
- JSC::JSValue asyncContextValue = globalObject->m_asyncContextData.get()->getInternalField(0);
-
- switch (callFrame->argumentCount()) {
- case 1: {
- global->queueMicrotask(global->performMicrotaskFunction(), job, asyncContextValue, JSC::JSValue {}, JSC::JSValue {});
- break;
- }
- case 2: {
- global->queueMicrotask(global->performMicrotaskFunction(), job, asyncContextValue, callFrame->uncheckedArgument(1), JSC::JSValue {});
- break;
- }
- case 3: {
- global->queueMicrotask(global->performMicrotaskFunction(), job, asyncContextValue, callFrame->uncheckedArgument(1), callFrame->uncheckedArgument(2));
- break;
- }
- default: {
- JSC::JSArray* args = JSC::constructEmptyArray(globalObject, nullptr, argCount - 1);
- if (UNLIKELY(!args)) {
- auto scope = DECLARE_THROW_SCOPE(vm);
- throwVMError(globalObject, scope, createOutOfMemoryError(globalObject));
- return JSC::JSValue::encode(JSC::JSValue {});
- }
-
- for (unsigned i = 1; i < argCount; i++) {
- args->putDirectIndex(globalObject, i - 1, callFrame->uncheckedArgument(i));
- }
-
- global->queueMicrotask(
- global->performMicrotaskVariadicFunction(), job, args, asyncContextValue, JSC::JSValue {});
-
- break;
- }
- }
-
- return JSC::JSValue::encode(jsUndefined());
-}
-
JSC_DECLARE_HOST_FUNCTION(Process_functionDlopen);
JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
(JSC::JSGlobalObject * globalObject_, JSC::CallFrame* callFrame))
@@ -279,7 +223,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
}
}
- JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject,
+ JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject * globalObject,
JSC::EncodedJSValue exports);
napi_register_module_v1 = reinterpret_cast<JSC::EncodedJSValue (*)(JSC::JSGlobalObject*,
@@ -1533,6 +1477,42 @@ static JSValue constructMemoryUsage(VM& vm, JSObject* processObject)
return memoryUsage;
}
+JSC_DEFINE_HOST_FUNCTION(jsFunctionReportUncaughtException, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ JSValue arg0 = callFrame->argument(0);
+ Bun__reportUnhandledError(globalObject, JSValue::encode(arg0));
+ return JSValue::encode(jsUndefined());
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsFunctionDrainMicrotaskQueue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ globalObject->vm().drainMicrotasks();
+ return JSValue::encode(jsUndefined());
+}
+
+static JSValue constructProcessNextTickFn(VM& vm, JSObject* processObject)
+{
+ JSGlobalObject* lexicalGlobalObject = processObject->globalObject();
+ Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
+ JSValue nextTickQueueObject;
+ if (!globalObject->m_nextTickQueue) {
+ Bun::JSNextTickQueue* queue = Bun::JSNextTickQueue::create(globalObject);
+ globalObject->m_nextTickQueue.set(vm, globalObject, queue);
+ nextTickQueueObject = queue;
+ } else {
+ nextTickQueueObject = jsCast<Bun::JSNextTickQueue*>(globalObject->m_nextTickQueue.get());
+ }
+
+ JSC::JSFunction* initializer = JSC::JSFunction::create(vm, processObjectInternalsInitializeNextTickQueueCodeGenerator(vm), lexicalGlobalObject);
+ JSC::MarkedArgumentBuffer args;
+ args.append(processObject);
+ args.append(nextTickQueueObject);
+ args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionDrainMicrotaskQueue, ImplementationVisibility::Private));
+ args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionReportUncaughtException, ImplementationVisibility::Private));
+
+ return JSC::call(globalObject, initializer, JSC::getCallData(initializer), globalObject->globalThis(), args);
+}
+
static JSValue constructFeatures(VM& vm, JSObject* processObject)
{
// {
@@ -1742,7 +1722,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionKill,
mainModule JSBuiltin ReadOnly|Builtin|Accessor|Function 0
memoryUsage constructMemoryUsage PropertyCallback
moduleLoadList Process_stubEmptyArray PropertyCallback
- nextTick Process_functionNextTick Function 1
+ nextTick constructProcessNextTickFn PropertyCallback
openStdin Process_functionOpenStdin Function 0
pid constructPid PropertyCallback
platform constructPlatform PropertyCallback
diff --git a/src/bun.js/bindings/Process.lut.h b/src/bun.js/bindings/Process.lut.h
index 3f2d9255d..4086fb19e 100644
--- a/src/bun.js/bindings/Process.lut.h
+++ b/src/bun.js/bindings/Process.lut.h
@@ -179,7 +179,7 @@ static const struct HashTableValue processObjectTableValues[62] = {
{ "mainModule"_s, ((static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::Builtin|PropertyAttribute::Accessor|PropertyAttribute::Function)) & ~PropertyAttribute::Function) | PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinGeneratorType, processObjectMainModuleCodeGenerator, 0 } },
{ "memoryUsage"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructMemoryUsage } },
{ "moduleLoadList"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, Process_stubEmptyArray } },
- { "nextTick"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionNextTick, 1 } },
+ { "nextTick"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessNextTickFn } },
{ "openStdin"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionOpenStdin, 0 } },
{ "pid"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructPid } },
{ "platform"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructPlatform } },
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index 7edbd42e6..286084b4d 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -130,6 +130,7 @@
#endif
#include "BunObject.h"
+#include "JSNextTickQueue.h"
using namespace Bun;
@@ -294,7 +295,7 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c
}
extern "C" void* Bun__getVM();
-extern "C" JSGlobalObject* Bun__getDefaultGlobal();
+extern "C" Zig::GlobalObject* Bun__getDefaultGlobal();
// Error.captureStackTrace may cause computeErrorInfo to be called twice
// Rather than figure out the plumbing in JSC, we just skip the next call
@@ -432,6 +433,44 @@ static String computeErrorInfo(JSC::VM& vm, Vector<StackFrame>& stackTrace, unsi
return computeErrorInfoWithoutPrepareStackTrace(vm, stackTrace, line, column, sourceURL, errorInstance);
}
+static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObject);
+
+static void checkIfNextTickWasCalledDuringMicrotask(JSC::VM& vm)
+{
+ auto* globalObject = Bun__getDefaultGlobal();
+ if (auto nextTickQueueValue = globalObject->m_nextTickQueue.get()) {
+ auto* queue = jsCast<Bun::JSNextTickQueue*>(nextTickQueueValue);
+ resetOnEachMicrotaskTick(vm, globalObject);
+ queue->drain(vm, globalObject);
+ }
+}
+
+static void cleanupAsyncHooksData(JSC::VM& vm)
+{
+ auto* globalObject = Bun__getDefaultGlobal();
+ globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined());
+ globalObject->asyncHooksNeedsCleanup = false;
+ if (!globalObject->m_nextTickQueue) {
+ vm.setOnEachMicrotaskTick(&checkIfNextTickWasCalledDuringMicrotask);
+ checkIfNextTickWasCalledDuringMicrotask(vm);
+ } else {
+ vm.setOnEachMicrotaskTick(nullptr);
+ }
+}
+
+static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObject)
+{
+ if (globalObject->asyncHooksNeedsCleanup) {
+ vm.setOnEachMicrotaskTick(&cleanupAsyncHooksData);
+ } else {
+ if (globalObject->m_nextTickQueue) {
+ vm.setOnEachMicrotaskTick(nullptr);
+ } else {
+ vm.setOnEachMicrotaskTick(&checkIfNextTickWasCalledDuringMicrotask);
+ }
+ }
+}
+
extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, int32_t executionContextId, bool miniMode, void* worker_ptr)
{
auto heapSize = miniMode ? JSC::HeapType::Small : JSC::HeapType::Large;
@@ -479,6 +518,16 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client,
JSC::gcProtect(globalObject);
+ vm.setOnEachMicrotaskTick([](JSC::VM& vm) -> void {
+ auto* globalObject = Bun__getDefaultGlobal();
+ if (auto nextTickQueue = globalObject->m_nextTickQueue.get()) {
+ resetOnEachMicrotaskTick(vm, globalObject);
+ Bun::JSNextTickQueue* queue = jsCast<Bun::JSNextTickQueue*>(nextTickQueue);
+ queue->drain(vm, globalObject);
+ return;
+ }
+ });
+
vm.ref();
return globalObject;
}
@@ -1115,6 +1164,17 @@ JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
return JSC::JSValue::encode(JSC::JSValue {});
}
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num), JSValue::encode(arguments));
}
@@ -1166,6 +1226,17 @@ JSC_DEFINE_HOST_FUNCTION(functionSetInterval,
return JSC::JSValue::encode(JSC::JSValue {});
}
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
return Bun__Timer__setInterval(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num), JSValue::encode(arguments));
}
@@ -1182,6 +1253,17 @@ JSC_DEFINE_HOST_FUNCTION(functionClearInterval,
JSC::JSValue num = callFrame->argument(0);
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
return Bun__Timer__clearInterval(globalObject, JSC::JSValue::encode(num));
}
@@ -1198,6 +1280,17 @@ JSC_DEFINE_HOST_FUNCTION(functionClearTimeout,
JSC::JSValue num = callFrame->argument(0);
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
return Bun__Timer__clearTimeout(globalObject, JSC::JSValue::encode(num));
}
@@ -1393,12 +1486,6 @@ JSC_DEFINE_HOST_FUNCTION(functionCallback, (JSC::JSGlobalObject * globalObject,
return JSC::JSValue::encode(JSC::call(globalObject, callback, callData, JSC::jsUndefined(), JSC::MarkedArgumentBuffer()));
}
-static void cleanupAsyncHooksData(JSC::VM& vm)
-{
- vm.setOnEachMicrotaskTick(nullptr);
- Bun__getDefaultGlobal()->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined());
-}
-
// $lazy("async_hooks").cleanupLater
JSC_DEFINE_HOST_FUNCTION(asyncHooksCleanupLater, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
@@ -1406,7 +1493,9 @@ JSC_DEFINE_HOST_FUNCTION(asyncHooksCleanupLater, (JSC::JSGlobalObject * globalOb
// - nobody else uses setOnEachMicrotaskTick
// - this is called by js if we set async context in a way we may not clear it
// - AsyncLocalStorage.prototype.run cleans up after itself and does not call this cb
- globalObject->vm().setOnEachMicrotaskTick(&cleanupAsyncHooksData);
+ auto* global = jsCast<Zig::GlobalObject*>(globalObject);
+ global->asyncHooksNeedsCleanup = true;
+ resetOnEachMicrotaskTick(globalObject->vm(), global);
return JSC::JSValue::encode(JSC::jsUndefined());
}
@@ -3955,6 +4044,23 @@ extern "C" bool JSC__JSGlobalObject__startRemoteInspector(JSC__JSGlobalObject* g
#endif
}
+void GlobalObject::drainMicrotasks()
+{
+ auto& vm = this->vm();
+ if (auto nextTickQueue = this->m_nextTickQueue.get()) {
+ Bun::JSNextTickQueue* queue = jsCast<Bun::JSNextTickQueue*>(nextTickQueue);
+ queue->drain(vm, this);
+ return;
+ }
+
+ vm.drainMicrotasks();
+}
+
+extern "C" void JSC__JSGlobalObject__drainMicrotasks(Zig::GlobalObject* globalObject)
+{
+ globalObject->drainMicrotasks();
+}
+
template<typename Visitor>
void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
@@ -4008,6 +4114,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.append(thisObject->m_JSWebSocketSetterValue);
visitor.append(thisObject->m_JSWorkerSetterValue);
+ visitor.append(thisObject->m_nextTickQueue);
+
thisObject->m_JSArrayBufferSinkClassStructure.visit(visitor);
thisObject->m_JSBufferListClassStructure.visit(visitor);
thisObject->m_JSFFIFunctionStructure.visit(visitor);
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 029f90132..e622016de 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -296,6 +296,8 @@ public:
return m_processEnvObject.getInitializedOnMainThread(this);
}
+ void drainMicrotasks();
+
void handleRejectedPromises();
void initGeneratedLazyClasses();
@@ -363,6 +365,8 @@ public:
return func;
}
+ bool asyncHooksNeedsCleanup = false;
+
/**
* WARNING: You must update visitChildrenImpl() if you add a new field.
*
@@ -381,6 +385,7 @@ public:
mutable WriteBarrier<JSFunction> m_readableStreamToText;
mutable WriteBarrier<JSFunction> m_readableStreamToFormData;
+ mutable WriteBarrier<Unknown> m_nextTickQueue;
mutable WriteBarrier<Unknown> m_BunCommonJSModuleValue;
mutable WriteBarrier<Unknown> m_JSBroadcastChannelSetterValue;
mutable WriteBarrier<Unknown> m_JSBufferSetterValue;
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index ca072f2b1..6413e0470 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -2527,7 +2527,6 @@ JSC__JSInternalPromise*
JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject,
const BunString* arg1)
{
- globalObject->vm().drainMicrotasks();
auto name = Bun::toWTFString(*arg1);
name.impl()->ref();
@@ -2546,9 +2545,7 @@ JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject,
JSC::JSInternalPromise::rejectedPromise(globalObject, callFrame->argument(0)));
});
- globalObject->vm().drainMicrotasks();
auto result = promise->then(globalObject, resolverFunction, rejecterFunction);
- globalObject->vm().drainMicrotasks();
// if (promise->status(globalObject->vm()) ==
// JSC::JSPromise::Status::Fulfilled) {
diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
index 4c09df6a5..e21a62bf8 100644
--- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
@@ -42,6 +42,7 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForProcessObject;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForInternalModuleRegistry;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBunInspectorConnection;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSNextTickQueue;
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
/* --- bun --- */
diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
index 2b834cf3c..806aa4454 100644
--- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
@@ -42,7 +42,7 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForProcessObject;
std::unique_ptr<IsoSubspace> m_subspaceForInternalModuleRegistry;
std::unique_ptr<IsoSubspace> m_subspaceForBunInspectorConnection;
-
+ std::unique_ptr<IsoSubspace> m_subspaceForJSNextTickQueue;
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
/*-- BUN --*/
diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig
index 640a9276c..f1367c239 100644
--- a/src/bun.js/event_loop.zig
+++ b/src/bun.js/event_loop.zig
@@ -529,14 +529,14 @@ pub const EventLoop = struct {
this.virtual_machine.event_loop_handle.?.tick();
}
}
-
- pub fn drainMicrotasksWithVM(this: *EventLoop, vm: *JSC.VM) void {
- vm.drainMicrotasks();
+ extern fn JSC__JSGlobalObject__drainMicrotasks(*JSC.JSGlobalObject) void;
+ fn drainMicrotasksWithGlobal(this: *EventLoop, globalObject: *JSC.JSGlobalObject) void {
+ JSC__JSGlobalObject__drainMicrotasks(globalObject);
this.drainDeferredTasks();
}
pub fn drainMicrotasks(this: *EventLoop) void {
- this.drainMicrotasksWithVM(this.global.vm());
+ this.drainMicrotasksWithGlobal(this.global);
}
pub fn ensureAliveForOneTick(this: *EventLoop) void {
@@ -666,7 +666,7 @@ pub const EventLoop = struct {
}
global_vm.releaseWeakRefs();
- this.drainMicrotasksWithVM(global_vm);
+ this.drainMicrotasksWithGlobal(global);
}
this.tasks.head = if (this.tasks.count == 0) 0 else this.tasks.head;
@@ -824,13 +824,14 @@ pub const EventLoop = struct {
this.processGCTimer();
- var global_vm = ctx.global.vm();
+ var global = ctx.global;
+ var global_vm = global.vm();
while (true) {
while (this.tickWithCount() > 0) : (this.global.handleRejectedPromises()) {
this.tickConcurrent();
} else {
global_vm.releaseWeakRefs();
- this.drainMicrotasksWithVM(global_vm);
+ this.drainMicrotasksWithGlobal(global);
this.tickConcurrent();
if (this.tasks.count > 0) continue;
}