diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 61fe743270..970d963d32 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -2030,13 +2030,14 @@ enum class BunProcessStdinFdType : int32_t { extern "C" BunProcessStdinFdType Bun__Process__getStdinFdType(void*, int fd); extern "C" void Bun__ForceFileSinkToBeSynchronousForProcessObjectStdio(JSC::JSGlobalObject*, JSC::EncodedJSValue); -static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int fd) +static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, JSC::JSObject* processObject, int fd) { auto& vm = JSC::getVM(globalObject); auto scope = DECLARE_CATCH_SCOPE(vm); JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject); JSC::MarkedArgumentBuffer args; + args.append(processObject); args.append(JSC::jsNumber(fd)); args.append(jsBoolean(bun_stdio_tty[fd])); BunProcessStdinFdType fdType = Bun__Process__getStdinFdType(Bun::vm(vm), fd); @@ -2045,8 +2046,11 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int JSC::CallData callData = JSC::getCallData(getStdioWriteStream); auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject->globalThis(), args); - scope.assertNoExceptionExceptTermination(); - CLEAR_AND_RETURN_IF_EXCEPTION(scope, jsUndefined()); + if (auto* exception = scope.exception()) { + Zig::GlobalObject::reportUncaughtExceptionAtEventLoop(globalObject, exception); + scope.clearException(); + return jsUndefined(); + } ASSERT_WITH_MESSAGE(JSC::isJSArray(result), "Expected an array from getStdioWriteStream"); JSC::JSArray* resultObject = JSC::jsCast(result); @@ -2077,12 +2081,12 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int static JSValue constructStdout(VM& vm, JSObject* processObject) { - return constructStdioWriteStream(processObject->globalObject(), 1); + return constructStdioWriteStream(processObject->globalObject(), processObject, 1); } static JSValue constructStderr(VM& vm, JSObject* processObject) { - return constructStdioWriteStream(processObject->globalObject(), 2); + return constructStdioWriteStream(processObject->globalObject(), processObject, 2); } #if OS(WINDOWS) @@ -2092,17 +2096,22 @@ static JSValue constructStderr(VM& vm, JSObject* processObject) static JSValue constructStdin(VM& vm, JSObject* processObject) { auto* globalObject = processObject->globalObject(); - auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject); + auto scope = DECLARE_CATCH_SCOPE(vm); + JSC::JSFunction* getStdinStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject); JSC::MarkedArgumentBuffer args; + args.append(processObject); args.append(JSC::jsNumber(STDIN_FILENO)); args.append(jsBoolean(bun_stdio_tty[STDIN_FILENO])); BunProcessStdinFdType fdType = Bun__Process__getStdinFdType(Bun::vm(vm), STDIN_FILENO); args.append(jsNumber(static_cast(fdType))); - JSC::CallData callData = JSC::getCallData(getStdioWriteStream); + JSC::CallData callData = JSC::getCallData(getStdinStream); - auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject, args); - RETURN_IF_EXCEPTION(scope, {}); + auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdinStream, callData, globalObject, args); + if (auto* exception = scope.exception()) { + Zig::GlobalObject::reportUncaughtExceptionAtEventLoop(globalObject, exception); + scope.clearException(); + return jsUndefined(); + } return result; } diff --git a/src/js/builtins/ProcessObjectInternals.ts b/src/js/builtins/ProcessObjectInternals.ts index cbb2de0c74..16749f4adf 100644 --- a/src/js/builtins/ProcessObjectInternals.ts +++ b/src/js/builtins/ProcessObjectInternals.ts @@ -30,8 +30,13 @@ const enum BunProcessStdinFdType { socket = 2, } -export function getStdioWriteStream(fd, isTTY: boolean, _fdType: BunProcessStdinFdType) { - $assert(typeof fd === "number", `Expected fd to be a number, got ${typeof fd}`); +export function getStdioWriteStream( + process: typeof globalThis.process, + fd: number, + isTTY: boolean, + _fdType: BunProcessStdinFdType, +) { + $assert(fd === 1 || fd === 2, `Expected fd to be 1 or 2, got ${fd}`); let stream; if (isTTY) { @@ -74,9 +79,14 @@ export function getStdioWriteStream(fd, isTTY: boolean, _fdType: BunProcessStdin return [stream, underlyingSink]; } -export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType) { +export function getStdinStream( + process: typeof globalThis.process, + fd: number, + isTTY: boolean, + fdType: BunProcessStdinFdType, +) { + $assert(fd === 0); const native = Bun.stdin.stream(); - // @ts-expect-error const source = native.$bunNativePtr; var reader: ReadableStreamDefaultReader | undefined; @@ -246,7 +256,12 @@ export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType return stream; } -export function initializeNextTickQueue(process, nextTickQueue, drainMicrotasksFn, reportUncaughtExceptionFn) { +export function initializeNextTickQueue( + process: typeof globalThis.process, + nextTickQueue, + drainMicrotasksFn, + reportUncaughtExceptionFn, +) { var queue; var process; var nextTickQueue = nextTickQueue; diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index e52439ca29..317cb32480 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -1114,3 +1114,20 @@ it("should handle user assigned `default` properties", async () => { await promise; }); + +it.each(["stdin", "stdout", "stderr"])("%s stream accessor should handle exceptions without crashing", stream => { + expect([ + /* js */ ` + const old = process; + process = null; + try { + old.${stream}; + } catch {} + if (typeof old.${stream} !== "undefined") { + console.log("wrong"); + } + `, + "", + 1, + ]).toRunInlineFixture(); +});