diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 15c4118c53..d35e18b690 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -1489,6 +1489,40 @@ const AutoFlusher = struct { } }; +pub const SinkDestructor = struct { + const Detached = opaque {}; + const Subprocess = JSC.API.Bun.Subprocess; + pub const Ptr = bun.TaggedPointerUnion(.{ + Detached, + Subprocess, + }); + + 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; + }, + .Subprocess => { + const subprocess = ptr.as(Subprocess); + subprocess.onStdinDestroyed(); + }, + else => { + Output.debugWarn("Unknown sink type", .{}); + }, + } + } +}; + pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { return struct { sink: SinkType, diff --git a/src/codegen/generate-jssink.ts b/src/codegen/generate-jssink.ts index 455224c02e..4880220b95 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); + static ${className}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t destructor = 0); static constexpr SinkID Sink = SinkID::${name}; DECLARE_EXPORT_INFO; @@ -105,11 +105,14 @@ function header() { void* m_sinkPtr; int m_refCount { 1 }; + + uintptr_t m_onDestroy { 0 }; - ${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + ${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) : Base(vm, structure) { m_sinkPtr = sinkPtr; + m_onDestroy = onDestroy; } void finishCreation(JSC::VM&); @@ -120,7 +123,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); + static ${controller}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy); static constexpr SinkID Sink = SinkID::${name}; DECLARE_EXPORT_INFO; @@ -158,11 +161,14 @@ function header() { mutable WriteBarrier m_onPull; mutable WriteBarrier m_onClose; mutable JSC::Weak m_weakReadableStream; + + uintptr_t m_onDestroy { 0 }; - ${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + ${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) : Base(vm, structure) { m_sinkPtr = sinkPtr; + m_onDestroy = onDestroy; } void finishCreation(JSC::VM&); @@ -267,7 +273,7 @@ async function implementation() { #include #include - +extern "C" void Bun__onSinkDestroyed(uintptr_t destructor, void* sinkPtr); namespace WebCore { using namespace JSC; @@ -403,7 +409,6 @@ JSC_DEFINE_CUSTOM_GETTER(function${name}__getter, (JSC::JSGlobalObject * lexical return JSC::JSValue::encode(globalObject->${name}()); } - JSC_DECLARE_HOST_FUNCTION(${controller}__close); JSC_DEFINE_HOST_FUNCTION(${controller}__close, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame *callFrame)) { @@ -562,6 +567,10 @@ 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); } @@ -570,6 +579,10 @@ ${className}::~${className}() ${controller}::~${controller}() { + if (m_onDestroy) { + Bun__onSinkDestroyed(m_onDestroy, m_sinkPtr); + } + if (m_sinkPtr) { ${name}__finalize(m_sinkPtr); } @@ -586,6 +599,12 @@ 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(); @@ -617,16 +636,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) +${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) { - ${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr); + ${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr, onDestroy); ptr->finishCreation(vm); return ptr; } -${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr) +${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy) { - ${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr); + ${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr, onDestroy); ptr->finishCreation(vm); return ptr; } @@ -679,6 +698,15 @@ 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) { @@ -817,12 +845,12 @@ default: const { className, controller, prototypeName, controllerPrototypeName, constructor } = names(name); templ += ` -extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr) +extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr, uintptr_t destructor) { 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)); + return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr, destructor)); } extern "C" void* ${name}__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) @@ -857,7 +885,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); + WebCore::${controller} *controller = WebCore::${controller}::create(vm, globalObject, structure, sinkPtr, 0); *controllerValue = reinterpret_cast(JSC::JSValue::encode(controller)); return globalObject->assignToStream(JSC::JSValue::decode(stream), controller); }