diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/bindings/JSBufferList.cpp | 56 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBufferList.h | 57 | ||||
-rw-r--r-- | src/bun.js/bindings/JSReadableState.cpp | 293 | ||||
-rw-r--r-- | src/bun.js/bindings/JSReadableState.h | 110 | ||||
-rw-r--r-- | src/bun.js/bindings/JSStringDecoder.cpp | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/JSStringDecoder.h | 9 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 30 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.h | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/exports.zig | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/DOMIsoSubspaces.h | 2 | ||||
-rw-r--r-- | src/bun.js/streams.exports.js | 68 |
12 files changed, 526 insertions, 123 deletions
diff --git a/src/bun.js/bindings/JSBufferList.cpp b/src/bun.js/bindings/JSBufferList.cpp index 50e0defe0..948eed296 100644 --- a/src/bun.js/bindings/JSBufferList.cpp +++ b/src/bun.js/bindings/JSBufferList.cpp @@ -181,35 +181,6 @@ JSC::GCClient::IsoSubspace* JSBufferList::subspaceForImpl(JSC::VM& vm) [](auto& spaces, auto&& space) { spaces.m_subspaceForBufferList = WTFMove(space); }); } -class JSBufferListPrototype : public JSC::JSNonFinalObject { -public: - using Base = JSC::JSNonFinalObject; - static JSBufferListPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) - { - JSBufferListPrototype* ptr = new (NotNull, JSC::allocateCell<JSBufferListPrototype>(vm)) JSBufferListPrototype(vm, structure); - ptr->finishCreation(vm, globalObject); - return ptr; - } - - DECLARE_INFO; - template<typename CellType, JSC::SubspaceAccess> - static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) - { - return &vm.plainObjectSpace(); - } - static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) - { - return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); - } - -private: - JSBufferListPrototype(JSC::VM& vm, JSC::Structure* structure) - : Base(vm, structure) - { - } - - void finishCreation(JSC::VM&, JSC::JSGlobalObject*); -}; STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSBufferListPrototype, JSBufferListPrototype::Base); static inline JSC::EncodedJSValue jsBufferListPrototypeFunction_pushBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBufferList>::ClassParameter castedThis) @@ -375,14 +346,31 @@ void JSBufferListPrototype::finishCreation(VM& vm, JSC::JSGlobalObject* globalTh const ClassInfo JSBufferListPrototype::s_info = { "BufferList"_s, nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSBufferListPrototype) }; -EncodedJSValue constructJSBufferList(JSGlobalObject* globalObject, CallFrame* callFrame) +void JSBufferListConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSBufferListPrototype* prototype) { - JSC::VM& vm = globalObject->vm(); - JSBufferListPrototype* prototype = JSBufferListPrototype::create( - vm, globalObject, JSBufferListPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); + Base::finishCreation(vm, 0, "BufferList"_s, PropertyAdditionMode::WithoutStructureTransition); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + ASSERT(inherits(info())); +} + +JSBufferListConstructor* JSBufferListConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSBufferListPrototype* prototype) { + JSBufferListConstructor* ptr = new (NotNull, JSC::allocateCell<JSBufferListConstructor>(vm)) JSBufferListConstructor(vm, structure, construct); + ptr->finishCreation(vm, globalObject, prototype); + return ptr; +} + +JSC::EncodedJSValue JSBufferListConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + JSC::VM& vm = lexicalGlobalObject->vm(); JSBufferList* bufferList = JSBufferList::create( - vm, globalObject, JSBufferList::createStructure(vm, globalObject, prototype)); + vm, lexicalGlobalObject, reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject)->JSBufferListStructure()); return JSC::JSValue::encode(bufferList); } +void JSBufferListConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSBufferListPrototype* prototype) +{ +} + +const ClassInfo JSBufferListConstructor::s_info = { "BufferList"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBufferListConstructor) }; + } // namespace Zig
\ No newline at end of file diff --git a/src/bun.js/bindings/JSBufferList.h b/src/bun.js/bindings/JSBufferList.h index 67d8d21f3..3c0b8d12c 100644 --- a/src/bun.js/bindings/JSBufferList.h +++ b/src/bun.js/bindings/JSBufferList.h @@ -85,6 +85,61 @@ private: Deque<WriteBarrier<Unknown>> m_deque; }; -EncodedJSValue constructJSBufferList(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame); +class JSBufferListPrototype : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSBufferListPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSBufferListPrototype* ptr = new (NotNull, JSC::allocateCell<JSBufferListPrototype>(vm)) JSBufferListPrototype(vm, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSBufferListPrototype(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); +}; + +class JSBufferListConstructor final : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + static JSBufferListConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSBufferListPrototype* prototype); + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSBufferListPrototype* prototype); + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + DECLARE_EXPORT_INFO; +private: + JSBufferListConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) + : Base(vm, structure, nativeFunction, nativeFunction) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSBufferListPrototype* prototype); +}; } diff --git a/src/bun.js/bindings/JSReadableState.cpp b/src/bun.js/bindings/JSReadableState.cpp new file mode 100644 index 000000000..fe19449ff --- /dev/null +++ b/src/bun.js/bindings/JSReadableState.cpp @@ -0,0 +1,293 @@ +#include "JSReadableState.h" +#include "JSBufferList.h" +#include "JSBuffer.h" +#include "JavaScriptCore/Lookup.h" +#include "JavaScriptCore/ObjectConstructor.h" +#include "ZigGlobalObject.h" +#include "JSDOMOperation.h" +#include "JSDOMAttribute.h" +#include "headers.h" +#include "JSDOMConvertEnumeration.h" + +namespace WebCore { + +using namespace JSC; + +static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_pipesCount); +static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_paused); +static JSC_DECLARE_CUSTOM_GETTER(setJSReadableState_paused); + +int64_t getHighWaterMark(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options) +{ + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSC::JSValue highWaterMarkVal = options->getDirect(vm, JSC::Identifier::fromString(vm, "highWaterMark"_s)); + if (isDuplex && (highWaterMarkVal.isUndefined() || highWaterMarkVal.isNull())) { + highWaterMarkVal = options->getDirect(vm, JSC::Identifier::fromString(vm, "readableObjectMode"_s)); + } + if (!highWaterMarkVal.isNull() && !highWaterMarkVal.isUndefined()) { + double customHightWaterMark = highWaterMarkVal.toNumber(globalObject); + RETURN_IF_EXCEPTION(throwScope, -1); + if (customHightWaterMark < 0) + return -1; + return floor(customHightWaterMark); + } + + return -1; +} + +void JSReadableState::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options) +{ + Base::finishCreation(vm); + + bool objectMode = false; + auto objectModeIdent = JSC::Identifier::fromString(vm, "objectMode"_s); + if (options != nullptr) { + JSC::JSValue objectModeVal = options->getDirect(vm, objectModeIdent); + if (isDuplex && !objectModeVal) { + objectModeVal = options->getDirect(vm, JSC::Identifier::fromString(vm, "readableObjectMode"_s)); + } + if (objectModeVal) + objectMode = objectModeVal.toBoolean(globalObject); + } + putDirect(vm, WTFMove(objectModeIdent), JSC::jsBoolean(objectMode)); + + int64_t highWaterMark = objectMode ? 16 : 16 * 1024; // default value + if (options != nullptr) { + int64_t customHightWaterMark = getHighWaterMark(vm, globalObject, isDuplex, options); + if (customHightWaterMark >= 0) + highWaterMark = customHightWaterMark; + } + putDirect(vm, JSC::Identifier::fromString(vm, "highWaterMark"_s), JSC::jsNumber(highWaterMark)); + + putDirect(vm, JSC::Identifier::fromString(vm, "buffer"_s), JSBufferList::create( + vm, globalObject, reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSBufferListStructure())); + putDirect(vm, JSC::Identifier::fromString(vm, "length"_s), JSC::jsNumber(0)); + putDirect(vm, JSC::Identifier::fromString(vm, "pipes"_s), JSC::constructEmptyArray(globalObject, nullptr, 0)); + putDirect(vm, JSC::Identifier::fromString(vm, "flowing"_s), JSC::jsNull()); + putDirect(vm, JSC::Identifier::fromString(vm, "ended"_s), JSC::jsBoolean(false)); + putDirect(vm, JSC::Identifier::fromString(vm, "endEmitted"_s), JSC::jsBoolean(false)); + // Stream is still being constructed and cannot be + // destroyed until construction finished or failed. + // Async construction is opt in, therefore we start as + // constructed. + putDirect(vm, JSC::Identifier::fromString(vm, "reading"_s), JSC::jsBoolean(false)); + + // A flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + putDirect(vm, JSC::Identifier::fromString(vm, "constructed"_s), JSC::jsBoolean(true)); + + // Whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + putDirect(vm, JSC::Identifier::fromString(vm, "sync"_s), JSC::jsBoolean(true)); + + putDirect(vm, JSC::Identifier::fromString(vm, "needReadable"_s), JSC::jsBoolean(false)); + putDirect(vm, JSC::Identifier::fromString(vm, "emittedReadable"_s), JSC::jsBoolean(false)); + putDirect(vm, JSC::Identifier::fromString(vm, "readableListening"_s), JSC::jsBoolean(false)); + putDirect(vm, JSC::Identifier::fromString(vm, "resumeScheduled"_s), JSC::jsBoolean(false)); + + // Should close be emitted on destroy. Defaults to true. + putDirect(vm, JSC::Identifier::fromString(vm, "errorEmitted"_s), JSC::jsBoolean(false)); + + if (options == nullptr) { + // Should .destroy() be called after 'end' (and potentially 'finish'). + putDirect(vm, JSC::Identifier::fromString(vm, "emitClose"_s), JSC::jsBoolean(false)); + // Has it been destroyed. + putDirect(vm, JSC::Identifier::fromString(vm, "autoDestroy"_s), JSC::jsBoolean(false)); + } else { + // Should .destroy() be called after 'end' (and potentially 'finish'). + auto emitCloseIdent = JSC::Identifier::fromString(vm, "emitClose"_s); + JSC::JSValue emitCloseVal = options->getDirect(vm, emitCloseIdent); + putDirect(vm, WTFMove(emitCloseIdent), JSC::jsBoolean(!emitCloseVal.isBoolean() || emitCloseVal.toBoolean(globalObject))); + // Has it been destroyed. + auto autoDestroyIdent = JSC::Identifier::fromString(vm, "autoDestroy"_s); + JSC::JSValue autoDestroyVal = options->getDirect(vm, autoDestroyIdent); + putDirect(vm, WTFMove(autoDestroyIdent), JSC::jsBoolean(!autoDestroyVal.isBoolean() || autoDestroyVal.toBoolean(globalObject))); + } + + // Indicates whether the stream has errored. When true no further + // _read calls, 'data' or 'readable' events should occur. This is needed + // since when autoDestroy is disabled we need a way to tell whether the + // stream has failed. + putDirect(vm, JSC::Identifier::fromString(vm, "destroyed"_s), JSC::jsBoolean(false)); + + // Indicates whether the stream has finished destroying. + putDirect(vm, JSC::Identifier::fromString(vm, "errored"_s), JSC::jsNull()); + + // True if close has been emitted or would have been emitted + // depending on emitClose. + putDirect(vm, JSC::Identifier::fromString(vm, "closed"_s), JSC::jsBoolean(false)); + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + putDirect(vm, JSC::Identifier::fromString(vm, "closeEmitted"_s), JSC::jsBoolean(false)); + + // Ref the piped dest which we need a drain event on it + // type: null | Writable | Set<Writable>. + auto defaultEncodingIdent = JSC::Identifier::fromString(vm, "defaultEncoding"_s); + if (options == nullptr) { + putDirect(vm, WTFMove(defaultEncodingIdent), JSC::jsString(vm, WTF::String("utf8"_s))); + } else { + JSC::JSValue defaultEncodingVal = getDirect(vm, defaultEncodingIdent); + if (defaultEncodingVal) { + putDirect(vm, WTFMove(defaultEncodingIdent), defaultEncodingVal); + } else { + putDirect(vm, WTFMove(defaultEncodingIdent), JSC::jsString(vm, WTF::String("utf8"_s))); + } + } + + putDirect(vm, JSC::Identifier::fromString(vm, "awaitDrainWriters"_s), JSC::jsNull()); + // If true, a maybeReadMore has been scheduled. + putDirect(vm, JSC::Identifier::fromString(vm, "multiAwaitDrain"_s), JSC::jsBoolean(false)); + + putDirect(vm, JSC::Identifier::fromString(vm, "readingMore"_s), JSC::jsBoolean(false)); + putDirect(vm, JSC::Identifier::fromString(vm, "dataEmitted"_s), JSC::jsBoolean(false)); + + auto decoderIdent = JSC::Identifier::fromString(vm, "decoder"_s); + auto encodingIdent = JSC::Identifier::fromString(vm, "encoding"_s); + if (options == nullptr) { + putDirect(vm, WTFMove(decoderIdent), JSC::jsNull()); + putDirect(vm, WTFMove(encodingIdent), JSC::jsNull()); + } else { + JSC::JSValue encodingVal = options->getDirect(vm, encodingIdent); + if (encodingVal) { + auto constructor = reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSStringDecoder(); + auto constructData = JSC::getConstructData(constructor); + MarkedArgumentBuffer args; + args.append(encodingVal); + JSObject* decoder = JSC::construct(globalObject, constructor, constructData, args); + putDirect(vm, WTFMove(decoderIdent), decoder); + putDirect(vm, WTFMove(encodingIdent), encodingVal); + } else { + putDirect(vm, WTFMove(decoderIdent), JSC::jsNull()); + putDirect(vm, WTFMove(encodingIdent), JSC::jsNull()); + } + } +} + +const JSC::ClassInfo JSReadableState::s_info = { "ReadableState"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableState) }; + +JSC::GCClient::IsoSubspace* JSReadableState::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSReadableState, UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForReadableState.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForReadableState = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForReadableState.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForReadableState = WTFMove(space); }); +} + +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSReadableStatePrototype, JSReadableStatePrototype::Base); + +JSC_DEFINE_CUSTOM_GETTER(jsReadableState_pipesCount, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSObject* thisObject = JSC::jsDynamicCast<JSObject*>(JSValue::decode(thisValue)); + if (!thisObject) { + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + } + JSC::JSValue pipesVal = thisObject->getDirect(vm, JSC::Identifier::fromString(vm, "pipes"_s)); + if (!pipesVal) { + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + } + JSArray* pipes = JSC::jsDynamicCast<JSArray*>(pipesVal); + if (!pipes) { + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + } + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(pipes->length()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsReadableState_paused, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); + if (!state) { + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + } + if (state->m_paused == 0) + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNull())); + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsBoolean(state->m_paused > 0))); +} + +JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_paused, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); + if (!state) { + RETURN_IF_EXCEPTION(throwScope, false); + } + state->m_paused = JSC::JSValue::decode(encodedValue).toBoolean(lexicalGlobalObject) ? 1 : -1; + RELEASE_AND_RETURN(throwScope, true); +} + +/* Hash table for prototype */ +static const HashTableValue JSReadableStatePrototypeTableValues[] + = { + { "pipesCount"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsReadableState_pipesCount, 0 } }, + { "paused"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsReadableState_paused, setJSReadableState_paused } }, + }; + +void JSReadableStatePrototype::finishCreation(VM& vm, JSC::JSGlobalObject* globalThis) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSReadableState::info(), JSReadableStatePrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSReadableStatePrototype::s_info = { "ReadableState"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableStatePrototype) }; + +void JSReadableStateConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype) +{ + Base::finishCreation(vm, 0, "ReadableState"_s, PropertyAdditionMode::WithoutStructureTransition); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + ASSERT(inherits(info())); +} + +JSReadableStateConstructor* JSReadableStateConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSReadableStatePrototype* prototype) { + JSReadableStateConstructor* ptr = new (NotNull, JSC::allocateCell<JSReadableStateConstructor>(vm)) JSReadableStateConstructor(vm, structure, construct); + ptr->finishCreation(vm, globalObject, prototype); + return ptr; +} + +JSC::EncodedJSValue JSReadableStateConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + JSC::VM& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + if (callFrame->argumentCount() < 3) { + throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); + return JSValue::encode(jsUndefined()); + } + JSValue optionsVal = callFrame->uncheckedArgument(0); + JSValue streamVal = callFrame->uncheckedArgument(1); + JSValue isDuplexVal = callFrame->uncheckedArgument(2); + + bool isDuplex; + if (!isDuplexVal.isBoolean()) { + // change this to `stream instanceof Duplex` after native Duplex is implemented. + JSC::throwTypeError(lexicalGlobalObject, throwScope, "isDuplex should be boolean"_s); + return JSValue::encode(jsUndefined()); + } + isDuplex = isDuplexVal.toBoolean(lexicalGlobalObject); + JSObject* options = nullptr; + if (optionsVal.toBoolean(lexicalGlobalObject) && optionsVal.isObject()) { + options = optionsVal.toObject(lexicalGlobalObject); + } + + JSReadableState* stringDecoder = JSReadableState::create( + vm, lexicalGlobalObject, reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject)->JSReadableStateStructure(), isDuplex, options); + return JSC::JSValue::encode(stringDecoder); +} + +void JSReadableStateConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype) +{ +} + +const ClassInfo JSReadableStateConstructor::s_info = { "ReadableState"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableStateConstructor) }; + +} // namespace Zig diff --git a/src/bun.js/bindings/JSReadableState.h b/src/bun.js/bindings/JSReadableState.h new file mode 100644 index 000000000..fd78f0411 --- /dev/null +++ b/src/bun.js/bindings/JSReadableState.h @@ -0,0 +1,110 @@ +#pragma once + +#include "root.h" +#include "BufferEncodingType.h" + +namespace WebCore { +using namespace JSC; + +class JSReadableState : public JSC::JSDestructibleObject { + using Base = JSC::JSDestructibleObject; + +public: + JSReadableState(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure), m_paused(0) + { + } + + DECLARE_INFO; + + static constexpr unsigned StructureFlags = Base::StructureFlags; + + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, + JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, + JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + static JSReadableState* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, bool isDuplex, JSObject* options) + { + JSReadableState* accessor = new (NotNull, JSC::allocateCell<JSReadableState>(vm)) JSReadableState(vm, structure); + accessor->finishCreation(vm, globalObject, isDuplex, options); + return accessor; + } + + void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options); + static void destroy(JSCell*) {} + + // 0 for null, 1 for true, -1 for false + int8_t m_paused; +}; + +class JSReadableStatePrototype : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSReadableStatePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSReadableStatePrototype* ptr = new (NotNull, JSC::allocateCell<JSReadableStatePrototype>(vm)) JSReadableStatePrototype(vm, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSReadableStatePrototype(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); +}; + +class JSReadableStateConstructor final : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + static JSReadableStateConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSReadableStatePrototype* prototype); + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype); + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + DECLARE_EXPORT_INFO; + +private: + JSReadableStateConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) + : Base(vm, structure, nativeFunction, nativeFunction) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype); +}; + +} diff --git a/src/bun.js/bindings/JSStringDecoder.cpp b/src/bun.js/bindings/JSStringDecoder.cpp index d9af6e49e..c4daad1dd 100644 --- a/src/bun.js/bindings/JSStringDecoder.cpp +++ b/src/bun.js/bindings/JSStringDecoder.cpp @@ -411,16 +411,6 @@ void JSStringDecoderConstructor::initializeProperties(VM& vm, JSC::JSGlobalObjec { } -JSC::GCClient::IsoSubspace* JSStringDecoderConstructor::subspaceForImpl(JSC::VM& vm) -{ - return WebCore::subspaceForImpl<JSStringDecoderConstructor, UseCustomHeapCellType::No>( - vm, - [](auto& spaces) { return spaces.m_clientSubspaceForStringDecoderConstructor.get(); }, - [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForStringDecoderConstructor = WTFMove(space); }, - [](auto& spaces) { return spaces.m_subspaceForStringDecoderConstructor.get(); }, - [](auto& spaces, auto&& space) { spaces.m_subspaceForStringDecoderConstructor = WTFMove(space); }); -} - const ClassInfo JSStringDecoderConstructor::s_info = { "StringDecoder"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSStringDecoderConstructor) }; } // namespace Zig diff --git a/src/bun.js/bindings/JSStringDecoder.h b/src/bun.js/bindings/JSStringDecoder.h index de2c54209..299c2fb96 100644 --- a/src/bun.js/bindings/JSStringDecoder.h +++ b/src/bun.js/bindings/JSStringDecoder.h @@ -103,15 +103,6 @@ public: return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); } - template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) - { - if constexpr (mode == JSC::SubspaceAccess::Concurrently) - return nullptr; - return subspaceForImpl(vm); - } - - static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); - void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype); // Must be defined for each specialization class. diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 2753707e8..34c12161e 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -88,6 +88,7 @@ #include "JSCloseEvent.h" #include "JSFetchHeaders.h" #include "JSStringDecoder.h" +#include "JSReadableState.h" #include "Process.h" @@ -1070,9 +1071,8 @@ JSC: if (string == bunStreamString) { auto* obj = constructEmptyObject(globalObject); - auto* bufferList = JSC::JSFunction::create( - vm, globalObject, 0, "BufferList"_s, WebCore::constructJSBufferList, ImplementationVisibility::Public, NoIntrinsic, WebCore::constructJSBufferList); - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "BufferList"_s)), bufferList, 0); + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "BufferList"_s)), reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSBufferList(), 0); + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "ReadableState"_s)), reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSReadableState(), 0); return JSValue::encode(obj); } @@ -1995,6 +1995,18 @@ void GlobalObject::finishCreation(VM& vm) init.setConstructor(constructor); }); + m_JSBufferListClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + auto* prototype = JSBufferListPrototype::create( + init.vm, init.global, JSBufferListPrototype::createStructure(init.vm, init.global, init.global->objectPrototype())); + auto* structure = JSBufferList::createStructure(init.vm, init.global, prototype); + auto* constructor = JSBufferListConstructor::create( + init.vm, init.global, JSBufferListConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype); + init.setPrototype(prototype); + init.setStructure(structure); + init.setConstructor(constructor); + }); + m_JSStringDecoderClassStructure.initLater( [](LazyClassStructure::Initializer& init) { auto* prototype = JSStringDecoderPrototype::create( @@ -2007,6 +2019,18 @@ void GlobalObject::finishCreation(VM& vm) init.setConstructor(constructor); }); + m_JSReadableStateClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + auto* prototype = JSReadableStatePrototype::create( + init.vm, init.global, JSReadableStatePrototype::createStructure(init.vm, init.global, init.global->objectPrototype())); + auto* structure = JSReadableState::createStructure(init.vm, init.global, prototype); + auto* constructor = JSReadableStateConstructor::create( + init.vm, init.global, JSReadableStateConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype); + init.setPrototype(prototype); + init.setStructure(structure); + init.setConstructor(constructor); + }); + m_JSFFIFunctionStructure.initLater( [](LazyClassStructure::Initializer& init) { init.setStructure(Zig::JSFFIFunction::createStructure(init.vm, init.global, init.global->functionPrototype())); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 160aef714..300d5d60d 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -192,10 +192,18 @@ public: JSC::JSValue HTTPSResponseSinkPrototype() { return m_JSHTTPSResponseSinkClassStructure.prototypeInitializedOnMainThread(this); } JSC::JSValue JSReadableHTTPSResponseSinkControllerPrototype() { return m_JSHTTPSResponseControllerPrototype.getInitializedOnMainThread(this); } + JSC::Structure* JSBufferListStructure() { return m_JSBufferListClassStructure.getInitializedOnMainThread(this); } + JSC::JSObject* JSBufferList() { return m_JSBufferListClassStructure.constructorInitializedOnMainThread(this); } + JSC::JSValue JSBufferListPrototype() { return m_JSBufferListClassStructure.prototypeInitializedOnMainThread(this); } + JSC::Structure* JSStringDecoderStructure() { return m_JSStringDecoderClassStructure.getInitializedOnMainThread(this); } JSC::JSObject* JSStringDecoder() { return m_JSStringDecoderClassStructure.constructorInitializedOnMainThread(this); } JSC::JSValue JSStringDecoderPrototype() { return m_JSStringDecoderClassStructure.prototypeInitializedOnMainThread(this); } + JSC::Structure* JSReadableStateStructure() { return m_JSReadableStateClassStructure.getInitializedOnMainThread(this); } + JSC::JSObject* JSReadableState() { return m_JSReadableStateClassStructure.constructorInitializedOnMainThread(this); } + JSC::JSValue JSReadableStatePrototype() { return m_JSReadableStateClassStructure.prototypeInitializedOnMainThread(this); } + JSC::JSMap* readableStreamNativeMap() { return m_lazyReadableStreamPrototypeMap.getInitializedOnMainThread(this); } JSC::JSMap* requireMap() { return m_requireMap.getInitializedOnMainThread(this); } JSC::JSObject* encodeIntoObjectPrototype() { return m_encodeIntoObjectPrototype.getInitializedOnMainThread(this); } @@ -348,7 +356,9 @@ private: LazyClassStructure m_JSArrayBufferSinkClassStructure; LazyClassStructure m_JSHTTPResponseSinkClassStructure; LazyClassStructure m_JSHTTPSResponseSinkClassStructure; + LazyClassStructure m_JSBufferListClassStructure; LazyClassStructure m_JSStringDecoderClassStructure; + LazyClassStructure m_JSReadableStateClassStructure; LazyProperty<JSGlobalObject, JSObject> m_JSArrayBufferControllerPrototype; LazyProperty<JSGlobalObject, JSObject> m_JSHTTPSResponseControllerPrototype; diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index 2186177ab..16a7c0468 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -2234,7 +2234,7 @@ pub const ZigConsoleClient = struct { while (props_iter.next()) |key| { var property_value = props_iter.value; - const tag = Tag.get(JSValue.fromRef(property_value.asObjectRef()), this.globalThis); + const tag = Tag.get(property_value, this.globalThis); if (tag.cell.isHidden()) continue; diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index 456ef6aa4..8f976919c 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -27,7 +27,7 @@ public: std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSinkController; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSink; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForStringDecoder; - std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForStringDecoderConstructor; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForReadableState; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPendingVirtualModuleResult; #include "ZigGeneratedClasses+DOMClientIsoSubspaces.h" /* --- bun --- */ diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index c5e01c902..98d7de159 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -27,7 +27,7 @@ public: std::unique_ptr<IsoSubspace> m_subspaceForJSSinkController; std::unique_ptr<IsoSubspace> m_subspaceForJSSink; std::unique_ptr<IsoSubspace> m_subspaceForStringDecoder; - std::unique_ptr<IsoSubspace> m_subspaceForStringDecoderConstructor; + std::unique_ptr<IsoSubspace> m_subspaceForReadableState; std::unique_ptr<IsoSubspace> m_subspaceForPendingVirtualModuleResult; #include "ZigGeneratedClasses+DOMIsoSubspaces.h" /*-- BUN --*/ diff --git a/src/bun.js/streams.exports.js b/src/bun.js/streams.exports.js index 21521bc61..99e63adba 100644 --- a/src/bun.js/streams.exports.js +++ b/src/bun.js/streams.exports.js @@ -2489,6 +2489,7 @@ var require_readable = __commonJS({ Symbol: Symbol2, } = require_primordials(); module.exports = Readable; + var ReadableState = globalThis[Symbol.for("Bun.lazy")]("bun:stream").ReadableState; Readable.ReadableState = ReadableState; var { EventEmitter: EE } = __require("events"); var { Stream, prependListener } = require_legacy(); @@ -2511,56 +2512,12 @@ var require_readable = __commonJS({ }, } = require_errors(); var { validateObject } = require_validators(); - var kPaused = Symbol2("kPaused"); var { StringDecoder } = __require("string_decoder"); var from = require_from(); ObjectSetPrototypeOf(Readable.prototype, Stream.prototype); ObjectSetPrototypeOf(Readable, Stream); var nop = () => {}; var { errorOrDestroy } = destroyImpl; - function ReadableState(options, stream, isDuplex) { - if (typeof isDuplex !== "boolean") - isDuplex = stream instanceof require_duplex(); - this.objectMode = !!(options && options.objectMode); - if (isDuplex) - this.objectMode = - this.objectMode || !!(options && options.readableObjectMode); - this.highWaterMark = options - ? getHighWaterMark(this, options, "readableHighWaterMark", isDuplex) - : getDefaultHighWaterMark(false); - this.buffer = new BufferList(); - this.length = 0; - this.pipes = []; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; - this.constructed = true; - this.sync = true; - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - this[kPaused] = null; - this.errorEmitted = false; - this.emitClose = !options || options.emitClose !== false; - this.autoDestroy = !options || options.autoDestroy !== false; - this.destroyed = false; - this.errored = null; - this.closed = false; - this.closeEmitted = false; - this.defaultEncoding = (options && options.defaultEncoding) || "utf8"; - this.awaitDrainWriters = null; - this.multiAwaitDrain = false; - this.readingMore = false; - this.dataEmitted = false; - this.decoder = null; - this.encoding = null; - if (options && options.encoding) { - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } - } function Readable(options) { if (!(this instanceof Readable)) return new Readable(options); const isDuplex = this instanceof require_duplex(); @@ -2681,7 +2638,7 @@ var require_readable = __commonJS({ } Readable.prototype.isPaused = function () { const state = this._readableState; - return state[kPaused] === true || state.flowing === false; + return state.paused === true || state.flowing === false; }; Readable.prototype.setEncoding = function (enc) { const decoder = new StringDecoder(enc); @@ -3074,7 +3031,7 @@ var require_readable = __commonJS({ function updateReadableListening(self) { const state = self._readableState; state.readableListening = self.listenerCount("readable") > 0; - if (state.resumeScheduled && state[kPaused] === false) { + if (state.resumeScheduled && state.paused === false) { state.flowing = true; } else if (self.listenerCount("data") > 0) { self.resume(); @@ -3093,7 +3050,7 @@ var require_readable = __commonJS({ state.flowing = !state.readableListening; resume(this, state); } - state[kPaused] = false; + state.paused = false; return this; }; function resume(stream, state) { @@ -3119,7 +3076,7 @@ var require_readable = __commonJS({ this._readableState.flowing = false; this.emit("pause"); } - this._readableState[kPaused] = true; + this._readableState.paused = true; return this; }; function flow(stream) { @@ -3340,21 +3297,6 @@ var require_readable = __commonJS({ }, }, }); - ObjectDefineProperties(ReadableState.prototype, { - pipesCount: { - get() { - return this.pipes.length; - }, - }, - paused: { - get() { - return this[kPaused] !== false; - }, - set(value) { - this[kPaused] = !!value; - }, - }, - }); Readable._fromList = fromList; function fromList(n, state) { if (state.length === 0) return null; |