diff options
author | 2023-09-07 07:45:00 -0700 | |
---|---|---|
committer | 2023-09-07 07:45:00 -0700 | |
commit | 36e5a072a94805d5644081052391860e2b51f710 (patch) | |
tree | 861b4ddc665e28ba321dd5ded17c29371b3358c5 /src/bun.js | |
parent | 57a06745a48093c25d0f4729ccea41a918d6427d (diff) | |
download | bun-36e5a072a94805d5644081052391860e2b51f710.tar.gz bun-36e5a072a94805d5644081052391860e2b51f710.tar.zst bun-36e5a072a94805d5644081052391860e2b51f710.zip |
revert (#4539)
* Revert "remove native events from streams"
This reverts commit e063a47a53744a2bf5b1c2dd433698c9e37b75d6.
* finish revert
* remove accidental submodule
* dfghj
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/bindings/JSReadableHelper.cpp | 263 | ||||
-rw-r--r-- | src/bun.js/bindings/JSReadableHelper.h | 13 | ||||
-rw-r--r-- | src/bun.js/bindings/JSReadableState.cpp | 426 | ||||
-rw-r--r-- | src/bun.js/bindings/JSReadableState.h | 154 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 62 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.h | 9 |
6 files changed, 916 insertions, 11 deletions
diff --git a/src/bun.js/bindings/JSReadableHelper.cpp b/src/bun.js/bindings/JSReadableHelper.cpp new file mode 100644 index 000000000..0c459f329 --- /dev/null +++ b/src/bun.js/bindings/JSReadableHelper.cpp @@ -0,0 +1,263 @@ +#include "JSReadableHelper.h" +#include "JSReadableState.h" +#include "JSBufferList.h" +#include "JSBuffer.h" +#include "JSEventEmitter.h" +#include "JSStringDecoder.h" +#include "JavaScriptCore/Lookup.h" +#include "JavaScriptCore/ObjectConstructor.h" +#include "ZigGlobalObject.h" +#include "JSDOMOperation.h" +#include "JSDOMAttribute.h" +#include "headers.h" +#include "JSDOMConvertEnumeration.h" +#include "JavaScriptCore/StrongInlines.h" +#include "BunClientData.h" + +namespace WebCore { +using namespace JSC; + +#define JSReadableHelper_EXTRACT_STREAM_STATE \ + VM& vm = lexicalGlobalObject->vm(); \ + auto throwScope = DECLARE_THROW_SCOPE(vm); \ + \ + if (callFrame->argumentCount() < 2) { \ + throwTypeError(lexicalGlobalObject, throwScope, "Not enough arguments"_s); \ + return JSValue::encode(jsUndefined()); \ + } \ + \ + JSObject* stream = callFrame->uncheckedArgument(0).toObject(lexicalGlobalObject); \ + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); \ + JSReadableState* state = jsCast<JSReadableState*>(callFrame->uncheckedArgument(1)); \ + if (!state) { \ + throwTypeError(lexicalGlobalObject, throwScope, "Second argument not ReadableState"_s); \ + return JSValue::encode(jsUndefined()); \ + } + +static bool callRead(JSValue stream, JSFunction* read, JSC::MarkedArgumentBuffer&& args, JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, EventEmitter& emitter) +{ + WTF::NakedPtr<JSC::Exception> exceptionPtr; + JSC::CallData callData = JSC::getCallData(read); + JSValue ret = call(lexicalGlobalObject, read, callData, JSValue(stream), WTFMove(args), exceptionPtr); + if (auto* exception = exceptionPtr.get()) { + JSC::Identifier errorEventName = JSC::Identifier::fromString(vm, "error"_s); + if (emitter.hasEventListeners(errorEventName)) { + args.clear(); + JSValue val = exception->value(); + if (!val) { + val = jsUndefined(); + } + args.append(val); + emitter.emitForBindings(errorEventName, args); + } else { + reportException(lexicalGlobalObject, exception); + } + return true; + } + + return !ret.isUndefinedOrNull(); +} + +JSC_DEFINE_HOST_FUNCTION(jsReadable_maybeReadMore, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + JSReadableHelper_EXTRACT_STREAM_STATE + + auto clientData + = WebCore::clientData(vm); + auto readIdentifier = clientData->builtinNames().readPublicName(); + auto read = stream->get(lexicalGlobalObject, readIdentifier); + + auto callData = JSC::getCallData(read); + if (callData.type == CallData::Type::None) { + throwException(lexicalGlobalObject, throwScope, createNotAFunctionError(lexicalGlobalObject, read)); + return JSValue::encode({}); + } + + auto* jsEmitter = jsEventEmitterCastFast(vm, lexicalGlobalObject, stream); + RETURN_IF_EXCEPTION(throwScope, {}); + if (UNLIKELY(!jsEmitter)) { + throwTypeError(lexicalGlobalObject, throwScope, "Stream must be an EventEmitter"_s); + return JSValue::encode(JSValue {}); + } + auto& emitter = jsEmitter->wrapped(); + + while ( + !state->getBool(JSReadableState::reading) && !state->getBool(JSReadableState::ended) && (state->m_length < state->m_highWaterMark || (state->m_flowing > 0 && state->m_length == 0))) { + int64_t len = state->m_length; + MarkedArgumentBuffer args; + args.append(jsNumber(0)); + + callRead(stream, jsCast<JSFunction*>(read), WTFMove(args), vm, lexicalGlobalObject, emitter); + + if (len == state->m_length) + break; + } + RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); +} + +void flow(JSGlobalObject* lexicalGlobalObject, JSObject* streamObj, JSReadableState* state) +{ + VM& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + auto clientData = WebCore::clientData(vm); + auto readIdentifier = clientData->builtinNames().readPublicName(); + auto read = streamObj->get(lexicalGlobalObject, readIdentifier); + + auto callData = JSC::getCallData(read); + if (callData.type == CallData::Type::None) { + throwException(lexicalGlobalObject, throwScope, createNotAFunctionError(lexicalGlobalObject, read)); + return; + } + + if (state->m_flowing > 0) { + WebCore::EventEmitter& emitter = jsEventEmitterCastFast(vm, lexicalGlobalObject, streamObj)->wrapped(); + + while (state->m_flowing > 0) { + + if (!callRead(streamObj, jsCast<JSFunction*>(read), MarkedArgumentBuffer(), vm, lexicalGlobalObject, emitter)) { + break; + } + } + } +} + +JSC_DEFINE_HOST_FUNCTION(jsReadable_resume, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + JSReadableHelper_EXTRACT_STREAM_STATE + + auto* jsEmitterWrap + = jsEventEmitterCastFast(vm, lexicalGlobalObject, stream); + + if (UNLIKELY(!jsEmitterWrap)) { + throwTypeError(lexicalGlobalObject, throwScope, "Stream must be an EventEmitter"_s); + return JSValue::encode(JSValue {}); + } + + auto& emitter = jsEmitterWrap->wrapped(); + auto clientData = WebCore::clientData(vm); + auto readIdentifier = clientData->builtinNames().readPublicName(); + + if (!state->getBool(JSReadableState::reading)) { + // stream.read(0) + MarkedArgumentBuffer args; + args.append(jsNumber(0)); + + callRead(stream, jsCast<JSFunction*>(stream->get(lexicalGlobalObject, readIdentifier)), WTFMove(args), vm, lexicalGlobalObject, emitter); + } + + state->setBool(JSReadableState::resumeScheduled, true); + // stream.emit('resume') + auto eventType = clientData->builtinNames().resumePublicName(); + MarkedArgumentBuffer args; + + emitter.emitForBindings(eventType, args); + + flow(lexicalGlobalObject, stream, state); + + if (state->m_flowing > 0 && !state->getBool(JSReadableState::reading)) { + // stream.read(0) + auto read = stream->get(lexicalGlobalObject, readIdentifier); + auto callData = JSC::getCallData(read); + if (callData.type == CallData::Type::None) { + throwException(lexicalGlobalObject, throwScope, createNotAFunctionError(lexicalGlobalObject, read)); + return JSValue::encode(jsUndefined()); + } + MarkedArgumentBuffer args; + args.append(jsNumber(0)); + callRead(stream, jsCast<JSFunction*>(read), WTFMove(args), vm, lexicalGlobalObject, emitter); + } + RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); +} + +EncodedJSValue emitReadable_(JSGlobalObject* lexicalGlobalObject, JSObject* stream, JSReadableState* state) +{ + VM& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSValue errored = state->m_errored.get(); + if (!state->getBool(JSReadableState::destroyed) && !errored.toBoolean(lexicalGlobalObject) && (state->m_length || state->getBool(JSReadableState::ended))) { + // stream.emit('readable') + auto clientData = WebCore::clientData(vm); + + auto eventType = clientData->builtinNames().readablePublicName(); + MarkedArgumentBuffer args; + auto* emitter + = jsEventEmitterCastFast(vm, lexicalGlobalObject, stream); + if (UNLIKELY(!emitter)) { + throwTypeError(lexicalGlobalObject, throwScope, "Stream must be an EventEmitter"_s); + return JSValue::encode(JSValue {}); + } + emitter->wrapped().emitForBindings(eventType, args); + + state->setBool(JSReadableState::emittedReadable, false); + } + + state->setBool(JSReadableState::needReadable, state->m_flowing <= 0 && !state->getBool(JSReadableState::ended) && state->m_length <= state->m_highWaterMark); + flow(lexicalGlobalObject, stream, state); + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsReadable_emitReadable_, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + JSReadableHelper_EXTRACT_STREAM_STATE + + emitReadable_(lexicalGlobalObject, stream, state); + + RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); +} + +EncodedJSValue emitReadable(JSGlobalObject* lexicalGlobalObject, JSObject* stream, JSReadableState* state) +{ + VM& vm = lexicalGlobalObject->vm(); + + state->setBool(JSReadableState::needReadable, false); + if (!state->getBool(JSReadableState::emittedReadable)) { + state->setBool(JSReadableState::emittedReadable, true); + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + globalObject->queueMicrotask(JSValue(globalObject->emitReadableNextTickFunction()), JSValue(stream), JSValue(state), JSValue {}, JSValue {}); + } + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsReadable_emitReadable, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + JSReadableHelper_EXTRACT_STREAM_STATE + + RELEASE_AND_RETURN(throwScope, emitReadable(lexicalGlobalObject, stream, state)); +} + +JSC_DEFINE_HOST_FUNCTION(jsReadable_onEofChunk, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + JSReadableHelper_EXTRACT_STREAM_STATE + + if (state->getBool(JSReadableState::ended)) + RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); + + auto decoder = jsDynamicCast<JSStringDecoder*>(state->m_decoder.get()); + if (decoder) { + JSString* chunk = jsDynamicCast<JSString*>(decoder->end(vm, lexicalGlobalObject, nullptr, 0)); + if (chunk && chunk->length()) { + auto buffer = jsDynamicCast<JSBufferList*>(state->m_buffer.get()); + if (!buffer) { + throwTypeError(lexicalGlobalObject, throwScope, "Not buffer on stream"_s); + return JSValue::encode(jsUndefined()); + } + buffer->push(vm, JSValue(chunk)); + state->m_length += state->getBool(JSReadableState::objectMode) ? 1 : chunk->length(); + } + } + + state->setBool(JSReadableState::ended, true); + + if (state->getBool(JSReadableState::sync)) { + RELEASE_AND_RETURN(throwScope, emitReadable(lexicalGlobalObject, stream, state)); + } else { + state->setBool(JSReadableState::needReadable, false); + state->setBool(JSReadableState::emittedReadable, true); + RELEASE_AND_RETURN(throwScope, emitReadable_(lexicalGlobalObject, stream, state)); + } +} + +#undef JSReadableHelper_EXTRACT_STREAM_STATE + +} // namespace WebCore diff --git a/src/bun.js/bindings/JSReadableHelper.h b/src/bun.js/bindings/JSReadableHelper.h new file mode 100644 index 000000000..3e2554c2b --- /dev/null +++ b/src/bun.js/bindings/JSReadableHelper.h @@ -0,0 +1,13 @@ +#pragma once + +#include "root.h" + +namespace WebCore { + +JSC_DECLARE_HOST_FUNCTION(jsReadable_maybeReadMore); +JSC_DECLARE_HOST_FUNCTION(jsReadable_resume); +JSC_DECLARE_HOST_FUNCTION(jsReadable_emitReadable); +JSC_DECLARE_HOST_FUNCTION(jsReadable_onEofChunk); +JSC_DECLARE_HOST_FUNCTION(jsReadable_emitReadable_); + +} // namespace WebCore diff --git a/src/bun.js/bindings/JSReadableState.cpp b/src/bun.js/bindings/JSReadableState.cpp new file mode 100644 index 000000000..1f3a36def --- /dev/null +++ b/src/bun.js/bindings/JSReadableState.cpp @@ -0,0 +1,426 @@ +#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" +#include "BunClientData.h" + +namespace WebCore { + +using namespace JSC; + +static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_pipesCount); + +int64_t getHighWaterMark(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options) +{ + auto throwScope = DECLARE_THROW_SCOPE(vm); + + // We must use getIfPropertyExists because: + // - it might be a getter + // - it might be from a super class + auto* clientData = WebCore::clientData(vm); + if (JSValue highWaterMarkVal = options->getIfPropertyExists(globalObject, clientData->builtinNames().highWaterMarkPublicName())) { + if (isDuplex && (highWaterMarkVal.isUndefined() || highWaterMarkVal.isNull())) { + highWaterMarkVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "readableObjectMode"_s)); + } + + if (highWaterMarkVal && highWaterMarkVal.isNumber()) { + return highWaterMarkVal.toInt32(globalObject); + } + } + + return -1; +} + +void JSReadableState::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options) +{ + Base::finishCreation(vm); + + if (options != nullptr) { + JSC::JSValue objectModeVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "objectMode"_s)); + if (isDuplex && !objectModeVal) { + objectModeVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "readableObjectMode"_s)); + } + if (objectModeVal && objectModeVal.toBoolean(globalObject)) + setBool(JSReadableState::Mask::objectMode, true); + } + + m_highWaterMark = getBool( + JSReadableState::Mask::objectMode) + ? 16 + : 16 * 1024; // default value + + if (options != nullptr) { + int64_t customHightWaterMark = getHighWaterMark(vm, globalObject, isDuplex, options); + if (customHightWaterMark >= 0) + m_highWaterMark = customHightWaterMark; + } + + m_buffer.set(vm, this, JSBufferList::create(vm, globalObject, reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSBufferListStructure())); + m_pipes.set(vm, this, JSC::constructEmptyArray(globalObject, nullptr, 0)); + + if (options != nullptr) { + JSC::JSValue emitCloseVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "emitClose"_s)); + if (!emitCloseVal || emitCloseVal.toBoolean(globalObject)) + setBool(JSReadableState::Mask::emitClose, true); + // Has it been destroyed. + JSC::JSValue autoDestroyVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "autoDestroy"_s)); + if (!autoDestroyVal || autoDestroyVal.toBoolean(globalObject)) + setBool(JSReadableState::Mask::autoDestroy, true); + } else { + setBool(JSReadableState::Mask::emitClose, true); + setBool(JSReadableState::Mask::autoDestroy, true); + } + + // Indicates whether the stream has finished destroying. + m_errored.set(vm, this, JSC::jsNull()); + + // Ref the piped dest which we need a drain event on it + // type: null | Writable | Set<Writable>. + if (options == nullptr) { + m_defaultEncoding.set(vm, this, JSC::jsString(vm, WTF::String("utf8"_s))); + } else { + if (JSC::JSValue defaultEncodingVal = getIfPropertyExists(globalObject, PropertyName(JSC::Identifier::fromString(vm, "defaultEncoding"_s)))) { + m_defaultEncoding.set(vm, this, defaultEncodingVal); + } else { + m_defaultEncoding.set(vm, this, JSC::jsString(vm, WTF::String("utf8"_s))); + } + } + + m_awaitDrainWriters.set(vm, this, JSC::jsNull()); + JSValue decodeValue = JSC::jsNull(); + JSValue encodingValue = JSC::jsNull(); + + if (options != nullptr) { + JSC::JSValue encodingVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "encoding"_s)); + if (encodingVal && encodingVal.isString()) { + 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); + decodeValue = decoder; + encodingValue = encodingVal; + } + } + + m_decoder.set(vm, this, decodeValue); + m_encoding.set(vm, this, encodingValue); + + // ReadableState.constructed is set to false during construction when a _construct method is implemented + // this is here so that the ReadableState behavior tracks the behavior in node, and that calling Readable.read + // will work when we return early from construct because there is no Readable._construct implemented + // See: https://github.com/nodejs/node/blob/main/lib/internal/streams/readable.js + setBool(JSReadableState::Mask::constructed, true); +} + +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 = std::forward<decltype(space)>(space); }, + [](auto& spaces) { return spaces.m_subspaceForReadableState.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForReadableState = std::forward<decltype(space)>(space); }); +} + +template<typename Visitor> +void JSReadableState::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSReadableState* state = jsCast<JSReadableState*>(cell); + ASSERT_GC_OBJECT_INHERITS(state, info()); + Base::visitChildren(state, visitor); + visitor.append(state->m_buffer); + visitor.append(state->m_pipes); + visitor.append(state->m_errored); + visitor.append(state->m_defaultEncoding); + visitor.append(state->m_awaitDrainWriters); + visitor.append(state->m_decoder); + visitor.append(state->m_encoding); +} +DEFINE_VISIT_CHILDREN(JSReadableState); + +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); + JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); + if (!state) { + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + } + JSArray* pipes = JSC::jsDynamicCast<JSArray*>(state->m_pipes.get()); + if (!pipes) { + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + } + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(pipes->length()))); +} + +#define JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(NAME) \ + static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (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_##NAME == 0) \ + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNull())); \ + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsBoolean(state->m_##NAME > 0))); \ + } \ + static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (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); \ + } \ + auto value = JSC::JSValue::decode(encodedValue); \ + state->m_##NAME = value.isNull() ? 0 : value.toBoolean(lexicalGlobalObject) ? 1 \ + : -1; \ + RELEASE_AND_RETURN(throwScope, true); \ + } + +JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(paused) + JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(flowing) + +#undef JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER + +#define JSReadableState_NUMBER_GETTER_SETTER(NAME) \ + static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (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())); \ + } \ + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(state->m_##NAME))); \ + } \ + \ + static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (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_##NAME = JSC::JSValue::decode(encodedValue).toNumber(lexicalGlobalObject); \ + RETURN_IF_EXCEPTION(throwScope, false); \ + RELEASE_AND_RETURN(throwScope, true); \ + } + + JSReadableState_NUMBER_GETTER_SETTER(length) + JSReadableState_NUMBER_GETTER_SETTER(highWaterMark) + +#undef JSReadableState_NUMBER_GETTER_SETTER + +#define JSReadableState_BOOLEAN_GETTER_SETTER(NAME) \ + static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (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())); \ + } \ + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsBoolean(state->getBool(JSReadableState::Mask::NAME)))); \ + } \ + \ + static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (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->setBool(JSReadableState::Mask::NAME, JSC::JSValue::decode(encodedValue).toBoolean(lexicalGlobalObject)); \ + RELEASE_AND_RETURN(throwScope, true); \ + } + + JSReadableState_BOOLEAN_GETTER_SETTER(objectMode) + JSReadableState_BOOLEAN_GETTER_SETTER(ended) + JSReadableState_BOOLEAN_GETTER_SETTER(endEmitted) + JSReadableState_BOOLEAN_GETTER_SETTER(reading) + JSReadableState_BOOLEAN_GETTER_SETTER(constructed) + JSReadableState_BOOLEAN_GETTER_SETTER(sync) + JSReadableState_BOOLEAN_GETTER_SETTER(needReadable) + JSReadableState_BOOLEAN_GETTER_SETTER(emittedReadable) + JSReadableState_BOOLEAN_GETTER_SETTER(readableListening) + JSReadableState_BOOLEAN_GETTER_SETTER(resumeScheduled) + JSReadableState_BOOLEAN_GETTER_SETTER(errorEmitted) + JSReadableState_BOOLEAN_GETTER_SETTER(emitClose) + JSReadableState_BOOLEAN_GETTER_SETTER(autoDestroy) + JSReadableState_BOOLEAN_GETTER_SETTER(destroyed) + JSReadableState_BOOLEAN_GETTER_SETTER(closed) + JSReadableState_BOOLEAN_GETTER_SETTER(closeEmitted) + JSReadableState_BOOLEAN_GETTER_SETTER(multiAwaitDrain) + JSReadableState_BOOLEAN_GETTER_SETTER(readingMore) + JSReadableState_BOOLEAN_GETTER_SETTER(dataEmitted) + +#undef JSReadableState_BOOLEAN_GETTER_SETTER + +#define JSReadableState_JSVALUE_GETTER_SETTER(NAME) \ + static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (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())); \ + } \ + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(state->m_##NAME.get())); \ + } \ + static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ + JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (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); \ + } \ + auto value = JSC::JSValue::decode(encodedValue); \ + state->m_##NAME.set(vm, state, value); \ + RELEASE_AND_RETURN(throwScope, true); \ + } + + JSReadableState_JSVALUE_GETTER_SETTER(buffer) + JSReadableState_JSVALUE_GETTER_SETTER(pipes) + JSReadableState_JSVALUE_GETTER_SETTER(errored) + JSReadableState_JSVALUE_GETTER_SETTER(defaultEncoding) + JSReadableState_JSVALUE_GETTER_SETTER(awaitDrainWriters) + JSReadableState_JSVALUE_GETTER_SETTER(decoder) + JSReadableState_JSVALUE_GETTER_SETTER(encoding) + +#undef JSReadableState_JSVALUE_GETTER_SETTER + +#define JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(NAME) \ + { \ +#NAME ""_s, static_cast < unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, \ + { \ + HashTableValue::GetterSetterType, jsReadableState_##NAME, setJSReadableState_##NAME \ + } \ + } + + /* 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 } }, + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(paused), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(flowing), + + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(objectMode), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(ended), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(endEmitted), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(reading), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(constructed), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(sync), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(needReadable), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(emittedReadable), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(readableListening), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(resumeScheduled), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(errorEmitted), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(emitClose), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(autoDestroy), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(destroyed), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(closed), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(closeEmitted), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(multiAwaitDrain), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(readingMore), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(dataEmitted), + + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(length), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(highWaterMark), + + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(buffer), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(pipes), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(errored), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(defaultEncoding), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(awaitDrainWriters), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(decoder), + JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(encoding), + }; + +#undef JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE + +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); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + JSObject* options = nullptr; + if (optionsVal && optionsVal.isObject()) { + options = optionsVal.toObject(lexicalGlobalObject); + } + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + 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..c67baebad --- /dev/null +++ b/src/bun.js/bindings/JSReadableState.h @@ -0,0 +1,154 @@ +#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_VISIT_CHILDREN; + 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*) {} + + enum Mask : uint32_t { + objectMode = 1 << 0, + emitClose = 1 << 1, + autoDestroy = 1 << 2, + ended = 1 << 3, + endEmitted = 1 << 4, + reading = 1 << 5, + constructed = 1 << 6, + sync = 1 << 7, + needReadable = 1 << 8, + emittedReadable = 1 << 9, + readableListening = 1 << 10, + resumeScheduled = 1 << 11, + errorEmitted = 1 << 12, + destroyed = 1 << 13, + closed = 1 << 14, + closeEmitted = 1 << 15, + multiAwaitDrain = 1 << 16, + readingMore = 1 << 17, + dataEmitted = 1 << 18, + }; + + constexpr bool getBool(Mask mask) { return m_bools.contains(mask); } + constexpr void setBool(Mask mask, bool val) + { + m_bools.set(mask, val); + } + + // 0 for null, 1 for true, -1 for false + int8_t m_paused = 0; + int8_t m_flowing = 0; + + WTF::OptionSet<Mask> m_bools; + + int64_t m_length = 0; + int64_t m_highWaterMark; + + mutable WriteBarrier<Unknown> m_buffer; + mutable WriteBarrier<Unknown> m_pipes; + mutable WriteBarrier<Unknown> m_errored; + mutable WriteBarrier<Unknown> m_defaultEncoding; + mutable WriteBarrier<Unknown> m_awaitDrainWriters; + mutable WriteBarrier<Unknown> m_decoder; + mutable WriteBarrier<Unknown> m_encoding; +}; + +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/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 091cc0b29..6398fb7a0 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -90,6 +90,8 @@ #include "JSCloseEvent.h" #include "JSFetchHeaders.h" #include "JSStringDecoder.h" +#include "JSReadableState.h" +#include "JSReadableHelper.h" #include "Process.h" #include "AsyncContextFrame.h" @@ -1626,7 +1628,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, switch (callFrame->argumentCount()) { case 0: { - JSC::throwTypeError(globalObject, scope, "$lazy needs 1 argument (a string)"_s); + JSC::throwTypeError(globalObject, scope, "lazyLoad needs 1 argument (a string)"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1635,7 +1637,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, if (moduleName.isNumber()) { switch (moduleName.toInt32(globalObject)) { case 0: { - JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); + JSC::throwTypeError(globalObject, scope, "lazyLoad expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1652,7 +1654,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, default: { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); + JSC::throwTypeError(globalObject, scope, "lazyLoad expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1661,7 +1663,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, auto string = moduleName.toWTFString(globalObject); if (string.isNull()) { - JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); + JSC::throwTypeError(globalObject, scope, "lazyLoad expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1671,6 +1673,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, } if (string == "worker_threads"_s) { + JSValue workerData = jsUndefined(); JSValue threadId = jsNumber(0); @@ -1705,6 +1708,27 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, JSFunction::create(vm, globalObject, 1, fileURLToPathString, functionFileURLToPath, ImplementationVisibility::Public, NoIntrinsic)); } + if (string == "bun:stream"_s) { + auto* obj = constructEmptyObject(globalObject); + 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); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "maybeReadMore"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "maybeReadMore"_s, jsReadable_maybeReadMore, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "resume"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "resume"_s, jsReadable_resume, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "emitReadable"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "emitReadable"_s, jsReadable_emitReadable, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "onEofChunk"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "onEofChunk"_s, jsReadable_onEofChunk, ImplementationVisibility::Public), 0); + return JSValue::encode(obj); + } + if (string == "events"_s) { + return JSValue::encode(WebCore::JSEventEmitter::getConstructor(vm, globalObject)); + } if (string == "internal/tls"_s) { auto* obj = constructEmptyObject(globalObject); @@ -1735,9 +1759,9 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, return JSValue::encode(obj); } - // if (string == "masqueradesAsUndefined"_s) { - // return JSValue::encode(InternalFunction::createFunctionThatMasqueradesAsUndefined(vm, globalObject, 0, String(), functionCallNotImplemented)); - // } + if (string == "masqueradesAsUndefined"_s) { + return JSValue::encode(InternalFunction::createFunctionThatMasqueradesAsUndefined(vm, globalObject, 0, String(), functionCallNotImplemented)); + } if (string == "vm"_s) { auto* obj = constructEmptyObject(globalObject); @@ -1794,9 +1818,9 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, return JSC::JSValue::encode(obj); } - JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); - scope.release(); - return JSC::JSValue::encode(JSC::JSValue {}); + return JSC::JSValue::encode(JSC::jsUndefined()); + + break; } } } @@ -2981,6 +3005,10 @@ void GlobalObject::finishCreation(VM& vm) [](const Initializer<JSFunction>& init) { init.set(JSFunction::create(init.vm, init.owner, 4, "performMicrotask"_s, jsFunctionPerformMicrotask, ImplementationVisibility::Public)); }); + m_emitReadableNextTickFunction.initLater( + [](const Initializer<JSFunction>& init) { + init.set(JSFunction::create(init.vm, init.owner, 4, "emitReadable"_s, WebCore::jsReadable_emitReadable_, ImplementationVisibility::Public)); + }); m_bunSleepThenCallback.initLater( [](const Initializer<JSFunction>& init) { @@ -3295,6 +3323,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())); @@ -4084,6 +4124,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_JSFileSinkClassStructure.visit(visitor); thisObject->m_JSHTTPResponseSinkClassStructure.visit(visitor); thisObject->m_JSHTTPSResponseSinkClassStructure.visit(visitor); + thisObject->m_JSReadableStateClassStructure.visit(visitor); thisObject->m_JSStringDecoderClassStructure.visit(visitor); thisObject->m_NapiClassStructure.visit(visitor); thisObject->m_JSBufferClassStructure.visit(visitor); @@ -4109,6 +4150,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_subtleCryptoObject.visit(visitor); thisObject->m_JSHTTPResponseController.visit(visitor); thisObject->m_callSiteStructure.visit(visitor); + thisObject->m_emitReadableNextTickFunction.visit(visitor); thisObject->m_JSBufferSubclassStructure.visit(visitor); thisObject->m_cryptoObject.visit(visitor); thisObject->m_JSDOMFileConstructor.visit(visitor); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 7377e6693..29c1cd09c 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -238,6 +238,10 @@ public: 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::Structure* NodeVMScriptStructure() { return m_NodeVMScriptClassStructure.getInitializedOnMainThread(this); } JSC::JSObject* NodeVMScript() { return m_NodeVMScriptClassStructure.constructorInitializedOnMainThread(this); } JSC::JSValue NodeVMScriptPrototype() { return m_NodeVMScriptClassStructure.prototypeInitializedOnMainThread(this); } @@ -257,6 +261,8 @@ public: JSC::JSFunction* utilInspectStylizeColorFunction() { return m_utilInspectStylizeColorFunction.getInitializedOnMainThread(this); } JSC::JSFunction* utilInspectStylizeNoColorFunction() { return m_utilInspectStylizeNoColorFunction.getInitializedOnMainThread(this); } + JSC::JSFunction* emitReadableNextTickFunction() { return m_emitReadableNextTickFunction.getInitializedOnMainThread(this); } + JSObject* requireFunctionUnbound() { return m_requireFunctionUnbound.getInitializedOnMainThread(this); } JSObject* requireResolveFunctionUnbound() { return m_requireResolveFunctionUnbound.getInitializedOnMainThread(this); } Bun::InternalModuleRegistry* internalModuleRegistry() { return m_internalModuleRegistry.getInitializedOnMainThread(this); } @@ -500,6 +506,7 @@ private: LazyClassStructure m_JSFileSinkClassStructure; LazyClassStructure m_JSHTTPResponseSinkClassStructure; LazyClassStructure m_JSHTTPSResponseSinkClassStructure; + LazyClassStructure m_JSReadableStateClassStructure; LazyClassStructure m_JSStringDecoderClassStructure; LazyClassStructure m_NapiClassStructure; LazyClassStructure m_callSiteStructure; @@ -523,7 +530,7 @@ private: LazyProperty<JSGlobalObject, JSFunction> m_utilInspectFunction; LazyProperty<JSGlobalObject, JSFunction> m_utilInspectStylizeColorFunction; LazyProperty<JSGlobalObject, JSFunction> m_utilInspectStylizeNoColorFunction; - + LazyProperty<JSGlobalObject, JSFunction> m_emitReadableNextTickFunction; LazyProperty<JSGlobalObject, JSMap> m_lazyReadableStreamPrototypeMap; LazyProperty<JSGlobalObject, JSMap> m_requireMap; LazyProperty<JSGlobalObject, Structure> m_encodeIntoObjectStructure; |