diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index cc2c932e12..56ea989b5d 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -703,18 +703,6 @@ pub const Subprocess = struct { return .undefined; } - pub fn onStdinDestroyed(this: *Subprocess) void { - this.island.clearStdin(); - - if (this.island.subprocess == null) { - // if the process has already been garbage collected, we can free the memory now - bun.default_allocator.destroy(this); - } else { - // otherwise update the pending activity flag - this.updateHasPendingActivity(); - } - } - pub fn doSend(this: *Subprocess, global: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSValue { const ipc_data = &(this.ipc_data orelse { if (this.hasExited()) { @@ -1180,13 +1168,6 @@ pub const Subprocess = struct { // When the stream has closed we need to be notified to prevent a use-after-free // We can test for this use-after-free by enabling hot module reloading on a file and then saving it twice pub fn onClose(this: *Writable, _: ?bun.sys.Error) void { - const process: *Subprocess = @fieldParentPtr("stdin", this); - - if (process.this_jsvalue != .zero) { - if (Subprocess.stdinGetCached(process.this_jsvalue)) |existing_value| { - JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0); - } - } switch (this.*) { .buffer => { @@ -1194,11 +1175,11 @@ pub const Subprocess = struct { }, .pipe => { this.pipe.deref(); + this.pipe.island.?.clearStdin(); }, else => {}, } - process.onStdinDestroyed(); this.* = .{ .ignore = {}, @@ -1230,6 +1211,7 @@ pub const Subprocess = struct { }, } pipe.writer.setParent(pipe); + pipe.island = island; island.stdin = pipe; return Writable{ @@ -1342,7 +1324,6 @@ pub const Subprocess = struct { } return pipe.toJSWithDestructor( globalThis, - JSC.WebCore.SinkDestructor.Ptr.init(subprocess.island), ); } }, @@ -1350,13 +1331,6 @@ pub const Subprocess = struct { } pub fn finalize(this: *Writable) void { - const subprocess: *Subprocess = @fieldParentPtr("stdin", this); - if (subprocess.this_jsvalue != .zero) { - if (JSC.Codegen.JSSubprocess.stdinGetCached(subprocess.this_jsvalue)) |existing_value| { - JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0); - } - } - return switch (this.*) { .pipe => |pipe| { if (pipe.signal.ptr == @as(*anyopaque, @ptrCast(this))) { @@ -1424,13 +1398,9 @@ pub const Subprocess = struct { this.stdin.buffer.close(); } - if (existing_stdin_value != .zero) { - JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_stdin_value, 0); - } - if (stdin) |pipe| { + _ = pipe; this.island.clearStdin(); - pipe.onAttachedProcessExit(); } var did_update_has_pending_activity = false; @@ -1559,10 +1529,7 @@ pub const Subprocess = struct { this.process.deref(); this.island.clearSubprocess(); - if (this.island.stdin == null) { - // if no file sink exists we can free immediately - bun.default_allocator.destroy(this); - } + bun.default_allocator.destroy(this); } pub fn getExited( @@ -2293,13 +2260,6 @@ pub const Subprocess = struct { subprocess: ?*Subprocess = null, stdin: ?*JSC.WebCore.FileSink = null, - pub fn onStdinDestroyed(this: *Island) void { - const had_subprocess = this.subprocess != null; - this.clearStdin(); - if (!had_subprocess) return; // dont uaf the next line - this.subprocess.?.onStdinDestroyed(); - } - pub fn clearSubprocess(this: *Island) void { this.subprocess = null; if (this.stdin == null) this.destroy(); diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index e16e72b349..dd007d4068 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -596,7 +596,7 @@ extern "C" JSC__JSValue SYSV_ABI Bun__Path__toNamespacedPath(JSC__JSGlobalObject #endif CPP_DECL JSC__JSValue ArrayBufferSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); -CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor); +CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1); CPP_DECL void ArrayBufferSink__detachPtr(JSC__JSValue JSValue0); CPP_DECL void* ArrayBufferSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL void ArrayBufferSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1); @@ -616,7 +616,7 @@ BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__write); #endif CPP_DECL JSC__JSValue HTTPSResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); -CPP_DECL JSC__JSValue HTTPSResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor); +CPP_DECL JSC__JSValue HTTPSResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1); CPP_DECL void HTTPSResponseSink__detachPtr(JSC__JSValue JSValue0); CPP_DECL void* HTTPSResponseSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL void HTTPSResponseSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1); @@ -636,7 +636,7 @@ BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__write); #endif CPP_DECL JSC__JSValue HTTPResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); -CPP_DECL JSC__JSValue HTTPResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor); +CPP_DECL JSC__JSValue HTTPResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1); CPP_DECL void HTTPResponseSink__detachPtr(JSC__JSValue JSValue0); CPP_DECL void* HTTPResponseSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL void HTTPResponseSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1); @@ -656,7 +656,7 @@ BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__write); #endif CPP_DECL JSC__JSValue FileSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); -CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor); +CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1); CPP_DECL void FileSink__detachPtr(JSC__JSValue JSValue0); CPP_DECL void* FileSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL void FileSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1); @@ -677,7 +677,7 @@ BUN_DECLARE_HOST_FUNCTION(FileSink__write); #endif CPP_DECL JSC__JSValue FileSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); -CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor); +CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1); CPP_DECL void FileSink__detachPtr(JSC__JSValue JSValue0); CPP_DECL void* FileSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL void FileSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index e718de6863..5083d4c2b9 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -357,30 +357,26 @@ pub extern fn Zig__GlobalObject__create(arg0: ?*anyopaque, arg1: i32, arg2: bool pub extern fn Zig__GlobalObject__getModuleRegistryMap(arg0: *bindings.JSGlobalObject) ?*anyopaque; pub extern fn Zig__GlobalObject__resetModuleRegistryMap(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) bool; pub extern fn ArrayBufferSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue; -pub extern fn ArrayBufferSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue; +pub extern fn ArrayBufferSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue; pub extern fn ArrayBufferSink__detachPtr(JSValue0: JSC__JSValue) void; -pub extern fn ArrayBufferSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void; pub extern fn ArrayBufferSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque; pub extern fn ArrayBufferSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void; pub extern fn ArrayBufferSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void; pub extern fn HTTPSResponseSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue; -pub extern fn HTTPSResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue; +pub extern fn HTTPSResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue; pub extern fn HTTPSResponseSink__detachPtr(JSValue0: JSC__JSValue) void; -pub extern fn HTTPSResponseSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void; pub extern fn HTTPSResponseSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque; pub extern fn HTTPSResponseSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void; pub extern fn HTTPSResponseSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void; pub extern fn HTTPResponseSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue; -pub extern fn HTTPResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue; +pub extern fn HTTPResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue; pub extern fn HTTPResponseSink__detachPtr(JSValue0: JSC__JSValue) void; -pub extern fn HTTPResponseSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void; pub extern fn HTTPResponseSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque; pub extern fn HTTPResponseSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void; pub extern fn HTTPResponseSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void; pub extern fn FileSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue; -pub extern fn FileSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue; +pub extern fn FileSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue; pub extern fn FileSink__detachPtr(JSValue0: JSC__JSValue) void; -pub extern fn FileSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void; pub extern fn FileSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque; pub extern fn FileSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void; pub extern fn FileSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void; diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index afb9d9dad4..60de2e9760 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -1528,40 +1528,6 @@ const AutoFlusher = struct { } }; -pub const SinkDestructor = struct { - const Detached = opaque {}; - const Subprocess = JSC.API.Bun.Subprocess; - pub const Ptr = bun.TaggedPointerUnion(.{ - Detached, - Subprocess.Island, - }); - - pub export fn Bun__onSinkDestroyed( - ptr_value: ?*anyopaque, - sink_ptr: ?*anyopaque, - ) callconv(.C) void { - _ = sink_ptr; // autofix - const ptr = Ptr.from(ptr_value); - - if (ptr.isNull()) { - return; - } - - switch (ptr.tag()) { - .Detached => { - return; - }, - .Island => { - const island = ptr.as(Subprocess.Island); - island.onStdinDestroyed(); - }, - else => { - Output.debugWarn("Unknown sink type", .{}); - }, - } - } -}; - pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return struct { sink: SinkType, @@ -1610,10 +1576,10 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return shim.cppFn("onStart", .{ ptr, globalThis }); } - pub fn createObject(globalThis: *JSGlobalObject, object: *anyopaque, destructor: usize) callconv(.C) JSValue { + pub fn createObject(globalThis: *JSGlobalObject, object: *anyopaque) callconv(.C) JSValue { JSC.markBinding(@src()); - return shim.cppFn("createObject", .{ globalThis, object, destructor }); + return shim.cppFn("createObject", .{ globalThis, object }); } pub fn fromJS(globalThis: *JSGlobalObject, value: JSValue) ?*anyopaque { @@ -1622,12 +1588,6 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return shim.cppFn("fromJS", .{ globalThis, value }); } - pub fn setDestroyCallback(value: JSValue, callback: usize) void { - JSC.markBinding(@src()); - - return shim.cppFn("setDestroyCallback", .{ value, callback }); - } - pub fn construct(globalThis: *JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) JSValue { JSC.markBinding(@src()); @@ -1651,7 +1611,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return .undefined; }; this.sink.construct(allocator); - return createObject(globalThis, this, 0); + return createObject(globalThis, this); } pub fn finalize(ptr: *anyopaque) callconv(.C) void { @@ -2954,6 +2914,7 @@ pub const FileSink = struct { is_socket: bool = false, fd: bun.FileDescriptor = bun.invalid_fd, has_js_called_unref: bool = false, + island: ?*JSC.Subprocess.Island = null, const log = Output.scoped(.FileSink, false); @@ -3367,11 +3328,11 @@ pub const FileSink = struct { } pub fn toJS(this: *FileSink, globalThis: *JSGlobalObject) JSValue { - return JSSink.createObject(globalThis, this, 0); + return JSSink.createObject(globalThis, this); } - pub fn toJSWithDestructor(this: *FileSink, globalThis: *JSGlobalObject, destructor: ?SinkDestructor.Ptr) JSValue { - return JSSink.createObject(globalThis, this, if (destructor) |dest| @intFromPtr(dest.ptr()) else 0); + pub fn toJSWithDestructor(this: *FileSink, globalThis: *JSGlobalObject) JSValue { + return JSSink.createObject(globalThis, this); } pub fn endFromJS(this: *FileSink, globalThis: *JSGlobalObject) JSC.Maybe(JSValue) { diff --git a/src/codegen/generate-jssink.ts b/src/codegen/generate-jssink.ts index 69e0699164..8f63581fca 100644 --- a/src/codegen/generate-jssink.ts +++ b/src/codegen/generate-jssink.ts @@ -64,7 +64,7 @@ function header() { class ${className} final : public JSC::JSDestructibleObject { public: using Base = JSC::JSDestructibleObject; - static ${className}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t destructor = 0); + static ${className}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr); static constexpr SinkID Sink = SinkID::${name}; DECLARE_EXPORT_INFO; @@ -106,13 +106,11 @@ function header() { void* m_sinkPtr; int m_refCount { 1 }; - uintptr_t m_onDestroy { 0 }; - ${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) + ${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) : Base(vm, structure) { m_sinkPtr = sinkPtr; - m_onDestroy = onDestroy; } void finishCreation(JSC::VM&); @@ -123,7 +121,7 @@ function header() { class ${controller} final : public JSC::JSDestructibleObject { public: using Base = JSC::JSDestructibleObject; - static ${controller}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy); + static ${controller}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr); static constexpr SinkID Sink = SinkID::${name}; DECLARE_EXPORT_INFO; @@ -162,13 +160,10 @@ function header() { mutable WriteBarrier m_onClose; mutable JSC::Weak m_weakReadableStream; - uintptr_t m_onDestroy { 0 }; - - ${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) + ${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) : Base(vm, structure) { m_sinkPtr = sinkPtr; - m_onDestroy = onDestroy; } void finishCreation(JSC::VM&); @@ -566,10 +561,6 @@ const ClassInfo ${controller}::s_info = { "${controllerName}"_s, &Base::s_info, ${className}::~${className}() { - if (m_onDestroy) { - Bun__onSinkDestroyed(m_onDestroy, m_sinkPtr); - } - if (m_sinkPtr) { ${name}__finalize(m_sinkPtr); } @@ -578,10 +569,6 @@ ${className}::~${className}() ${controller}::~${controller}() { - if (m_onDestroy) { - Bun__onSinkDestroyed(m_onDestroy, m_sinkPtr); - } - if (m_sinkPtr) { ${name}__finalize(m_sinkPtr); } @@ -598,12 +585,6 @@ JSObject* JS${controllerName}::createPrototype(VM& vm, JSDOMGlobalObject& global } void JS${controllerName}::detach() { - if (m_onDestroy) { - auto destroy = m_onDestroy; - m_onDestroy = 0; - Bun__onSinkDestroyed(destroy, m_sinkPtr); - } - m_sinkPtr = nullptr; m_onPull.clear(); @@ -635,16 +616,16 @@ ${constructor}* ${constructor}::create(JSC::VM& vm, JSC::JSGlobalObject* globalO return ptr; } -${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) +${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr) { - ${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr, onDestroy); + ${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr); ptr->finishCreation(vm); return ptr; } -${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) +${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr) { - ${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr, onDestroy); + ${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr); ptr->finishCreation(vm); return ptr; } @@ -697,16 +678,6 @@ void ${controller}::finishCreation(VM& vm) ASSERT(inherits(info())); } -extern "C" void ${name}__setDestroyCallback(EncodedJSValue encodedValue, uintptr_t callback) -{ - JSValue value = JSValue::decode(encodedValue); - if (auto* sink = JSC::jsDynamicCast(value)) { - sink->m_onDestroy = callback; - } else if (auto* controller = JSC::jsDynamicCast(value)) { - controller->m_onDestroy = callback; - } -} - void ${className}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) { auto* thisObject = jsCast<${className}*>(cell); @@ -844,12 +815,12 @@ default: const { className, controller, prototypeName, controllerPrototypeName, constructor } = names(name); templ += ` -extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr, uintptr_t destructor) +extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr) { auto& vm = arg0->vm(); Zig::GlobalObject* globalObject = reinterpret_cast(arg0); JSC::Structure* structure = globalObject->${name}Structure(); - return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr, destructor)); + return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr)); } extern "C" void* ${name}__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) @@ -883,7 +854,7 @@ extern "C" JSC__JSValue ${name}__assignToStream(JSC__JSGlobalObject* arg0, JSC__ Zig::GlobalObject* globalObject = reinterpret_cast(arg0); JSC::Structure* structure = WebCore::getDOMStructure(vm, *globalObject); - WebCore::${controller} *controller = WebCore::${controller}::create(vm, globalObject, structure, sinkPtr, 0); + WebCore::${controller} *controller = WebCore::${controller}::create(vm, globalObject, structure, sinkPtr); *controllerValue = reinterpret_cast(JSC::JSValue::encode(controller)); return globalObject->assignToStream(JSC::JSValue::decode(stream), controller); } diff --git a/src/shell/subproc.zig b/src/shell/subproc.zig index b8583e4fef..bd4f249064 100644 --- a/src/shell/subproc.zig +++ b/src/shell/subproc.zig @@ -246,36 +246,7 @@ pub const ShellSubprocess = struct { } } - pub fn toJS(this: *Writable, globalThis: *JSC.JSGlobalObject, subprocess: *Subprocess) JSValue { - return switch (this.*) { - .fd => |fd| JSValue.jsNumber(fd), - .memfd, .ignore => JSValue.jsUndefined(), - .buffer, .inherit => JSValue.jsUndefined(), - .pipe => |pipe| { - this.* = .{ .ignore = {} }; - if (subprocess.process.hasExited() and !subprocess.flags.has_stdin_destructor_called) { - pipe.onAttachedProcessExit(); - return pipe.toJS(globalThis); - } else { - subprocess.flags.has_stdin_destructor_called = false; - subprocess.weak_file_sink_stdin_ptr = pipe; - return pipe.toJSWithDestructor( - globalThis, - JSC.WebCore.SinkDestructor.Ptr.init(subprocess), - ); - } - }, - }; - } - pub fn finalize(this: *Writable) void { - const subprocess: *Subprocess = @fieldParentPtr("stdin", this); - if (subprocess.this_jsvalue != .zero) { - if (JSC.Codegen.JSSubprocess.stdinGetCached(subprocess.this_jsvalue)) |existing_value| { - JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0); - } - } - return switch (this.*) { .pipe => |pipe| { pipe.deref(); diff --git a/test/js/bun/spawn/spawn-stdin-regression.js b/test/js/bun/spawn/spawn-stdin-regression.js new file mode 100644 index 0000000000..003a0aa7ed --- /dev/null +++ b/test/js/bun/spawn/spawn-stdin-regression.js @@ -0,0 +1,31 @@ +const jsc = require("bun:jsc"); + +for (let i = 0; i < 30; i++) { + test(`this used to crash :: ${i}`, async () => { + const buffer = Buffer.alloc(1024 * 1024, "a"); + + async function getStdin() { + { + let subprocess = Bun.spawn({ + cmd: [process.argv0, "-e", "Bun.sleep(100)"], + stdio: ["pipe", "ignore", "ignore"], + }); + subprocess.unref(); + subprocess.stdin.write(buffer); + process.kill(subprocess.pid, "SIGKILL"); + await subprocess.stdin.end().catch(() => {}); + } + } + + async function iter() { + await getStdin(); + } + + await Promise.all(Array.from({ length: 200 }, () => iter())); + Bun.gc(true); + await Bun.sleep(10); + const { objectTypeCounts } = jsc.heapStats(); + console.log("objectTypeCounts:", objectTypeCounts.FileSink, objectTypeCounts.Subprocess); + console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB"); + }); +}