aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/bindings/JSBundlerPlugin.cpp3
-rw-r--r--src/bun.js/bindings/Process.cpp290
-rw-r--r--src/bun.js/bindings/Process.h2
-rw-r--r--src/bun.js/bindings/ScriptExecutionContext.cpp6
-rw-r--r--src/bun.js/bindings/ScriptExecutionContext.h8
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.cpp7
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.h6
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitter.cpp15
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitter.h3
-rw-r--r--test/js/node/process/call-raise.js15
-rw-r--r--test/js/node/process/process-signal-handler.fixture.js63
-rw-r--r--test/js/node/process/process.test.js26
12 files changed, 352 insertions, 92 deletions
diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp
index d55c5fc2e..ec3933574 100644
--- a/src/bun.js/bindings/JSBundlerPlugin.cpp
+++ b/src/bun.js/bindings/JSBundlerPlugin.cpp
@@ -157,7 +157,8 @@ public:
JSC::LazyProperty<JSBundlerPlugin, JSC::JSFunction> setupFunction;
private:
- JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync)
+ JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target,
+ JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync)
: JSC::JSNonFinalObject(vm, structure)
, plugin(BundlerPlugin(config, target, addError, onLoadAsync, onResolveAsync))
{
diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp
index 7d7bdd982..f9fb85b95 100644
--- a/src/bun.js/bindings/Process.cpp
+++ b/src/bun.js/bindings/Process.cpp
@@ -437,91 +437,215 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionChdir,
return JSC::JSValue::encode(result);
}
-// static const NeverDestroyed<String> signalNames[] = {
-// MAKE_STATIC_STRING_IMPL("SIGHUP"),
-// MAKE_STATIC_STRING_IMPL("SIGINT"),
-// MAKE_STATIC_STRING_IMPL("SIGQUIT"),
-// MAKE_STATIC_STRING_IMPL("SIGILL"),
-// MAKE_STATIC_STRING_IMPL("SIGTRAP"),
-// MAKE_STATIC_STRING_IMPL("SIGABRT"),
-// MAKE_STATIC_STRING_IMPL("SIGIOT"),
-// MAKE_STATIC_STRING_IMPL("SIGBUS"),
-// MAKE_STATIC_STRING_IMPL("SIGFPE"),
-// MAKE_STATIC_STRING_IMPL("SIGKILL"),
-// MAKE_STATIC_STRING_IMPL("SIGUSR1"),
-// MAKE_STATIC_STRING_IMPL("SIGSEGV"),
-// MAKE_STATIC_STRING_IMPL("SIGUSR2"),
-// MAKE_STATIC_STRING_IMPL("SIGPIPE"),
-// MAKE_STATIC_STRING_IMPL("SIGALRM"),
-// MAKE_STATIC_STRING_IMPL("SIGTERM"),
-// MAKE_STATIC_STRING_IMPL("SIGCHLD"),
-// MAKE_STATIC_STRING_IMPL("SIGCONT"),
-// MAKE_STATIC_STRING_IMPL("SIGSTOP"),
-// MAKE_STATIC_STRING_IMPL("SIGTSTP"),
-// MAKE_STATIC_STRING_IMPL("SIGTTIN"),
-// MAKE_STATIC_STRING_IMPL("SIGTTOU"),
-// MAKE_STATIC_STRING_IMPL("SIGURG"),
-// MAKE_STATIC_STRING_IMPL("SIGXCPU"),
-// MAKE_STATIC_STRING_IMPL("SIGXFSZ"),
-// MAKE_STATIC_STRING_IMPL("SIGVTALRM"),
-// MAKE_STATIC_STRING_IMPL("SIGPROF"),
-// MAKE_STATIC_STRING_IMPL("SIGWINCH"),
-// MAKE_STATIC_STRING_IMPL("SIGIO"),
-// MAKE_STATIC_STRING_IMPL("SIGINFO"),
-// MAKE_STATIC_STRING_IMPL("SIGSYS"),
-// };
-// static const int signalNumbers[] = {
-// SIGHUP,
-// SIGINT,
-// SIGQUIT,
-// SIGILL,
-// SIGTRAP,
-// SIGABRT,
-// SIGIOT,
-// SIGBUS,
-// SIGFPE,
-// SIGKILL,
-// SIGUSR1,
-// SIGSEGV,
-// SIGUSR2,
-// SIGPIPE,
-// SIGALRM,
-// SIGTERM,
-// SIGCHLD,
-// SIGCONT,
-// SIGSTOP,
-// SIGTSTP,
-// SIGTTIN,
-// SIGTTOU,
-// SIGURG,
-// SIGXCPU,
-// SIGXFSZ,
-// SIGVTALRM,
-// SIGPROF,
-// SIGWINCH,
-// SIGIO,
-// SIGINFO,
-// SIGSYS,
-// };
-
-// JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, CallFrame* callFrame))
-// {
-// VM& vm = globalObject->vm();
-// auto scope = DECLARE_THROW_SCOPE(vm);
-
-// if (callFrame->argumentCount() < 2) {
-// throwVMError(globalObject, scope, "Not enough arguments"_s);
-// return JSValue::encode(jsUndefined());
-// }
-
-// String eventName = callFrame->uncheckedArgument(0).toWTFString(globalObject);
-// RETURN_IF_EXCEPTION(scope, encodedJSValue());
-// }
+static HashMap<String, int>* signalNameToNumberMap = nullptr;
+static HashMap<int, String>* signalNumberToNameMap = nullptr;
+
+// signal number to array of script execution context ids that care about the signal
+static HashMap<int, HashSet<uint32_t>>* signalToContextIdsMap = nullptr;
+static Lock signalToContextIdsMapLock;
+
+static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded)
+{
+
+ static const NeverDestroyed<String> signalNames[] = {
+ MAKE_STATIC_STRING_IMPL("SIGHUP"),
+ MAKE_STATIC_STRING_IMPL("SIGINT"),
+ MAKE_STATIC_STRING_IMPL("SIGQUIT"),
+ MAKE_STATIC_STRING_IMPL("SIGILL"),
+ MAKE_STATIC_STRING_IMPL("SIGTRAP"),
+ MAKE_STATIC_STRING_IMPL("SIGABRT"),
+ MAKE_STATIC_STRING_IMPL("SIGIOT"),
+ MAKE_STATIC_STRING_IMPL("SIGBUS"),
+ MAKE_STATIC_STRING_IMPL("SIGFPE"),
+ MAKE_STATIC_STRING_IMPL("SIGKILL"),
+ MAKE_STATIC_STRING_IMPL("SIGUSR1"),
+ MAKE_STATIC_STRING_IMPL("SIGSEGV"),
+ MAKE_STATIC_STRING_IMPL("SIGUSR2"),
+ MAKE_STATIC_STRING_IMPL("SIGPIPE"),
+ MAKE_STATIC_STRING_IMPL("SIGALRM"),
+ MAKE_STATIC_STRING_IMPL("SIGTERM"),
+ MAKE_STATIC_STRING_IMPL("SIGCHLD"),
+ MAKE_STATIC_STRING_IMPL("SIGCONT"),
+ MAKE_STATIC_STRING_IMPL("SIGSTOP"),
+ MAKE_STATIC_STRING_IMPL("SIGTSTP"),
+ MAKE_STATIC_STRING_IMPL("SIGTTIN"),
+ MAKE_STATIC_STRING_IMPL("SIGTTOU"),
+ MAKE_STATIC_STRING_IMPL("SIGURG"),
+ MAKE_STATIC_STRING_IMPL("SIGXCPU"),
+ MAKE_STATIC_STRING_IMPL("SIGXFSZ"),
+ MAKE_STATIC_STRING_IMPL("SIGVTALRM"),
+ MAKE_STATIC_STRING_IMPL("SIGPROF"),
+ MAKE_STATIC_STRING_IMPL("SIGWINCH"),
+ MAKE_STATIC_STRING_IMPL("SIGIO"),
+ MAKE_STATIC_STRING_IMPL("SIGINFO"),
+ MAKE_STATIC_STRING_IMPL("SIGSYS"),
+ };
+
+ static std::once_flag signalNameToNumberMapOnceFlag;
+ std::call_once(signalNameToNumberMapOnceFlag, [] {
+ signalNameToNumberMap = new HashMap<String, int>();
+ signalNameToNumberMap->reserveInitialCapacity(31);
+ signalNameToNumberMap->add(signalNames[0], SIGHUP);
+ signalNameToNumberMap->add(signalNames[1], SIGINT);
+ signalNameToNumberMap->add(signalNames[2], SIGQUIT);
+ signalNameToNumberMap->add(signalNames[3], SIGILL);
+ signalNameToNumberMap->add(signalNames[4], SIGTRAP);
+ signalNameToNumberMap->add(signalNames[5], SIGABRT);
+ signalNameToNumberMap->add(signalNames[6], SIGIOT);
+ signalNameToNumberMap->add(signalNames[7], SIGBUS);
+ signalNameToNumberMap->add(signalNames[8], SIGFPE);
+ // signalNameToNumberMap->add(signalNames[9], SIGKILL);
+ signalNameToNumberMap->add(signalNames[10], SIGUSR1);
+ signalNameToNumberMap->add(signalNames[11], SIGSEGV);
+ signalNameToNumberMap->add(signalNames[12], SIGUSR2);
+ signalNameToNumberMap->add(signalNames[13], SIGPIPE);
+ signalNameToNumberMap->add(signalNames[14], SIGALRM);
+ signalNameToNumberMap->add(signalNames[15], SIGTERM);
+ signalNameToNumberMap->add(signalNames[16], SIGCHLD);
+ signalNameToNumberMap->add(signalNames[17], SIGCONT);
+ // signalNameToNumberMap->add(signalNames[18], SIGSTOP);
+ signalNameToNumberMap->add(signalNames[19], SIGTSTP);
+ signalNameToNumberMap->add(signalNames[20], SIGTTIN);
+ signalNameToNumberMap->add(signalNames[21], SIGTTOU);
+ signalNameToNumberMap->add(signalNames[22], SIGURG);
+ signalNameToNumberMap->add(signalNames[23], SIGXCPU);
+ signalNameToNumberMap->add(signalNames[24], SIGXFSZ);
+ signalNameToNumberMap->add(signalNames[25], SIGVTALRM);
+ signalNameToNumberMap->add(signalNames[26], SIGPROF);
+ signalNameToNumberMap->add(signalNames[27], SIGWINCH);
+ signalNameToNumberMap->add(signalNames[28], SIGIO);
+#ifdef SIGINFO
+ signalNameToNumberMap->add(signalNames[29], SIGINFO);
+#endif
+
+#ifndef SIGINFO
+ signalNameToNumberMap->add(signalNames[29], 255);
+#endif
+ signalNameToNumberMap->add(signalNames[30], SIGSYS);
+ });
+
+ static std::once_flag signalNumberToNameMapOnceFlag;
+ std::call_once(signalNumberToNameMapOnceFlag, [] {
+ signalNumberToNameMap = new HashMap<int, String>();
+ signalNumberToNameMap->reserveInitialCapacity(31);
+ signalNumberToNameMap->add(SIGHUP, signalNames[0]);
+ signalNumberToNameMap->add(SIGINT, signalNames[1]);
+ signalNumberToNameMap->add(SIGQUIT, signalNames[2]);
+ signalNumberToNameMap->add(SIGILL, signalNames[3]);
+ signalNumberToNameMap->add(SIGTRAP, signalNames[4]);
+ signalNumberToNameMap->add(SIGABRT, signalNames[5]);
+ signalNumberToNameMap->add(SIGIOT, signalNames[6]);
+ signalNumberToNameMap->add(SIGBUS, signalNames[7]);
+ signalNumberToNameMap->add(SIGFPE, signalNames[8]);
+ // signalNumberToNameMap->add(SIGKILL, signalNames[9]);
+ signalNumberToNameMap->add(SIGUSR1, signalNames[10]);
+ signalNumberToNameMap->add(SIGSEGV, signalNames[11]);
+ signalNumberToNameMap->add(SIGUSR2, signalNames[12]);
+ signalNumberToNameMap->add(SIGPIPE, signalNames[13]);
+ signalNumberToNameMap->add(SIGALRM, signalNames[14]);
+ signalNumberToNameMap->add(SIGTERM, signalNames[15]);
+ signalNumberToNameMap->add(SIGCHLD, signalNames[16]);
+ signalNumberToNameMap->add(SIGCONT, signalNames[17]);
+ // signalNumberToNameMap->add(SIGSTOP, signalNames[18]);
+ signalNumberToNameMap->add(SIGTSTP, signalNames[19]);
+ signalNumberToNameMap->add(SIGTTIN, signalNames[20]);
+ signalNumberToNameMap->add(SIGTTOU, signalNames[21]);
+ signalNumberToNameMap->add(SIGURG, signalNames[22]);
+ signalNumberToNameMap->add(SIGXCPU, signalNames[23]);
+ signalNumberToNameMap->add(SIGXFSZ, signalNames[24]);
+ signalNumberToNameMap->add(SIGVTALRM, signalNames[25]);
+ signalNumberToNameMap->add(SIGPROF, signalNames[26]);
+ signalNumberToNameMap->add(SIGWINCH, signalNames[27]);
+ signalNumberToNameMap->add(SIGIO, signalNames[28]);
+#ifdef SIGINFO
+ signalNameToNumberMap->add(signalNames[29], SIGINFO);
+#endif
+ signalNumberToNameMap->add(SIGSYS, signalNames[30]);
+ });
+
+ if (!signalToContextIdsMap) {
+ signalToContextIdsMap = new HashMap<int, HashSet<uint32_t>>();
+ }
+
+ if (isAdded) {
+ if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) {
+ uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier();
+ Locker lock { signalToContextIdsMapLock };
+ if (!signalToContextIdsMap->contains(signalNumber)) {
+ HashSet<uint32_t> contextIds;
+ contextIds.add(contextId);
+ signalToContextIdsMap->set(signalNumber, contextIds);
+
+ lock.unlockEarly();
+
+ struct sigaction action;
+ memset(&action, 0, sizeof(struct sigaction));
+
+ // Set the handler in the action struct
+ action.sa_handler = [](int signalNumber) {
+ if (UNLIKELY(signalNumberToNameMap->find(signalNumber) == signalNumberToNameMap->end()))
+ return;
+
+ Locker lock { signalToContextIdsMapLock };
+ if (UNLIKELY(signalToContextIdsMap->find(signalNumber) == signalToContextIdsMap->end()))
+ return;
+ auto contextIds = signalToContextIdsMap->get(signalNumber);
+
+ for (int contextId : contextIds) {
+ auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId);
+ if (UNLIKELY(!context))
+ continue;
+
+ JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject();
+ Zig::GlobalObject* globalObject = static_cast<Zig::GlobalObject*>(lexicalGlobalObject);
+
+ Process* process = jsCast<Process*>(globalObject->processObject());
+
+ context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber);
+ }
+ };
+
+ // Clear the sa_mask
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, signalNumber);
+ action.sa_flags = SA_RESTART;
+
+ sigaction(signalNumber, &action, nullptr);
+ } else {
+ auto contextIds = signalToContextIdsMap->get(signalNumber);
+ contextIds.add(contextId);
+ signalToContextIdsMap->set(signalNumber, contextIds);
+ }
+ }
+ } else {
+ if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) {
+ uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier();
+ Locker lock { signalToContextIdsMapLock };
+ if (signalToContextIdsMap->find(signalNumber) != signalToContextIdsMap->end()) {
+ HashSet<uint32_t> contextIds = signalToContextIdsMap->get(signalNumber);
+ contextIds.remove(contextId);
+ if (contextIds.isEmpty()) {
+ signal(signalNumber, SIG_DFL);
+ signalToContextIdsMap->remove(signalNumber);
+ } else {
+ signalToContextIdsMap->set(signalNumber, contextIds);
+ }
+ }
+ }
+ }
+}
+
+void Process::emitSignalEvent(int signalNumber)
+{
+ String signalName = signalNumberToNameMap->get(signalNumber);
+ Identifier signalNameIdentifier = Identifier::fromString(vm(), signalName);
+ MarkedArgumentBuffer args;
+ args.append(jsNumber(signalNumber));
+ wrapped().emitForBindings(signalNameIdentifier, args);
+}
Process::~Process()
{
- for (auto& listener : this->wrapped().eventListenerMap().entries()) {
- }
}
JSC_DEFINE_HOST_FUNCTION(Process_functionAbort, (JSGlobalObject * globalObject, CallFrame*))
@@ -1550,6 +1674,8 @@ void Process::finishCreation(JSC::VM& vm)
{
Base::finishCreation(vm);
+ this->wrapped().onDidChangeListener = &onDidChangeListeners;
+
this->cpuUsageStructure.initLater([](const JSC::LazyProperty<JSC::JSObject, JSC::Structure>::Initializer& init) {
init.set(constructCPUUsageStructure(init.vm, init.owner->globalObject()));
});
diff --git a/src/bun.js/bindings/Process.h b/src/bun.js/bindings/Process.h
index fbad9b1ff..0ee6f4243 100644
--- a/src/bun.js/bindings/Process.h
+++ b/src/bun.js/bindings/Process.h
@@ -19,6 +19,8 @@ public:
{
}
+ void emitSignalEvent(int signalNumber);
+
DECLARE_EXPORT_INFO;
static void destroy(JSC::JSCell* cell)
diff --git a/src/bun.js/bindings/ScriptExecutionContext.cpp b/src/bun.js/bindings/ScriptExecutionContext.cpp
index e8cae5e33..3262bdb5d 100644
--- a/src/bun.js/bindings/ScriptExecutionContext.cpp
+++ b/src/bun.js/bindings/ScriptExecutionContext.cpp
@@ -20,6 +20,12 @@ static HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>& allSc
return contexts;
}
+ScriptExecutionContext* ScriptExecutionContext::getScriptExecutionContext(ScriptExecutionContextIdentifier identifier)
+{
+ Locker locker { allScriptExecutionContextsMapLock };
+ return allScriptExecutionContextsMap().get(identifier);
+}
+
template<bool SSL, bool isServer>
static void registerHTTPContextForWebSocket(ScriptExecutionContext* script, us_socket_context_t* ctx, us_loop_t* loop)
{
diff --git a/src/bun.js/bindings/ScriptExecutionContext.h b/src/bun.js/bindings/ScriptExecutionContext.h
index 5f6c56a90..aed7977a5 100644
--- a/src/bun.js/bindings/ScriptExecutionContext.h
+++ b/src/bun.js/bindings/ScriptExecutionContext.h
@@ -96,7 +96,12 @@ public:
}
}
- const WTF::URL& url() const { return m_url; }
+ static ScriptExecutionContext* getScriptExecutionContext(ScriptExecutionContextIdentifier identifier);
+
+ const WTF::URL& url() const
+ {
+ return m_url;
+ }
bool activeDOMObjectsAreSuspended() { return false; }
bool activeDOMObjectsAreStopped() { return false; }
bool isContextThread() { return true; }
@@ -141,6 +146,7 @@ public:
auto* task = new EventLoopTask(WTFMove(lambda));
postTaskOnTimeout(task, timeout);
}
+
template<typename... Arguments>
void postCrossThreadTask(Arguments&&... arguments)
{
diff --git a/src/bun.js/bindings/webcore/EventEmitter.cpp b/src/bun.js/bindings/webcore/EventEmitter.cpp
index 0650d624c..0e273042b 100644
--- a/src/bun.js/bindings/webcore/EventEmitter.cpp
+++ b/src/bun.js/bindings/webcore/EventEmitter.cpp
@@ -35,6 +35,8 @@ bool EventEmitter::addListener(const Identifier& eventType, Ref<EventListener>&&
}
eventListenersDidChange();
+ if (this->onDidChangeListener)
+ this->onDidChangeListener(*this, eventType, true);
return true;
}
@@ -62,6 +64,9 @@ bool EventEmitter::removeListener(const Identifier& eventType, EventListener& li
if (data->eventListenerMap.remove(eventType, listener)) {
eventListenersDidChange();
+
+ if (this->onDidChangeListener)
+ this->onDidChangeListener(*this, eventType, false);
return true;
}
return false;
@@ -93,6 +98,8 @@ bool EventEmitter::removeAllListeners(const Identifier& eventType)
if (data->eventListenerMap.removeAll(eventType)) {
eventListenersDidChange();
+ if (this->onDidChangeListener)
+ this->onDidChangeListener(*this, eventType, false);
return true;
}
return false;
diff --git a/src/bun.js/bindings/webcore/EventEmitter.h b/src/bun.js/bindings/webcore/EventEmitter.h
index b46bcff5d..8db59c188 100644
--- a/src/bun.js/bindings/webcore/EventEmitter.h
+++ b/src/bun.js/bindings/webcore/EventEmitter.h
@@ -67,6 +67,8 @@ public:
bool hasActiveEventListeners(const Identifier& eventType) const;
bool hasEventListeners(JSC::VM& vm, ASCIILiteral eventType) const;
+ WTF::Function<void(EventEmitter&, const Identifier& eventName, bool isAdded)> onDidChangeListener = WTF::Function<void(EventEmitter&, const Identifier& eventName, bool isAdded)>(nullptr);
+
unsigned getMaxListeners() const { return m_maxListeners; };
void setMaxListeners(unsigned count);
@@ -101,7 +103,9 @@ private:
EventEmitterData* eventTargetData() { return &m_eventTargetData; }
EventEmitterData* eventTargetDataConcurrently() { return &m_eventTargetData; }
EventEmitterData& ensureEventEmitterData() { return m_eventTargetData; }
- void eventListenersDidChange() {}
+ void eventListenersDidChange()
+ {
+ }
void innerInvokeEventListeners(const Identifier&, SimpleEventListenerVector, const MarkedArgumentBuffer& arguments);
void invalidateEventListenerRegions();
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
index 231ae0db4..959cbd8d7 100644
--- a/src/bun.js/bindings/webcore/JSEventEmitter.cpp
+++ b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
@@ -219,7 +219,7 @@ JSC_DEFINE_CUSTOM_GETTER(jsEventEmitterConstructor, (JSGlobalObject * lexicalGlo
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)
+inline JSC::EncodedJSValue JSEventEmitter::addListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis, bool once, bool prepend)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
@@ -251,7 +251,7 @@ static inline JSC::EncodedJSValue addListener(JSC::JSGlobalObject* lexicalGlobal
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_addListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
- return addListener(lexicalGlobalObject, callFrame, castedThis, false, false);
+ return JSEventEmitter::addListener(lexicalGlobalObject, callFrame, castedThis, false, false);
}
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_setMaxListenersBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
@@ -280,17 +280,17 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_getMaxListener
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_addOnceListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
- return addListener(lexicalGlobalObject, callFrame, castedThis, true, false);
+ return JSEventEmitter::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);
+ return JSEventEmitter::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);
+ return JSEventEmitter::addListener(lexicalGlobalObject, callFrame, castedThis, true, true);
}
JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_addListener, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
@@ -325,6 +325,11 @@ JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_prependOnceListener, (J
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_removeListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
+ return JSEventEmitter::removeListener(lexicalGlobalObject, callFrame, castedThis);
+}
+
+inline JSC::EncodedJSValue JSEventEmitter::removeListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis)
+{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
JSC::JSValue actualThis = callFrame->thisValue();
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.h b/src/bun.js/bindings/webcore/JSEventEmitter.h
index 855241011..30d62d792 100644
--- a/src/bun.js/bindings/webcore/JSEventEmitter.h
+++ b/src/bun.js/bindings/webcore/JSEventEmitter.h
@@ -27,6 +27,9 @@ public:
static EventEmitter* toWrapped(JSC::VM&, JSC::JSValue);
static void destroy(JSC::JSCell*);
+ static inline JSC::EncodedJSValue addListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis, bool once, bool prepend);
+ static inline JSC::EncodedJSValue removeListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis);
+
DECLARE_INFO;
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
diff --git a/test/js/node/process/call-raise.js b/test/js/node/process/call-raise.js
new file mode 100644
index 000000000..898906759
--- /dev/null
+++ b/test/js/node/process/call-raise.js
@@ -0,0 +1,15 @@
+import { dlopen } from "bun:ffi";
+
+var lazyRaise;
+export function raise(signal) {
+ if (!lazyRaise) {
+ const suffix = process.platform === "darwin" ? "dylib" : "so.6";
+ lazyRaise = dlopen(`libc.${suffix}`, {
+ raise: {
+ args: ["int"],
+ returns: "int",
+ },
+ }).symbols.raise;
+ }
+ lazyRaise(signal);
+}
diff --git a/test/js/node/process/process-signal-handler.fixture.js b/test/js/node/process/process-signal-handler.fixture.js
new file mode 100644
index 000000000..de5a78bda
--- /dev/null
+++ b/test/js/node/process/process-signal-handler.fixture.js
@@ -0,0 +1,63 @@
+import os from "os";
+import { raise } from "./call-raise";
+
+var counter = 0;
+function done() {
+ counter++;
+ if (counter === 2) {
+ setTimeout(() => {
+ if (counter !== 2) {
+ console.log(counter);
+ console.log("FAIL");
+ process.exit(1);
+ }
+
+ console.log("PASS");
+ process.exit(0);
+ }, 1);
+ }
+}
+
+var counter2 = 0;
+function done2() {
+ counter2++;
+ if (counter2 === 2) {
+ setTimeout(() => {
+ if (counter2 !== 2) {
+ console.log(counter2);
+ console.log("FAIL");
+ process.exit(1);
+ }
+
+ console.log("PASS");
+ process.exit(0);
+ }, 1);
+ }
+}
+
+const SIGUSR1 = os.constants.signals.SIGUSR1;
+const SIGUSR2 = os.constants.signals.SIGUSR2;
+
+switch (process.argv.at(-1)) {
+ case "SIGUSR1": {
+ process.on("SIGUSR1", () => {
+ done();
+ });
+ process.on("SIGUSR1", () => {
+ done();
+ });
+ raise(SIGUSR1);
+ break;
+ }
+ case "SIGUSR2": {
+ process.on("SIGUSR2", () => {
+ done2();
+ });
+ process.emit("SIGUSR2");
+ raise(SIGUSR2);
+ break;
+ }
+ default: {
+ throw new Error("Unknown argument: " + process.argv.at(-1));
+ }
+}
diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js
index e038383de..51825b2b4 100644
--- a/test/js/node/process/process.test.js
+++ b/test/js/node/process/process.test.js
@@ -1,6 +1,6 @@
-import { resolveSync, spawnSync, which } from "bun";
+import { spawnSync, which } from "bun";
import { describe, expect, it } from "bun:test";
-import { existsSync, readFileSync, realpathSync } from "fs";
+import { existsSync, readFileSync } from "fs";
import { bunEnv, bunExe } from "harness";
import { basename, join, resolve } from "path";
@@ -381,6 +381,28 @@ it("process.getuid", () => {
expect(typeof process.getuid()).toBe("number");
});
+describe("signal", () => {
+ const fixture = join(import.meta.dir, "./process-signal-handler.fixture.js");
+ it("simple case works", async () => {
+ const child = Bun.spawn({
+ cmd: [bunExe(), fixture, "SIGUSR1"],
+ env: bunEnv,
+ });
+
+ expect(await child.exited).toBe(0);
+ expect(await new Response(child.stdout).text()).toBe("PASS\n");
+ });
+ it("process.emit will call signal events", async () => {
+ const child = Bun.spawn({
+ cmd: [bunExe(), fixture, "SIGUSR2"],
+ env: bunEnv,
+ });
+
+ expect(await child.exited).toBe(0);
+ expect(await new Response(child.stdout).text()).toBe("PASS\n");
+ });
+});
+
const undefinedStubs = [
"_debugEnd",
"_debugProcess",