aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp24
-rw-r--r--src/bun.js/bindings/exports.zig1
-rw-r--r--src/bun.js/bindings/headers-handwritten.h1
-rw-r--r--src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h1
-rw-r--r--src/bun.js/bindings/webcore/DOMConstructors.h1
-rw-r--r--src/bun.js/bindings/webcore/DOMIsoSubspaces.h1
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.cpp245
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.h135
-rw-r--r--src/bun.js/bindings/webcore/EventListenerMap.cpp29
-rw-r--r--src/bun.js/bindings/webcore/EventListenerMap.h2
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitter.cpp570
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitter.h85
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp52
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitterCustom.h52
-rw-r--r--src/bun.js/javascript.zig16
-rw-r--r--src/bun.js/modules/EventsModule.h27
16 files changed, 1241 insertions, 1 deletions
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index d8db25e9a..1882ef6cd 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -78,6 +78,7 @@
#include "JSURLSearchParams.h"
#include "JSDOMException.h"
#include "JSEventTarget.h"
+#include "JSEventEmitter.h"
#include "EventTargetConcrete.h"
#include "JSAbortSignal.h"
#include "JSCustomEvent.h"
@@ -156,6 +157,7 @@ using JSBuffer = WebCore::JSBuffer;
#include <JavaScriptCore/DFGAbstractHeap.h>
#include "../modules/BufferModule.h"
+#include "../modules/EventsModule.h"
#include "../modules/ProcessModule.h"
// #include <iostream>
@@ -2634,6 +2636,16 @@ static JSC_DEFINE_HOST_FUNCTION(functionFulfillModuleSync,
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
}
+ case SyntheticModuleType::Events: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(
+ generateEventsSourceCode,
+ JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath("node:events"_s)), WTFMove(moduleKey)));
+
+ globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
+ RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
+ }
default: {
auto provider = Zig::SourceProvider::create(res.result.value);
globalObject->moduleLoader()->provideFetch(globalObject, key, JSC::SourceCode(provider));
@@ -2719,6 +2731,18 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb
scope.release();
return promise;
}
+ case SyntheticModuleType::Events: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(generateEventsSourceCode,
+ JSC::SourceOrigin(), WTFMove(moduleKey)));
+
+ auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
+ RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
+
+ promise->resolve(globalObject, sourceCode);
+ scope.release();
+ return promise;
+ }
default: {
auto provider = Zig::SourceProvider::create(res.result.value);
auto jsSourceCode = JSC::JSSourceCode::create(vm, JSC::SourceCode(provider));
diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig
index 8f8f6f6f2..199259a69 100644
--- a/src/bun.js/bindings/exports.zig
+++ b/src/bun.js/bindings/exports.zig
@@ -246,6 +246,7 @@ pub const ResolvedSource = extern struct {
@"node:buffer" = 1024,
@"node:process" = 1025,
+ @"node:events" = 1026,
};
};
diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h
index eb27088b8..2e6bbb8a4 100644
--- a/src/bun.js/bindings/headers-handwritten.h
+++ b/src/bun.js/bindings/headers-handwritten.h
@@ -180,6 +180,7 @@ typedef struct {
enum SyntheticModuleType : uint64_t {
Buffer = 1024,
Process = 1025,
+ Events = 1026,
};
extern "C" ZigErrorCode Zig_ErrorCodeParserError;
diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
index 40b634220..74ab4ba3e 100644
--- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
@@ -881,5 +881,6 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForEvent;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForEventListener;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForEventTarget;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForEventEmitter;
};
} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/DOMConstructors.h b/src/bun.js/bindings/webcore/DOMConstructors.h
index b0219fffc..296eb1e4d 100644
--- a/src/bun.js/bindings/webcore/DOMConstructors.h
+++ b/src/bun.js/bindings/webcore/DOMConstructors.h
@@ -856,6 +856,7 @@ enum class DOMConstructorID : uint16_t {
// --bun--
Buffer,
+ EventEmitter,
};
static constexpr unsigned numberOfDOMConstructorsBase = 846;
diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
index ddd488749..aac8e0cd6 100644
--- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
@@ -874,6 +874,7 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForEvent;
std::unique_ptr<IsoSubspace> m_subspaceForEventListener;
std::unique_ptr<IsoSubspace> m_subspaceForEventTarget;
+ std::unique_ptr<IsoSubspace> m_subspaceForEventEmitter;
std::unique_ptr<IsoSubspace> m_subspaceForZigGlobalObject;
diff --git a/src/bun.js/bindings/webcore/EventEmitter.cpp b/src/bun.js/bindings/webcore/EventEmitter.cpp
new file mode 100644
index 000000000..1c71f9b72
--- /dev/null
+++ b/src/bun.js/bindings/webcore/EventEmitter.cpp
@@ -0,0 +1,245 @@
+#include <iostream>
+#include "config.h"
+#include "Event.h"
+
+#include "EventEmitter.h"
+
+#include "AddEventListenerOptions.h"
+#include "DOMWrapperWorld.h"
+#include "EventNames.h"
+#include "JSErrorHandler.h"
+#include "JSEventListener.h"
+#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Ref.h>
+#include <wtf/SetForScope.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(EventEmitter);
+WTF_MAKE_ISO_ALLOCATED_IMPL(EventEmitterWithInlineData);
+
+Ref<EventEmitter> EventEmitter::create(ScriptExecutionContext& context)
+{
+ return adoptRef(*new EventEmitter(context));
+}
+
+bool EventEmitter::addListener(const AtomString& eventType, Ref<EventListener>&& listener, bool once, bool prepend)
+{
+ bool listenerCreatedFromScript = is<JSEventListener>(listener) && !downcast<JSEventListener>(listener.get()).wasCreatedFromMarkup();
+
+ if (prepend) {
+ if (!ensureEventEmitterData().eventListenerMap.prepend(eventType, listener.copyRef(), { false, false, once }))
+ return false;
+ } else {
+ if (!ensureEventEmitterData().eventListenerMap.add(eventType, listener.copyRef(), { false, false, once }))
+ return false;
+ }
+
+
+ eventListenersDidChange();
+ return true;
+}
+
+void EventEmitter::addListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&& listener, bool once, bool prepend)
+{
+ if (!listener)
+ return;
+
+ addListener(eventType, listener.releaseNonNull(), once, prepend);
+}
+
+void EventEmitter::removeListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&& listener)
+{
+ if (!listener)
+ return;
+
+ removeListener(eventType, *listener);
+}
+
+bool EventEmitter::removeListener(const AtomString& eventType, EventListener& listener)
+{
+ auto* data = eventTargetData();
+ if (!data)
+ return false;
+
+ if (data->eventListenerMap.remove(eventType, listener, false)) {
+ if (eventNames().isWheelEventType(eventType))
+ invalidateEventListenerRegions();
+
+ eventListenersDidChange();
+ return true;
+ }
+ return false;
+}
+
+void EventEmitter::removeAllListenersForBindings(const AtomString& eventType)
+{
+ removeAllListeners(eventType);
+}
+
+bool EventEmitter::removeAllListeners(const AtomString& eventType)
+{
+ auto* data = eventTargetData();
+ if (!data)
+ return false;
+
+ if (data->eventListenerMap.removeAll(eventType)) {
+ if (eventNames().isWheelEventType(eventType))
+ invalidateEventListenerRegions();
+
+ eventListenersDidChange();
+ return true;
+ }
+ return false;
+}
+
+bool EventEmitter::hasActiveEventListeners(const AtomString& eventType) const
+{
+ auto* data = eventTargetData();
+ return data && data->eventListenerMap.containsActive(eventType);
+}
+
+bool EventEmitter::emitForBindings(const AtomString& eventType, const MarkedArgumentBuffer& arguments)
+{
+ if (!scriptExecutionContext())
+ return false;
+
+ emit(eventType, arguments);
+ return true;
+}
+
+void EventEmitter::emit(const AtomString& eventType, const MarkedArgumentBuffer& arguments)
+{
+ fireEventListeners(eventType, arguments);
+}
+
+void EventEmitter::uncaughtExceptionInEventHandler()
+{
+}
+
+Vector<AtomString> EventEmitter::getEventNames()
+{
+ auto* data = eventTargetData();
+ if (!data)
+ return {};
+ return data->eventListenerMap.eventTypes();
+}
+
+int EventEmitter::listenerCount(const AtomString& eventType)
+{
+ auto* data = eventTargetData();
+ if (!data)
+ return 0;
+ int result = 0;
+ if (auto* listenersVector = data->eventListenerMap.find(eventType)) {
+ for (auto& registeredListener : *listenersVector) {
+ if (UNLIKELY(registeredListener->wasRemoved()))
+ continue;
+
+ if (JSC::JSObject* jsFunction = registeredListener->callback().jsFunction()) {
+ result++;
+ }
+ }
+ }
+ return result;
+}
+
+Vector<JSObject*> EventEmitter::getListeners(const AtomString& eventType)
+{
+ auto* data = eventTargetData();
+ if (!data)
+ return {};
+ Vector<JSObject*> listeners;
+ if (auto* listenersVector = data->eventListenerMap.find(eventType)) {
+ for (auto& registeredListener : *listenersVector) {
+ if (UNLIKELY(registeredListener->wasRemoved()))
+ continue;
+
+ if (JSC::JSObject* jsFunction = registeredListener->callback().jsFunction()) {
+ listeners.append(jsFunction);
+ }
+ }
+ }
+ return listeners;
+}
+
+static const AtomString& legacyType(const Event& event)
+{
+
+ return nullAtom();
+}
+
+// https://dom.spec.whatwg.org/#concept-event-listener-invoke
+void EventEmitter::fireEventListeners(const AtomString& eventType, const MarkedArgumentBuffer& arguments)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread());
+
+ auto* data = eventTargetData();
+ if (!data)
+ return;
+
+ SetForScope firingEventListenersScope(data->isFiringEventListeners, true);
+
+ if (auto* listenersVector = data->eventListenerMap.find(eventType)) {
+ innerInvokeEventListeners(eventType, *listenersVector, arguments);
+ return;
+ }
+}
+
+// Intentionally creates a copy of the listeners vector to avoid event listeners added after this point from being run.
+// Note that removal still has an effect due to the removed field in RegisteredEventListener.
+// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
+void EventEmitter::innerInvokeEventListeners(const AtomString& eventType, EventListenerVector listeners, const MarkedArgumentBuffer& arguments)
+{
+ Ref<EventEmitter> protectedThis(*this);
+ ASSERT(!listeners.isEmpty());
+ ASSERT(scriptExecutionContext());
+
+ auto& context = *scriptExecutionContext();
+ VM& vm = context.vm();
+
+ for (auto& registeredListener : listeners) {
+ if (UNLIKELY(registeredListener->wasRemoved()))
+ continue;
+
+ // Make sure the JS wrapper and function stay alive until the end of this scope. Otherwise,
+ // event listeners with 'once' flag may get collected as soon as they get unregistered below,
+ // before we call the js function.
+ JSC::EnsureStillAliveScope wrapperProtector(registeredListener->callback().wrapper());
+ JSC::EnsureStillAliveScope jsFunctionProtector(registeredListener->callback().jsFunction());
+
+ // Do this before invocation to avoid reentrancy issues.
+ if (registeredListener->isOnce())
+ removeListener(eventType, registeredListener->callback());
+
+ if (JSC::JSObject* jsFunction = registeredListener->callback().jsFunction()) {
+ JSC::JSGlobalObject* lexicalGlobalObject = jsFunction->globalObject();
+ auto callData = JSC::getCallData(jsFunction);
+ JSC::call(jsFunction->globalObject(), jsFunction, callData, JSC::jsUndefined(), arguments);
+ }
+ }
+}
+
+Vector<AtomString> EventEmitter::eventTypes()
+{
+ if (auto* data = eventTargetData())
+ return data->eventListenerMap.eventTypes();
+ return {};
+}
+
+const EventListenerVector& EventEmitter::eventListeners(const AtomString& eventType)
+{
+ auto* data = eventTargetData();
+ auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
+ static NeverDestroyed<EventListenerVector> emptyVector;
+ return listenerVector ? *listenerVector : emptyVector.get();
+}
+
+void EventEmitter::invalidateEventListenerRegions()
+{
+}
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/EventEmitter.h b/src/bun.js/bindings/webcore/EventEmitter.h
new file mode 100644
index 000000000..35650f380
--- /dev/null
+++ b/src/bun.js/bindings/webcore/EventEmitter.h
@@ -0,0 +1,135 @@
+#pragma once
+
+#include "EventListenerMap.h"
+#include "EventListenerOptions.h"
+#include "ExceptionOr.h"
+#include "ContextDestructionObserver.h"
+#include "ScriptWrappable.h"
+#include <memory>
+#include <variant>
+#include <wtf/Forward.h>
+
+#include <wtf/WeakPtr.h>
+
+#include "root.h"
+
+namespace JSC {
+class JSValue;
+class JSObject;
+}
+
+namespace WebCore {
+
+struct AddEventListenerOptions;
+class DOMWrapperWorld;
+class JSEventListener;
+
+struct EventEmitterData {
+ WTF_MAKE_NONCOPYABLE(EventEmitterData);
+ WTF_MAKE_FAST_ALLOCATED;
+
+public:
+ EventEmitterData() = default;
+ EventListenerMap eventListenerMap;
+ bool isFiringEventListeners { false };
+};
+
+class EventEmitter final : public ScriptWrappable, public CanMakeWeakPtr<EventEmitter>, public RefCounted<EventEmitter>, public ContextDestructionObserver {
+ WTF_MAKE_ISO_ALLOCATED(EventEmitter);
+
+public:
+ static Ref<EventEmitter> create(ScriptExecutionContext&);
+ WEBCORE_EXPORT ~EventEmitter() = default;
+
+ using RefCounted::deref;
+ using RefCounted::ref;
+
+ ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); };
+
+ WEBCORE_EXPORT bool isNode() const { return false; };
+
+ WEBCORE_EXPORT void addListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&&, bool, bool);
+ WEBCORE_EXPORT void removeListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&&);
+ WEBCORE_EXPORT void removeAllListenersForBindings(const AtomString& eventType);
+ WEBCORE_EXPORT bool emitForBindings(const AtomString&, const MarkedArgumentBuffer&);
+
+ WEBCORE_EXPORT bool addListener(const AtomString& eventType, Ref<EventListener>&&, bool, bool);
+ WEBCORE_EXPORT bool removeListener(const AtomString& eventType, EventListener&);
+ WEBCORE_EXPORT bool removeAllListeners(const AtomString& eventType);
+
+ WEBCORE_EXPORT void emit(const AtomString&, const MarkedArgumentBuffer&);
+ WEBCORE_EXPORT void uncaughtExceptionInEventHandler();
+
+ WEBCORE_EXPORT Vector<AtomString> getEventNames();
+ WEBCORE_EXPORT Vector<JSObject*> getListeners(const AtomString& eventType);
+ WEBCORE_EXPORT int listenerCount(const AtomString& eventType);
+
+ bool hasEventListeners() const;
+ bool hasEventListeners(const AtomString& eventType) const;
+ bool hasCapturingEventListeners(const AtomString& eventType);
+ bool hasActiveEventListeners(const AtomString& eventType) const;
+
+ Vector<AtomString> eventTypes();
+ const EventListenerVector& eventListeners(const AtomString& eventType);
+
+ void fireEventListeners(const AtomString& eventName, const MarkedArgumentBuffer& arguments);
+ bool isFiringEventListeners() const;
+
+ template<typename Visitor> void visitJSEventListeners(Visitor&);
+ void invalidateJSEventListeners(JSC::JSObject*);
+
+ const EventEmitterData* eventTargetData() const;
+
+private:
+ EventEmitter(ScriptExecutionContext& context) : ContextDestructionObserver(&context)
+ {
+ }
+
+ EventEmitterData* eventTargetData() { return &m_eventTargetData; }
+ EventEmitterData* eventTargetDataConcurrently() { return &m_eventTargetData; }
+ EventEmitterData& ensureEventEmitterData() { return m_eventTargetData; }
+ void eventListenersDidChange() {}
+
+ void innerInvokeEventListeners(const AtomString&, EventListenerVector, const MarkedArgumentBuffer& arguments);
+ void invalidateEventListenerRegions();
+
+ EventEmitterData m_eventTargetData;
+};
+
+inline const EventEmitterData* EventEmitter::eventTargetData() const
+{
+ return const_cast<EventEmitter*>(this)->eventTargetData();
+}
+
+inline bool EventEmitter::isFiringEventListeners() const
+{
+ auto* data = eventTargetData();
+ return data && data->isFiringEventListeners;
+}
+
+inline bool EventEmitter::hasEventListeners() const
+{
+ auto* data = eventTargetData();
+ return data && !data->eventListenerMap.isEmpty();
+}
+
+inline bool EventEmitter::hasEventListeners(const AtomString& eventType) const
+{
+ auto* data = eventTargetData();
+ return data && data->eventListenerMap.contains(eventType);
+}
+
+inline bool EventEmitter::hasCapturingEventListeners(const AtomString& eventType)
+{
+ auto* data = eventTargetData();
+ return data && data->eventListenerMap.containsCapturing(eventType);
+}
+
+template<typename Visitor>
+void EventEmitter::visitJSEventListeners(Visitor& visitor)
+{
+ if (auto* data = eventTargetDataConcurrently())
+ data->eventListenerMap.visitJSEventListeners(visitor);
+}
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/EventListenerMap.cpp b/src/bun.js/bindings/webcore/EventListenerMap.cpp
index 5014cfa00..bb6cf0a21 100644
--- a/src/bun.js/bindings/webcore/EventListenerMap.cpp
+++ b/src/bun.js/bindings/webcore/EventListenerMap.cpp
@@ -129,6 +129,21 @@ bool EventListenerMap::add(const AtomString& eventType, Ref<EventListener>&& lis
return true;
}
+bool EventListenerMap::prepend(const AtomString& eventType, Ref<EventListener>&& listener, const RegisteredEventListener::Options& options)
+{
+ Locker locker { m_lock };
+
+ if (auto* listeners = find(eventType)) {
+ if (findListener(*listeners, listener, options.capture) != notFound)
+ return false; // Duplicate listener.
+ listeners->insert(0, RegisteredEventListener::create(WTFMove(listener), options));
+ return true;
+ }
+
+ m_entries.append({ eventType, EventListenerVector { RegisteredEventListener::create(WTFMove(listener), options) } });
+ return true;
+}
+
static bool removeListenerFromVector(EventListenerVector& listeners, EventListener& listener, bool useCapture)
{
size_t indexOfRemovedListener = findListener(listeners, listener, useCapture);
@@ -156,6 +171,20 @@ bool EventListenerMap::remove(const AtomString& eventType, EventListener& listen
return false;
}
+bool EventListenerMap::removeAll(const AtomString& eventType)
+{
+ Locker locker { m_lock };
+
+ for (unsigned i = 0; i < m_entries.size(); ++i) {
+ if (m_entries[i].first == eventType) {
+ m_entries.remove(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
EventListenerVector* EventListenerMap::find(const AtomString& eventType)
{
for (auto& entry : m_entries) {
diff --git a/src/bun.js/bindings/webcore/EventListenerMap.h b/src/bun.js/bindings/webcore/EventListenerMap.h
index d5b83c220..3467e8403 100644
--- a/src/bun.js/bindings/webcore/EventListenerMap.h
+++ b/src/bun.js/bindings/webcore/EventListenerMap.h
@@ -58,7 +58,9 @@ public:
void replace(const AtomString& eventType, EventListener& oldListener, Ref<EventListener>&& newListener, const RegisteredEventListener::Options&);
bool add(const AtomString& eventType, Ref<EventListener>&&, const RegisteredEventListener::Options&);
+ bool prepend(const AtomString& eventType, Ref<EventListener>&&, const RegisteredEventListener::Options&);
bool remove(const AtomString& eventType, EventListener&, bool useCapture);
+ bool removeAll(const AtomString& eventType);
WEBCORE_EXPORT EventListenerVector* find(const AtomString& eventType);
const EventListenerVector* find(const AtomString& eventType) const { return const_cast<EventListenerMap*>(this)->find(eventType); }
Vector<AtomString> eventTypes() const;
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
new file mode 100644
index 000000000..3971243f9
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
@@ -0,0 +1,570 @@
+#include "config.h"
+#include "JSEventEmitter.h"
+
+#include "ActiveDOMObject.h"
+#include "ExtendedDOMClientIsoSubspaces.h"
+#include "ExtendedDOMIsoSubspaces.h"
+#include "IDLTypes.h"
+#include "JSAddEventListenerOptions.h"
+#include "JSDOMBinding.h"
+#include "JSDOMConstructor.h"
+#include "JSDOMConvertBase.h"
+#include "JSDOMConvertBoolean.h"
+#include "JSDOMConvertDictionary.h"
+#include "JSDOMConvertEventListener.h"
+#include "JSDOMConvertInterface.h"
+#include "JSDOMConvertNullable.h"
+#include "JSDOMConvertStrings.h"
+#include "JSDOMConvertUnion.h"
+#include "JSDOMExceptionHandling.h"
+#include "JSDOMGlobalObjectInlines.h"
+#include "JSDOMOperation.h"
+#include "JSDOMWrapperCache.h"
+#include "JSEvent.h"
+#include "JSEventListener.h"
+#include "JSEventListenerOptions.h"
+#include "ScriptExecutionContext.h"
+#include "WebCoreJSClientData.h"
+#include <JavaScriptCore/FunctionPrototype.h>
+#include <JavaScriptCore/HeapAnalyzer.h>
+#include <JavaScriptCore/JSCInlines.h>
+#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h>
+#include <JavaScriptCore/SlotVisitorMacros.h>
+#include <JavaScriptCore/SubspaceInlines.h>
+#include <variant>
+#include <wtf/GetPtr.h>
+#include <wtf/PointerPreparations.h>
+#include <wtf/URL.h>
+
+namespace WebCore {
+using namespace JSC;
+
+// Functions
+
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_addListener);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_addOnceListener);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_prependListener);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_prependOnceListener);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_removeListener);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_removeAllListeners);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_emit);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_eventNames);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_listenerCount);
+static JSC_DECLARE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_listeners);
+
+// Attributes
+
+static JSC_DECLARE_CUSTOM_GETTER(jsEventEmitterConstructor);
+
+class JSEventEmitterPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+ static JSEventEmitterPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSEventEmitterPrototype* ptr = new (NotNull, JSC::allocateCell<JSEventEmitterPrototype>(vm)) JSEventEmitterPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSEventEmitterPrototype, Base);
+ return &vm.plainObjectSpace();
+ }
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+private:
+ JSEventEmitterPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure)
+ : JSC::JSNonFinalObject(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&);
+
+public:
+ static constexpr unsigned StructureFlags = Base::StructureFlags | JSC::IsImmutablePrototypeExoticObject;
+};
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSEventEmitterPrototype, JSEventEmitterPrototype::Base);
+
+using JSEventEmitterDOMConstructor = JSDOMConstructor<JSEventEmitter>;
+
+template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSEventEmitterDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
+{
+ VM& vm = lexicalGlobalObject->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* castedThis = jsCast<JSEventEmitterDOMConstructor*>(callFrame->jsCallee());
+ ASSERT(castedThis);
+ auto* context = castedThis->scriptExecutionContext();
+ if (UNLIKELY(!context))
+ return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "EventEmitter");
+ auto object = EventEmitter::create(*context);
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef);
+ auto jsValue = toJSNewlyCreated<IDLInterface<EventEmitter>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object));
+ if constexpr (IsExceptionOr<decltype(object)>)
+ RETURN_IF_EXCEPTION(throwScope, {});
+ setSubclassStructureIfNeeded<EventEmitter>(lexicalGlobalObject, callFrame, asObject(jsValue));
+ RETURN_IF_EXCEPTION(throwScope, {});
+ return JSValue::encode(jsValue);
+}
+JSC_ANNOTATE_HOST_FUNCTION(JSEventEmitterDOMConstructorConstruct, JSEventEmitterDOMConstructor::construct);
+
+template<> const ClassInfo JSEventEmitterDOMConstructor::s_info = { "EventEmitter"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSEventEmitterDOMConstructor) };
+
+template<> JSValue JSEventEmitterDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject)
+{
+ UNUSED_PARAM(vm);
+ return globalObject.functionPrototype();
+}
+
+template<> void JSEventEmitterDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ JSString* nameString = jsNontrivialString(vm, "EventEmitter"_s);
+ m_originalName.set(vm, this, nameString);
+ putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ putDirect(vm, vm.propertyNames->prototype, JSEventEmitter::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete);
+}
+
+/* Hash table for prototype */
+
+static const HashTableValue JSEventEmitterPrototypeTableValues[] = {
+ { "constructor"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsEventEmitterConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
+ { "addListener"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_addListener), (intptr_t)(2) } },
+ { "on"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_addListener), (intptr_t)(2) } },
+ { "once"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_addOnceListener), (intptr_t)(2) } },
+ { "prepend"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_prependListener), (intptr_t)(2) } },
+ { "prependOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_prependOnceListener), (intptr_t)(2) } },
+ { "removeListener"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_removeListener), (intptr_t)(2) } },
+ { "off"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_removeListener), (intptr_t)(2) } },
+ { "removeAllListeners"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_removeAllListeners), (intptr_t)(1) } },
+ { "emit"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_emit), (intptr_t)(1) } },
+ { "eventNames"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_eventNames), (intptr_t)(0) } },
+ { "listenerCount"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_listenerCount), (intptr_t)(1) } },
+ { "listeners"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_listeners), (intptr_t)(1) } },
+ // Need to double check the difference between rawListeners and listeners.
+ { "rawListeners"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsEventEmitterPrototypeFunction_listeners), (intptr_t)(1) } },
+};
+
+const ClassInfo JSEventEmitterPrototype::s_info = { "EventEmitter"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSEventEmitterPrototype) };
+
+void JSEventEmitterPrototype::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ reifyStaticProperties(vm, JSEventEmitter::info(), JSEventEmitterPrototypeTableValues, *this);
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+const ClassInfo JSEventEmitter::s_info = { "EventEmitter"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSEventEmitter) };
+
+JSEventEmitter::JSEventEmitter(Structure* structure, JSDOMGlobalObject& globalObject, Ref<EventEmitter>&& impl)
+ : JSDOMWrapper<EventEmitter>(structure, globalObject, WTFMove(impl))
+{
+}
+
+void JSEventEmitter::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+
+ // static_assert(!std::is_base_of<ActiveDOMObject, EventEmitter>::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject.");
+}
+
+JSObject* JSEventEmitter::createPrototype(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ return JSEventEmitterPrototype::create(vm, &globalObject, JSEventEmitterPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()));
+}
+
+JSObject* JSEventEmitter::prototype(VM& vm, JSDOMGlobalObject& globalObject)
+{
+ return getDOMPrototype<JSEventEmitter>(vm, globalObject);
+}
+
+JSValue JSEventEmitter::getConstructor(VM& vm, const JSGlobalObject* globalObject)
+{
+ return getDOMConstructor<JSEventEmitterDOMConstructor, DOMConstructorID::EventEmitter>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject));
+}
+
+void JSEventEmitter::destroy(JSC::JSCell* cell)
+{
+ JSEventEmitter* thisObject = static_cast<JSEventEmitter*>(cell);
+ thisObject->JSEventEmitter::~JSEventEmitter();
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsEventEmitterConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* prototype = jsDynamicCast<JSEventEmitterPrototype*>(JSValue::decode(thisValue));
+ if (UNLIKELY(!prototype))
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ return JSValue::encode(JSEventEmitter::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject()));
+}
+
+static inline JSC::EncodedJSValue addListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis, bool once, bool prepend)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 2))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto type = convert<IDLAtomStringAdaptor<IDLDOMString>>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
+ auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventEmitter", "addListener"); });
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.addListenerForBindings(WTFMove(type), WTFMove(listener), once, prepend); }));
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ vm.writeBarrier(&static_cast<JSObject&>(*castedThis), argument1.value());
+ return result;
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_addListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ return addListener(lexicalGlobalObject, callFrame, castedThis, false, false);
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_addOnceListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ return addListener(lexicalGlobalObject, callFrame, castedThis, true, false);
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_prependListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ return addListener(lexicalGlobalObject, callFrame, castedThis, false, true);
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_prependOnceListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ return addListener(lexicalGlobalObject, callFrame, castedThis, true, true);
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_addListener, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_addListenerBody>(*lexicalGlobalObject, *callFrame, "addListener");
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_addOnceListener, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_addOnceListenerBody>(*lexicalGlobalObject, *callFrame, "once");
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_prependListener, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_prependListenerBody>(*lexicalGlobalObject, *callFrame, "prependListener");
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_prependOnceListener, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_prependOnceListenerBody>(*lexicalGlobalObject, *callFrame, "prependOnceListener");
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_removeListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 2))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto type = convert<IDLAtomStringAdaptor<IDLDOMString>>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
+ auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventEmitter", "removeListener"); });
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.removeListenerForBindings(WTFMove(type), WTFMove(listener)); }));
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ vm.writeBarrier(&static_cast<JSObject&>(*castedThis), argument1.value());
+ return result;
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_removeListener, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_removeListenerBody>(*lexicalGlobalObject, *callFrame, "removeListener");
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_removeAllListenersBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
+ auto type = convert<IDLAtomStringAdaptor<IDLDOMString>>(*lexicalGlobalObject, argument0.value());
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.removeAllListenersForBindings(WTFMove(type)); }));
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ return result;
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_removeAllListeners, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_removeAllListenersBody>(*lexicalGlobalObject, *callFrame, "removeAllListeners");
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_emitBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ size_t argumentCount = callFrame->argumentCount();
+ if (UNLIKELY(argumentCount < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ auto* argument0 = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ JSC::MarkedArgumentBuffer args;
+ for (size_t i = 1; i < argumentCount; ++i) {
+ args.append(callFrame->uncheckedArgument(i));
+ }
+ auto eventType = argument0->toAtomString(lexicalGlobalObject);
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLBoolean>(*lexicalGlobalObject, throwScope, impl.emitForBindings(eventType, args))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_emit, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_emitBody>(*lexicalGlobalObject, *callFrame, "emit");
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_eventNamesBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ JSC::MarkedArgumentBuffer args;
+ for (auto& name : impl.getEventNames()) {
+ args.append(jsOwnedString(vm, name));
+ }
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::constructArray(lexicalGlobalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), WTFMove(args))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_eventNames, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_eventNamesBody>(*lexicalGlobalObject, *callFrame, "eventNames");
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_listenerCountBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ auto* argument0 = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto eventType = argument0->toAtomString(lexicalGlobalObject);
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(impl.listenerCount(eventType))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_listenerCount, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_listenerCountBody>(*lexicalGlobalObject, *callFrame, "listeners");
+}
+
+static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_listenersBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ auto& impl = castedThis->wrapped();
+ if (UNLIKELY(callFrame->argumentCount() < 1))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ auto* argument0 = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto eventType = argument0->toAtomString(lexicalGlobalObject);
+ JSC::MarkedArgumentBuffer args;
+ for (auto* listener : impl.getListeners(eventType)) {
+ args.append(listener);
+ }
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::constructArray(lexicalGlobalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), WTFMove(args))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_listeners, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ return IDLOperation<JSEventEmitter>::call<jsEventEmitterPrototypeFunction_listenersBody>(*lexicalGlobalObject, *callFrame, "listeners");
+}
+
+JSC::GCClient::IsoSubspace* JSEventEmitter::subspaceForImpl(JSC::VM& vm)
+{
+ return WebCore::subspaceForImpl<JSEventEmitter, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForEventEmitter.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForEventEmitter = WTFMove(space); },
+ [](auto& spaces) { return spaces.m_subspaceForEventEmitter.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForEventEmitter = WTFMove(space); });
+}
+
+template<typename Visitor>
+void JSEventEmitter::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ auto* thisObject = jsCast<JSEventEmitter*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+ thisObject->visitAdditionalChildren(visitor);
+}
+
+DEFINE_VISIT_CHILDREN(JSEventEmitter);
+
+template<typename Visitor>
+void JSEventEmitter::visitOutputConstraints(JSCell* cell, Visitor& visitor)
+{
+ auto* thisObject = jsCast<JSEventEmitter*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitOutputConstraints(thisObject, visitor);
+ thisObject->visitAdditionalChildren(visitor);
+}
+
+template void JSEventEmitter::visitOutputConstraints(JSCell*, AbstractSlotVisitor&);
+template void JSEventEmitter::visitOutputConstraints(JSCell*, SlotVisitor&);
+void JSEventEmitter::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSEventEmitter*>(cell);
+ analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped());
+ if (thisObject->scriptExecutionContext())
+ analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ Base::analyzeHeap(cell, analyzer);
+}
+
+bool JSEventEmitterOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason)
+{
+ auto* jsEventEmitter = jsCast<JSEventEmitter*>(handle.slot()->asCell());
+ if (jsEventEmitter->wrapped().isFiringEventListeners()) {
+ if (UNLIKELY(reason))
+ *reason = "EventEmitter firing event listeners";
+ return true;
+ }
+ UNUSED_PARAM(visitor);
+ UNUSED_PARAM(reason);
+ return false;
+}
+
+void JSEventEmitterOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)
+{
+ auto* jsEventEmitter = static_cast<JSEventEmitter*>(handle.slot()->asCell());
+ auto& world = *static_cast<DOMWrapperWorld*>(context);
+ uncacheWrapper(world, &jsEventEmitter->wrapped(), jsEventEmitter);
+}
+
+JSC_DEFINE_HOST_FUNCTION(Events_functionGetEventListeners,
+ (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ if (UNLIKELY(callFrame->argumentCount() < 2))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0));
+ if (UNLIKELY(!argument0)) {
+ throwException(lexicalGlobalObject, throwScope, createError(lexicalGlobalObject, "Expected EventEmitter"_s));
+ return JSValue::encode(JSC::jsUndefined());
+ }
+ auto* impl = JSEventEmitter::toWrapped(vm, argument0);
+ auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto eventType = argument1->toAtomString(lexicalGlobalObject);
+ JSC::MarkedArgumentBuffer args;
+ for (auto* listener : impl->getListeners(eventType)) {
+ args.append(listener);
+ }
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::constructArray(lexicalGlobalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), WTFMove(args))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(Events_functionListenerCount,
+ (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+ if (UNLIKELY(callFrame->argumentCount() < 2))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0));
+ if (UNLIKELY(!argument0)) {
+ throwException(lexicalGlobalObject, throwScope, createError(lexicalGlobalObject, "Expected EventEmitter"_s));
+ return JSValue::encode(JSC::jsUndefined());
+ }
+ auto* impl = JSEventEmitter::toWrapped(vm, argument0);
+ auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto eventType = argument1->toAtomString(lexicalGlobalObject);
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(impl->listenerCount(eventType))));
+}
+
+JSC_DEFINE_HOST_FUNCTION(Events_functionOnce,
+ (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+
+ if (UNLIKELY(callFrame->argumentCount() < 3))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0));
+ if (UNLIKELY(!argument0)) {
+ throwException(lexicalGlobalObject, throwScope, createError(lexicalGlobalObject, "Expected EventEmitter"_s));
+ return JSValue::encode(JSC::jsUndefined());
+ }
+ auto* impl = JSEventEmitter::toWrapped(vm, argument0);
+ auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto eventType = argument1->toAtomString(lexicalGlobalObject);
+ EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2);
+ auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener", "EventEmitter", "removeListener"); });
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl->addListenerForBindings(WTFMove(eventType), WTFMove(listener), true, false); }));
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ vm.writeBarrier(argument0, argument2.value());
+ return result;
+}
+
+JSC_DEFINE_HOST_FUNCTION(Events_functionOn,
+ (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(throwScope);
+ UNUSED_PARAM(callFrame);
+
+ if (UNLIKELY(callFrame->argumentCount() < 3))
+ return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0));
+ if (UNLIKELY(!argument0)) {
+ throwException(lexicalGlobalObject, throwScope, createError(lexicalGlobalObject, "Expected EventEmitter"_s));
+ return JSValue::encode(JSC::jsUndefined());
+ }
+ auto* impl = JSEventEmitter::toWrapped(vm, argument0);
+ auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto eventType = argument1->toAtomString(lexicalGlobalObject);
+ EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2);
+ auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener", "EventEmitter", "removeListener"); });
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl->addListenerForBindings(WTFMove(eventType), WTFMove(listener), false, false); }));
+ RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+ vm.writeBarrier(argument0, argument2.value());
+ return result;
+}
+
+}
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.h b/src/bun.js/bindings/webcore/JSEventEmitter.h
new file mode 100644
index 000000000..855241011
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSEventEmitter.h
@@ -0,0 +1,85 @@
+#pragma once
+
+#include "root.h"
+#include "EventEmitter.h"
+#include "JSDOMWrapper.h"
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+JSC_DECLARE_HOST_FUNCTION(Events_functionGetEventListeners);
+JSC_DECLARE_HOST_FUNCTION(Events_functionListenerCount);
+JSC_DECLARE_HOST_FUNCTION(Events_functionOnce);
+JSC_DECLARE_HOST_FUNCTION(Events_functionOn);
+
+class JSEventEmitter : public JSDOMWrapper<EventEmitter> {
+public:
+ using Base = JSDOMWrapper<EventEmitter>;
+ static JSEventEmitter* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<EventEmitter>&& impl)
+ {
+ JSEventEmitter* ptr = new (NotNull, JSC::allocateCell<JSEventEmitter>(globalObject->vm())) JSEventEmitter(structure, *globalObject, WTFMove(impl));
+ ptr->finishCreation(globalObject->vm());
+ return ptr;
+ }
+
+ static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);
+ static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);
+ static EventEmitter* toWrapped(JSC::VM&, JSC::JSValue);
+ static void destroy(JSC::JSCell*);
+
+ DECLARE_INFO;
+
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray);
+ }
+
+ static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return subspaceForImpl(vm);
+ }
+ static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
+ DECLARE_VISIT_CHILDREN;
+ template<typename Visitor> void visitAdditionalChildren(Visitor&);
+
+ template<typename Visitor> static void visitOutputConstraints(JSCell*, Visitor&);
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+
+protected:
+ JSEventEmitter(JSC::Structure*, JSDOMGlobalObject&, Ref<EventEmitter>&&);
+
+ void finishCreation(JSC::VM&);
+};
+
+class JSEventEmitterOwner final : public JSC::WeakHandleOwner {
+public:
+ bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, const char**) final;
+ void finalize(JSC::Handle<JSC::Unknown>, void* context) final;
+};
+
+inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, EventEmitter*)
+{
+ static NeverDestroyed<JSEventEmitterOwner> owner;
+ return &owner.get();
+}
+
+inline void* wrapperKey(EventEmitter* wrappableObject)
+{
+ return wrappableObject;
+}
+
+JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, EventEmitter&);
+inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, EventEmitter* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); }
+JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<EventEmitter>&&);
+inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<EventEmitter>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }
+
+template<> struct JSDOMWrapperConverterTraits<EventEmitter> {
+ using WrapperClass = JSEventEmitter;
+ using ToWrappedReturnType = EventEmitter*;
+};
+
+} // namespace WebCore
+#include "JSEventEmitterCustom.h"
diff --git a/src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp b/src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp
new file mode 100644
index 000000000..0d11b9350
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp
@@ -0,0 +1,52 @@
+#include "config.h"
+#include "JSEventEmitter.h"
+
+#include "EventEmitter.h"
+#include "JSDOMWrapperCache.h"
+#include "JSEventListener.h"
+
+namespace WebCore {
+using namespace JSC;
+
+JSValue toJSNewlyCreated(JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<EventEmitter>&& value)
+{
+ return createWrapper<EventEmitter>(globalObject, WTFMove(value));
+}
+
+EventEmitter* JSEventEmitter::toWrapped(VM& vm, JSValue value)
+{
+ if (value.inherits<JSEventEmitter>())
+ return &jsCast<JSEventEmitter*>(asObject(value))->wrapped();
+ return nullptr;
+}
+
+std::unique_ptr<JSEventEmitterWrapper> jsEventEmitterCast(VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSValue thisValue)
+{
+ if (auto* target = jsDynamicCast<JSEventEmitter*>(thisValue))
+ return makeUnique<JSEventEmitterWrapper>(target->wrapped(), *target);
+ if (auto* object = jsDynamicCast<JSNonFinalObject*>(thisValue)) {
+ // need to create a EventEmitter for Object.
+ // use `mapPrivateName` as it is not occupied.
+ auto emitterTag = WebCore::clientData(vm)->builtinNames().mapPrivateName();
+ JSC::JSValue value = object->getDirect(vm, emitterTag);
+ if (!value) {
+ Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
+ value = WebCore::toJSNewlyCreated(lexicalGlobalObject, globalObject, EventEmitter::create(*globalObject->scriptExecutionContext()));
+ object->putDirect(vm, emitterTag, value);
+ }
+ auto* target = jsDynamicCast<JSEventEmitter*>(value);
+ return makeUnique<JSEventEmitterWrapper>(target->wrapped(), *target);
+ }
+
+ return nullptr;
+}
+
+template<typename Visitor>
+void JSEventEmitter::visitAdditionalChildren(Visitor& visitor)
+{
+ wrapped().visitJSEventListeners(visitor);
+}
+
+DEFINE_VISIT_ADDITIONAL_CHILDREN(JSEventEmitter);
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/JSEventEmitterCustom.h b/src/bun.js/bindings/webcore/JSEventEmitterCustom.h
new file mode 100644
index 000000000..ed2ffed0c
--- /dev/null
+++ b/src/bun.js/bindings/webcore/JSEventEmitterCustom.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "JSDOMBinding.h"
+#include "JSDOMOperation.h"
+
+namespace WebCore {
+
+// Wrapper type for JSEventEmitter's castedThis because JSDOMWindow and JSWorkerGlobalScope do not inherit JSEventEmitter.
+class JSEventEmitterWrapper {
+ WTF_MAKE_FAST_ALLOCATED;
+
+public:
+ JSEventEmitterWrapper(EventEmitter& wrapped, JSC::JSObject& wrapper)
+ : m_wrapped(wrapped)
+ , m_wrapper(wrapper)
+ {
+ }
+
+ EventEmitter& wrapped() { return m_wrapped; }
+
+ operator JSC::JSObject&() { return m_wrapper; }
+
+private:
+ EventEmitter& m_wrapped;
+ JSC::JSObject& m_wrapper;
+};
+
+std::unique_ptr<JSEventEmitterWrapper> jsEventEmitterCast(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue thisValue);
+
+template<> class IDLOperation<JSEventEmitter> {
+public:
+ using ClassParameter = JSEventEmitterWrapper*;
+ using Operation = JSC::EncodedJSValue(JSC::JSGlobalObject*, JSC::CallFrame*, ClassParameter);
+
+ template<Operation operation, CastedThisErrorBehavior = CastedThisErrorBehavior::Throw>
+ static JSC::EncodedJSValue call(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, const char* operationName)
+ {
+ auto& vm = JSC::getVM(&lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+ auto thisValue = callFrame.thisValue().toThis(&lexicalGlobalObject, JSC::ECMAMode::strict());
+ auto thisObject = jsEventEmitterCast(vm, &lexicalGlobalObject, thisValue.isUndefinedOrNull() ? JSC::JSValue(&lexicalGlobalObject) : thisValue);
+ if (UNLIKELY(!thisObject))
+ return throwThisTypeError(lexicalGlobalObject, throwScope, "EventEmitter", operationName);
+
+ auto& wrapped = thisObject->wrapped();
+
+ RELEASE_AND_RETURN(throwScope, (operation(&lexicalGlobalObject, &callFrame, thisObject.get())));
+ }
+};
+
+} // namespace WebCore
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index ab256c588..04d4eaeac 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -813,6 +813,16 @@ pub const VirtualMachine = struct {
.tag = ResolvedSource.Tag.@"node:buffer",
};
},
+ .@"node:events" => {
+ return ResolvedSource{
+ .allocator = null,
+ .source_code = ZigString.init(""),
+ .specifier = ZigString.init("node:events"),
+ .source_url = ZigString.init("node:events"),
+ .hash = 0,
+ .tag = ResolvedSource.Tag.@"node:events",
+ };
+ },
.@"node:fs/promises" => {
return ResolvedSource{
.allocator = null,
@@ -2768,6 +2778,7 @@ pub const HardcodedModule = enum {
@"depd",
@"detect-libc",
@"node:buffer",
+ @"node:events",
@"node:fs",
@"node:fs/promises",
@"node:http",
@@ -2793,11 +2804,13 @@ pub const HardcodedModule = enum {
.{ "bun:sqlite", HardcodedModule.@"bun:sqlite" },
.{ "depd", HardcodedModule.@"depd" },
.{ "detect-libc", HardcodedModule.@"detect-libc" },
+ .{ "events", HardcodedModule.@"node:events" },
.{ "ffi", HardcodedModule.@"bun:ffi" },
.{ "fs", HardcodedModule.@"node:fs" },
.{ "http", HardcodedModule.@"node:http" },
.{ "module", HardcodedModule.@"node:module" },
.{ "node:buffer", HardcodedModule.@"node:buffer" },
+ .{ "node:events", HardcodedModule.@"node:events" },
.{ "node:fs", HardcodedModule.@"node:fs" },
.{ "node:fs/promises", HardcodedModule.@"node:fs/promises" },
.{ "node:http", HardcodedModule.@"node:http" },
@@ -2830,13 +2843,14 @@ pub const HardcodedModule = enum {
.{ "depd", "depd" },
.{ "detect-libc", "detect-libc" },
.{ "detect-libc/lib/detect-libc.js", "detect-libc" },
+ .{ "events", "node:events" },
.{ "ffi", "bun:ffi" },
.{ "fs", "node:fs" },
.{ "fs/promises", "node:fs/promises" },
.{ "http", "node:http" },
.{ "module", "node:module" },
.{ "node:buffer", "node:buffer" },
- .{ "buffer", "node:buffer" },
+ .{ "node:events", "node:events" },
.{ "node:fs", "node:fs" },
.{ "node:fs/promises", "node:fs/promises" },
.{ "node:http", "node:http" },
diff --git a/src/bun.js/modules/EventsModule.h b/src/bun.js/modules/EventsModule.h
new file mode 100644
index 000000000..5adb19d01
--- /dev/null
+++ b/src/bun.js/modules/EventsModule.h
@@ -0,0 +1,27 @@
+#include "../bindings/ZigGlobalObject.h"
+#include "JavaScriptCore/JSGlobalObject.h"
+
+namespace Zig {
+
+inline void generateEventsSourceCode(JSC::JSGlobalObject* lexicalGlobalObject, JSC::Identifier moduleKey, Vector<JSC::Identifier, 4>& exportNames, JSC::MarkedArgumentBuffer& exportValues) {
+ JSC::VM& vm = lexicalGlobalObject->vm();
+ GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject);
+
+ exportNames.append(JSC::Identifier::fromString(vm, "EventEmitter"_s));
+ exportValues.append(WebCore::JSEventEmitter::getConstructor(vm, globalObject));
+
+ exportNames.append(JSC::Identifier::fromString(vm, "getEventListeners"_s));
+ exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
+ MAKE_STATIC_STRING_IMPL("getEventListeners"), Events_functionGetEventListeners, ImplementationVisibility::Public));
+ exportNames.append(JSC::Identifier::fromString(vm, "listenerCount"_s));
+ exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
+ MAKE_STATIC_STRING_IMPL("listenerCount"), Events_functionListenerCount, ImplementationVisibility::Public));
+ exportNames.append(JSC::Identifier::fromString(vm, "once"_s));
+ exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
+ MAKE_STATIC_STRING_IMPL("once"), Events_functionOnce, ImplementationVisibility::Public));
+ exportNames.append(JSC::Identifier::fromString(vm, "on"_s));
+ exportValues.append(JSC::JSFunction::create(vm, lexicalGlobalObject, 0,
+ MAKE_STATIC_STRING_IMPL("on"), Events_functionOn, ImplementationVisibility::Public));
+}
+
+}