diff options
| -rw-r--r-- | packages/bun-types/bun.d.ts | 37 | ||||
| -rw-r--r-- | src/bun.js/bindings/JSSink.cpp | 190 | ||||
| -rw-r--r-- | src/bun.js/bindings/JSSink.h | 18 | ||||
| -rw-r--r-- | src/bun.js/bindings/JSSinkLookupTable.h | 40 | ||||
| -rw-r--r-- | src/bun.js/bindings/headers.h | 12 | ||||
| -rw-r--r-- | src/bun.js/scripts/generate-jssink.js | 62 | ||||
| -rw-r--r-- | src/global.zig | 41 | ||||
| -rw-r--r-- | test/bun.js/spawn-streaming-stdin.test.ts | 32 | ||||
| -rw-r--r-- | test/bun.js/stdin-repro.js | 10 | ||||
| -rw-r--r-- | test/bun.js/streams.test.js | 12 | 
10 files changed, 382 insertions, 72 deletions
| diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index a0d9f8f15..ccdcf2c2c 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -411,6 +411,43 @@ declare module "bun" {       * Close the file descriptor. This also flushes the internal buffer.       */      end(error?: Error): number | Promise<number>; + +    start(options?: { +      /** +       * Preallocate an internal buffer of this size +       * This can significantly improve performance when the chunk size is small +       */ +      highWaterMark?: number; +    }): void; + +    /** +     * For FIFOs & pipes, this lets you decide whether Bun's process should +     * remain alive until the pipe is closed. +     * +     * By default, it is automatically managed. While the stream is open, the +     * process remains alive and once the other end hangs up or the stream +     * closes, the process exits. +     * +     * If you previously called {@link unref}, you can call this again to re-enable automatic management. +     * +     * Internally, it will reference count the number of times this is called. By default, that number is 1 +     * +     * If the file is not a FIFO or pipe, {@link ref} and {@link unref} do +     * nothing. If the pipe is already closed, this does nothing. +     */ +    ref(): void; + +    /** +     * For FIFOs & pipes, this lets you decide whether Bun's process should +     * remain alive until the pipe is closed. +     * +     * If you want to allow Bun's process to terminate while the stream is open, +     * call this. +     * +     * If the file is not a FIFO or pipe, {@link ref} and {@link unref} do +     * nothing. If the pipe is already closed, this does nothing. +     */ +    unref(): void;    }    /** diff --git a/src/bun.js/bindings/JSSink.cpp b/src/bun.js/bindings/JSSink.cpp index ed3b6a5ac..a93e4e298 100644 --- a/src/bun.js/bindings/JSSink.cpp +++ b/src/bun.js/bindings/JSSink.cpp @@ -1,6 +1,6 @@  // AUTO-GENERATED FILE. DO NOT EDIT. -// Generated by 'make generate-sink' at 2022-11-20T12:51:04.404Z +// Generated by 'make generate-sink' at 2022-11-25T07:36:18.561Z  // To regenerate this file, run:  //  //   make generate-sink @@ -127,6 +127,48 @@ JSC_DEFINE_HOST_FUNCTION(functionStartDirectStream, (JSC::JSGlobalObject * lexic      RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsUndefined()));  } +void JSArrayBufferSink::ref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount++; +    if (m_refCount == 1) { +        ArrayBufferSink__updateRef(m_sinkPtr, true); +    } +} + +void JSArrayBufferSink::unref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount = std::max(0, m_refCount - 1); +    if (!m_refCount) { +        ArrayBufferSink__updateRef(m_sinkPtr, false); +    } +} + +JSC_DEFINE_HOST_FUNCTION(ArrayBufferSink__ref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSArrayBufferSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->ref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(ArrayBufferSink__unref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSArrayBufferSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->unref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} +  JSC_DEFINE_CUSTOM_GETTER(functionArrayBufferSink__getter, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))  {      auto& vm = lexicalGlobalObject->vm(); @@ -203,6 +245,48 @@ JSC_DEFINE_HOST_FUNCTION(ArrayBufferSink__doClose, (JSC::JSGlobalObject * lexica      return JSC::JSValue::encode(JSC::jsUndefined());  } +void JSFileSink::ref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount++; +    if (m_refCount == 1) { +        FileSink__updateRef(m_sinkPtr, true); +    } +} + +void JSFileSink::unref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount = std::max(0, m_refCount - 1); +    if (!m_refCount) { +        FileSink__updateRef(m_sinkPtr, false); +    } +} + +JSC_DEFINE_HOST_FUNCTION(FileSink__ref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSFileSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->ref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(FileSink__unref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSFileSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->unref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} +  JSC_DEFINE_CUSTOM_GETTER(functionFileSink__getter, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))  {      auto& vm = lexicalGlobalObject->vm(); @@ -279,6 +363,48 @@ JSC_DEFINE_HOST_FUNCTION(FileSink__doClose, (JSC::JSGlobalObject * lexicalGlobal      return JSC::JSValue::encode(JSC::jsUndefined());  } +void JSHTTPResponseSink::ref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount++; +    if (m_refCount == 1) { +        HTTPResponseSink__updateRef(m_sinkPtr, true); +    } +} + +void JSHTTPResponseSink::unref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount = std::max(0, m_refCount - 1); +    if (!m_refCount) { +        HTTPResponseSink__updateRef(m_sinkPtr, false); +    } +} + +JSC_DEFINE_HOST_FUNCTION(HTTPResponseSink__ref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSHTTPResponseSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->ref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(HTTPResponseSink__unref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSHTTPResponseSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->unref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} +  JSC_DEFINE_CUSTOM_GETTER(functionHTTPResponseSink__getter, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))  {      auto& vm = lexicalGlobalObject->vm(); @@ -355,6 +481,48 @@ JSC_DEFINE_HOST_FUNCTION(HTTPResponseSink__doClose, (JSC::JSGlobalObject * lexic      return JSC::JSValue::encode(JSC::jsUndefined());  } +void JSHTTPSResponseSink::ref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount++; +    if (m_refCount == 1) { +        HTTPSResponseSink__updateRef(m_sinkPtr, true); +    } +} + +void JSHTTPSResponseSink::unref() +{ +    if (!m_sinkPtr) +        return; + +    m_refCount = std::max(0, m_refCount - 1); +    if (!m_refCount) { +        HTTPSResponseSink__updateRef(m_sinkPtr, false); +    } +} + +JSC_DEFINE_HOST_FUNCTION(HTTPSResponseSink__ref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSHTTPSResponseSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->ref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(HTTPSResponseSink__unref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::JSHTTPSResponseSink*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->unref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); +} +  JSC_DEFINE_CUSTOM_GETTER(functionHTTPSResponseSink__getter, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))  {      auto& vm = lexicalGlobalObject->vm(); @@ -440,6 +608,8 @@ JSC_DEFINE_HOST_FUNCTION(HTTPSResponseSink__doClose, (JSC::JSGlobalObject * lexi    end        ArrayBufferSink__end             ReadOnly|DontDelete|Function 0    start      ArrayBufferSink__start           ReadOnly|DontDelete|Function 1    write      ArrayBufferSink__write           ReadOnly|DontDelete|Function 1 +  ref        ArrayBufferSink__ref             ReadOnly|DontDelete|Function 0 +  unref      ArrayBufferSink__unref           ReadOnly|DontDelete|Function 0  @end  */ @@ -460,6 +630,8 @@ JSC_DEFINE_HOST_FUNCTION(HTTPSResponseSink__doClose, (JSC::JSGlobalObject * lexi    end        FileSink__end             ReadOnly|DontDelete|Function 0    start      FileSink__start           ReadOnly|DontDelete|Function 1    write      FileSink__write           ReadOnly|DontDelete|Function 1 +  ref        FileSink__ref             ReadOnly|DontDelete|Function 0 +  unref      FileSink__unref           ReadOnly|DontDelete|Function 0  @end  */ @@ -480,6 +652,8 @@ JSC_DEFINE_HOST_FUNCTION(HTTPSResponseSink__doClose, (JSC::JSGlobalObject * lexi    end        HTTPResponseSink__end             ReadOnly|DontDelete|Function 0    start      HTTPResponseSink__start           ReadOnly|DontDelete|Function 1    write      HTTPResponseSink__write           ReadOnly|DontDelete|Function 1 +  ref        HTTPResponseSink__ref             ReadOnly|DontDelete|Function 0 +  unref      HTTPResponseSink__unref           ReadOnly|DontDelete|Function 0  @end  */ @@ -500,6 +674,8 @@ JSC_DEFINE_HOST_FUNCTION(HTTPSResponseSink__doClose, (JSC::JSGlobalObject * lexi    end        HTTPSResponseSink__end             ReadOnly|DontDelete|Function 0    start      HTTPSResponseSink__start           ReadOnly|DontDelete|Function 1    write      HTTPSResponseSink__write           ReadOnly|DontDelete|Function 1 +  ref        HTTPSResponseSink__ref             ReadOnly|DontDelete|Function 0 +  unref      HTTPSResponseSink__unref           ReadOnly|DontDelete|Function 0  @end  */ @@ -1603,8 +1779,7 @@ extern "C" JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0,  {      auto& vm = arg0->vm();      Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); -    JSC::JSValue prototype = globalObject->ArrayBufferSinkPrototype(); -    JSC::Structure* structure = WebCore::JSArrayBufferSink::createStructure(vm, globalObject, prototype); +    JSC::Structure* structure = globalObject->ArrayBufferSinkStructure();      return JSC::JSValue::encode(WebCore::JSArrayBufferSink::create(vm, globalObject, structure, sinkPtr));  } @@ -1690,8 +1865,7 @@ extern "C" JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void*  {      auto& vm = arg0->vm();      Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); -    JSC::JSValue prototype = globalObject->FileSinkPrototype(); -    JSC::Structure* structure = WebCore::JSFileSink::createStructure(vm, globalObject, prototype); +    JSC::Structure* structure = globalObject->FileSinkStructure();      return JSC::JSValue::encode(WebCore::JSFileSink::create(vm, globalObject, structure, sinkPtr));  } @@ -1777,8 +1951,7 @@ extern "C" JSC__JSValue HTTPResponseSink__createObject(JSC__JSGlobalObject* arg0  {      auto& vm = arg0->vm();      Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); -    JSC::JSValue prototype = globalObject->HTTPResponseSinkPrototype(); -    JSC::Structure* structure = WebCore::JSHTTPResponseSink::createStructure(vm, globalObject, prototype); +    JSC::Structure* structure = globalObject->HTTPResponseSinkStructure();      return JSC::JSValue::encode(WebCore::JSHTTPResponseSink::create(vm, globalObject, structure, sinkPtr));  } @@ -1864,8 +2037,7 @@ extern "C" JSC__JSValue HTTPSResponseSink__createObject(JSC__JSGlobalObject* arg  {      auto& vm = arg0->vm();      Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); -    JSC::JSValue prototype = globalObject->HTTPSResponseSinkPrototype(); -    JSC::Structure* structure = WebCore::JSHTTPSResponseSink::createStructure(vm, globalObject, prototype); +    JSC::Structure* structure = globalObject->HTTPSResponseSinkStructure();      return JSC::JSValue::encode(WebCore::JSHTTPSResponseSink::create(vm, globalObject, structure, sinkPtr));  } diff --git a/src/bun.js/bindings/JSSink.h b/src/bun.js/bindings/JSSink.h index 68caf12fc..3bdfaec9a 100644 --- a/src/bun.js/bindings/JSSink.h +++ b/src/bun.js/bindings/JSSink.h @@ -1,6 +1,6 @@  // AUTO-GENERATED FILE. DO NOT EDIT. -// Generated by 'make generate-sink' at 2022-11-20T12:51:04.403Z +// Generated by 'make generate-sink' at 2022-11-25T07:36:18.559Z  //  #pragma once @@ -97,7 +97,11 @@ public:      static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); +    void ref(); +    void unref(); +      void* m_sinkPtr; +    int m_refCount { 1 };      JSArrayBufferSink(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)          : Base(vm, structure) @@ -240,7 +244,11 @@ public:      static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); +    void ref(); +    void unref(); +      void* m_sinkPtr; +    int m_refCount { 1 };      JSFileSink(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)          : Base(vm, structure) @@ -383,7 +391,11 @@ public:      static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); +    void ref(); +    void unref(); +      void* m_sinkPtr; +    int m_refCount { 1 };      JSHTTPResponseSink(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)          : Base(vm, structure) @@ -526,7 +538,11 @@ public:      static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); +    void ref(); +    void unref(); +      void* m_sinkPtr; +    int m_refCount { 1 };      JSHTTPSResponseSink(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)          : Base(vm, structure) diff --git a/src/bun.js/bindings/JSSinkLookupTable.h b/src/bun.js/bindings/JSSinkLookupTable.h index 14d547708..a4ace6dc3 100644 --- a/src/bun.js/bindings/JSSinkLookupTable.h +++ b/src/bun.js/bindings/JSSinkLookupTable.h @@ -10,7 +10,7 @@ static const struct CompactHashIndex JSArrayBufferSinkPrototypeTableIndex[19] =      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 6, -1 },      { -1, -1 },      { -1, -1 },      { -1, -1 }, @@ -19,23 +19,25 @@ static const struct CompactHashIndex JSArrayBufferSinkPrototypeTableIndex[19] =      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 5, -1 },      { 4, -1 },      { 1, 17 },      { 2, 18 },      { 3, -1 },  }; -static const struct HashTableValue JSArrayBufferSinkPrototypeTableValues[5] = { +static const struct HashTableValue JSArrayBufferSinkPrototypeTableValues[7] = {     { "close"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ArrayBufferSink__doClose, 0 } },     { "flush"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ArrayBufferSink__flush, 1 } },     { "end"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ArrayBufferSink__end, 0 } },     { "start"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ArrayBufferSink__start, 1 } },     { "write"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ArrayBufferSink__write, 1 } }, +   { "ref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ArrayBufferSink__ref, 0 } }, +   { "unref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ArrayBufferSink__unref, 0 } },  };  static const struct HashTable JSArrayBufferSinkPrototypeTable = -    { 5, 15, false, nullptr, JSArrayBufferSinkPrototypeTableValues, JSArrayBufferSinkPrototypeTableIndex }; +    { 7, 15, false, nullptr, JSArrayBufferSinkPrototypeTableValues, JSArrayBufferSinkPrototypeTableIndex }; @@ -88,7 +90,7 @@ static const struct CompactHashIndex JSFileSinkPrototypeTableIndex[19] = {      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 6, -1 },      { -1, -1 },      { -1, -1 },      { -1, -1 }, @@ -97,23 +99,25 @@ static const struct CompactHashIndex JSFileSinkPrototypeTableIndex[19] = {      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 5, -1 },      { 4, -1 },      { 1, 17 },      { 2, 18 },      { 3, -1 },  }; -static const struct HashTableValue JSFileSinkPrototypeTableValues[5] = { +static const struct HashTableValue JSFileSinkPrototypeTableValues[7] = {     { "close"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, FileSink__doClose, 0 } },     { "flush"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, FileSink__flush, 1 } },     { "end"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, FileSink__end, 0 } },     { "start"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, FileSink__start, 1 } },     { "write"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, FileSink__write, 1 } }, +   { "ref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, FileSink__ref, 0 } }, +   { "unref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, FileSink__unref, 0 } },  };  static const struct HashTable JSFileSinkPrototypeTable = -    { 5, 15, false, nullptr, JSFileSinkPrototypeTableValues, JSFileSinkPrototypeTableIndex }; +    { 7, 15, false, nullptr, JSFileSinkPrototypeTableValues, JSFileSinkPrototypeTableIndex }; @@ -166,7 +170,7 @@ static const struct CompactHashIndex JSHTTPResponseSinkPrototypeTableIndex[19] =      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 6, -1 },      { -1, -1 },      { -1, -1 },      { -1, -1 }, @@ -175,23 +179,25 @@ static const struct CompactHashIndex JSHTTPResponseSinkPrototypeTableIndex[19] =      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 5, -1 },      { 4, -1 },      { 1, 17 },      { 2, 18 },      { 3, -1 },  }; -static const struct HashTableValue JSHTTPResponseSinkPrototypeTableValues[5] = { +static const struct HashTableValue JSHTTPResponseSinkPrototypeTableValues[7] = {     { "close"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPResponseSink__doClose, 0 } },     { "flush"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPResponseSink__flush, 1 } },     { "end"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPResponseSink__end, 0 } },     { "start"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPResponseSink__start, 1 } },     { "write"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPResponseSink__write, 1 } }, +   { "ref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPResponseSink__ref, 0 } }, +   { "unref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPResponseSink__unref, 0 } },  };  static const struct HashTable JSHTTPResponseSinkPrototypeTable = -    { 5, 15, false, nullptr, JSHTTPResponseSinkPrototypeTableValues, JSHTTPResponseSinkPrototypeTableIndex }; +    { 7, 15, false, nullptr, JSHTTPResponseSinkPrototypeTableValues, JSHTTPResponseSinkPrototypeTableIndex }; @@ -244,7 +250,7 @@ static const struct CompactHashIndex JSHTTPSResponseSinkPrototypeTableIndex[19]      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 6, -1 },      { -1, -1 },      { -1, -1 },      { -1, -1 }, @@ -253,23 +259,25 @@ static const struct CompactHashIndex JSHTTPSResponseSinkPrototypeTableIndex[19]      { -1, -1 },      { -1, -1 },      { -1, -1 }, -    { -1, -1 }, +    { 5, -1 },      { 4, -1 },      { 1, 17 },      { 2, 18 },      { 3, -1 },  }; -static const struct HashTableValue JSHTTPSResponseSinkPrototypeTableValues[5] = { +static const struct HashTableValue JSHTTPSResponseSinkPrototypeTableValues[7] = {     { "close"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPSResponseSink__doClose, 0 } },     { "flush"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPSResponseSink__flush, 1 } },     { "end"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPSResponseSink__end, 0 } },     { "start"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPSResponseSink__start, 1 } },     { "write"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPSResponseSink__write, 1 } }, +   { "ref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPSResponseSink__ref, 0 } }, +   { "unref"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::DontDelete|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, HTTPSResponseSink__unref, 0 } },  };  static const struct HashTable JSHTTPSResponseSinkPrototypeTable = -    { 5, 15, false, nullptr, JSHTTPSResponseSinkPrototypeTableValues, JSHTTPSResponseSinkPrototypeTableIndex }; +    { 7, 15, false, nullptr, JSHTTPSResponseSinkPrototypeTableValues, JSHTTPSResponseSinkPrototypeTableIndex }; diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 67dc9e393..419a8965a 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@  // clang-format off -//-- AUTOGENERATED FILE -- 1669267948 +//-- AUTOGENERATED FILE -- 1669352137  #pragma once  #include <stddef.h> @@ -30,7 +30,7 @@ typedef void* JSClassRef;   typedef char* bJSC__SourceCode_buf;   typedef struct bWTF__URL { unsigned char bytes[40]; } bWTF__URL;   typedef char* bWTF__URL_buf; - typedef struct bJSC__JSModuleRecord { unsigned char bytes[208]; } bJSC__JSModuleRecord; + typedef struct bJSC__JSModuleRecord { unsigned char bytes[216]; } bJSC__JSModuleRecord;   typedef char* bJSC__JSModuleRecord_buf;   typedef struct bJSC__ThrowScope { unsigned char bytes[8]; } bJSC__ThrowScope;   typedef char* bJSC__ThrowScope_buf; @@ -38,7 +38,7 @@ typedef void* JSClassRef;   typedef char* bJSC__PropertyName_buf;   typedef struct bJSC__JSFunction { unsigned char bytes[32]; } bJSC__JSFunction;   typedef char* bJSC__JSFunction_buf; - typedef struct bJSC__JSGlobalObject { unsigned char bytes[2840]; } bJSC__JSGlobalObject; + typedef struct bJSC__JSGlobalObject { unsigned char bytes[3128]; } bJSC__JSGlobalObject;   typedef char* bJSC__JSGlobalObject_buf;   typedef struct bJSC__JSCell { unsigned char bytes[8]; } bJSC__JSCell;   typedef char* bJSC__JSCell_buf; @@ -54,7 +54,7 @@ typedef void* JSClassRef;   typedef char* bInspector__ScriptArguments_buf;   typedef struct bJSC__Exception { unsigned char bytes[40]; } bJSC__Exception;   typedef char* bJSC__Exception_buf; - typedef struct bJSC__VM { unsigned char bytes[52008]; } bJSC__VM; + typedef struct bJSC__VM { unsigned char bytes[52176]; } bJSC__VM;   typedef char* bJSC__VM_buf;   typedef struct bJSC__JSString { unsigned char bytes[16]; } bJSC__JSString;   typedef char* bJSC__JSString_buf; @@ -853,6 +853,7 @@ ZIG_DECL JSC__JSValue ArrayBufferSink__endWithSink(void* arg0, JSC__JSGlobalObje  ZIG_DECL void ArrayBufferSink__finalize(void* arg0);  ZIG_DECL JSC__JSValue ArrayBufferSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  ZIG_DECL JSC__JSValue ArrayBufferSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL void ArrayBufferSink__updateRef(void* arg0, bool arg1);  ZIG_DECL JSC__JSValue ArrayBufferSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  #endif @@ -872,6 +873,7 @@ ZIG_DECL JSC__JSValue HTTPSResponseSink__endWithSink(void* arg0, JSC__JSGlobalOb  ZIG_DECL void HTTPSResponseSink__finalize(void* arg0);  ZIG_DECL JSC__JSValue HTTPSResponseSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  ZIG_DECL JSC__JSValue HTTPSResponseSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL void HTTPSResponseSink__updateRef(void* arg0, bool arg1);  ZIG_DECL JSC__JSValue HTTPSResponseSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  #endif @@ -891,6 +893,7 @@ ZIG_DECL JSC__JSValue HTTPResponseSink__endWithSink(void* arg0, JSC__JSGlobalObj  ZIG_DECL void HTTPResponseSink__finalize(void* arg0);  ZIG_DECL JSC__JSValue HTTPResponseSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  ZIG_DECL JSC__JSValue HTTPResponseSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL void HTTPResponseSink__updateRef(void* arg0, bool arg1);  ZIG_DECL JSC__JSValue HTTPResponseSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  #endif @@ -910,6 +913,7 @@ ZIG_DECL JSC__JSValue FileSink__endWithSink(void* arg0, JSC__JSGlobalObject* arg  ZIG_DECL void FileSink__finalize(void* arg0);  ZIG_DECL JSC__JSValue FileSink__flush(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  ZIG_DECL JSC__JSValue FileSink__start(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1); +ZIG_DECL void FileSink__updateRef(void* arg0, bool arg1);  ZIG_DECL JSC__JSValue FileSink__write(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1);  #endif diff --git a/src/bun.js/scripts/generate-jssink.js b/src/bun.js/scripts/generate-jssink.js index 174efcb06..b9b6b2c17 100644 --- a/src/bun.js/scripts/generate-jssink.js +++ b/src/bun.js/scripts/generate-jssink.js @@ -102,11 +102,15 @@ function header() {              void detach() {                  m_sinkPtr = nullptr; -            }                        +            } + +            static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); -            static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);                                                                                                                    +            void ref(); +            void unref();              void* m_sinkPtr; +            int m_refCount { 1 };              ${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)                                                                                                                      : Base(vm, structure)                                                                                                                                                @@ -355,6 +359,51 @@ JSC_DEFINE_HOST_FUNCTION(functionStartDirectStream, (JSC::JSGlobalObject * lexic      const protopad = `${controller}__close`.length;      const padding = `${name}__doClose`.length;      templ += ` + +  void ${className}::ref() { +    if (!m_sinkPtr) +      return; + +    m_refCount++; +    if (m_refCount == 1) { +      ${name}__updateRef(m_sinkPtr, true); +    } +  } + +  void ${className}::unref() { +    if (!m_sinkPtr) +      return; + +      m_refCount = std::max(0, m_refCount - 1); +      if (!m_refCount) +      { +        ${name}__updateRef(m_sinkPtr, false); +      } +  } + +JSC_DEFINE_HOST_FUNCTION(${name}__ref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame *callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::${className}*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->ref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); + +} + + +JSC_DEFINE_HOST_FUNCTION(${name}__unref, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame *callFrame)) +{ +    auto& vm = lexicalGlobalObject->vm(); +    auto* sink = jsDynamicCast<WebCore::${className}*>(callFrame->thisValue()); +    if (LIKELY(sink)) { +        sink->unref(); +    } +    return JSC::JSValue::encode(JSC::jsUndefined()); + +} +  JSC_DEFINE_CUSTOM_GETTER(function${name}__getter, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))  {      auto& vm = lexicalGlobalObject->vm(); @@ -472,6 +521,12 @@ JSC_DEFINE_HOST_FUNCTION(${name}__doClose, (JSC::JSGlobalObject * lexicalGlobalO    write      ${`${name}__write`.padEnd(      padding + 8,    )} ReadOnly|DontDelete|Function 1 +  ref        ${`${name}__ref`.padEnd( +    padding + 8, +  )} ReadOnly|DontDelete|Function 0 +  unref      ${`${name}__unref`.padEnd( +    padding + 8, +  )} ReadOnly|DontDelete|Function 0  @end  */ @@ -849,8 +904,7 @@ extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* s  {      auto& vm = arg0->vm();      Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); -    JSC::JSValue prototype = globalObject->${name}Prototype(); -    JSC::Structure* structure = WebCore::JS${name}::createStructure(vm, globalObject, prototype); +    JSC::Structure* structure = globalObject->${name}Structure();      return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr));  } diff --git a/src/global.zig b/src/global.zig index 1dccb15e7..635e759ce 100644 --- a/src/global.zig +++ b/src/global.zig @@ -341,24 +341,27 @@ pub fn ensureNonBlocking(fd: anytype) void {  }  const global_scope_log = Output.scoped(.bun, false); -pub fn isReadable(fd: std.os.fd_t) bool { -    _ = fd; -    return false; -    // var polls = &[_]std.os.pollfd{ -    //     .{ -    //         .fd = fd, -    //         .events = std.os.POLL.IN | std.os.POLL.ERR, -    //         .revents = 0, -    //     }, -    // }; - -    // const result = (std.os.poll(polls, 0) catch 0) != 0; -    // global_scope_log("isReadable: {d}", .{result}); -    // return result; +pub fn isReadable(fd: std.os.fd_t) PollFlag { +    var polls = &[_]std.os.pollfd{ +        .{ +            .fd = fd, +            .events = std.os.POLL.IN | std.os.POLL.ERR, +            .revents = 0, +        }, +    }; + +    const result = (std.os.poll(polls, 0) catch 0) != 0; +    global_scope_log("isReadable: {d} ({d})", .{ result, polls[0].revents }); +    return if (result and polls[0].revents & std.os.POLL.HUP != 0) +        PollFlag.hup +    else if (result) +        PollFlag.ready +    else +        PollFlag.not_ready;  } -pub const WritableFlag = enum { writable, not_writable, hup }; -pub fn isWritable(fd: std.os.fd_t) WritableFlag { +pub const PollFlag = enum { ready, not_ready, hup }; +pub fn isWritable(fd: std.os.fd_t) PollFlag {      var polls = &[_]std.os.pollfd{          .{              .fd = fd, @@ -370,11 +373,11 @@ pub fn isWritable(fd: std.os.fd_t) WritableFlag {      const result = (std.os.poll(polls, 0) catch 0) != 0;      global_scope_log("isWritable: {d} ({d})", .{ result, polls[0].revents });      if (result and polls[0].revents & std.os.POLL.HUP != 0) { -        return WritableFlag.hup; +        return PollFlag.hup;      } else if (result) { -        return WritableFlag.writable; +        return PollFlag.ready;      } else { -        return WritableFlag.not_writable; +        return PollFlag.not_ready;      }  } diff --git a/test/bun.js/spawn-streaming-stdin.test.ts b/test/bun.js/spawn-streaming-stdin.test.ts index 953548071..6424eadfc 100644 --- a/test/bun.js/spawn-streaming-stdin.test.ts +++ b/test/bun.js/spawn-streaming-stdin.test.ts @@ -12,30 +12,40 @@ test("spawn can write to stdin multiple chunks", async () => {          cmd: [bunExe(), import.meta.dir + "/stdin-repro.js"],          stdout: "pipe",          stdin: "pipe", -        stderr: "inherit", +        stderr: Bun.file("/tmp/out.log"),          env: {            BUN_DEBUG_QUIET_LOGS: 1,          },        }); +      // (async function () { +      //   for await (var chunk of proc.stderr) { +      //     console.error("[stderr]", new TextDecoder().decode(chunk)); +      //   } +      // })();        exited = proc.exited;        var counter = 0;        var inCounter = 0;        const prom2 = (async function () { -        while (inCounter++ < 4) { +        while (true) {            await new Promise((resolve, reject) => setTimeout(resolve, 8));            proc.stdin.write("Wrote to stdin!"); -          await proc.stdin.flush(); +          inCounter++; + +          if (inCounter === 4) break;          } -        await proc.stdin.end(); +        await new Promise((resolve) => +          Promise.resolve(proc.stdin.end()).then(resolve), +        );        })(); +      var chunks = [];        const prom = (async function () {          try {            for await (var chunk of proc.stdout) { -            expect(new TextDecoder().decode(chunk)).toBe("Wrote to stdin!\n"); +            chunks.push(chunk);              counter++; -            if (counter > 3) break; +            if (counter === 4) break;            }          } catch (e) {            console.log(e.stack); @@ -43,11 +53,15 @@ test("spawn can write to stdin multiple chunks", async () => {          }        })();        await Promise.all([prom, prom2]); +      const code = await exited; +      console.log(code);        expect(counter).toBe(4); +      expect(Buffer.concat(chunks).toString().trim()).toBe( +        "Wrote to stdin!\n".repeat(4).trim(), +      );        //   proc.kill(); + +      gcTick(true);      })(); -    await exited;    } - -  gcTick(true);  }); diff --git a/test/bun.js/stdin-repro.js b/test/bun.js/stdin-repro.js index 05daf0637..5f945be7c 100644 --- a/test/bun.js/stdin-repro.js +++ b/test/bun.js/stdin-repro.js @@ -1,5 +1,7 @@ -while (true) { -  for await (let chunk of Bun.stdin.stream()) { -    console.log(new Buffer(chunk).toString()); -  } +var count = 5; +for await (let chunk of Bun.stdin.stream()) { +  const str = new Buffer(chunk).toString(); +  console.error("how many?", count, chunk.byteLength); +  count -= str.split("\n").length; +  console.log(str);  } diff --git a/test/bun.js/streams.test.js b/test/bun.js/streams.test.js index a872b7701..406c80852 100644 --- a/test/bun.js/streams.test.js +++ b/test/bun.js/streams.test.js @@ -225,7 +225,7 @@ it("Bun.file() read text from pipe", async () => {    const large = "HELLO!".repeat((((1024 * 65) / "HELLO!".length) | 0) + 1);    const chunks = []; -  var out = Bun.file("/tmp/fifo").stream(); +    const proc = Bun.spawn({      cmd: [        "bash", @@ -244,17 +244,17 @@ it("Bun.file() read text from pipe", async () => {    const prom = (async function () {      while (chunks.length === 0) { +      var out = Bun.file("/tmp/fifo").stream();        for await (const chunk of out) {          chunks.push(chunk);        } -      console.log("done");      } +    return Buffer.concat(chunks).toString();    })();    const [status, output] = await Promise.all([exited, prom]); -  console.log("here"); -  expect(output.length).toBe(large.length); -  expect(output).toBe(large); +  expect(output.length).toBe(large.length + 1); +  expect(output).toBe(large + "\n");    expect(status).toBe(0);  }); @@ -452,7 +452,7 @@ it("ReadableStream for Blob", async () => {  it("ReadableStream for File", async () => {    var blob = file(import.meta.dir + "/fetch.js.txt"); -  var stream = blob.stream(24); +  var stream = blob.stream();    const chunks = [];    var reader = stream.getReader();    stream = undefined; | 
