mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 13:22:07 +00:00
Add native helper functions for Readable and convert ReadableState properties to getter/setter (#1218)
This commit is contained in:
@@ -14,8 +14,6 @@ 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)
|
||||
{
|
||||
@@ -52,79 +50,31 @@ void JSReadableState::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObj
|
||||
}
|
||||
putDirect(vm, WTFMove(objectModeIdent), JSC::jsBoolean(objectMode));
|
||||
|
||||
int64_t highWaterMark = objectMode ? 16 : 16 * 1024; // default value
|
||||
m_highWaterMark = objectMode ? 16 : 16 * 1024; // default value
|
||||
if (options != nullptr) {
|
||||
int64_t customHightWaterMark = getHighWaterMark(vm, globalObject, isDuplex, options);
|
||||
if (customHightWaterMark >= 0)
|
||||
highWaterMark = customHightWaterMark;
|
||||
m_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));
|
||||
m_emitClose = false;
|
||||
m_autoDestroy = 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)));
|
||||
JSC::JSValue emitCloseVal = options->getDirect(vm, JSC::Identifier::fromString(vm, "emitClose"_s));
|
||||
m_emitClose = !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)));
|
||||
JSC::JSValue autoDestroyVal = options->getDirect(vm, JSC::Identifier::fromString(vm, "autoDestroy"_s));
|
||||
m_autoDestroy = !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);
|
||||
@@ -140,11 +90,6 @@ void JSReadableState::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObj
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -201,38 +146,133 @@ JSC_DEFINE_CUSTOM_GETTER(jsReadableState_pipesCount, (JSGlobalObject * lexicalGl
|
||||
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()));
|
||||
#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); \
|
||||
}
|
||||
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);
|
||||
JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(paused)
|
||||
JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(flowing)
|
||||
|
||||
#undef JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER
|
||||
|
||||
#define JSReadableState_GETTER_SETTER(NAME, TYPE) \
|
||||
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::js##TYPE(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).to##TYPE(lexicalGlobalObject); \
|
||||
RETURN_IF_EXCEPTION(throwScope, false); \
|
||||
RELEASE_AND_RETURN(throwScope, true); \
|
||||
}
|
||||
state->m_paused = JSC::JSValue::decode(encodedValue).toBoolean(lexicalGlobalObject) ? 1 : -1;
|
||||
RELEASE_AND_RETURN(throwScope, true);
|
||||
}
|
||||
|
||||
#define JSReadableState_BOOLEAN_GETTER_SETTER(NAME) \
|
||||
JSReadableState_GETTER_SETTER(NAME, Boolean)
|
||||
|
||||
#define JSReadableState_NUMBER_GETTER_SETTER(NAME) \
|
||||
JSReadableState_GETTER_SETTER(NAME, Number)
|
||||
|
||||
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)
|
||||
|
||||
JSReadableState_NUMBER_GETTER_SETTER(length)
|
||||
JSReadableState_NUMBER_GETTER_SETTER(highWaterMark)
|
||||
|
||||
#undef JSReadableState_NUMBER_GETTER_SETTER
|
||||
#undef JSReadableState_BOOLEAN_GETTER_SETTER
|
||||
#undef JSReadableState_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 } },
|
||||
{ "paused"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsReadableState_paused, setJSReadableState_paused } },
|
||||
JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(paused),
|
||||
JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(flowing),
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
#undef JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE
|
||||
|
||||
void JSReadableStatePrototype::finishCreation(VM& vm, JSC::JSGlobalObject* globalThis)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
|
||||
Reference in New Issue
Block a user