diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 24 | ||||
-rw-r--r-- | src/bun.js/bindings/exports.zig | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-handwritten.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/DOMConstructors.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/DOMIsoSubspaces.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/EventEmitter.cpp | 245 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/EventEmitter.h | 135 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/EventListenerMap.cpp | 29 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/EventListenerMap.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/JSEventEmitter.cpp | 570 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/JSEventEmitter.h | 85 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp | 52 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/JSEventEmitterCustom.h | 52 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 16 | ||||
-rw-r--r-- | src/bun.js/modules/EventsModule.h | 27 |
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)); +} + +} |