From 862b8eb1640be4e373c43b6c549f0df6e13ef57d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 1 Jul 2025 22:30:00 +0000 Subject: [PATCH] Add async context tracking for event listeners in JSEventListener Co-authored-by: jarred --- .../bindings/webcore/JSEventListener.cpp | 49 ++++++++++++++++++- src/bun.js/bindings/webcore/JSEventListener.h | 5 ++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/bun.js/bindings/webcore/JSEventListener.cpp b/src/bun.js/bindings/webcore/JSEventListener.cpp index fe36e1dfef..d6828979a0 100644 --- a/src/bun.js/bindings/webcore/JSEventListener.cpp +++ b/src/bun.js/bindings/webcore/JSEventListener.cpp @@ -20,6 +20,7 @@ #include "config.h" #include "JSEventListener.h" +#include "AsyncContextFrame.h" #include "BunProcess.h" // #include "BeforeUnloadEvent.h" // #include "ContentSecurityPolicy.h" @@ -58,6 +59,17 @@ JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isA ASSERT(wrapper); m_jsFunction = JSC::Weak(function); m_isInitialized = true; + + // Capture the current async context if available + if (auto* globalObject = wrapper->globalObject()) { + if (globalObject->isAsyncContextTrackingEnabled()) { + JSValue context = globalObject->m_asyncContextData.get()->getInternalField(0); + if (!context.isUndefined()) { + // Store the current async context + m_asyncContext = JSC::Weak(context); + } + } + } } } @@ -87,6 +99,23 @@ void JSEventListener::replaceJSFunctionForAttributeListener(JSObject* function, m_wrapper = Weak { wrapper }; m_isInitialized = true; } + + // Capture the current async context for the new function + if (auto* globalObject = wrapper->globalObject()) { + if (globalObject->isAsyncContextTrackingEnabled()) { + JSValue context = globalObject->m_asyncContextData.get()->getInternalField(0); + if (!context.isUndefined()) { + // Store the current async context + m_asyncContext = JSC::Weak(context); + } else { + // Clear the async context if no context is available + m_asyncContext = JSC::Weak(); + } + } else { + // Clear the async context if tracking is disabled + m_asyncContext = JSC::Weak(); + } + } } JSValue eventHandlerAttribute(EventTarget& eventTarget, const AtomString& eventType, DOMWrapperWorld& isolatedWorld) @@ -109,6 +138,10 @@ inline void JSEventListener::visitJSFunctionImpl(Visitor& visitor) return; visitor.append(m_jsFunction); + + // Visit the async context to prevent it from being garbage collected + if (m_asyncContext) + visitor.append(m_asyncContext); } void JSEventListener::visitJSFunction(AbstractSlotVisitor& visitor) { visitJSFunctionImpl(visitor); } @@ -228,7 +261,21 @@ void JSEventListener::handleEvent(ScriptExecutionContext& scriptExecutionContext JSValue thisValue = handleEventFunction == jsFunction ? toJS(lexicalGlobalObject, globalObject, event.currentTarget()) : jsFunction; NakedPtr uncaughtException; - JSValue retval = JSC::profiledCall(lexicalGlobalObject, JSC::ProfilingReason::Other, handleEventFunction, callData, thisValue, args, uncaughtException); + + JSValue retval; + // If we have an async context, restore it during the call + if (m_asyncContext && m_asyncContext.get()) { + auto* asyncContextData = lexicalGlobalObject->m_asyncContextData.get(); + JSValue restoreAsyncContext = asyncContextData->getInternalField(0); + asyncContextData->putInternalField(lexicalGlobalObject->vm(), 0, m_asyncContext.get()); + + retval = JSC::profiledCall(lexicalGlobalObject, JSC::ProfilingReason::Other, handleEventFunction, callData, thisValue, args, uncaughtException); + + // Restore the previous async context + asyncContextData->putInternalField(lexicalGlobalObject->vm(), 0, restoreAsyncContext); + } else { + retval = JSC::profiledCall(lexicalGlobalObject, JSC::ProfilingReason::Other, handleEventFunction, callData, thisValue, args, uncaughtException); + } // InspectorInstrumentation::didCallFunction(&scriptExecutionContext); diff --git a/src/bun.js/bindings/webcore/JSEventListener.h b/src/bun.js/bindings/webcore/JSEventListener.h index fb8da1bb62..af0e91ea93 100644 --- a/src/bun.js/bindings/webcore/JSEventListener.h +++ b/src/bun.js/bindings/webcore/JSEventListener.h @@ -34,6 +34,8 @@ namespace WebCore { +class AsyncContextFrame; + class JSEventListener : public EventListener { public: WEBCORE_EXPORT static Ref create(JSC::JSObject& listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld&); @@ -88,6 +90,9 @@ private: mutable JSC::Weak m_jsFunction; mutable JSC::Weak m_wrapper; + // Optional async context captured when the listener was created + mutable JSC::Weak m_asyncContext; + Ref m_isolatedWorld; };