diff options
author | 2022-12-05 00:19:23 -0800 | |
---|---|---|
committer | 2022-12-05 00:19:23 -0800 | |
commit | 682af03cdce591d2a91cb78103b91f05325b8921 (patch) | |
tree | c126b032cb6e1656d804b39212ce18430aeaec5d /src | |
parent | 1564f75d06a5b361b1f9be98233b9f797d2ea7e2 (diff) | |
download | bun-682af03cdce591d2a91cb78103b91f05325b8921.tar.gz bun-682af03cdce591d2a91cb78103b91f05325b8921.tar.zst bun-682af03cdce591d2a91cb78103b91f05325b8921.zip |
"Fix" monkey-patching EventEmitter prototype
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/bindings/webcore/JSEventEmitter.cpp | 24 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp | 65 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/JSEventEmitterCustom.h | 18 | ||||
-rw-r--r-- | src/bun.js/builtins/BunBuiltinNames.h | 1 |
4 files changed, 73 insertions, 35 deletions
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp index bcd0d9a50..8ef858166 100644 --- a/src/bun.js/bindings/webcore/JSEventEmitter.cpp +++ b/src/bun.js/bindings/webcore/JSEventEmitter.cpp @@ -517,16 +517,16 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionGetEventListeners, UNUSED_PARAM(callFrame); if (UNLIKELY(callFrame->argumentCount() < 2)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0)); + auto argument0 = jsEventEmitterCast(vm, lexicalGlobalObject, 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& impl = argument0->wrapped(); auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); JSC::MarkedArgumentBuffer args; - for (auto* listener : impl->getListeners(eventType)) { + 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)))); @@ -541,15 +541,15 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionListenerCount, UNUSED_PARAM(callFrame); if (UNLIKELY(callFrame->argumentCount() < 2)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0)); + auto argument0 = jsEventEmitterCast(vm, lexicalGlobalObject, 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& impl = argument0->wrapped(); auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(impl->listenerCount(eventType)))); + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(impl.listenerCount(eventType)))); } JSC_DEFINE_HOST_FUNCTION(Events_functionOnce, @@ -562,18 +562,18 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionOnce, if (UNLIKELY(callFrame->argumentCount() < 3)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0)); + auto argument0 = jsEventEmitterCastFast(vm, lexicalGlobalObject, 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& impl = argument0->wrapped(); auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); 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); })); + 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; @@ -589,18 +589,18 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionOn, if (UNLIKELY(callFrame->argumentCount() < 3)) return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - auto* argument0 = JSC::jsDynamicCast<JSEventEmitter*>(callFrame->uncheckedArgument(0)); + auto argument0 = jsEventEmitterCastFast(vm, lexicalGlobalObject, 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& impl = argument0->wrapped(); auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); 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); })); + 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/JSEventEmitterCustom.cpp b/src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp index 64ae6e1c8..15284fe6c 100644 --- a/src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp +++ b/src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp @@ -4,6 +4,17 @@ #include "EventEmitter.h" #include "JSDOMWrapperCache.h" #include "JSEventListener.h" +#include "ZigGlobalObject.h" + +#include "JSDOMConstructor.h" +#include "JSDOMConvertBase.h" +#include "JSDOMConvertBoolean.h" +#include "JSDOMConvertDictionary.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMConvertNullable.h" +#include "JSDOMConvertNumbers.h" +#include "JSDOMConvertSequences.h" +#include "JSDOMConvertStrings.h" namespace WebCore { using namespace JSC; @@ -22,25 +33,51 @@ EventEmitter* JSEventEmitter::toWrapped(VM& vm, JSValue value) 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 = jsCast<JSEventEmitter*>(value); - return makeUnique<JSEventEmitterWrapper>(target->wrapped(), *target); + if (auto* emitter = jsEventEmitterCastFast(vm, lexicalGlobalObject, thisValue)) { + return std::make_unique<JSEventEmitterWrapper>(emitter->wrapped(), asObject(thisValue)); } return nullptr; } +JSEventEmitter* jsEventEmitterCastFast(VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSValue thisValue) +{ + if (thisValue.inherits<JSEventEmitter>()) + return jsCast<JSEventEmitter*>(asObject(thisValue)); + + if (UNLIKELY(thisValue.isUndefinedOrNull() || !thisValue.isObject())) { + return nullptr; + } + + auto* thisObject = asObject(thisValue); + + auto clientData = WebCore::clientData(vm); + auto name = clientData->builtinNames()._eventsPublicName(); + if (JSValue _events = thisObject->getIfPropertyExists(lexicalGlobalObject, name)) { + if (!_events.isUndefinedOrNull() && _events.inherits<JSEventEmitter>()) { + return jsCast<JSEventEmitter*>(asObject(_events)); + } + } + + auto scope = DECLARE_CATCH_SCOPE(vm); + auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto impl = EventEmitter::create(*globalObject->scriptExecutionContext()); + + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto result = toJSNewlyCreated<IDLInterface<EventEmitter>>(*lexicalGlobalObject, *globalObject, throwScope, WTFMove(impl)); + + thisObject->putDirect(vm, name, result, 0); + + if (scope.exception()) { + scope.clearException(); + return nullptr; + } + + RETURN_IF_EXCEPTION(throwScope, nullptr); + + return jsCast<JSEventEmitter*>(asObject(result)); +} + template<typename Visitor> void JSEventEmitter::visitAdditionalChildren(Visitor& visitor) { diff --git a/src/bun.js/bindings/webcore/JSEventEmitterCustom.h b/src/bun.js/bindings/webcore/JSEventEmitterCustom.h index ed2ffed0c..b75ee5774 100644 --- a/src/bun.js/bindings/webcore/JSEventEmitterCustom.h +++ b/src/bun.js/bindings/webcore/JSEventEmitterCustom.h @@ -10,7 +10,7 @@ class JSEventEmitterWrapper { WTF_MAKE_FAST_ALLOCATED; public: - JSEventEmitterWrapper(EventEmitter& wrapped, JSC::JSObject& wrapper) + JSEventEmitterWrapper(EventEmitter& wrapped, JSC::JSObject* wrapper) : m_wrapped(wrapped) , m_wrapper(wrapper) { @@ -18,18 +18,20 @@ public: EventEmitter& wrapped() { return m_wrapped; } - operator JSC::JSObject&() { return m_wrapper; } + operator JSC::JSObject&() { return *m_wrapper; } private: EventEmitter& m_wrapped; - JSC::JSObject& m_wrapper; + JSC::JSObject* m_wrapper; }; std::unique_ptr<JSEventEmitterWrapper> jsEventEmitterCast(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue thisValue); +JSEventEmitter* jsEventEmitterCastFast(VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSValue thisValue); -template<> class IDLOperation<JSEventEmitter> { +template<> +class IDLOperation<JSEventEmitter> { public: - using ClassParameter = JSEventEmitterWrapper*; + using ClassParameter = JSEventEmitter*; using Operation = JSC::EncodedJSValue(JSC::JSGlobalObject*, JSC::CallFrame*, ClassParameter); template<Operation operation, CastedThisErrorBehavior = CastedThisErrorBehavior::Throw> @@ -39,13 +41,11 @@ public: 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); + auto* thisObject = jsEventEmitterCastFast(vm, &lexicalGlobalObject, thisValue); if (UNLIKELY(!thisObject)) return throwThisTypeError(lexicalGlobalObject, throwScope, "EventEmitter", operationName); - auto& wrapped = thisObject->wrapped(); - - RELEASE_AND_RETURN(throwScope, (operation(&lexicalGlobalObject, &callFrame, thisObject.get()))); + RELEASE_AND_RETURN(throwScope, (operation(&lexicalGlobalObject, &callFrame, thisObject))); } }; diff --git a/src/bun.js/builtins/BunBuiltinNames.h b/src/bun.js/builtins/BunBuiltinNames.h index 5977f05dd..ba4e659ec 100644 --- a/src/bun.js/builtins/BunBuiltinNames.h +++ b/src/bun.js/builtins/BunBuiltinNames.h @@ -247,6 +247,7 @@ using namespace JSC; macro(writer) \ macro(writing) \ macro(written) \ + macro(_events) \ BUN_ADDITIONAL_PRIVATE_IDENTIFIERS(macro) \ class BunBuiltinNames { |