From 0cf05eeae781a7094eeb80a0ec86cd22551f2830 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 15:46:29 -0700 Subject: [PATCH 01/23] signal events --- src/bun.js/bindings/Process.cpp | 234 ++++++++++++------ .../bindings/webcore/JSEventEmitter.cpp | 10 +- src/bun.js/bindings/webcore/JSEventEmitter.h | 2 + 3 files changed, 160 insertions(+), 86 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 6320deaf183bd..3e46a2bd6051a 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -268,7 +268,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, } } - JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject * globalObject, + JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue exports); napi_register_module_v1 = reinterpret_cast 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 const NeverDestroyed 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"), +}; + +const HashMap signalNameToNumberMap = { + { signalNames[0], SIGHUP }, + { signalNames[1], SIGINT }, + { signalNames[2], SIGQUIT }, + { signalNames[3], SIGILL }, + { signalNames[4], SIGTRAP }, + { signalNames[5], SIGABRT }, + { signalNames[6], SIGIOT }, + { signalNames[7], SIGBUS }, + { signalNames[8], SIGFPE }, + // { signalNames[9], SIGKILL }, + { signalNames[10], SIGUSR1 }, + { signalNames[11], SIGSEGV }, + { signalNames[12], SIGUSR2 }, + { signalNames[13], SIGPIPE }, + { signalNames[14], SIGALRM }, + { signalNames[15], SIGTERM }, + { signalNames[16], SIGCHLD }, + { signalNames[17], SIGCONT }, + { signalNames[18], SIGSTOP }, + { signalNames[19], SIGTSTP }, + { signalNames[20], SIGTTIN }, + { signalNames[21], SIGTTOU }, + { signalNames[22], SIGURG }, + { signalNames[23], SIGXCPU }, + { signalNames[24], SIGXFSZ }, + { signalNames[25], SIGVTALRM }, + { signalNames[26], SIGPROF }, + { signalNames[27], SIGWINCH }, + { signalNames[28], SIGIO }, + { signalNames[29], SIGINFO }, + { signalNames[30], SIGSYS }, +}; + +const HashMap signalNumberToNameMap = { + { SIGHUP, signalNames[0] }, + { SIGINT, signalNames[1] }, + { SIGQUIT, signalNames[2] }, + { SIGILL, signalNames[3] }, + { SIGTRAP, signalNames[4] }, + { SIGABRT, signalNames[5] }, + { SIGIOT, signalNames[6] }, + { SIGBUS, signalNames[7] }, + { SIGFPE, signalNames[8] }, + // { SIGKILL, signalNames[9] }, + { SIGUSR1, signalNames[10] }, + { SIGSEGV, signalNames[11] }, + { SIGUSR2, signalNames[12] }, + { SIGPIPE, signalNames[13] }, + { SIGALRM, signalNames[14] }, + { SIGTERM, signalNames[15] }, + { SIGCHLD, signalNames[16] }, + { SIGCONT, signalNames[17] }, + { SIGSTOP, signalNames[18] }, + { SIGTSTP, signalNames[19] }, + { SIGTTIN, signalNames[20] }, + { SIGTTOU, signalNames[21] }, + { SIGURG, signalNames[22] }, + { SIGXCPU, signalNames[23] }, + { SIGXFSZ, signalNames[24] }, + { SIGVTALRM, signalNames[25] }, + { SIGPROF, signalNames[26] }, + { SIGWINCH, signalNames[27] }, + { SIGIO, signalNames[28] }, + { SIGINFO, signalNames[29] }, + { SIGSYS, signalNames[30] }, +}; + +std::function signalHandler; +void _signalHandler(int signalNumber) +{ + signalHandler(signalNumber); +} + +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()); + + JSValue thisValue = callFrame->thisValue(); + JSObject* thisObject = thisValue.getObject(); + if (UNLIKELY(!thisObject)) + return JSValue::encode(jsUndefined()); + + Process* process = jsCast(thisObject); + + if (signalNameToNumberMap.find(eventName) != signalNameToNumberMap.end()) { + signalHandler = [process](int signalNumber) { + if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) + return; + + JSEventEmitter* eventEmitter = jsCast(process); + String signalName = signalNumberToNameMap.get(signalNumber); + eventEmitter->wrapped().emitForBindings(Identifier::fromString(eventEmitter->vm(), signalName), {}); + }; + + int signalNumber = signalNameToNumberMap.get(eventName); + signal(signalNumber, _signalHandler); + } + + WebCore::JSEventEmitter::addListener(globalObject, callFrame, jsCast(thisObject), false, false); + + return JSValue::encode(thisValue); +} Process::~Process() { @@ -791,6 +857,12 @@ void Process::finishCreation(JSC::VM& vm) this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(this->vm(), "emitWarning"_s), 1, Process_emitWarning, ImplementationVisibility::Public, NoIntrinsic, 0); + this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "on"_s), + 2, jsFunctionProcessOn, ImplementationVisibility::Public, NoIntrinsic, 0); + + this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "addListener"_s), + 2, jsFunctionProcessOn, ImplementationVisibility::Public, NoIntrinsic, 0); + JSC::JSFunction* requireDotMainFunction = JSFunction::create( vm, moduleMainCodeGenerator(vm), diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp index 231ae0db43a8c..22c77f765c861 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::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::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::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::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::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::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)) diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.h b/src/bun.js/bindings/webcore/JSEventEmitter.h index 855241011da04..77895eb117dd9 100644 --- a/src/bun.js/bindings/webcore/JSEventEmitter.h +++ b/src/bun.js/bindings/webcore/JSEventEmitter.h @@ -27,6 +27,8 @@ class JSEventEmitter : public JSDOMWrapper { 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); + DECLARE_INFO; static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) From 7d01677c057d848d0ed98c50e1e726d484503317 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 16:43:32 -0700 Subject: [PATCH 02/23] simple tests --- test/js/node/process/process.test.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 61ac3839c23bd..25be5e6a76042 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -218,6 +218,28 @@ it("process.emitWarning", () => { expect(called).toBe(1); }); +it("process.on signal events", () => { + var called = false; + expect( + process.on("SIGUSR2", () => { + called = true; + }), + ).toBeInstanceOf(process.constructor); + process.emit("SIGUSR2"); + process.removeListener("SIGUSR2"); + expect(called).toBeTruthy(); + + // works with addListener too + called = false; + expect( + process.addListener("SIGUSR2", () => { + called = true; + }), + ).toBeInstanceOf(process.constructor); + process.emit("SIGUSR2"); + expect(called).toBeTruthy(); +}); + it("process.execArgv", () => { expect(process.execArgv instanceof Array).toBe(true); }); From 38df28e3ac36d5a2bab24257c9a811a293f07b5a Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 17:03:46 -0700 Subject: [PATCH 03/23] ignore SIGSTOP --- src/bun.js/bindings/Process.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 3e46a2bd6051a..f0cbca5e4e521 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -505,7 +505,7 @@ const HashMap signalNameToNumberMap = { { signalNames[15], SIGTERM }, { signalNames[16], SIGCHLD }, { signalNames[17], SIGCONT }, - { signalNames[18], SIGSTOP }, + // { signalNames[18], SIGSTOP }, { signalNames[19], SIGTSTP }, { signalNames[20], SIGTTIN }, { signalNames[21], SIGTTOU }, @@ -539,7 +539,7 @@ const HashMap signalNumberToNameMap = { { SIGTERM, signalNames[15] }, { SIGCHLD, signalNames[16] }, { SIGCONT, signalNames[17] }, - { SIGSTOP, signalNames[18] }, + // { SIGSTOP, signalNames[18] }, { SIGTSTP, signalNames[19] }, { SIGTTIN, signalNames[20] }, { SIGTTOU, signalNames[21] }, From c6194c040fc372943b73ea65669e0830a4913303 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 20:12:27 -0700 Subject: [PATCH 04/23] better tests --- test/js/node/process/call-raise.js | 15 +++++++++++++++ test/js/node/process/process.test.js | 12 ++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 test/js/node/process/call-raise.js diff --git a/test/js/node/process/call-raise.js b/test/js/node/process/call-raise.js new file mode 100644 index 0000000000000..102023f22211d --- /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"; + lazyRaise = dlopen(`libc.${suffix}`, { + raise: { + args: ["int"], + returns: "int", + }, + }).symbols.raise; + } + lazyRaise(signal); +} diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 25be5e6a76042..12f4d2b239c1a 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -3,6 +3,7 @@ import { describe, expect, it } from "bun:test"; import { existsSync, readFileSync, realpathSync } from "fs"; import { bunExe } from "harness"; import { basename, resolve } from "path"; +import { raise } from "./call-raise.js"; it("process", () => { // this property isn't implemented yet but it should at least return a string @@ -238,6 +239,17 @@ it("process.on signal events", () => { ).toBeInstanceOf(process.constructor); process.emit("SIGUSR2"); expect(called).toBeTruthy(); + + called = false; + raise(31); + expect(called).toBeTruthy(); + + called = false; + process.on("SIGUSR1", () => { + called = true; + }); + raise(30); + expect(called).toBeTruthy(); }); it("process.execArgv", () => { From 0be1a1cfda38502f53cc9d503da6531cf0b9481d Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 20:12:54 -0700 Subject: [PATCH 05/23] use `EventEmitter` --- src/bun.js/bindings/Process.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index f0cbca5e4e521..1f088960f5a33 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -578,16 +578,21 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, Ca if (UNLIKELY(!thisObject)) return JSValue::encode(jsUndefined()); - Process* process = jsCast(thisObject); - if (signalNameToNumberMap.find(eventName) != signalNameToNumberMap.end()) { - signalHandler = [process](int signalNumber) { + + WebCore::Process* process = jsDynamicCast(thisObject); + WeakPtr eventEmitter = process->wrapped(); + + signalHandler = [eventEmitter](int signalNumber) { if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) return; - JSEventEmitter* eventEmitter = jsCast(process); - String signalName = signalNumberToNameMap.get(signalNumber); - eventEmitter->wrapped().emitForBindings(Identifier::fromString(eventEmitter->vm(), signalName), {}); + if (auto* context = eventEmitter->scriptExecutionContext()) { + String signalName = signalNumberToNameMap.get(signalNumber); + MarkedArgumentBuffer args; + args.append(jsNumber(signalNumber)); + eventEmitter->emitForBindings(Identifier::fromString(context->vm(), signalName), args); + } }; int signalNumber = signalNameToNumberMap.get(eventName); From a95a76b0e355ed20fdee249712cb92b6d1c9bd9c Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 20:26:59 -0700 Subject: [PATCH 06/23] use `Bun__getDefaultGlobal` --- src/bun.js/bindings/Process.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 1f088960f5a33..a30735a1cd784 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -554,11 +554,7 @@ const HashMap signalNumberToNameMap = { { SIGSYS, signalNames[30] }, }; -std::function signalHandler; -void _signalHandler(int signalNumber) -{ - signalHandler(signalNumber); -} +extern "C" JSGlobalObject* Bun__getDefaultGlobal(); JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, CallFrame* callFrame)) { @@ -579,24 +575,20 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, Ca return JSValue::encode(jsUndefined()); if (signalNameToNumberMap.find(eventName) != signalNameToNumberMap.end()) { - - WebCore::Process* process = jsDynamicCast(thisObject); - WeakPtr eventEmitter = process->wrapped(); - - signalHandler = [eventEmitter](int signalNumber) { + int signalNumber = signalNameToNumberMap.get(eventName); + signal(signalNumber, [](int signalNumber) { if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) return; - if (auto* context = eventEmitter->scriptExecutionContext()) { - String signalName = signalNumberToNameMap.get(signalNumber); - MarkedArgumentBuffer args; - args.append(jsNumber(signalNumber)); - eventEmitter->emitForBindings(Identifier::fromString(context->vm(), signalName), args); - } - }; + JSGlobalObject* lexicalGlobalObject = Bun__getDefaultGlobal(); + Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); - int signalNumber = signalNameToNumberMap.get(eventName); - signal(signalNumber, _signalHandler); + Process* process = jsCast(globalObject->processObject()); + String signalName = signalNumberToNameMap.get(signalNumber); + MarkedArgumentBuffer args; + args.append(jsNumber(signalNumber)); + process->wrapped().emitForBindings(Identifier::fromString(globalObject->vm(), signalName), args); + }); } WebCore::JSEventEmitter::addListener(globalObject, callFrame, jsCast(thisObject), false, false); From f8e852a4b5af141318106a06e90bc901c879bd1d Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 22:12:08 -0700 Subject: [PATCH 07/23] progress --- src/bun.js/bindings/Process.cpp | 60 ++++++++++++++++--- src/bun.js/bindings/Process.h | 2 + .../bindings/ScriptExecutionContext.cpp | 6 ++ src/bun.js/bindings/ScriptExecutionContext.h | 8 ++- 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index a30735a1cd784..6ca50fbb5692e 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -556,9 +556,15 @@ const HashMap signalNumberToNameMap = { extern "C" JSGlobalObject* Bun__getDefaultGlobal(); -JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, CallFrame* callFrame)) +// signal number to array of script execution context ids that care about the signal +HashMap> signalToContextIdsMap; +static Lock signalToContextIdsMapLock; + +JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) { - VM& vm = globalObject->vm(); + VM& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); if (callFrame->argumentCount() < 2) { @@ -575,19 +581,46 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, Ca return JSValue::encode(jsUndefined()); if (signalNameToNumberMap.find(eventName) != signalNameToNumberMap.end()) { + int signalNumber = signalNameToNumberMap.get(eventName); + uint32_t contextId = globalObject->scriptExecutionContext()->identifier(); + Locker lock { signalToContextIdsMapLock }; + if (signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end()) { + Vector contextIds; + contextIds.append(contextId); + signalToContextIdsMap.add(signalNumber, contextIds); + } else { + Vector contextIds = signalToContextIdsMap.get(signalNumber); + contextIds.append(contextId); + signalToContextIdsMap.set(signalNumber, contextIds); + } + signal(signalNumber, [](int signalNumber) { if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) return; + String signalName = signalNumberToNameMap.get(signalNumber); + + if (UNLIKELY(signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end())) + return; + Vector contextIds = signalToContextIdsMap.get(signalNumber); - JSGlobalObject* lexicalGlobalObject = Bun__getDefaultGlobal(); - Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); + for (int contextId : contextIds) { + JSGlobalObject* lexicalGlobalObject = Bun__getDefaultGlobal(); + Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); + Identifier signalNameIdentifier = Identifier::fromString(globalObject->vm(), signalName); - Process* process = jsCast(globalObject->processObject()); - String signalName = signalNumberToNameMap.get(signalNumber); - MarkedArgumentBuffer args; - args.append(jsNumber(signalNumber)); - process->wrapped().emitForBindings(Identifier::fromString(globalObject->vm(), signalName), args); + Process* process = jsCast(globalObject->processObject()); + // MarkedArgumentBuffer args; + // args.append(jsNumber(signalNumber)); + + auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId); + + // context->postCrossThreadTask(std::function::target, [process, signalNameIdentifier, args] { + // process->wrapped().eventEmitter().emit(signalNameIdentifier, args); + // })); + + context->postCrossThreadTask(process, &Process::emitSignalEvent, signalNumber); + } }); } @@ -596,6 +629,15 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, Ca return JSValue::encode(thisValue); } +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()) { diff --git a/src/bun.js/bindings/Process.h b/src/bun.js/bindings/Process.h index 322b39078d652..1fb18774ba614 100644 --- a/src/bun.js/bindings/Process.h +++ b/src/bun.js/bindings/Process.h @@ -19,6 +19,8 @@ class Process : public WebCore::JSEventEmitter { { } + void Process::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 e8cae5e338a90..a06a800c86556 100644 --- a/src/bun.js/bindings/ScriptExecutionContext.cpp +++ b/src/bun.js/bindings/ScriptExecutionContext.cpp @@ -20,6 +20,12 @@ static HashMap& allSc return contexts; } +ScriptExecutionContext* getScriptExecutionContext(ScriptExecutionContextIdentifier identifier) +{ + Locker locker { allScriptExecutionContextsMapLock }; + return allScriptExecutionContextsMap().get(identifier); +} + template 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 5f6c56a904033..aed7977a551b4 100644 --- a/src/bun.js/bindings/ScriptExecutionContext.h +++ b/src/bun.js/bindings/ScriptExecutionContext.h @@ -96,7 +96,12 @@ class ScriptExecutionContext : public CanMakeWeakPtr { } } - 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 @@ class ScriptExecutionContext : public CanMakeWeakPtr { auto* task = new EventLoopTask(WTFMove(lambda)); postTaskOnTimeout(task, timeout); } + template void postCrossThreadTask(Arguments&&... arguments) { From e1b9072ce1282717c79155d079c92a3029812e00 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 22:35:09 -0700 Subject: [PATCH 08/23] don't use 'Bun__getDefaultGlobal` --- src/bun.js/bindings/Process.cpp | 18 ++++-------------- src/bun.js/bindings/Process.h | 2 +- src/bun.js/bindings/ScriptExecutionContext.cpp | 2 +- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 6ca50fbb5692e..d2366a3e564a0 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -554,8 +554,6 @@ const HashMap signalNumberToNameMap = { { SIGSYS, signalNames[30] }, }; -extern "C" JSGlobalObject* Bun__getDefaultGlobal(); - // signal number to array of script execution context ids that care about the signal HashMap> signalToContextIdsMap; static Lock signalToContextIdsMapLock; @@ -598,28 +596,20 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * lexicalGlobalObj signal(signalNumber, [](int signalNumber) { if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) return; - String signalName = signalNumberToNameMap.get(signalNumber); if (UNLIKELY(signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end())) return; Vector contextIds = signalToContextIdsMap.get(signalNumber); for (int contextId : contextIds) { - JSGlobalObject* lexicalGlobalObject = Bun__getDefaultGlobal(); + auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId); + + JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject(); Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); - Identifier signalNameIdentifier = Identifier::fromString(globalObject->vm(), signalName); Process* process = jsCast(globalObject->processObject()); - // MarkedArgumentBuffer args; - // args.append(jsNumber(signalNumber)); - - auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId); - - // context->postCrossThreadTask(std::function::target, [process, signalNameIdentifier, args] { - // process->wrapped().eventEmitter().emit(signalNameIdentifier, args); - // })); - context->postCrossThreadTask(process, &Process::emitSignalEvent, signalNumber); + context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber); } }); } diff --git a/src/bun.js/bindings/Process.h b/src/bun.js/bindings/Process.h index 1fb18774ba614..5451186da795b 100644 --- a/src/bun.js/bindings/Process.h +++ b/src/bun.js/bindings/Process.h @@ -19,7 +19,7 @@ class Process : public WebCore::JSEventEmitter { { } - void Process::emitSignalEvent(int signalNumber); + void emitSignalEvent(int signalNumber); DECLARE_EXPORT_INFO; diff --git a/src/bun.js/bindings/ScriptExecutionContext.cpp b/src/bun.js/bindings/ScriptExecutionContext.cpp index a06a800c86556..3262bdb5d3681 100644 --- a/src/bun.js/bindings/ScriptExecutionContext.cpp +++ b/src/bun.js/bindings/ScriptExecutionContext.cpp @@ -20,7 +20,7 @@ static HashMap& allSc return contexts; } -ScriptExecutionContext* getScriptExecutionContext(ScriptExecutionContextIdentifier identifier) +ScriptExecutionContext* ScriptExecutionContext::getScriptExecutionContext(ScriptExecutionContextIdentifier identifier) { Locker locker { allScriptExecutionContextsMapLock }; return allScriptExecutionContextsMap().get(identifier); From eb036b091626c6e4e0ade5ea6d7494ef9d782521 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 22:35:26 -0700 Subject: [PATCH 09/23] fix tests --- test/js/node/process/process.test.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 12f4d2b239c1a..7a21e53e05d26 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -220,36 +220,40 @@ it("process.emitWarning", () => { }); it("process.on signal events", () => { - var called = false; + var called2 = false; expect( process.on("SIGUSR2", () => { - called = true; + called2 = true; }), ).toBeInstanceOf(process.constructor); process.emit("SIGUSR2"); process.removeListener("SIGUSR2"); - expect(called).toBeTruthy(); + expect(called2).toBeTruthy(); // works with addListener too - called = false; + called2 = false; expect( process.addListener("SIGUSR2", () => { - called = true; + called2 = true; }), ).toBeInstanceOf(process.constructor); process.emit("SIGUSR2"); - expect(called).toBeTruthy(); + expect(called2).toBeTruthy(); - called = false; + var called2 = false; raise(31); - expect(called).toBeTruthy(); + process.nextTick(() => { + expect(called2).toBeTruthy(); + }); - called = false; + var called1 = false; process.on("SIGUSR1", () => { - called = true; + called1 = true; }); raise(30); - expect(called).toBeTruthy(); + process.nextTick(() => { + expect(called1).toBeTruthy(); + }); }); it("process.execArgv", () => { From 504600bc1ccb6458b8ca7fc9e0039cf5e8ee8ea4 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 23:04:47 -0700 Subject: [PATCH 10/23] remove signals from map --- src/bun.js/bindings/Process.cpp | 41 +++++++++++++++++++ .../bindings/webcore/JSEventEmitter.cpp | 5 +++ src/bun.js/bindings/webcore/JSEventEmitter.h | 1 + 3 files changed, 47 insertions(+) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index d2366a3e564a0..25583ce94dacf 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -628,6 +628,41 @@ void Process::emitSignalEvent(int signalNumber) wrapped().emitForBindings(signalNameIdentifier, args); } +JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOff, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + + VM& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); + + auto scope = DECLARE_THROW_SCOPE(vm); + + if (callFrame->argumentCount() < 1) { + throwVMError(globalObject, scope, "Not enough arguments"_s); + return JSValue::encode(jsUndefined()); + } + + String eventName = callFrame->uncheckedArgument(0).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + JSValue thisValue = callFrame->thisValue(); + JSObject* thisObject = thisValue.getObject(); + if (UNLIKELY(!thisObject)) + return JSValue::encode(jsUndefined()); + + if (signalNameToNumberMap.find(eventName) != signalNameToNumberMap.end()) { + int signalNumber = signalNameToNumberMap.get(eventName); + uint32_t contextId = globalObject->scriptExecutionContext()->identifier(); + Locker lock { signalToContextIdsMapLock }; + if (signalToContextIdsMap.find(signalNumber) != signalToContextIdsMap.end()) { + Vector contextIds = signalToContextIdsMap.get(signalNumber); + contextIds.removeFirst(contextId); + signalToContextIdsMap.set(signalNumber, contextIds); + } + } + + return WebCore::JSEventEmitter::removeListener(globalObject, callFrame, jsCast(thisObject)); +} + Process::~Process() { for (auto& listener : this->wrapped().eventListenerMap().entries()) { @@ -892,6 +927,12 @@ void Process::finishCreation(JSC::VM& vm) this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "addListener"_s), 2, jsFunctionProcessOn, ImplementationVisibility::Public, NoIntrinsic, 0); + this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "off"_s), + 2, jsFunctionProcessOff, ImplementationVisibility::Public, NoIntrinsic, 0); + + this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "removeListener"_s), + 2, jsFunctionProcessOff, ImplementationVisibility::Public, NoIntrinsic, 0); + JSC::JSFunction* requireDotMainFunction = JSFunction::create( vm, moduleMainCodeGenerator(vm), diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp index 22c77f765c861..959cbd8d7fb31 100644 --- a/src/bun.js/bindings/webcore/JSEventEmitter.cpp +++ b/src/bun.js/bindings/webcore/JSEventEmitter.cpp @@ -324,6 +324,11 @@ JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_prependOnceListener, (J } static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_removeListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation::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); diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.h b/src/bun.js/bindings/webcore/JSEventEmitter.h index 77895eb117dd9..30d62d7924442 100644 --- a/src/bun.js/bindings/webcore/JSEventEmitter.h +++ b/src/bun.js/bindings/webcore/JSEventEmitter.h @@ -28,6 +28,7 @@ class JSEventEmitter : public JSDOMWrapper { 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; From 88f057ee4293a9eb44f4e8651bc8d59a7482657f Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 23:05:17 -0700 Subject: [PATCH 11/23] update tests --- test/js/node/process/process.test.js | 55 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 7a21e53e05d26..07cf206097e65 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -219,33 +219,7 @@ it("process.emitWarning", () => { expect(called).toBe(1); }); -it("process.on signal events", () => { - var called2 = false; - expect( - process.on("SIGUSR2", () => { - called2 = true; - }), - ).toBeInstanceOf(process.constructor); - process.emit("SIGUSR2"); - process.removeListener("SIGUSR2"); - expect(called2).toBeTruthy(); - - // works with addListener too - called2 = false; - expect( - process.addListener("SIGUSR2", () => { - called2 = true; - }), - ).toBeInstanceOf(process.constructor); - process.emit("SIGUSR2"); - expect(called2).toBeTruthy(); - - var called2 = false; - raise(31); - process.nextTick(() => { - expect(called2).toBeTruthy(); - }); - +it("process on SIGUSR1", done => { var called1 = false; process.on("SIGUSR1", () => { called1 = true; @@ -253,9 +227,36 @@ it("process.on signal events", () => { raise(30); process.nextTick(() => { expect(called1).toBeTruthy(); + done(); + }); +}); + +it("process on and off SIGINFO", done => { + var calledSIGINFO = false; + process.on("SIGINFO", () => { + calledSIGINFO = true; + }); + process.off("SIGINFO"); + raise(29); + process.nextTick(() => { + expect(calledSIGINFO).toBeFalsy(); + done(); }); }); +it("process signal event works like normal event", () => { + var called = false; + process.on("SIGUSR2", () => { + called = true; + }); + process.emit("SIGUSR2"); + expect(called).toBeTruthy(); + process.removeListener("SIGUSR2"); + called = false; + process.emit("SIGUSR2"); + expect(called).toBeFalsy(); +}); + it("process.execArgv", () => { expect(process.execArgv instanceof Array).toBe(true); }); From df84702801cf3d2e9284f206acb3c8ab1756e451 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Jul 2023 23:39:29 -0700 Subject: [PATCH 12/23] don't overwrite event emitter methods --- src/bun.js/bindings/Process.cpp | 140 ++++++------------- src/bun.js/bindings/webcore/EventEmitter.cpp | 7 + src/bun.js/bindings/webcore/EventEmitter.h | 6 +- 3 files changed, 55 insertions(+), 98 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 25583ce94dacf..59ac9debb43fc 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -555,68 +555,60 @@ const HashMap signalNumberToNameMap = { }; // signal number to array of script execution context ids that care about the signal -HashMap> signalToContextIdsMap; +static HashMap> signalToContextIdsMap; static Lock signalToContextIdsMapLock; -JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded) { - VM& vm = lexicalGlobalObject->vm(); - Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); - - 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()); - - JSValue thisValue = callFrame->thisValue(); - JSObject* thisObject = thisValue.getObject(); - if (UNLIKELY(!thisObject)) - return JSValue::encode(jsUndefined()); + if (isAdded) { + if (signalNameToNumberMap.find(eventName.string()) != signalNameToNumberMap.end()) { + + int signalNumber = signalNameToNumberMap.get(eventName.string()); + uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier(); + Locker lock { signalToContextIdsMapLock }; + if (signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end()) { + Vector contextIds; + contextIds.append(contextId); + signalToContextIdsMap.add(signalNumber, contextIds); + } else { + Vector contextIds = signalToContextIdsMap.get(signalNumber); + contextIds.append(contextId); + signalToContextIdsMap.set(signalNumber, contextIds); + } - if (signalNameToNumberMap.find(eventName) != signalNameToNumberMap.end()) { - - int signalNumber = signalNameToNumberMap.get(eventName); - uint32_t contextId = globalObject->scriptExecutionContext()->identifier(); - Locker lock { signalToContextIdsMapLock }; - if (signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end()) { - Vector contextIds; - contextIds.append(contextId); - signalToContextIdsMap.add(signalNumber, contextIds); - } else { - Vector contextIds = signalToContextIdsMap.get(signalNumber); - contextIds.append(contextId); - signalToContextIdsMap.set(signalNumber, contextIds); - } + signal(signalNumber, [](int signalNumber) { + if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) + return; - signal(signalNumber, [](int signalNumber) { - if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) - return; + if (UNLIKELY(signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end())) + return; + Vector contextIds = signalToContextIdsMap.get(signalNumber); - if (UNLIKELY(signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end())) - return; - Vector contextIds = signalToContextIdsMap.get(signalNumber); + for (int contextId : contextIds) { + auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId); - for (int contextId : contextIds) { - auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId); + JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject(); + Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); - JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject(); - Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); + Process* process = jsCast(globalObject->processObject()); - Process* process = jsCast(globalObject->processObject()); + context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber); + } + }); + } - context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber); + } else { + if (signalNameToNumberMap.find(eventName.string()) != signalNameToNumberMap.end()) { + int signalNumber = signalNameToNumberMap.get(eventName.string()); + uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier(); + Locker lock { signalToContextIdsMapLock }; + if (signalToContextIdsMap.find(signalNumber) != signalToContextIdsMap.end()) { + Vector contextIds = signalToContextIdsMap.get(signalNumber); + contextIds.removeFirst(contextId); + signalToContextIdsMap.set(signalNumber, contextIds); } - }); + } } - - WebCore::JSEventEmitter::addListener(globalObject, callFrame, jsCast(thisObject), false, false); - - return JSValue::encode(thisValue); } void Process::emitSignalEvent(int signalNumber) @@ -628,41 +620,6 @@ void Process::emitSignalEvent(int signalNumber) wrapped().emitForBindings(signalNameIdentifier, args); } -JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOff, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - - VM& vm = lexicalGlobalObject->vm(); - Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); - - auto scope = DECLARE_THROW_SCOPE(vm); - - if (callFrame->argumentCount() < 1) { - throwVMError(globalObject, scope, "Not enough arguments"_s); - return JSValue::encode(jsUndefined()); - } - - String eventName = callFrame->uncheckedArgument(0).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, encodedJSValue()); - - JSValue thisValue = callFrame->thisValue(); - JSObject* thisObject = thisValue.getObject(); - if (UNLIKELY(!thisObject)) - return JSValue::encode(jsUndefined()); - - if (signalNameToNumberMap.find(eventName) != signalNameToNumberMap.end()) { - int signalNumber = signalNameToNumberMap.get(eventName); - uint32_t contextId = globalObject->scriptExecutionContext()->identifier(); - Locker lock { signalToContextIdsMapLock }; - if (signalToContextIdsMap.find(signalNumber) != signalToContextIdsMap.end()) { - Vector contextIds = signalToContextIdsMap.get(signalNumber); - contextIds.removeFirst(contextId); - signalToContextIdsMap.set(signalNumber, contextIds); - } - } - - return WebCore::JSEventEmitter::removeListener(globalObject, callFrame, jsCast(thisObject)); -} - Process::~Process() { for (auto& listener : this->wrapped().eventListenerMap().entries()) { @@ -758,6 +715,7 @@ void Process::finishCreation(JSC::VM& vm) Base::finishCreation(vm); auto clientData = WebCore::clientData(vm); auto* globalObject = reinterpret_cast(this->globalObject()); + this->wrapped().onDidChangeListener = &onDidChangeListeners; putDirectCustomAccessor(vm, clientData->builtinNames().pidPublicName(), JSC::CustomGetterSetter::create(vm, Process_getPID, nullptr), @@ -921,18 +879,6 @@ void Process::finishCreation(JSC::VM& vm) this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(this->vm(), "emitWarning"_s), 1, Process_emitWarning, ImplementationVisibility::Public, NoIntrinsic, 0); - this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "on"_s), - 2, jsFunctionProcessOn, ImplementationVisibility::Public, NoIntrinsic, 0); - - this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "addListener"_s), - 2, jsFunctionProcessOn, ImplementationVisibility::Public, NoIntrinsic, 0); - - this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "off"_s), - 2, jsFunctionProcessOff, ImplementationVisibility::Public, NoIntrinsic, 0); - - this->putDirectNativeFunction(vm, globalObject, Identifier::fromString(this->vm(), "removeListener"_s), - 2, jsFunctionProcessOff, ImplementationVisibility::Public, NoIntrinsic, 0); - JSC::JSFunction* requireDotMainFunction = JSFunction::create( vm, moduleMainCodeGenerator(vm), diff --git a/src/bun.js/bindings/webcore/EventEmitter.cpp b/src/bun.js/bindings/webcore/EventEmitter.cpp index 0650d624c88ae..0e273042bfd4e 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&& } 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 b46bcff5d7cdf..8db59c1887581 100644 --- a/src/bun.js/bindings/webcore/EventEmitter.h +++ b/src/bun.js/bindings/webcore/EventEmitter.h @@ -67,6 +67,8 @@ class EventEmitter final : public ScriptWrappable, public CanMakeWeakPtr onDidChangeListener = WTF::Function(nullptr); + unsigned getMaxListeners() const { return m_maxListeners; }; void setMaxListeners(unsigned count); @@ -101,7 +103,9 @@ class EventEmitter final : public ScriptWrappable, public CanMakeWeakPtr Date: Fri, 7 Jul 2023 23:45:04 -0700 Subject: [PATCH 13/23] avoid two lookups --- src/bun.js/bindings/Process.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 59ac9debb43fc..958b3bdf90043 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -561,9 +561,7 @@ static Lock signalToContextIdsMapLock; void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded) { if (isAdded) { - if (signalNameToNumberMap.find(eventName.string()) != signalNameToNumberMap.end()) { - - int signalNumber = signalNameToNumberMap.get(eventName.string()); + if (auto signalNumber = signalNameToNumberMap.get(eventName.string())) { uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier(); Locker lock { signalToContextIdsMapLock }; if (signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end()) { @@ -596,10 +594,8 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam } }); } - } else { - if (signalNameToNumberMap.find(eventName.string()) != signalNameToNumberMap.end()) { - int signalNumber = signalNameToNumberMap.get(eventName.string()); + if (auto signalNumber = signalNameToNumberMap.get(eventName.string())) { uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier(); Locker lock { signalToContextIdsMapLock }; if (signalToContextIdsMap.find(signalNumber) != signalToContextIdsMap.end()) { From 19c23ba15bba60db0336a1db1e2835d425adee3a Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Sat, 8 Jul 2023 00:02:43 -0700 Subject: [PATCH 14/23] use `std::once` --- src/bun.js/bindings/Process.cpp | 149 +++++++++++++++++--------------- 1 file changed, 78 insertions(+), 71 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 958b3bdf90043..12b1c8f6f890e 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -486,73 +486,8 @@ static const NeverDestroyed signalNames[] = { MAKE_STATIC_STRING_IMPL("SIGSYS"), }; -const HashMap signalNameToNumberMap = { - { signalNames[0], SIGHUP }, - { signalNames[1], SIGINT }, - { signalNames[2], SIGQUIT }, - { signalNames[3], SIGILL }, - { signalNames[4], SIGTRAP }, - { signalNames[5], SIGABRT }, - { signalNames[6], SIGIOT }, - { signalNames[7], SIGBUS }, - { signalNames[8], SIGFPE }, - // { signalNames[9], SIGKILL }, - { signalNames[10], SIGUSR1 }, - { signalNames[11], SIGSEGV }, - { signalNames[12], SIGUSR2 }, - { signalNames[13], SIGPIPE }, - { signalNames[14], SIGALRM }, - { signalNames[15], SIGTERM }, - { signalNames[16], SIGCHLD }, - { signalNames[17], SIGCONT }, - // { signalNames[18], SIGSTOP }, - { signalNames[19], SIGTSTP }, - { signalNames[20], SIGTTIN }, - { signalNames[21], SIGTTOU }, - { signalNames[22], SIGURG }, - { signalNames[23], SIGXCPU }, - { signalNames[24], SIGXFSZ }, - { signalNames[25], SIGVTALRM }, - { signalNames[26], SIGPROF }, - { signalNames[27], SIGWINCH }, - { signalNames[28], SIGIO }, - { signalNames[29], SIGINFO }, - { signalNames[30], SIGSYS }, -}; - -const HashMap signalNumberToNameMap = { - { SIGHUP, signalNames[0] }, - { SIGINT, signalNames[1] }, - { SIGQUIT, signalNames[2] }, - { SIGILL, signalNames[3] }, - { SIGTRAP, signalNames[4] }, - { SIGABRT, signalNames[5] }, - { SIGIOT, signalNames[6] }, - { SIGBUS, signalNames[7] }, - { SIGFPE, signalNames[8] }, - // { SIGKILL, signalNames[9] }, - { SIGUSR1, signalNames[10] }, - { SIGSEGV, signalNames[11] }, - { SIGUSR2, signalNames[12] }, - { SIGPIPE, signalNames[13] }, - { SIGALRM, signalNames[14] }, - { SIGTERM, signalNames[15] }, - { SIGCHLD, signalNames[16] }, - { SIGCONT, signalNames[17] }, - // { SIGSTOP, signalNames[18] }, - { SIGTSTP, signalNames[19] }, - { SIGTTIN, signalNames[20] }, - { SIGTTOU, signalNames[21] }, - { SIGURG, signalNames[22] }, - { SIGXCPU, signalNames[23] }, - { SIGXFSZ, signalNames[24] }, - { SIGVTALRM, signalNames[25] }, - { SIGPROF, signalNames[26] }, - { SIGWINCH, signalNames[27] }, - { SIGIO, signalNames[28] }, - { SIGINFO, signalNames[29] }, - { SIGSYS, signalNames[30] }, -}; +HashMap* signalNameToNumberMap = nullptr; +HashMap* signalNumberToNameMap = nullptr; // signal number to array of script execution context ids that care about the signal static HashMap> signalToContextIdsMap; @@ -560,8 +495,80 @@ static Lock signalToContextIdsMapLock; void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded) { + static std::once_flag signalNameToNumberMapOnceFlag; + std::call_once(signalNameToNumberMapOnceFlag, [] { + signalNameToNumberMap = new HashMap(); + 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); + signalNameToNumberMap->add(signalNames[29], SIGINFO); + signalNameToNumberMap->add(signalNames[30], SIGSYS); + }); + + static std::once_flag signalNumberToNameMapOnceFlag; + std::call_once(signalNumberToNameMapOnceFlag, [] { + signalNumberToNameMap = new HashMap(); + 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]); + signalNumberToNameMap->add(SIGINFO, signalNames[29]); + signalNumberToNameMap->add(SIGSYS, signalNames[30]); + }); + if (isAdded) { - if (auto signalNumber = signalNameToNumberMap.get(eventName.string())) { + if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) { uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier(); Locker lock { signalToContextIdsMapLock }; if (signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end()) { @@ -575,7 +582,7 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam } signal(signalNumber, [](int signalNumber) { - if (UNLIKELY(signalNumberToNameMap.find(signalNumber) == signalNumberToNameMap.end())) + if (UNLIKELY(signalNumberToNameMap->find(signalNumber) == signalNumberToNameMap->end())) return; if (UNLIKELY(signalToContextIdsMap.find(signalNumber) == signalToContextIdsMap.end())) @@ -595,7 +602,7 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam }); } } else { - if (auto signalNumber = signalNameToNumberMap.get(eventName.string())) { + if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) { uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier(); Locker lock { signalToContextIdsMapLock }; if (signalToContextIdsMap.find(signalNumber) != signalToContextIdsMap.end()) { @@ -609,7 +616,7 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam void Process::emitSignalEvent(int signalNumber) { - String signalName = signalNumberToNameMap.get(signalNumber); + String signalName = signalNumberToNameMap->get(signalNumber); Identifier signalNameIdentifier = Identifier::fromString(vm(), signalName); MarkedArgumentBuffer args; args.append(jsNumber(signalNumber)); From eab9f6ede175dbfc47af8b03f2d4333f1e6017e3 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Sat, 8 Jul 2023 00:07:09 -0700 Subject: [PATCH 15/23] releaseEarly() --- src/bun.js/bindings/Process.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 12b1c8f6f890e..ba8b0a795ece2 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -581,6 +581,8 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam signalToContextIdsMap.set(signalNumber, contextIds); } + lock.unlockEarly(); + signal(signalNumber, [](int signalNumber) { if (UNLIKELY(signalNumberToNameMap->find(signalNumber) == signalNumberToNameMap->end())) return; From f6744df978345b33e506522d985cf04feafdff22 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Sat, 8 Jul 2023 02:33:55 -0700 Subject: [PATCH 16/23] Remove signal handler after use --- src/bun.js/bindings/Process.cpp | 92 +++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index ba8b0a795ece2..d34c35650ff71 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -268,7 +268,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, } } - JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject, + JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue exports); napi_register_module_v1 = reinterpret_cast 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"), -}; - HashMap* signalNameToNumberMap = nullptr; HashMap* signalNumberToNameMap = nullptr; @@ -495,9 +461,45 @@ static Lock signalToContextIdsMapLock; void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded) { + + static const NeverDestroyed 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(); + signalNameToNumberMap->reserveInitialCapacity(31); signalNameToNumberMap->add(signalNames[0], SIGHUP); signalNameToNumberMap->add(signalNames[1], SIGINT); signalNameToNumberMap->add(signalNames[2], SIGQUIT); @@ -527,13 +529,20 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam 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(); + signalNumberToNameMap->reserveInitialCapacity(31); signalNumberToNameMap->add(SIGHUP, signalNames[0]); signalNumberToNameMap->add(SIGINT, signalNames[1]); signalNumberToNameMap->add(SIGQUIT, signalNames[2]); @@ -563,7 +572,9 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam signalNumberToNameMap->add(SIGPROF, signalNames[26]); signalNumberToNameMap->add(SIGWINCH, signalNames[27]); signalNumberToNameMap->add(SIGIO, signalNames[28]); - signalNumberToNameMap->add(SIGINFO, signalNames[29]); +#ifdef SIGINFO + signalNameToNumberMap->add(signalNames[29], SIGINFO); +#endif signalNumberToNameMap->add(SIGSYS, signalNames[30]); }); @@ -610,7 +621,12 @@ void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventNam if (signalToContextIdsMap.find(signalNumber) != signalToContextIdsMap.end()) { Vector contextIds = signalToContextIdsMap.get(signalNumber); contextIds.removeFirst(contextId); - signalToContextIdsMap.set(signalNumber, contextIds); + if (contextIds.isEmpty()) { + signal(signalNumber, SIG_DFL); + signalToContextIdsMap.remove(signalNumber); + } else { + signalToContextIdsMap.set(signalNumber, contextIds); + } } } } @@ -627,8 +643,6 @@ void Process::emitSignalEvent(int signalNumber) Process::~Process() { - for (auto& listener : this->wrapped().eventListenerMap().entries()) { - } } JSC_DEFINE_HOST_FUNCTION(Process_functionAbort, (JSGlobalObject * globalObject, CallFrame*)) From aaad2f959bf574f99ae289296110646a30c04f3f Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Sat, 8 Jul 2023 03:09:05 -0700 Subject: [PATCH 17/23] Update call-raise.js --- test/js/node/process/call-raise.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/js/node/process/call-raise.js b/test/js/node/process/call-raise.js index 102023f22211d..898906759e87d 100644 --- a/test/js/node/process/call-raise.js +++ b/test/js/node/process/call-raise.js @@ -3,7 +3,7 @@ import { dlopen } from "bun:ffi"; var lazyRaise; export function raise(signal) { if (!lazyRaise) { - const suffix = process.platform === "darwin" ? "dylib" : "so"; + const suffix = process.platform === "darwin" ? "dylib" : "so.6"; lazyRaise = dlopen(`libc.${suffix}`, { raise: { args: ["int"], From 15ec56bf43936a5ab68ed37232bad3e7bed7ebe2 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:20:32 -0700 Subject: [PATCH 18/23] Create process-signal-handler.fixture.js --- .../process/process-signal-handler.fixture.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/js/node/process/process-signal-handler.fixture.js 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 0000000000000..35440db60eeae --- /dev/null +++ b/test/js/node/process/process-signal-handler.fixture.js @@ -0,0 +1,25 @@ +import { raise } from "./call-raise"; + +function done() { + console.log("PASS"); + process.exit(0); +} + +const SIGUSR1 = { + ["linux"]: 10, + ["darwin"]: 30, + ["win32"]: 16, +}[process.platform]; + +switch (process.argv.at(-1)) { + case "SIGUSR1": { + process.on("SIGUSR1", () => { + done(); + }); + raise(SIGUSR1); + break; + } + default: { + throw new Error("Unknown argument: " + process.argv.at(-1)); + } +} From af9b43dfa03272a2ef4347dec17880ba9d54070c Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:37:01 -0700 Subject: [PATCH 19/23] Don't register duplicates --- src/bun.js/bindings/Process.cpp | 72 ++++++++++--------- .../process/process-signal-handler.fixture.js | 19 ++++- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 2fdda03db8b81..cb38d91d7795d 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -441,7 +441,7 @@ static HashMap* signalNameToNumberMap = nullptr; static HashMap* signalNumberToNameMap = nullptr; // signal number to array of script execution context ids that care about the signal -static HashMap>* signalToContextIdsMap = nullptr; +static HashMap>* signalToContextIdsMap = nullptr; static Lock signalToContextIdsMapLock; static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded) @@ -564,63 +564,65 @@ static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& e }); if (!signalToContextIdsMap) { - signalToContextIdsMap = new HashMap>(); + signalToContextIdsMap = new HashMap>(); } if (isAdded) { if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) { uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier(); Locker lock { signalToContextIdsMapLock }; - if (signalToContextIdsMap->find(signalNumber) == signalToContextIdsMap->end()) { - Vector contextIds; - contextIds.append(contextId); - signalToContextIdsMap->add(signalNumber, contextIds); - } else { - Vector contextIds = signalToContextIdsMap->get(signalNumber); - contextIds.append(contextId); + if (!signalToContextIdsMap->contains(signalNumber)) { + HashSet contextIds; + contextIds.add(contextId); signalToContextIdsMap->set(signalNumber, contextIds); - } - lock.unlockEarly(); + lock.unlockEarly(); - struct sigaction action; - memset(&action, 0, sizeof(struct sigaction)); + 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; + // Set the handler in the action struct + action.sa_handler = [](int signalNumber) { + if (UNLIKELY(signalNumberToNameMap->find(signalNumber) == signalNumberToNameMap->end())) + return; - if (UNLIKELY(signalToContextIdsMap->find(signalNumber) == signalToContextIdsMap->end())) - return; - Vector contextIds = signalToContextIdsMap->get(signalNumber); + if (UNLIKELY(signalToContextIdsMap->find(signalNumber) == signalToContextIdsMap->end())) + return; + auto contextIds = signalToContextIdsMap->get(signalNumber); - for (int contextId : contextIds) { - auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId); + for (int contextId : contextIds) { + auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId); + if (UNLIKELY(!context)) + continue; - JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject(); - Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); + JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject(); + Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); - Process* process = jsCast(globalObject->processObject()); + Process* process = jsCast(globalObject->processObject()); - context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber); - } - }; + context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber); + } + }; - // Clear the sa_mask - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, signalNumber); - action.sa_flags = SA_RESTART; + // Clear the sa_mask + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, signalNumber); + action.sa_flags = SA_RESTART; - sigaction(signalNumber, &action, nullptr); + 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()) { - Vector contextIds = signalToContextIdsMap->get(signalNumber); - contextIds.removeFirst(contextId); + HashSet contextIds = signalToContextIdsMap->get(signalNumber); + contextIds.remove(contextId); if (contextIds.isEmpty()) { signal(signalNumber, SIG_DFL); signalToContextIdsMap->remove(signalNumber); diff --git a/test/js/node/process/process-signal-handler.fixture.js b/test/js/node/process/process-signal-handler.fixture.js index 35440db60eeae..7b436630e9523 100644 --- a/test/js/node/process/process-signal-handler.fixture.js +++ b/test/js/node/process/process-signal-handler.fixture.js @@ -1,8 +1,20 @@ import { raise } from "./call-raise"; +var counter = 0; function done() { - console.log("PASS"); - process.exit(0); + counter++; + if (counter === 2) { + setTimeout(() => { + if (counter !== 2) { + console.log(counter); + console.log("FAIL"); + process.exit(1); + } + + console.log("PASS"); + process.exit(0); + }, 1); + } } const SIGUSR1 = { @@ -13,6 +25,9 @@ const SIGUSR1 = { switch (process.argv.at(-1)) { case "SIGUSR1": { + process.on("SIGUSR1", () => { + done(); + }); process.on("SIGUSR1", () => { done(); }); From 66d630cee1d2fa3e46c2e998bb81f2104a87d05b Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:38:33 -0700 Subject: [PATCH 20/23] Add missing lock --- src/bun.js/bindings/Process.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index cb38d91d7795d..f9fb85b95532c 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -586,6 +586,7 @@ static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& e if (UNLIKELY(signalNumberToNameMap->find(signalNumber) == signalNumberToNameMap->end())) return; + Locker lock { signalToContextIdsMapLock }; if (UNLIKELY(signalToContextIdsMap->find(signalNumber) == signalToContextIdsMap->end())) return; auto contextIds = signalToContextIdsMap->get(signalNumber); From fb8656dd3fa8530407350015eeec85113aed1691 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Mon, 10 Jul 2023 12:21:33 -0700 Subject: [PATCH 21/23] another test --- .../process/process-signal-handler.fixture.js | 22 ++++++++++++++----- test/js/node/process/process.test.js | 9 ++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/test/js/node/process/process-signal-handler.fixture.js b/test/js/node/process/process-signal-handler.fixture.js index 7b436630e9523..7782040353b11 100644 --- a/test/js/node/process/process-signal-handler.fixture.js +++ b/test/js/node/process/process-signal-handler.fixture.js @@ -1,3 +1,4 @@ +import os from "os"; import { raise } from "./call-raise"; var counter = 0; @@ -17,11 +18,8 @@ function done() { } } -const SIGUSR1 = { - ["linux"]: 10, - ["darwin"]: 30, - ["win32"]: 16, -}[process.platform]; +const SIGUSR1 = os.constants.signals.SIGUSR1; +const SIGUSR2 = os.constants.signals.SIGUSR2; switch (process.argv.at(-1)) { case "SIGUSR1": { @@ -34,6 +32,20 @@ switch (process.argv.at(-1)) { raise(SIGUSR1); break; } + case "SIGUSR2": { + var callbackSIGUSR2 = false; + process.on("SIGUSR2", () => { + callbackSIGUSR2 = true; + }); + process.emit("SIGUSR2"); + if (!callbackSIGUSR2) { + console.log("FAIL"); + process.exit(1); + } + + console.log("PASS"); + process.exit(0); + } 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 e4ecfb19f1fb7..51825b2b43d9e 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -389,6 +389,15 @@ describe("signal", () => { 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"); }); From e19d64fa8b9e552606bee51d7f079df9696e62ce Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Mon, 10 Jul 2023 12:43:17 -0700 Subject: [PATCH 22/23] update test --- .../process/process-signal-handler.fixture.js | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/test/js/node/process/process-signal-handler.fixture.js b/test/js/node/process/process-signal-handler.fixture.js index 7782040353b11..de5a78bda6096 100644 --- a/test/js/node/process/process-signal-handler.fixture.js +++ b/test/js/node/process/process-signal-handler.fixture.js @@ -18,6 +18,23 @@ function done() { } } +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; @@ -33,18 +50,12 @@ switch (process.argv.at(-1)) { break; } case "SIGUSR2": { - var callbackSIGUSR2 = false; process.on("SIGUSR2", () => { - callbackSIGUSR2 = true; + done2(); }); process.emit("SIGUSR2"); - if (!callbackSIGUSR2) { - console.log("FAIL"); - process.exit(1); - } - - console.log("PASS"); - process.exit(0); + raise(SIGUSR2); + break; } default: { throw new Error("Unknown argument: " + process.argv.at(-1)); From c8d0b9bef3131e2b3f199b1f5112484d7f9daaa5 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Mon, 10 Jul 2023 17:21:03 -0700 Subject: [PATCH 23/23] revert some changes --- src/bun.js/bindings/JSBundlerPlugin.cpp | 53 +++++++++++++++---------- src/bun.js/bindings/JSBundlerPlugin.h | 15 ++++++- src/bun.js/bindings/JSStringDecoder.cpp | 4 +- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index cae6a4b229abf..ec3933574e08e 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -54,7 +54,7 @@ void BundlerPlugin::NamespaceList::append(JSC::VM& vm, JSC::RegExp* filter, Stri nsGroup->append(WTFMove(regex)); } -bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const ZigString* namespaceStr, const ZigString* path, bool isOnLoad) +bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const BunString* namespaceStr, const BunString* path, bool isOnLoad) { constexpr bool usesPatternContextBuffer = false; if (isOnLoad) { @@ -62,7 +62,7 @@ bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const ZigString* namespac return false; // Avoid unnecessary string copies - auto namespaceString = namespaceStr ? Zig::toString(*namespaceStr) : String(); + auto namespaceString = namespaceStr ? Bun::toWTFString(*namespaceStr) : String(); auto* group = this->onLoad.group(namespaceString); if (group == nullptr) { @@ -70,7 +70,7 @@ bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const ZigString* namespac } auto& filters = *group; - auto pathString = Zig::toString(*path); + auto pathString = Bun::toWTFString(*path); for (auto& filter : filters) { Yarr::MatchingContextHolder regExpContext(vm, usesPatternContextBuffer, nullptr, Yarr::MatchFrom::CompilerThread); @@ -84,14 +84,14 @@ bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const ZigString* namespac return false; // Avoid unnecessary string copies - auto namespaceString = namespaceStr ? Zig::toString(*namespaceStr) : String(); + auto namespaceString = namespaceStr ? Bun::toWTFString(*namespaceStr) : String(); auto* group = this->onResolve.group(namespaceString); if (group == nullptr) { return false; } - auto pathString = Zig::toString(*path); + auto pathString = Bun::toWTFString(*path); auto& filters = *group; for (auto& filter : filters) { @@ -115,9 +115,19 @@ static const HashTableValue JSBundlerPluginHashTable[] = { class JSBundlerPlugin final : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; - static JSBundlerPlugin* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* config, BunPluginTarget target) + static JSBundlerPlugin* create(JSC::VM& vm, + JSC::JSGlobalObject* globalObject, + JSC::Structure* structure, + void* config, + BunPluginTarget target, + JSBundlerPluginAddErrorCallback addError = JSBundlerPlugin__addError, + JSBundlerPluginOnLoadAsyncCallback onLoadAsync = JSBundlerPlugin__onLoadAsync, + JSBundlerPluginOnResolveAsyncCallback onResolveAsync = JSBundlerPlugin__onResolveAsync) { - JSBundlerPlugin* ptr = new (NotNull, JSC::allocateCell(vm)) JSBundlerPlugin(vm, globalObject, structure, config, target); + JSBundlerPlugin* ptr = new (NotNull, JSC::allocateCell(vm)) JSBundlerPlugin(vm, globalObject, structure, config, target, + addError, + onLoadAsync, + onResolveAsync); ptr->finishCreation(vm); return ptr; } @@ -147,9 +157,10 @@ class JSBundlerPlugin final : public JSC::JSNonFinalObject { JSC::LazyProperty setupFunction; private: - JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target) + 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)) + , plugin(BundlerPlugin(config, target, addError, onLoadAsync, onResolveAsync)) { } @@ -199,7 +210,7 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_addError, (JSC::JSGlobalObject { JSBundlerPlugin* thisObject = jsCast(callFrame->thisValue()); if (!thisObject->plugin.tombstoned) { - JSBundlerPlugin__addError( + thisObject->plugin.addError( UNWRAP_BUNDLER_PLUGIN(callFrame), thisObject->plugin.config, JSValue::encode(callFrame->argument(1)), @@ -212,7 +223,7 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_onLoadAsync, (JSC::JSGlobalObje { JSBundlerPlugin* thisObject = jsCast(callFrame->thisValue()); if (!thisObject->plugin.tombstoned) { - JSBundlerPlugin__onLoadAsync( + thisObject->plugin.onLoadAsync( UNWRAP_BUNDLER_PLUGIN(callFrame), thisObject->plugin.config, JSValue::encode(callFrame->argument(1)), @@ -225,7 +236,7 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_onResolveAsync, (JSC::JSGlobalO { JSBundlerPlugin* thisObject = jsCast(callFrame->thisValue()); if (!thisObject->plugin.tombstoned) { - JSBundlerPlugin__onResolveAsync( + thisObject->plugin.onResolveAsync( UNWRAP_BUNDLER_PLUGIN(callFrame), thisObject->plugin.config, JSValue::encode(callFrame->argument(1)), @@ -274,15 +285,15 @@ void JSBundlerPlugin::finishCreation(JSC::VM& vm) reifyStaticProperties(vm, JSBundlerPlugin::info(), JSBundlerPluginHashTable, *this); } -extern "C" bool JSBundlerPlugin__anyMatches(Bun::JSBundlerPlugin* pluginObject, const ZigString* namespaceString, const ZigString* path, bool isOnLoad) +extern "C" bool JSBundlerPlugin__anyMatches(Bun::JSBundlerPlugin* pluginObject, const BunString* namespaceString, const BunString* path, bool isOnLoad) { return pluginObject->plugin.anyMatchesCrossThread(pluginObject->vm(), namespaceString, path, isOnLoad); } -extern "C" void JSBundlerPlugin__matchOnLoad(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const ZigString* namespaceString, const ZigString* path, void* context, uint8_t defaultLoaderId) +extern "C" void JSBundlerPlugin__matchOnLoad(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const BunString* namespaceString, const BunString* path, void* context, uint8_t defaultLoaderId) { - WTF::String namespaceStringStr = namespaceString ? Zig::toStringCopy(*namespaceString) : WTF::String(); - WTF::String pathStr = path ? Zig::toStringCopy(*path) : WTF::String(); + WTF::String namespaceStringStr = namespaceString ? Bun::toWTFString(*namespaceString) : WTF::String(); + WTF::String pathStr = path ? Bun::toWTFString(*path) : WTF::String(); JSFunction* function = plugin->onLoadFunction.get(plugin); if (UNLIKELY(!function)) @@ -306,7 +317,7 @@ extern "C" void JSBundlerPlugin__matchOnLoad(JSC::JSGlobalObject* globalObject, auto exception = scope.exception(); scope.clearException(); if (!plugin->plugin.tombstoned) { - JSBundlerPlugin__addError( + plugin->plugin.addError( context, plugin->plugin.config, JSC::JSValue::encode(exception), @@ -315,14 +326,14 @@ extern "C" void JSBundlerPlugin__matchOnLoad(JSC::JSGlobalObject* globalObject, } } -extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const ZigString* namespaceString, const ZigString* path, const ZigString* importer, void* context, uint8_t kindId) +extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const BunString* namespaceString, const BunString* path, const BunString* importer, void* context, uint8_t kindId) { - WTF::String namespaceStringStr = namespaceString ? Zig::toStringCopy(*namespaceString) : WTF::String("file"_s); + WTF::String namespaceStringStr = namespaceString ? Bun::toWTFString(*namespaceString) : WTF::String("file"_s); if (namespaceStringStr.length() == 0) { namespaceStringStr = WTF::String("file"_s); } - WTF::String pathStr = path ? Zig::toStringCopy(*path) : WTF::String(); - WTF::String importerStr = importer ? Zig::toStringCopy(*importer) : WTF::String(); + WTF::String pathStr = path ? Bun::toWTFString(*path) : WTF::String(); + WTF::String importerStr = importer ? Bun::toWTFString(*importer) : WTF::String(); auto& vm = globalObject->vm(); JSFunction* function = plugin->onResolveFunction.get(plugin); diff --git a/src/bun.js/bindings/JSBundlerPlugin.h b/src/bun.js/bindings/JSBundlerPlugin.h index 08aa1d14033b1..4d82cdc1bbaac 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.h +++ b/src/bun.js/bindings/JSBundlerPlugin.h @@ -9,6 +9,10 @@ #include #include +typedef void (*JSBundlerPluginAddErrorCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); +typedef void (*JSBundlerPluginOnLoadAsyncCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); +typedef void (*JSBundlerPluginOnResolveAsyncCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::EncodedJSValue); + namespace Bun { using namespace JSC; @@ -42,10 +46,13 @@ class BundlerPlugin final { }; public: - bool anyMatchesCrossThread(JSC::VM&, const ZigString* namespaceStr, const ZigString* path, bool isOnLoad); + bool anyMatchesCrossThread(JSC::VM&, const BunString* namespaceStr, const BunString* path, bool isOnLoad); void tombstone() { tombstoned = true; } - BundlerPlugin(void* config, BunPluginTarget target) + BundlerPlugin(void* config, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync) + : addError(addError) + , onLoadAsync(onLoadAsync) + , onResolveAsync(onResolveAsync) { this->target = target; this->config = config; @@ -54,6 +61,10 @@ class BundlerPlugin final { NamespaceList onLoad = {}; NamespaceList onResolve = {}; BunPluginTarget target { BunPluginTargetBrowser }; + + JSBundlerPluginAddErrorCallback addError; + JSBundlerPluginOnLoadAsyncCallback onLoadAsync; + JSBundlerPluginOnResolveAsyncCallback onResolveAsync; void* config { nullptr }; bool tombstoned { false }; }; diff --git a/src/bun.js/bindings/JSStringDecoder.cpp b/src/bun.js/bindings/JSStringDecoder.cpp index 5ec2585223f3a..b8c2dd50cd1e6 100644 --- a/src/bun.js/bindings/JSStringDecoder.cpp +++ b/src/bun.js/bindings/JSStringDecoder.cpp @@ -129,7 +129,7 @@ uint8_t JSStringDecoder::utf8CheckIncomplete(uint8_t* bufPtr, uint32_t length, u m_lastNeed = nb - 1; return nb; } - if (--j < i || nb == -2) + if (j == 0 || --j < i || nb == -2) return 0; nb = utf8CheckByte(bufPtr[j]); if (nb >= 0) { @@ -137,7 +137,7 @@ uint8_t JSStringDecoder::utf8CheckIncomplete(uint8_t* bufPtr, uint32_t length, u m_lastNeed = nb - 2; return nb; } - if (--j < i || nb == -2) + if (j == 0 || --j < i || nb == -2) return 0; nb = utf8CheckByte(bufPtr[j]); if (nb >= 0) {