mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
more
This commit is contained in:
@@ -7,10 +7,25 @@
|
||||
#include "BunReadableStream.h"
|
||||
#include <JavaScriptCore/ObjectConstructor.h>
|
||||
#include <JavaScriptCore/Completion.h>
|
||||
#include "BunReadableStreamDefaultReader.h"
|
||||
#include "DOMIsoSubspaces.h"
|
||||
#include "BunClientData.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess mode>
|
||||
JSC::GCClient::IsoSubspace* JSReadableStreamDefaultController::subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return WebCore::subspaceForImpl<JSReadableStreamDefaultController, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForJSReadableStreamDefaultController.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSReadableStreamDefaultController = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForJSReadableStreamDefaultController.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSReadableStreamDefaultController = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsReadableStreamDefaultControllerPrototypeClose, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
@@ -380,7 +395,7 @@ bool JSReadableStreamDefaultController::shouldCallPull() const
|
||||
return false;
|
||||
|
||||
// Only pull if we need more chunks
|
||||
if (reader->numReadRequests() == 0)
|
||||
if (reader->length() == 0)
|
||||
return false;
|
||||
|
||||
double desiredSize = m_strategyHWM - m_queueTotalSize;
|
||||
|
||||
@@ -19,12 +19,7 @@ public:
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm);
|
||||
|
||||
static JSReadableStreamDefaultController* create(JSC::VM&, JSC::Structure*, JSReadableStream* stream);
|
||||
static JSC::Structure* createStructure(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue prototype);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "ErrorCode+List.h"
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
@@ -13,7 +14,7 @@
|
||||
#include "BunReadableStreamDefaultController.h"
|
||||
#include <JavaScriptCore/Completion.h>
|
||||
#include "BunReadableStreamDefaultReader.h"
|
||||
|
||||
#include "ErrorCode.h"
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
@@ -25,7 +26,7 @@ public:
|
||||
static JSReadableStreamDefaultReaderPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSReadableStreamDefaultReaderPrototype* ptr = new (NotNull, JSC::allocateCell<JSReadableStreamDefaultReaderPrototype>(vm)) JSReadableStreamDefaultReaderPrototype(vm, globalObject, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
ptr->finishCreation(vm);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -47,17 +48,9 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
void finishCreation(JSC::VM&);
|
||||
};
|
||||
|
||||
JSReadableStreamDefaultReader* JSReadableStreamDefaultReader::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSReadableStream* stream)
|
||||
{
|
||||
JSReadableStreamDefaultReader* reader = new (NotNull, JSC::allocateCell<JSReadableStreamDefaultReader>(vm)) JSReadableStreamDefaultReader(vm, structure);
|
||||
reader->finishCreation(vm);
|
||||
reader->m_stream.set(vm, reader, stream);
|
||||
return reader;
|
||||
}
|
||||
|
||||
// JSReadableStreamDefaultReader.cpp
|
||||
|
||||
static JSC_DECLARE_CUSTOM_GETTER(readableStreamDefaultReaderClosedGetter);
|
||||
@@ -108,7 +101,7 @@ template<typename Visitor>
|
||||
void JSReadableStreamDefaultReader::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* reader = jsCast<JSReadableStreamDefaultReader*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(reader, info());
|
||||
ASSERT_GC_OBJECT_INHERITS(reader, JSReadableStreamDefaultReader::info());
|
||||
Base::visitChildren(reader, visitor);
|
||||
visitor.append(reader->m_stream);
|
||||
visitor.append(reader->m_readyPromise);
|
||||
@@ -118,11 +111,6 @@ void JSReadableStreamDefaultReader::visitChildrenImpl(JSCell* cell, Visitor& vis
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSReadableStreamDefaultReader);
|
||||
|
||||
void JSReadableStreamDefaultReader::destroy(JSCell* cell)
|
||||
{
|
||||
static_cast<JSReadableStreamDefaultReader*>(cell)->JSReadableStreamDefaultReader::~JSReadableStreamDefaultReader();
|
||||
}
|
||||
|
||||
void JSReadableStreamDefaultReader::finishCreation(JSC::VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
@@ -143,7 +131,7 @@ void JSReadableStreamDefaultReader::releaseLock()
|
||||
return;
|
||||
|
||||
// Release the stream's reader reference
|
||||
m_stream->clearReader();
|
||||
m_stream->setReader(nullptr);
|
||||
detach();
|
||||
}
|
||||
|
||||
@@ -236,7 +224,7 @@ const ClassInfo JSReadableStreamDefaultReaderConstructor::s_info = { "ReadableSt
|
||||
|
||||
void JSReadableStreamDefaultReaderConstructor::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSReadableStreamDefaultReaderPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 1, "ReadableStreamDefaultReader"_s, PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
|
||||
Base::finishCreation(vm, 1, "ReadableStreamDefaultReader"_s, PropertyAdditionMode::WithStructureTransition);
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
@@ -245,7 +233,8 @@ JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSReadableStreamDefaultReaderConstr
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(globalObject, scope, "ReadableStreamDefaultReader"));
|
||||
Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_ILLEGAL_CONSTRUCTOR, "ReadableStreamDefaultReader constructor cannot be called as a function"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSReadableStreamDefaultReaderConstructor::construct(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)
|
||||
@@ -268,8 +257,19 @@ JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSReadableStreamDefaultReaderConstr
|
||||
return throwVMTypeError(globalObject, scope, "Cannot construct a ReadableStreamDefaultReader for a locked ReadableStream"_s);
|
||||
}
|
||||
|
||||
JSObject* newTarget = asObject(callFrame->newTarget());
|
||||
Structure* structure = JSC::InternalFunction::createSubclassStructure(globalObject, newTarget, getFunctionRealm(globalObject, newTarget));
|
||||
JSC::JSObject* newTarget = callFrame->newTarget().getObject();
|
||||
JSC::JSObject* constructor = callFrame->jsCallee();
|
||||
|
||||
auto* structure = defaultGlobalObject(globalObject)->readableStreamDefaultReaderStructure();
|
||||
|
||||
// TODO: double-check this.
|
||||
if (!(!newTarget || newTarget == constructor)) {
|
||||
if (newTarget) {
|
||||
structure = JSC::InternalFunction::createSubclassStructure(getFunctionRealm(globalObject, newTarget), newTarget, structure);
|
||||
} else {
|
||||
structure = JSC::InternalFunction::createSubclassStructure(globalObject, constructor, structure);
|
||||
}
|
||||
}
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
JSReadableStreamDefaultReader* reader = JSReadableStreamDefaultReader::create(vm, globalObject, structure, stream);
|
||||
@@ -283,9 +283,10 @@ JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSReadableStreamDefaultReaderConstr
|
||||
JSValue error = stream->storedError();
|
||||
if (!error)
|
||||
error = jsUndefined();
|
||||
|
||||
reader->readyPromise()->reject(globalObject, error);
|
||||
} else {
|
||||
reader->readyPromise()->resolve(globalObject, jsUndefined());
|
||||
reader->readyPromise()->fulfillWithNonPromise(globalObject, jsUndefined());
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(reader));
|
||||
|
||||
@@ -45,6 +45,8 @@ public:
|
||||
bool isActive() const { return !!m_stream; }
|
||||
void detach();
|
||||
|
||||
unsigned length() const { return m_readRequests.get()->length(); }
|
||||
|
||||
// Implements ReadableStreamDefaultReader
|
||||
void releaseLock();
|
||||
|
||||
|
||||
361
src/bun.js/bindings/BunTransformStream.cpp
Normal file
361
src/bun.js/bindings/BunTransformStream.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
#include <JavaScriptCore/JSPromise.h>
|
||||
#include "ErrorCode.h"
|
||||
#include "BunTransformStream.h"
|
||||
// #include "BunTransformStreamDefaultController.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
// Prototype implementation
|
||||
class JSTransformStreamPrototype final : public JSC::JSNonFinalObject {
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
public:
|
||||
static JSTransformStreamPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSTransformStreamPrototype* ptr = new (NotNull, JSC::allocateCell<JSTransformStreamPrototype>(vm)) JSTransformStreamPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTransformStreamPrototype, Base);
|
||||
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:
|
||||
JSTransformStreamPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
// Constructor implementation
|
||||
class JSTransformStreamConstructor final : public JSC::InternalFunction {
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
public:
|
||||
static JSTransformStreamConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSTransformStreamPrototype* prototype)
|
||||
{
|
||||
JSTransformStreamConstructor* constructor = new (NotNull, JSC::allocateCell<JSTransformStreamConstructor>(vm)) JSTransformStreamConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.internalFunctionSpace();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
private:
|
||||
JSTransformStreamConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*, JSTransformStreamPrototype*);
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
};
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsTransformStreamReadableGetter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSTransformStream*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return throwVMTypeError(globalObject, scope, "Cannot get readable property of non-TransformStream"_s);
|
||||
}
|
||||
|
||||
ASSERT(thisObject->readable());
|
||||
return JSValue::encode(thisObject->readable());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsTransformStreamWritableGetter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSTransformStream*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
return throwVMTypeError(globalObject, scope, "Cannot get writable property of non-TransformStream"_s);
|
||||
}
|
||||
|
||||
ASSERT(thisObject->writable());
|
||||
return JSValue::encode(thisObject->writable());
|
||||
}
|
||||
|
||||
// Implementing the constructor binding
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsTransformStreamConstructor,
|
||||
(JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* prototype = jsDynamicCast<JSTransformStreamPrototype*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!prototype))
|
||||
return throwVMTypeError(globalObject, scope, "Cannot get constructor for TransformStream"_s);
|
||||
|
||||
return JSValue::encode(globalObject->transformStreamConstructor());
|
||||
}
|
||||
|
||||
// All static properties for the prototype
|
||||
static const HashTableValue JSTransformStreamPrototypeTableValues[] = {
|
||||
{ "readable"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsTransformStreamReadableGetter, nullptr } },
|
||||
{ "writable"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsTransformStreamWritableGetter, nullptr } },
|
||||
{ "constructor"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsTransformStreamConstructor, nullptr } }
|
||||
};
|
||||
|
||||
// And now the constructor implementation
|
||||
const ClassInfo JSTransformStreamConstructor::s_info = {
|
||||
"Function"_s,
|
||||
&Base::s_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(JSTransformStreamConstructor)
|
||||
};
|
||||
|
||||
JSTransformStreamConstructor::JSTransformStreamConstructor(VM& vm, Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void JSTransformStreamConstructor::finishCreation(VM& vm, JSTransformStreamPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 3, "TransformStream"_s, PropertyAdditionMode::WithoutStructureTransition);
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype,
|
||||
PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
}
|
||||
|
||||
// Constructor function implementation for both 'new TransformStream()' and TransformStream() call
|
||||
JSC_DEFINE_HOST_FUNCTION(JSTransformStreamConstructor::construct, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSObject* newTarget = asObject(callFrame->newTarget());
|
||||
Structure* structure = JSC::InternalFunction::createSubclassStructure(
|
||||
globalObject, newTarget, globalObject->transformStreamStructure());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Extract constructor arguments per spec:
|
||||
// new TransformStream(transformer = undefined, writableStrategy = {}, readableStrategy = {})
|
||||
JSValue transformerArg = callFrame->argument(0);
|
||||
JSValue writableStrategyArg = callFrame->argument(1);
|
||||
JSValue readableStrategyArg = callFrame->argument(2);
|
||||
|
||||
// Create the underlying transform stream
|
||||
JSTransformStream* transformStream = JSTransformStream::create(vm, globalObject, structure);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Set up readable and writable sides with provided strategies
|
||||
if (!writableStrategyArg.isUndefined()) {
|
||||
// Apply writable strategy
|
||||
JSValue highWaterMark = writableStrategyArg.get(globalObject, vm.propertyNames->highWaterMark);
|
||||
JSValue size = writableStrategyArg.get(globalObject, vm.propertyNames->size);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
// ... apply strategy to writable side
|
||||
}
|
||||
|
||||
if (!readableStrategyArg.isUndefined()) {
|
||||
// Apply readable strategy
|
||||
JSValue highWaterMark = readableStrategyArg.get(globalObject, vm.propertyNames->highWaterMark);
|
||||
JSValue size = readableStrategyArg.get(globalObject, vm.propertyNames->size);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
// ... apply strategy to readable side
|
||||
}
|
||||
|
||||
// Handle transformer setup if provided
|
||||
if (!transformerArg.isUndefined()) {
|
||||
JSValue transformFn = transformerArg.get(globalObject, vm.propertyNames->transform);
|
||||
JSValue flushFn = transformerArg.get(globalObject, vm.propertyNames->flush);
|
||||
JSValue startFn = transformerArg.get(globalObject, vm.propertyNames->start);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Set up transform algorithm
|
||||
if (!transformFn.isUndefined()) {
|
||||
// Install transform function
|
||||
}
|
||||
|
||||
// Set up flush algorithm
|
||||
if (!flushFn.isUndefined()) {
|
||||
// Install flush function
|
||||
}
|
||||
|
||||
// Call start if present
|
||||
if (!startFn.isUndefined()) {
|
||||
JSValue controller = transformStream->controller();
|
||||
callData.thisValue = transformerArg;
|
||||
MarkedArgumentBuffer args;
|
||||
args.append(controller);
|
||||
JSValue startResult = call(globalObject, startFn, callData, args);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(transformStream));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(call, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
Bun::throwError(globalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, "Cannot call TransformStream"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const ClassInfo JSTransformStream::s_info = {
|
||||
"TransformStream"_s,
|
||||
&Base::s_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(JSTransformStream)
|
||||
};
|
||||
|
||||
template<typename Visitor>
|
||||
void JSTransformStream::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* thisObject = jsCast<JSTransformStream*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
visitor.append(thisObject->m_readable);
|
||||
visitor.append(thisObject->m_writable);
|
||||
visitor.append(thisObject->m_controller);
|
||||
visitor.append(thisObject->m_backpressureChangePromise);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSTransformStream);
|
||||
|
||||
JSTransformStream::JSTransformStream(VM& vm, Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
JSTransformStream::~JSTransformStream()
|
||||
{
|
||||
// Clean up any resources
|
||||
}
|
||||
|
||||
void JSTransformStream::destroy(JSCell* cell)
|
||||
{
|
||||
static_cast<JSTransformStream*>(cell)->JSTransformStream::~JSTransformStream();
|
||||
}
|
||||
|
||||
void JSTransformStream::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
|
||||
// Initialize readable/writable sides and controller
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// Initialize with empty promises that will be fulfilled when ready
|
||||
m_backpressureChangePromise.set(vm, JSPromise::create(vm, globalObject->promiseStructure()));
|
||||
|
||||
// Set up the controller
|
||||
m_controller.set(vm, JSTransformStreamDefaultController::create(vm, globalObject, globalObject->transformStreamDefaultControllerStructure()));
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, void());
|
||||
}
|
||||
|
||||
void JSTransformStream::enqueue(VM& vm, JSGlobalObject* globalObject, JSValue chunk)
|
||||
{
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (m_controller)
|
||||
m_controller->enqueue(vm, globalObject, chunk);
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, void());
|
||||
}
|
||||
|
||||
void JSTransformStream::error(VM& vm, JSGlobalObject* globalObject, JSValue error)
|
||||
{
|
||||
if (m_controller)
|
||||
m_controller->error(vm, globalObject, error);
|
||||
}
|
||||
|
||||
void JSTransformStream::terminate(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
if (m_controller)
|
||||
m_controller->terminate(vm, globalObject);
|
||||
}
|
||||
|
||||
JSTransformStream* JSTransformStream::create(
|
||||
VM& vm,
|
||||
JSGlobalObject* globalObject,
|
||||
Structure* structure)
|
||||
{
|
||||
JSTransformStream* ptr = new (
|
||||
NotNull,
|
||||
JSC::allocateCell<JSTransformStream>(vm)) JSTransformStream(vm, structure);
|
||||
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Prototype implementation (JSTransformStreamPrototype.cpp)
|
||||
static const HashTableValue JSTransformStreamPrototypeTableValues[] = {
|
||||
{ "readable"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsTransformStreamReadableGetter, nullptr } },
|
||||
{ "writable"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsTransformStreamWritableGetter, nullptr } }
|
||||
};
|
||||
|
||||
const ClassInfo JSTransformStreamPrototype::s_info = {
|
||||
"TransformStream"_s,
|
||||
&Base::s_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(JSTransformStreamPrototype)
|
||||
};
|
||||
|
||||
void JSTransformStreamPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(
|
||||
vm,
|
||||
JSTransformStream::info(),
|
||||
JSTransformStreamPrototypeTableValues,
|
||||
*this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
63
src/bun.js/bindings/BunTransformStream.h
Normal file
63
src/bun.js/bindings/BunTransformStream.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/JSPromise.h>
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSTransformStream final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
// For garbage collection
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm);
|
||||
|
||||
static JSTransformStream* create(
|
||||
JSC::VM& vm,
|
||||
JSC::JSGlobalObject* globalObject,
|
||||
JSC::Structure* structure);
|
||||
|
||||
static JSC::Structure* createStructure(
|
||||
JSC::VM& vm,
|
||||
JSC::JSGlobalObject* globalObject,
|
||||
JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(
|
||||
vm,
|
||||
globalObject,
|
||||
prototype,
|
||||
JSC::TypeInfo(JSC::JSType::ObjectType, StructureFlags),
|
||||
info());
|
||||
}
|
||||
|
||||
// Readable side operations
|
||||
JSC::JSValue readable() { return m_readable.get(); }
|
||||
JSC::JSValue writable() { return m_writable.get(); }
|
||||
|
||||
// Direct C++ API
|
||||
void enqueue(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue chunk);
|
||||
void error(JSC::VM&, JSC::JSGlobalObject*, JSC::JSValue error);
|
||||
void terminate(JSC::VM&, JSC::JSGlobalObject*);
|
||||
|
||||
~JSTransformStream();
|
||||
|
||||
private:
|
||||
JSTransformStream(JSC::VM&, JSC::Structure*);
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
|
||||
// The readable and writable sides of the transform stream
|
||||
JSC::WriteBarrier<JSC::JSObject> m_readable;
|
||||
JSC::WriteBarrier<JSC::JSObject> m_writable;
|
||||
JSC::WriteBarrier<JSTransformStreamDefaultController> m_controller;
|
||||
|
||||
// State flags
|
||||
bool m_backpressure { false };
|
||||
JSC::WriteBarrier<JSC::JSPromise> m_backpressureChangePromise;
|
||||
};
|
||||
|
||||
}
|
||||
237
src/bun.js/bindings/BunTransformStreamDefaultController.cpp
Normal file
237
src/bun.js/bindings/BunTransformStreamDefaultController.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include "root.h"
|
||||
|
||||
#include "BunTransformStreamDefaultController.h"
|
||||
#include "BunTransformStream.h"
|
||||
#include "BunReadableStream.h"
|
||||
#include "BunWritableStream.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
class JSTransformStreamDefaultControllerPrototype final : public JSC::JSNonFinalObject {
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
public:
|
||||
static JSTransformStreamDefaultControllerPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSTransformStreamDefaultControllerPrototype* ptr = new (NotNull, JSC::allocateCell<JSTransformStreamDefaultControllerPrototype>(vm))
|
||||
JSTransformStreamDefaultControllerPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTransformStreamDefaultControllerPrototype, Base);
|
||||
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:
|
||||
JSTransformStreamDefaultControllerPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSTransformStreamDefaultControllerConstructor final : public JSC::InternalFunction {
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
public:
|
||||
static JSTransformStreamDefaultControllerConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSTransformStreamDefaultControllerPrototype* prototype)
|
||||
{
|
||||
JSTransformStreamDefaultControllerConstructor* constructor = new (NotNull, JSC::allocateCell<JSTransformStreamDefaultControllerConstructor>(vm))
|
||||
JSTransformStreamDefaultControllerConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.internalFunctionSpace();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
private:
|
||||
JSTransformStreamDefaultControllerConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*, JSTransformStreamDefaultControllerPrototype*);
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
};
|
||||
|
||||
static const HashTableValue JSTransformStreamDefaultControllerPrototypeTableValues[] = {
|
||||
{ "enqueue"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsTransformStreamDefaultControllerEnqueue, 1 } },
|
||||
{ "error"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsTransformStreamDefaultControllerError, 1 } },
|
||||
{ "terminate"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsTransformStreamDefaultControllerTerminate, 0 } },
|
||||
{ "desiredSize"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsTransformStreamDefaultControllerDesiredSize, 0 } },
|
||||
};
|
||||
|
||||
const ClassInfo JSTransformStreamDefaultController::s_info = { "TransformStreamDefaultController"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTransformStreamDefaultController) };
|
||||
const ClassInfo JSTransformStreamDefaultControllerConstructor::s_info = { "TransformStreamDefaultController"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTransformStreamDefaultControllerConstructor) };
|
||||
|
||||
const ClassInfo JSTransformStreamDefaultControllerPrototype::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTransformStreamDefaultControllerPrototype) };
|
||||
JSTransformStreamDefaultController* JSTransformStreamDefaultController::create(
|
||||
JSC::VM& vm,
|
||||
JSC::JSGlobalObject* globalObject,
|
||||
JSC::Structure* structure,
|
||||
JSTransformStream* transformStream)
|
||||
{
|
||||
JSTransformStreamDefaultController* controller = new (NotNull, JSC::allocateCell<JSTransformStreamDefaultController>(vm))
|
||||
JSTransformStreamDefaultController(vm, structure);
|
||||
controller->finishCreation(vm, globalObject, transformStream);
|
||||
return controller;
|
||||
}
|
||||
|
||||
void JSTransformStreamDefaultController::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSTransformStream* transformStream)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
|
||||
m_stream.set(vm, this, transformStream);
|
||||
}
|
||||
|
||||
void JSTransformStreamDefaultControllerPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
|
||||
reifyStaticProperties(vm, info(), JSTransformStreamDefaultControllerPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
void JSTransformStreamDefaultControllerConstructor::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSTransformStreamDefaultControllerPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 2, "TransformStreamDefaultController"_s, PropertyAdditionMode::WithoutStructureTransition);
|
||||
ASSERT(inherits(info()));
|
||||
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0);
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSTransformStreamDefaultController::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* thisObject = jsCast<JSTransformStreamDefaultController*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
|
||||
visitor.append(thisObject->m_stream);
|
||||
visitor.append(thisObject->m_flushPromise);
|
||||
visitor.append(thisObject->m_transformAlgorithm);
|
||||
visitor.append(thisObject->m_flushAlgorithm);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSTransformStreamDefaultController);
|
||||
|
||||
bool JSTransformStreamDefaultController::enqueue(JSC::JSGlobalObject* globalObject, JSC::JSValue chunk)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// Implementation following spec's TransformStreamDefaultControllerEnqueue
|
||||
// This would integrate with the ReadableStream's controller to actually enqueue the chunk
|
||||
// and handle backpressure
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JSTransformStreamDefaultController::error(JSC::JSGlobalObject* globalObject, JSC::JSValue error)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// Implementation following spec's TransformStreamDefaultControllerError
|
||||
// This would propagate the error to both the readable and writable sides
|
||||
}
|
||||
|
||||
void JSTransformStreamDefaultController::terminate(JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// Implementation following spec's TransformStreamDefaultControllerTerminate
|
||||
// This would close the readable side and error the writable side
|
||||
}
|
||||
|
||||
// JavaScript binding implementations
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsTransformStreamDefaultControllerDesiredSize, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSTransformStreamDefaultController*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject))
|
||||
return throwVMTypeError(globalObject, scope, "Receiver must be a TransformStreamDefaultController"_s);
|
||||
|
||||
// Return the desired size per spec
|
||||
return JSValue::encode(jsNumber(0)); // Placeholder
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsTransformStreamDefaultControllerEnqueue, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* controller = jsDynamicCast<JSTransformStreamDefaultController*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!controller))
|
||||
return throwVMTypeError(globalObject, scope, "Receiver must be a TransformStreamDefaultController"_s);
|
||||
|
||||
JSValue chunk = callFrame->argument(0);
|
||||
|
||||
if (!controller->enqueue(globalObject, chunk))
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsTransformStreamDefaultControllerError, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* controller = jsDynamicCast<JSTransformStreamDefaultController*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!controller))
|
||||
return throwVMTypeError(globalObject, scope, "Receiver must be a TransformStreamDefaultController"_s);
|
||||
|
||||
controller->error(globalObject, callFrame->argument(0));
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsTransformStreamDefaultControllerTerminate, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* controller = jsDynamicCast<JSTransformStreamDefaultController*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!controller))
|
||||
return throwVMTypeError(globalObject, scope, "Receiver must be a TransformStreamDefaultController"_s);
|
||||
|
||||
controller->terminate(globalObject);
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
73
src/bun.js/bindings/BunTransformStreamDefaultController.h
Normal file
73
src/bun.js/bindings/BunTransformStreamDefaultController.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "root.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
// JSTransformStreamDefaultController.h
|
||||
class JSTransformStream;
|
||||
|
||||
class JSTransformStreamDefaultController final : public JSC::JSDestructibleObject {
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
|
||||
public:
|
||||
static constexpr bool needsDestruction = true;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
|
||||
return subspaceForImpl<JSTransformStreamDefaultController, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForTransformStreamController.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTransformStreamController = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForTransformStreamController.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForTransformStreamController = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
static JSTransformStreamDefaultController* create(
|
||||
JSC::VM& vm,
|
||||
JSC::JSGlobalObject* globalObject,
|
||||
JSC::Structure* structure,
|
||||
JSTransformStream* transformStream);
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// C++ methods for direct manipulation
|
||||
bool enqueue(JSC::JSGlobalObject*, JSC::JSValue chunk);
|
||||
void error(JSC::JSGlobalObject*, JSC::JSValue error);
|
||||
void terminate(JSC::JSGlobalObject*);
|
||||
JSC::JSValue desiredSize(JSC::JSGlobalObject*);
|
||||
|
||||
// For garbage collection
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
private:
|
||||
JSTransformStreamDefaultController(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*, JSTransformStream* transformStream);
|
||||
|
||||
// Member variables
|
||||
JSC::WriteBarrier<JSTransformStream> m_stream;
|
||||
JSC::WriteBarrier<JSC::JSPromise> m_flushPromise;
|
||||
JSC::WriteBarrier<JSC::Unknown> m_transformAlgorithm;
|
||||
JSC::WriteBarrier<JSC::Unknown> m_flushAlgorithm;
|
||||
};
|
||||
|
||||
// Function declarations for JavaScript bindings
|
||||
JSC_DECLARE_CUSTOM_GETTER(jsTransformStreamDefaultControllerDesiredSize);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsTransformStreamDefaultControllerEnqueue);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsTransformStreamDefaultControllerError);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsTransformStreamDefaultControllerTerminate);
|
||||
|
||||
} // namespace Bun
|
||||
525
src/bun.js/bindings/BunWritableStream.cpp
Normal file
525
src/bun.js/bindings/BunWritableStream.cpp
Normal file
@@ -0,0 +1,525 @@
|
||||
#include "root.h"
|
||||
|
||||
#include "BunWritableStream.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
// JSWritableStreamPrototype bindings
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_abort, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStream* stream = jsDynamicCast<JSWritableStream*>(callFrame->thisValue());
|
||||
if (!stream)
|
||||
return throwVMTypeError(globalObject, scope, "WritableStream.prototype.abort called on non-WritableStream object"_s);
|
||||
|
||||
JSValue reason = callFrame->argument(0);
|
||||
return JSValue::encode(stream->abort(globalObject, reason));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_close, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStream* stream = jsDynamicCast<JSWritableStream*>(callFrame->thisValue());
|
||||
if (!stream)
|
||||
return throwVMTypeError(globalObject, scope, "WritableStream.prototype.close called on non-WritableStream object"_s);
|
||||
|
||||
return JSValue::encode(stream->close(globalObject));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamPrototypeFunction_getWriter, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStream* stream = jsDynamicCast<JSWritableStream*>(callFrame->thisValue());
|
||||
if (!stream)
|
||||
return throwVMTypeError(globalObject, scope, "WritableStream.prototype.getWriter called on non-WritableStream object"_s);
|
||||
|
||||
if (stream->isLocked())
|
||||
return throwVMTypeError(globalObject, scope, "Cannot get writer for locked WritableStream"_s);
|
||||
|
||||
Structure* writerStructure = globalObject->writableStreamDefaultWriterStructure();
|
||||
auto* writer = JSWritableStreamDefaultWriter::create(vm, globalObject, writerStructure, stream);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
stream->setWriter(vm, writer);
|
||||
return JSValue::encode(writer);
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsWritableStreamPrototypeLockedGetter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStream* stream = jsDynamicCast<JSWritableStream*>(JSValue::decode(thisValue));
|
||||
if (!stream)
|
||||
return throwVMTypeError(globalObject, scope, "WritableStream.prototype.locked called on non-WritableStream object"_s);
|
||||
|
||||
return JSValue::encode(jsBoolean(stream->isLocked()));
|
||||
}
|
||||
|
||||
// Static hash table of properties
|
||||
static const HashTableValue JSWritableStreamPrototypeTableValues[] = {
|
||||
{ "abort"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::Function),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamPrototypeFunction_abort, 1 } },
|
||||
{ "close"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::Function),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamPrototypeFunction_close, 0 } },
|
||||
{ "getWriter"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::Function),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamPrototypeFunction_getWriter, 0 } },
|
||||
{ "locked"_s,
|
||||
static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly),
|
||||
NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsWritableStreamPrototypeLockedGetter, nullptr } }
|
||||
};
|
||||
|
||||
class JSWritableStreamPrototype final : public JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSNonFinalObject;
|
||||
|
||||
static JSWritableStreamPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure);
|
||||
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
|
||||
{
|
||||
auto* structure = Base::createStructure(vm, globalObject, prototype);
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, SubspaceAccess>
|
||||
static GCClient::IsoSubspace* subspaceFor(VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWritableStreamPrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
private:
|
||||
JSWritableStreamPrototype(VM& vm, Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(VM& vm, JSGlobalObject* globalObject);
|
||||
};
|
||||
|
||||
class JSWritableStreamConstructor final : public InternalFunction {
|
||||
public:
|
||||
using Base = InternalFunction;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSWritableStreamConstructor* create(VM&, JSGlobalObject*, Structure*, JSWritableStreamPrototype*);
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, SubspaceAccess mode>
|
||||
static GCClient::IsoSubspace* subspaceFor(VM& vm)
|
||||
{
|
||||
if constexpr (mode == SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<JSWritableStreamConstructor,
|
||||
WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForConstructor.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForConstructor = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForConstructor.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForConstructor = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
|
||||
static EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSGlobalObject*, CallFrame*);
|
||||
static EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSGlobalObject*, CallFrame*);
|
||||
|
||||
private:
|
||||
JSWritableStreamConstructor(VM& vm, Structure* structure);
|
||||
void finishCreation(VM& vm, JSGlobalObject* globalObject, JSWritableStreamPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 1, "WritableStream"_s, PropertyAdditionMode::WithStructureTransition);
|
||||
this->putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Prototype Implementation
|
||||
const ClassInfo JSWritableStreamPrototype::s_info = { "WritableStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWritableStreamPrototype) };
|
||||
|
||||
JSWritableStreamPrototype* JSWritableStreamPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
|
||||
{
|
||||
auto* prototype = new (NotNull, allocateCell<JSWritableStreamPrototype>(vm)) JSWritableStreamPrototype(vm, structure);
|
||||
prototype->finishCreation(vm, globalObject);
|
||||
return prototype;
|
||||
}
|
||||
|
||||
void JSWritableStreamPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSWritableStream::info(), JSWritableStreamPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// Constructor Implementation
|
||||
const ClassInfo JSWritableStreamConstructor::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWritableStreamConstructor) };
|
||||
|
||||
JSWritableStreamConstructor::JSWritableStreamConstructor(VM& vm, Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
JSWritableStreamConstructor* JSWritableStreamConstructor::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSWritableStreamPrototype* prototype)
|
||||
{
|
||||
JSWritableStreamConstructor* constructor = new (NotNull, allocateCell<JSWritableStreamConstructor>(vm)) JSWritableStreamConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
Structure* JSWritableStreamConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
|
||||
{
|
||||
return Structure::create(vm, globalObject, prototype, TypeInfo(InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamConstructor, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSValue newTarget = callFrame->newTarget();
|
||||
if (newTarget.isUndefined())
|
||||
return throwVMTypeError(globalObject, scope, "WritableStream constructor must be called with 'new'"_s);
|
||||
|
||||
JSObject* underlyingSink = callFrame->argument(0).getObject();
|
||||
JSValue strategy = callFrame->argument(1);
|
||||
|
||||
JSObject* constructor = asObject(newTarget);
|
||||
Structure* structure = JSC::InternalFunction::createSubclassStructure(globalObject, newTarget, globalObject->writableStreamStructure());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
JSWritableStream* stream = JSWritableStream::create(vm, globalObject, structure);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Initialize with underlying sink if provided
|
||||
if (underlyingSink) {
|
||||
// Set up controller with underlying sink...
|
||||
auto controller = JSWritableStreamDefaultController::create(vm, globalObject, stream, underlyingSink);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
stream->setController(controller);
|
||||
}
|
||||
|
||||
return JSValue::encode(stream);
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamPrivateConstructor, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
// Similar to above but for internal usage
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
Structure* structure = globalObject->writableStreamStructure();
|
||||
JSWritableStream* stream = JSWritableStream::create(vm, globalObject, structure);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
return JSValue::encode(stream);
|
||||
}
|
||||
|
||||
// WritableStream implementation
|
||||
const ClassInfo JSWritableStream::s_info = { "WritableStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWritableStream) };
|
||||
|
||||
JSWritableStream::JSWritableStream(VM& vm, Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void JSWritableStream::finishCreation(VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
|
||||
JSWritableStream* JSWritableStream::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
|
||||
{
|
||||
JSWritableStream* stream = new (NotNull, allocateCell<JSWritableStream>(vm))
|
||||
JSWritableStream(vm, structure);
|
||||
stream->finishCreation(vm);
|
||||
return stream;
|
||||
}
|
||||
|
||||
void JSWritableStream::destroy(JSCell* cell)
|
||||
{
|
||||
static_cast<JSWritableStream*>(cell)->JSWritableStream::~JSWritableStream();
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSWritableStream::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* thisObject = jsCast<JSWritableStream*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
|
||||
visitor.append(thisObject->m_controller);
|
||||
visitor.append(thisObject->m_writer);
|
||||
visitor.append(thisObject->m_closeRequest);
|
||||
visitor.append(thisObject->m_inFlightWriteRequest);
|
||||
visitor.append(thisObject->m_inFlightCloseRequest);
|
||||
visitor.append(thisObject->m_storedError);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSWritableStream);
|
||||
|
||||
Structure* JSWritableStream::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
|
||||
{
|
||||
return Structure::create(vm, globalObject, prototype,
|
||||
TypeInfo(ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
bool JSWritableStream::isLocked() const
|
||||
{
|
||||
return !!m_writer;
|
||||
}
|
||||
|
||||
JSValue JSWritableStream::error(JSGlobalObject* globalObject, JSValue error)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (m_state != State::Writable)
|
||||
return jsUndefined();
|
||||
|
||||
m_state = State::Errored;
|
||||
m_storedError.set(vm, this, error);
|
||||
|
||||
if (m_writer)
|
||||
m_writer->error(globalObject, error);
|
||||
|
||||
RELEASE_AND_RETURN(scope, jsUndefined());
|
||||
}
|
||||
|
||||
namespace Operations {
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveAbortPromiseWithUndefined, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSPromise* promise = jsDynamicCast<JSPromise*>(callFrame->argument(1));
|
||||
promise->fulfillWithNonPromise(globalObject, jsUndefined());
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunctionRejectAbortPromiseWithReason, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSPromise* promise = jsDynamicCast<JSPromise*>(callFrame->argument(1));
|
||||
promise->reject(globalObject, callFrame->argument(0));
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
static void WritableStreamStartErroring(JSWritableStream* stream, JSValue reason)
|
||||
{
|
||||
// 1. Assert: stream.[[storedError]] is undefined.
|
||||
ASSERT(!stream->storedError() || stream->storedError().isUndefined());
|
||||
|
||||
// 2. Assert: stream.[[state]] is "writable".
|
||||
ASSERT(stream->state() == JSWritableStream::State::Writable);
|
||||
|
||||
// 3. Let controller be stream.[[writableStreamController]].
|
||||
auto* controller = stream->controller();
|
||||
ASSERT(controller);
|
||||
|
||||
// 4. Set stream.[[state]] to "erroring".
|
||||
stream->setState(JSWritableStream::State::Erroring);
|
||||
|
||||
// 5. Set stream.[[storedError]] to reason.
|
||||
stream->setStoredError(reason);
|
||||
|
||||
// 6. Let writer be stream.[[writer]].
|
||||
auto* writer = stream->writer();
|
||||
|
||||
// 7. If writer is not undefined, perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason).
|
||||
if (writer)
|
||||
WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason);
|
||||
|
||||
// 8. If ! WritableStreamHasOperationMarkedInFlight(stream) is false and controller.[[started]] is true,
|
||||
// perform ! WritableStreamFinishErroring(stream).
|
||||
if (!stream->hasOperationMarkedInFlight() && controller->started())
|
||||
WritableStreamFinishErroring(stream);
|
||||
}
|
||||
|
||||
static void WritableStreamFinishErroring(JSWritableStream* stream)
|
||||
{
|
||||
// 1. Assert: stream.[[state]] is "erroring".
|
||||
ASSERT(stream->state() == JSWritableStream::State::Erroring);
|
||||
|
||||
// 2. Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is false.
|
||||
ASSERT(!stream->hasOperationMarkedInFlight());
|
||||
|
||||
// 3. Set stream.[[state]] to "errored".
|
||||
stream->setState(JSWritableStream::State::Errored);
|
||||
|
||||
// 4. Perform ! WritableStreamDefaultControllerErrorSteps(stream.[[writableStreamController]]).
|
||||
stream->controller()->errorSteps();
|
||||
|
||||
JSValue storedError = stream->storedError();
|
||||
|
||||
// 5. Let writer be stream.[[writer]].
|
||||
auto* writer = stream->writer();
|
||||
|
||||
// 6. If writer is not undefined,
|
||||
if (writer) {
|
||||
// a. Let writeRequests be writer.[[writeRequests]].
|
||||
// b. Set writer.[[writeRequests]] to an empty List.
|
||||
// c. For each writeRequest of writeRequests,
|
||||
// 1. Reject writeRequest with stream.[[storedError]].
|
||||
writer->rejectWriteRequests(storedError);
|
||||
}
|
||||
|
||||
JSPromise* abortPromise = stream->pendingAbortRequestPromise();
|
||||
|
||||
// 7. Let pendingAbortRequest be stream.[[pendingAbortRequest]].
|
||||
// 8. If pendingAbortRequest is undefined, return.
|
||||
if (!abortPromise)
|
||||
return;
|
||||
|
||||
// 9. Set stream.[[pendingAbortRequest]] to undefined.
|
||||
|
||||
JSValue abortReason = stream->pendingAbortRequestReason();
|
||||
bool wasAlreadyErroring = stream->wasAlreadyErroring();
|
||||
stream->clearPendingAbortRequest();
|
||||
|
||||
// 10. If pendingAbortRequest.[[wasAlreadyErroring]] is true,
|
||||
if (wasAlreadyErroring) {
|
||||
// a. Reject pendingAbortRequest.[[promise]] with pendingAbortRequest.[[reason]].
|
||||
abortPromise->(abortReason);
|
||||
// b. Return.
|
||||
return;
|
||||
}
|
||||
|
||||
// 11. Let abortAlgorithm be stream.[[writableStreamController]].[[abortAlgorithm]].
|
||||
// 12. Let result be the result of performing abortAlgorithm with argument pendingAbortRequest.[[reason]].
|
||||
JSValue result = stream->controller()->performAbortAlgorithm(abortReason);
|
||||
|
||||
// 13. Upon fulfillment of result,
|
||||
// a. Resolve pendingAbortRequest.[[promise]] with undefined.
|
||||
// 14. Upon rejection of result with reason r,
|
||||
// a. Reject pendingAbortRequest.[[promise]] with r.
|
||||
if (JSPromise* resultPromise = jsDynamicCast<JSPromise*>(result)) {
|
||||
Bun::performPromiseThen(vm, globalObject, resultPromise,
|
||||
jsFunctionResolveAbortPromiseWithUndefined,
|
||||
jsFunctionRejectAbortPromiseWithReason);
|
||||
} else {
|
||||
// If not a promise, treat as fulfilled
|
||||
abortPromise->resolve(jsUndefined());
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue WritableStreamAbort(JSGlobalObject* globalObject, JSWritableStream* stream, JSValue reason)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// 1. Let state be stream.[[state]].
|
||||
auto state = stream->state();
|
||||
|
||||
// 2. If state is "closed" or state is "errored", return a promise resolved with undefined.
|
||||
if (state == JSWritableStream::State::Closed || state == JSWritableStream::State::Errored) {
|
||||
return JSPromise::resolvedPromise(globalObject, jsUndefined());
|
||||
}
|
||||
|
||||
// 3. If stream.[[pendingAbortRequest]] is not undefined, return stream.[[pendingAbortRequest]].[[promise]].
|
||||
if (stream->pendingAbortRequest())
|
||||
return stream->pendingAbortRequest();
|
||||
|
||||
// 4. Assert: state is "writable" or state is "erroring".
|
||||
ASSERT(state == JSWritableStream::State::Writable || state == JSWritableStream::State::Erroring);
|
||||
|
||||
// 5. Let wasAlreadyErroring be false.
|
||||
bool wasAlreadyErroring = false;
|
||||
|
||||
// 6. If state is "erroring",
|
||||
if (state == JSWritableStream::State::Erroring) {
|
||||
// a. Set wasAlreadyErroring to true.
|
||||
wasAlreadyErroring = true;
|
||||
// b. Set reason to undefined.
|
||||
reason = jsUndefined();
|
||||
}
|
||||
|
||||
// 7. Let promise be a new promise.
|
||||
JSPromise* promise = JSPromise::create(vm, globalObject->promiseStructure());
|
||||
|
||||
// 8. Set stream.[[pendingAbortRequest]] to record {[[promise]]: promise, [[reason]]: reason,
|
||||
// [[wasAlreadyErroring]]: wasAlreadyErroring}.
|
||||
stream->setPendingAbortRequest(promise, reason, wasAlreadyErroring);
|
||||
|
||||
// 9. If wasAlreadyErroring is false, perform ! WritableStreamStartErroring(stream, reason).
|
||||
if (!wasAlreadyErroring) {
|
||||
WritableStreamStartErroring(stream, reason);
|
||||
}
|
||||
|
||||
// 10. If stream.[[state]] is "errored", perform ! WritableStreamFinishErroring(stream).
|
||||
if (stream->state() == JSWritableStream::State::Errored) {
|
||||
WritableStreamFinishErroring(stream);
|
||||
}
|
||||
|
||||
// 11. Return promise.
|
||||
return promise;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JSValue JSWritableStream::abort(JSGlobalObject* globalObject, JSValue reason)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// 1. If ! IsWritableStreamLocked(this) is true, return a promise rejected with a TypeError exception.
|
||||
if (isLocked())
|
||||
return JSPromise::rejectedPromise(globalObject, createTypeError(globalObject, "Cannot abort a locked WritableStream"_s));
|
||||
|
||||
// 2. Return ! WritableStreamAbort(this, reason).
|
||||
return Operations::WritableStreamAbort(globalObject, this, reason);
|
||||
}
|
||||
|
||||
JSValue JSWritableStream::close(JSGlobalObject* globalObject)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// Cannot close locked stream
|
||||
if (isLocked())
|
||||
return throwVMTypeError(globalObject, scope, "Cannot close a locked WritableStream"_s);
|
||||
|
||||
// Cannot close unless in writable state
|
||||
if (m_state != State::Writable)
|
||||
return throwVMTypeError(globalObject, scope, "Cannot close stream in non-writable state"_s);
|
||||
|
||||
// Cannot close if already closing
|
||||
if (m_closeRequest || m_inFlightCloseRequest)
|
||||
return throwVMTypeError(globalObject, scope, "Cannot close an already closing stream"_s);
|
||||
|
||||
// Create close promise
|
||||
JSPromise* promise = JSPromise::create(vm, globalObject->promiseStructure());
|
||||
m_closeRequest.set(vm, this, promise);
|
||||
|
||||
// If we have in-flight write request, wait for it to finish
|
||||
if (m_inFlightWriteRequest) {
|
||||
RELEASE_AND_RETURN(scope, promise);
|
||||
}
|
||||
|
||||
// Note: The controller just queues up the close operation
|
||||
m_controller->close(globalObject);
|
||||
|
||||
m_inFlightCloseRequest.set(vm, this, m_closeRequest.get());
|
||||
m_closeRequest.clear();
|
||||
|
||||
RELEASE_AND_RETURN(scope, m_inFlightCloseRequest.get());
|
||||
}
|
||||
|
||||
}
|
||||
144
src/bun.js/bindings/BunWritableStream.h
Normal file
144
src/bun.js/bindings/BunWritableStream.h
Normal file
@@ -0,0 +1,144 @@
|
||||
#pragma once
|
||||
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
#include <JavaScriptCore/JSObjectInlines.h>
|
||||
#include "JavaScriptCore/JSCast.h"
|
||||
#include <JavaScriptCore/JSPromise.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
#include <JavaScriptCore/WriteBarrier.h>
|
||||
#include <JavaScriptCore/Completion.h>
|
||||
#include "DOMIsoSubspaces.h"
|
||||
#include "BunClientData.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSWritableStreamDefaultController;
|
||||
class JSWritableStreamDefaultWriter;
|
||||
class UnderlyingSink;
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
// Main WritableStream object implementation
|
||||
class JSWritableStream final : public JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSDestructibleObject;
|
||||
static constexpr bool needsDestruction = true;
|
||||
|
||||
static JSWritableStream* create(VM&, JSGlobalObject*, Structure*);
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, SubspaceAccess mode>
|
||||
static GCClient::IsoSubspace* subspaceFor(VM& vm)
|
||||
{
|
||||
if constexpr (mode == SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<JSWritableStream, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForWritableStream.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForWritableStream = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForWritableStream.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForWritableStream = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
|
||||
|
||||
// Internal state tracking
|
||||
enum class State : uint8_t {
|
||||
Writable,
|
||||
Erroring,
|
||||
Errored,
|
||||
Closing,
|
||||
Closed
|
||||
};
|
||||
|
||||
JSWritableStreamDefaultController* controller() { return m_controller.get(); }
|
||||
JSPromise* closeRequest() { return m_closeRequest.get(); }
|
||||
JSPromise* inFlightWriteRequest() { return m_inFlightWriteRequest.get(); }
|
||||
JSValue storedError() const { return m_storedError.get(); }
|
||||
State state() const { return m_state; }
|
||||
bool backpressure() const { return m_backpressure; }
|
||||
JSWritableStreamDefaultWriter* writer() { return m_writer.get(); }
|
||||
|
||||
// Public C++ API
|
||||
JSValue error(JSGlobalObject*, JSValue error);
|
||||
bool isLocked() const;
|
||||
JSValue abort(JSGlobalObject*, JSValue reason);
|
||||
JSValue close(JSGlobalObject*);
|
||||
void setController(JSC::VM& vm, JSWritableStreamDefaultController* controller)
|
||||
{
|
||||
m_controller.set(vm, this, controller);
|
||||
}
|
||||
void setWriter(JSC::VM& vm, JSWritableStreamDefaultWriter* writer)
|
||||
{
|
||||
m_writer.set(vm, this, writer);
|
||||
}
|
||||
|
||||
static JSObject* createPrototype(VM&, JSGlobalObject*);
|
||||
static JSObject* createConstructor(VM&, JSGlobalObject*, JSValue);
|
||||
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
void setPendingAbortRequest(JSC::VM& vm, JSPromise* promise, JSValue reason, bool wasAlreadyErroring)
|
||||
{
|
||||
m_pendingAbortRequestPromise.set(vm, this, promise);
|
||||
m_pendingAbortRequestReason.set(vm, this, reason);
|
||||
m_wasAlreadyErroring = wasAlreadyErroring;
|
||||
}
|
||||
|
||||
JSPromise* pendingAbortRequestPromise() { return m_pendingAbortRequestPromise.get(); }
|
||||
JSValue pendingAbortRequestReason() { return m_pendingAbortRequestReason.get(); }
|
||||
bool wasAlreadyErroring() { return m_wasAlreadyErroring; }
|
||||
|
||||
void clearPendingAbortRequest()
|
||||
{
|
||||
m_pendingAbortRequestPromise.clear();
|
||||
m_pendingAbortRequestReason.clear();
|
||||
m_wasAlreadyErroring = false;
|
||||
}
|
||||
|
||||
void setStoredError(JSC::VM& vm, JSValue error)
|
||||
{
|
||||
m_storedError.set(vm, this, error);
|
||||
}
|
||||
|
||||
void clearStoredError()
|
||||
{
|
||||
m_storedError.clear();
|
||||
}
|
||||
|
||||
void setState(State state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
void setBackpressure(bool backpressure)
|
||||
{
|
||||
m_backpressure = backpressure;
|
||||
}
|
||||
|
||||
bool hasOperationMarkedInFlight() const { return m_inFlightWriteRequest || m_inFlightCloseRequest; }
|
||||
|
||||
private:
|
||||
JSWritableStream(VM&, Structure*);
|
||||
void finishCreation(VM&);
|
||||
static void destroy(JSCell*);
|
||||
|
||||
// Internal state tracking
|
||||
State m_state { State::Writable };
|
||||
bool m_backpressure { false };
|
||||
|
||||
WriteBarrier<JSWritableStreamDefaultController> m_controller;
|
||||
WriteBarrier<JSWritableStreamDefaultWriter> m_writer;
|
||||
WriteBarrier<JSPromise> m_closeRequest;
|
||||
WriteBarrier<JSPromise> m_inFlightWriteRequest;
|
||||
WriteBarrier<JSPromise> m_inFlightCloseRequest;
|
||||
WriteBarrier<JSPromise> m_pendingAbortRequestPromise;
|
||||
WriteBarrier<Unknown> m_pendingAbortRequestReason;
|
||||
WriteBarrier<Unknown> m_storedError;
|
||||
|
||||
bool m_wasAlreadyErroring { false };
|
||||
};
|
||||
|
||||
}
|
||||
264
src/bun.js/bindings/BunWritableStreamDefaultController.cpp
Normal file
264
src/bun.js/bindings/BunWritableStreamDefaultController.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
#include <JavaScriptCore/JSPromise.h>
|
||||
#include "JSAbortController.h"
|
||||
|
||||
#include "BunWritableStreamDefaultController.h"
|
||||
#include "BunWritableStream.h"
|
||||
#include "JSAbortSignal.h"
|
||||
#include "IDLTypes.h"
|
||||
#include "JSDOMBinding.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSWritableStreamDefaultControllerPrototype final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
static JSWritableStreamDefaultControllerPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSWritableStreamDefaultControllerPrototype* ptr = new (NotNull, JSC::allocateCell<JSWritableStreamDefaultControllerPrototype>(vm)) JSWritableStreamDefaultControllerPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWritableStreamDefaultControllerPrototype, Base);
|
||||
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:
|
||||
JSWritableStreamDefaultControllerPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSWritableStreamDefaultControllerConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
static constexpr bool needsDestruction = false;
|
||||
|
||||
static JSWritableStreamDefaultControllerConstructor* create(
|
||||
JSC::VM& vm,
|
||||
JSC::JSGlobalObject* globalObject,
|
||||
JSC::Structure* structure,
|
||||
JSWritableStreamDefaultControllerPrototype* prototype);
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSGlobalObject*, CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSGlobalObject*, CallFrame*);
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<JSWritableStreamDefaultControllerConstructor,
|
||||
WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForStreamConstructor.get(); },
|
||||
[](auto& spaces, auto&& space) {
|
||||
spaces.m_clientSubspaceForStreamConstructor = std::forward<decltype(space)>(space);
|
||||
},
|
||||
[](auto& spaces) { return spaces.m_subspaceForStreamConstructor.get(); },
|
||||
[](auto& spaces, auto&& space) {
|
||||
spaces.m_subspaceForStreamConstructor = std::forward<decltype(space)>(space);
|
||||
});
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
private:
|
||||
JSWritableStreamDefaultControllerConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*, JSWritableStreamDefaultControllerPrototype*);
|
||||
};
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamDefaultControllerErrorFunction, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStreamDefaultController* controller = jsDynamicCast<JSWritableStreamDefaultController*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!controller)) {
|
||||
scope.throwException(globalObject, createTypeError(globalObject, "WritableStreamDefaultController.prototype.error called on non-WritableStreamDefaultController"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(controller->error(callFrame->argument(0)));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsWritableStreamDefaultControllerGetSignal, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSWritableStreamDefaultController*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
scope.throwException(lexicalGlobalObject, createTypeError(lexicalGlobalObject, "WritableStreamDefaultController.prototype.signal called on non-WritableStreamDefaultController"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(thisObject->abortSignal());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsWritableStreamDefaultControllerGetDesiredSize, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSWritableStreamDefaultController*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
scope.throwException(lexicalGlobalObject, createTypeError(lexicalGlobalObject, "WritableStreamDefaultController.prototype.desiredSize called on non-WritableStreamDefaultController"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (thisObject->stream()->state()) {
|
||||
case JSWritableStream::State::Errored:
|
||||
return JSValue::encode(jsNull());
|
||||
case JSWritableStream::State::Closed:
|
||||
return JSValue::encode(jsNumber(0));
|
||||
default:
|
||||
return JSValue::encode(jsNumber(thisObject->getDesiredSize()));
|
||||
}
|
||||
}
|
||||
|
||||
static const HashTableValue JSWritableStreamDefaultControllerPrototypeTableValues[] = {
|
||||
{ "error"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamDefaultControllerErrorFunction, 1 } },
|
||||
{ "signal"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsWritableStreamDefaultControllerGetSignal, 0 } },
|
||||
};
|
||||
|
||||
void JSWritableStreamDefaultControllerPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSWritableStreamDefaultController::info(), JSWritableStreamDefaultControllerPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
const JSC::ClassInfo JSWritableStreamDefaultControllerPrototype::s_info = {
|
||||
"WritableStreamDefaultController"_s, &Base::s_info, nullptr, nullptr,
|
||||
CREATE_METHOD_TABLE(JSWritableStreamDefaultControllerPrototype)
|
||||
};
|
||||
|
||||
// JSWritableStreamDefaultController.cpp
|
||||
|
||||
JSWritableStreamDefaultController* JSWritableStreamDefaultController::create(
|
||||
JSC::VM& vm,
|
||||
JSC::Structure* structure,
|
||||
JSWritableStream* stream,
|
||||
double highWaterMark,
|
||||
JSC::JSObject* underlyingSinkObj)
|
||||
{
|
||||
JSWritableStreamDefaultController* controller = new (
|
||||
NotNull, JSC::allocateCell<JSWritableStreamDefaultController>(vm))
|
||||
JSWritableStreamDefaultController(vm, structure);
|
||||
|
||||
controller->finishCreation(vm);
|
||||
return controller;
|
||||
}
|
||||
|
||||
void JSWritableStreamDefaultController::finishCreation(JSC::VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
m_queue.set(vm, JSC::constructEmptyArray(vm, nullptr));
|
||||
m_abortController.set(vm, WebCore::JSAbortController::create(vm, nullptr, nullptr));
|
||||
}
|
||||
|
||||
JSC::JSValue JSWritableStreamDefaultController::abortSignal() const
|
||||
{
|
||||
auto& vm = this->globalObject()->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
return WebCore::toJS<WebCore::IDLInterface<WebCore::AbortSignal>>(this->globalObject(), defaultGlobalObject(this->globalObject()), throwScope, m_abortController->wrapped().signal());
|
||||
}
|
||||
|
||||
JSC::JSValue JSWritableStreamDefaultController::error(JSC::JSValue reason)
|
||||
{
|
||||
auto* globalObject = JSC::jsCast<JSC::JSGlobalObject*>(m_stream->globalObject());
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (m_stream->state() != JSWritableStream::State::Writable)
|
||||
return JSC::jsUndefined();
|
||||
|
||||
performWritableStreamDefaultControllerError(this, reason);
|
||||
|
||||
RELEASE_AND_RETURN(scope, JSC::jsUndefined());
|
||||
}
|
||||
|
||||
bool JSWritableStreamDefaultController::shouldCallWrite() const
|
||||
{
|
||||
if (!m_started)
|
||||
return false;
|
||||
|
||||
if (m_writing)
|
||||
return false;
|
||||
|
||||
if (m_inFlightWriteRequest)
|
||||
return false;
|
||||
|
||||
if (m_stream->state() != JSWritableStream::State::Writable)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double JSWritableStreamDefaultController::getDesiredSize() const
|
||||
{
|
||||
return m_strategyHWM - m_queueTotalSize;
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSWritableStreamDefaultController::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
JSWritableStreamDefaultController* thisObject = jsCast<JSWritableStreamDefaultController*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
thisObject->visitAdditionalChildren(visitor);
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSWritableStreamDefaultController::visitAdditionalChildren(Visitor& visitor)
|
||||
{
|
||||
visitor.append(m_stream);
|
||||
visitor.append(m_abortAlgorithm);
|
||||
visitor.append(m_closeAlgorithm);
|
||||
visitor.append(m_writeAlgorithm);
|
||||
visitor.append(m_strategySizeAlgorithm);
|
||||
visitor.append(m_queue);
|
||||
visitor.append(m_abortController);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSWritableStreamDefaultController);
|
||||
DEFINE_VISIT_ADDITIONAL_CHILDREN(JSWritableStreamDefaultController);
|
||||
|
||||
const JSC::ClassInfo JSWritableStreamDefaultController::s_info = {
|
||||
"WritableStreamDefaultController"_s,
|
||||
&Base::s_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(JSWritableStreamDefaultController)
|
||||
};
|
||||
}
|
||||
94
src/bun.js/bindings/BunWritableStreamDefaultController.h
Normal file
94
src/bun.js/bindings/BunWritableStreamDefaultController.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/JSPromise.h>
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
|
||||
namespace WebCore {
|
||||
class JSAbortController;
|
||||
}
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSWritableStream;
|
||||
|
||||
class JSWritableStreamDefaultController final : public JSC::JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
static constexpr bool needsDestruction = true;
|
||||
|
||||
static JSWritableStreamDefaultController* create(
|
||||
JSC::VM& vm,
|
||||
JSC::Structure* structure,
|
||||
JSWritableStream* stream,
|
||||
double highWaterMark,
|
||||
JSC::JSObject* underlyingSinkObj);
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(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());
|
||||
}
|
||||
|
||||
// JavaScript-facing methods
|
||||
JSC::JSValue error(JSC::JSValue reason);
|
||||
|
||||
// C++-facing methods
|
||||
bool shouldCallWrite() const;
|
||||
double getDesiredSize() const;
|
||||
|
||||
// For garbage collection
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
JSC::JSValue abortSignal() const;
|
||||
|
||||
template<typename Visitor> void visitAdditionalChildren(Visitor&);
|
||||
|
||||
JSWritableStream* stream() const { return m_stream.get(); }
|
||||
JSC::JSPromise* abortAlgorithm() const { return m_abortAlgorithm.get(); }
|
||||
JSC::JSPromise* closeAlgorithm() const { return m_closeAlgorithm.get(); }
|
||||
JSC::JSPromise* writeAlgorithm() const { return m_writeAlgorithm.get(); }
|
||||
|
||||
void setStream(JSC::VM& vm, JSWritableStream* stream) { m_stream.set(vm, this, stream); }
|
||||
void setAbortAlgorithm(JSC::VM& vm, JSC::JSPromise* abortAlgorithm) { m_abortAlgorithm.set(vm, this, abortAlgorithm); }
|
||||
void setCloseAlgorithm(JSC::VM& vm, JSC::JSPromise* closeAlgorithm) { m_closeAlgorithm.set(vm, this, closeAlgorithm); }
|
||||
void setWriteAlgorithm(JSC::VM& vm, JSC::JSPromise* writeAlgorithm) { m_writeAlgorithm.set(vm, this, writeAlgorithm); }
|
||||
|
||||
void clearQueue() { m_queue.clear(); }
|
||||
|
||||
~JSWritableStreamDefaultController();
|
||||
static void destroy(JSC::JSCell* cell)
|
||||
{
|
||||
static_cast<JSWritableStreamDefaultController*>(cell)->JSWritableStreamDefaultController::~JSWritableStreamDefaultController();
|
||||
}
|
||||
|
||||
private:
|
||||
JSWritableStreamDefaultController(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
|
||||
// Internal slots per spec
|
||||
JSC::WriteBarrier<JSWritableStream> m_stream;
|
||||
JSC::WriteBarrier<JSC::JSPromise> m_abortAlgorithm;
|
||||
JSC::WriteBarrier<JSC::JSPromise> m_closeAlgorithm;
|
||||
JSC::WriteBarrier<JSC::JSPromise> m_writeAlgorithm;
|
||||
|
||||
double m_strategyHWM { 1.0 };
|
||||
JSC::WriteBarrier<JSC::JSObject> m_strategySizeAlgorithm;
|
||||
JSC::WriteBarrier<JSC::JSObject> m_queue;
|
||||
double m_queueTotalSize { 0.0 };
|
||||
bool m_started { false };
|
||||
bool m_writing { false };
|
||||
bool m_inFlightWriteRequest { false };
|
||||
bool m_closeRequested { false };
|
||||
JSC::WriteBarrier<WebCore::JSAbortController> m_abortController;
|
||||
};
|
||||
|
||||
}
|
||||
427
src/bun.js/bindings/BunWritableStreamDefaultWriter.cpp
Normal file
427
src/bun.js/bindings/BunWritableStreamDefaultWriter.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
#include "root.h"
|
||||
|
||||
#include "BunWritableStreamDefaultWriter.h"
|
||||
#include "BunWritableStream.h"
|
||||
#include "JSDOMWrapper.h"
|
||||
#include <wtf/NeverDestroyed.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
class JSWritableStreamDefaultWriter;
|
||||
class JSWritableStreamDefaultWriterPrototype;
|
||||
|
||||
class JSWritableStreamDefaultWriterConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
static JSWritableStreamDefaultWriterConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSWritableStreamDefaultWriterPrototype* prototype);
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
|
||||
return WebCore::subspaceForImpl<JSWritableStreamDefaultWriterConstructor, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForBunClassConstructor.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForBunClassConstructor = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForBunClassConstructor.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForBunClassConstructor = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
|
||||
private:
|
||||
JSWritableStreamDefaultWriterConstructor(JSC::VM&, JSC::Structure*);
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*, JSWritableStreamDefaultWriterPrototype*);
|
||||
};
|
||||
|
||||
class JSWritableStreamDefaultWriterPrototype final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
static JSWritableStreamDefaultWriterPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSWritableStreamDefaultWriterPrototype* ptr = new (NotNull, JSC::allocateCell<JSWritableStreamDefaultWriterPrototype>(vm)) JSWritableStreamDefaultWriterPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWritableStreamDefaultWriterPrototype, Base);
|
||||
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:
|
||||
JSWritableStreamDefaultWriterPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsWritableStreamDefaultWriterClosedGetter);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsWritableStreamDefaultWriterReadyGetter);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsWritableStreamDefaultWriterDesiredSizeGetter);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamDefaultWriterWrite);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamDefaultWriterAbort);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamDefaultWriterClose);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsWritableStreamDefaultWriterReleaseLock);
|
||||
|
||||
// Property attributes for standard WritableStreamDefaultWriter prototype properties
|
||||
static const unsigned ProtoAccessorDontDelete = PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor;
|
||||
static const unsigned ProtoFunctionDontEnum = PropertyAttribute::DontEnum | PropertyAttribute::Function;
|
||||
|
||||
// Table of prototype properties and methods
|
||||
static const HashTableValue JSWritableStreamDefaultWriterPrototypeTableValues[] = {
|
||||
{ "closed"_s, ProtoAccessorDontDelete, NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsWritableStreamDefaultWriterClosedGetter, nullptr } },
|
||||
{ "ready"_s, ProtoAccessorDontDelete, NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsWritableStreamDefaultWriterReadyGetter, nullptr } },
|
||||
{ "desiredSize"_s, ProtoAccessorDontDelete, NoIntrinsic,
|
||||
{ HashTableValue::GetterSetterType, jsWritableStreamDefaultWriterDesiredSizeGetter, nullptr } },
|
||||
{ "write"_s, ProtoFunctionDontEnum, NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamDefaultWriterWrite, 1 } },
|
||||
{ "abort"_s, ProtoFunctionDontEnum, NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamDefaultWriterAbort, 1 } },
|
||||
{ "close"_s, ProtoFunctionDontEnum, NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamDefaultWriterClose, 0 } },
|
||||
{ "releaseLock"_s, ProtoFunctionDontEnum, NoIntrinsic,
|
||||
{ HashTableValue::NativeFunctionType, jsWritableStreamDefaultWriterReleaseLock, 0 } },
|
||||
};
|
||||
|
||||
const ClassInfo JSWritableStreamDefaultWriterPrototype::s_info = {
|
||||
"WritableStreamDefaultWriter"_s,
|
||||
&Base::s_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(JSWritableStreamDefaultWriterPrototype)
|
||||
};
|
||||
|
||||
void JSWritableStreamDefaultWriterPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, info(), JSWritableStreamDefaultWriterPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// Getter implementations
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsWritableStreamDefaultWriterClosedGetter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* writer = jsDynamicCast<JSWritableStreamDefaultWriter*>(JSValue::decode(thisValue));
|
||||
if (!writer) {
|
||||
throwTypeError(globalObject, scope, "Not a WritableStreamDefaultWriter"_s);
|
||||
return encodedJSValue();
|
||||
}
|
||||
|
||||
return JSValue::encode(writer->closed());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsWritableStreamDefaultWriterReadyGetter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* writer = jsDynamicCast<JSWritableStreamDefaultWriter*>(JSValue::decode(thisValue));
|
||||
if (!writer) {
|
||||
throwTypeError(globalObject, scope, "Not a WritableStreamDefaultWriter"_s);
|
||||
return encodedJSValue();
|
||||
}
|
||||
|
||||
return JSValue::encode(writer->ready());
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsWritableStreamDefaultWriterDesiredSizeGetter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* writer = jsDynamicCast<JSWritableStreamDefaultWriter*>(JSValue::decode(thisValue));
|
||||
if (!writer) {
|
||||
throwTypeError(globalObject, scope, "Not a WritableStreamDefaultWriter"_s);
|
||||
return encodedJSValue();
|
||||
}
|
||||
|
||||
return JSValue::encode(jsNumber(writer->desiredSize()));
|
||||
}
|
||||
|
||||
// Additional JS method implementation
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamDefaultWriterReleaseLock, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStreamDefaultWriter* writer = jsDynamicCast<JSWritableStreamDefaultWriter*>(callFrame->thisValue());
|
||||
if (!writer) {
|
||||
scope.throwException(globalObject, createTypeError(globalObject, "Not a WritableStreamDefaultWriter"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
writer->release();
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
const ClassInfo JSWritableStreamDefaultWriterConstructor::s_info = {
|
||||
"Function"_s,
|
||||
&Base::s_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(JSWritableStreamDefaultWriterConstructor)
|
||||
};
|
||||
|
||||
JSWritableStreamDefaultWriterConstructor::JSWritableStreamDefaultWriterConstructor(VM& vm, Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void JSWritableStreamDefaultWriterConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, JSWritableStreamDefaultWriterPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 1, "WritableStreamDefaultWriter"_s, PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
|
||||
JSWritableStreamDefaultWriterConstructor* JSWritableStreamDefaultWriterConstructor::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSWritableStreamDefaultWriterPrototype* prototype)
|
||||
{
|
||||
JSWritableStreamDefaultWriterConstructor* constructor = new (NotNull, allocateCell<JSWritableStreamDefaultWriterConstructor>(vm)) JSWritableStreamDefaultWriterConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
// This is called when constructing a new writer with new WritableStreamDefaultWriter(stream)
|
||||
EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSWritableStreamDefaultWriterConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (!callFrame->argumentCount()) {
|
||||
throwTypeError(lexicalGlobalObject, scope, "WritableStreamDefaultWriter constructor requires a WritableStream argument"_s);
|
||||
return encodedJSValue();
|
||||
}
|
||||
|
||||
JSValue streamValue = callFrame->argument(0);
|
||||
JSWritableStream* stream = jsDynamicCast<JSWritableStream*>(streamValue);
|
||||
if (!stream) {
|
||||
throwTypeError(lexicalGlobalObject, scope, "WritableStreamDefaultWriter constructor argument must be a WritableStream"_s);
|
||||
return encodedJSValue();
|
||||
}
|
||||
|
||||
// Check if stream is locked
|
||||
if (stream->locked()) {
|
||||
throwTypeError(lexicalGlobalObject, scope, "Cannot construct a WritableStreamDefaultWriter for a locked WritableStream"_s);
|
||||
return encodedJSValue();
|
||||
}
|
||||
|
||||
Structure* structure = globalObject->WritableStreamDefaultWriterStructure();
|
||||
JSWritableStreamDefaultWriter* writer = JSWritableStreamDefaultWriter::create(vm, structure, stream);
|
||||
return JSValue::encode(writer);
|
||||
}
|
||||
|
||||
// This handles direct calls to WritableStreamDefaultWriter as a function, which should throw an error
|
||||
EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSWritableStreamDefaultWriterConstructor::call(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
return throwVMTypeError(globalObject, scope, "WritableStreamDefaultWriter constructor cannot be called as a function"_s);
|
||||
}
|
||||
|
||||
const ClassInfo JSWritableStreamDefaultWriter::s_info = {
|
||||
"WritableStreamDefaultWriter"_s,
|
||||
&Base::s_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(JSWritableStreamDefaultWriter)
|
||||
};
|
||||
|
||||
JSWritableStreamDefaultWriter::JSWritableStreamDefaultWriter(VM& vm, Structure* structure, JSWritableStream* stream)
|
||||
: Base(vm, structure)
|
||||
, m_stream(vm, this, stream)
|
||||
, m_closedPromise(vm, this, JSPromise::create(vm, globalObject->promiseStructure()))
|
||||
, m_readyPromise(vm, this, JSPromise::create(vm, globalObject->promiseStructure()))
|
||||
{
|
||||
}
|
||||
|
||||
JSWritableStreamDefaultWriter* JSWritableStreamDefaultWriter::create(VM& vm, Structure* structure, JSWritableStream* stream)
|
||||
{
|
||||
JSWritableStreamDefaultWriter* writer = new (
|
||||
NotNull,
|
||||
allocateCell<JSWritableStreamDefaultWriter>(vm)) JSWritableStreamDefaultWriter(vm, structure, stream);
|
||||
|
||||
writer->finishCreation(vm);
|
||||
return writer;
|
||||
}
|
||||
|
||||
void JSWritableStreamDefaultWriter::finishCreation(VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
|
||||
void JSWritableStreamDefaultWriter::destroy(JSCell* cell)
|
||||
{
|
||||
static_cast<JSWritableStreamDefaultWriter*>(cell)->JSWritableStreamDefaultWriter::~JSWritableStreamDefaultWriter();
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSWritableStreamDefaultWriter::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* writer = jsCast<JSWritableStreamDefaultWriter*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(writer, info());
|
||||
|
||||
Base::visitChildren(writer, visitor);
|
||||
writer->visitAdditionalChildren(visitor);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSWritableStreamDefaultWriter);
|
||||
|
||||
template<typename Visitor>
|
||||
void JSWritableStreamDefaultWriter::visitAdditionalChildren(Visitor& visitor)
|
||||
{
|
||||
visitor.append(m_stream);
|
||||
visitor.append(m_closedPromise);
|
||||
visitor.append(m_readyPromise);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_ADDITIONAL_CHILDREN(JSWritableStreamDefaultWriter);
|
||||
|
||||
// JS Interface Methods
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamDefaultWriterWrite, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStreamDefaultWriter* writer = jsDynamicCast<JSWritableStreamDefaultWriter*>(callFrame->thisValue());
|
||||
if (!writer) {
|
||||
scope.throwException(globalObject,
|
||||
createTypeError(globalObject, "Not a WritableStreamDefaultWriter"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSValue chunk = callFrame->argument(0);
|
||||
|
||||
JSValue error;
|
||||
if (!writer->write(globalObject, chunk, &error)) {
|
||||
scope.throwException(globalObject, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamDefaultWriterClose, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStreamDefaultWriter* writer = jsDynamicCast<JSWritableStreamDefaultWriter*>(callFrame->thisValue());
|
||||
if (!writer) {
|
||||
scope.throwException(globalObject,
|
||||
createTypeError(globalObject, "Not a WritableStreamDefaultWriter"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSValue error;
|
||||
if (!writer->close(globalObject, &error)) {
|
||||
scope.throwException(globalObject, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsWritableStreamDefaultWriterAbort, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSWritableStreamDefaultWriter* writer = jsDynamicCast<JSWritableStreamDefaultWriter*>(callFrame->thisValue());
|
||||
if (!writer) {
|
||||
scope.throwException(globalObject,
|
||||
createTypeError(globalObject, "Not a WritableStreamDefaultWriter"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSValue reason = callFrame->argument(0);
|
||||
|
||||
JSValue error;
|
||||
if (!writer->abort(globalObject, reason, &error)) {
|
||||
scope.throwException(globalObject, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
// Non-JS Methods for C++ Use
|
||||
|
||||
bool JSWritableStreamDefaultWriter::write(JSGlobalObject* globalObject, JSValue chunk, JSValue* error)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
|
||||
if (!m_stream) {
|
||||
if (error)
|
||||
*error = createTypeError(globalObject, "Writer has no associated stream"_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_stream->write(globalObject, chunk, error);
|
||||
}
|
||||
|
||||
bool JSWritableStreamDefaultWriter::close(JSGlobalObject* globalObject, JSValue* error)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
|
||||
if (!m_stream) {
|
||||
if (error)
|
||||
*error = createTypeError(globalObject, "Writer has no associated stream"_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_stream->close(globalObject, error);
|
||||
}
|
||||
|
||||
bool JSWritableStreamDefaultWriter::abort(JSGlobalObject* globalObject, JSValue reason, JSValue* error)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
|
||||
if (!m_stream) {
|
||||
if (error)
|
||||
*error = createTypeError(globalObject, "Writer has no associated stream"_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_stream->abort(globalObject, reason, error);
|
||||
}
|
||||
|
||||
void JSWritableStreamDefaultWriter::release()
|
||||
{
|
||||
m_stream.clear();
|
||||
m_closedPromise->reject(vm(), jsUndefined());
|
||||
m_readyPromise->reject(vm(), jsUndefined());
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
57
src/bun.js/bindings/BunWritableStreamDefaultWriter.h
Normal file
57
src/bun.js/bindings/BunWritableStreamDefaultWriter.h
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/JSPromise.h>
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class JSWritableStream;
|
||||
|
||||
class JSWritableStreamDefaultWriter final : public JSC::JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
static constexpr bool needsDestruction = true;
|
||||
|
||||
static JSWritableStreamDefaultWriter* create(JSC::VM&, JSC::Structure*, JSWritableStream*);
|
||||
static JSWritableStreamDefaultWriter* createForSubclass(JSC::VM&, JSC::Structure*, JSWritableStream*);
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(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());
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
// JavaScript-visible properties
|
||||
JSC::JSPromise* closed() { return m_closedPromise.get(); }
|
||||
JSC::JSPromise* ready() { return m_readyPromise.get(); }
|
||||
double desiredSize();
|
||||
|
||||
// Internal APIs for C++ use
|
||||
JSWritableStream* stream() { return m_stream.get(); }
|
||||
void release(); // For releaseLock()
|
||||
bool write(JSC::JSGlobalObject*, JSC::JSValue chunk, JSC::JSValue* error = nullptr);
|
||||
bool abort(JSC::JSGlobalObject*, JSC::JSValue reason = JSC::JSValue(), JSC::JSValue* error = nullptr);
|
||||
bool close(JSC::JSGlobalObject*, JSC::JSValue* error = nullptr);
|
||||
|
||||
void visitAdditionalChildren(JSC::SlotVisitor&);
|
||||
|
||||
protected:
|
||||
JSWritableStreamDefaultWriter(JSC::VM&, JSC::Structure*, JSWritableStream*);
|
||||
void finishCreation(JSC::VM&);
|
||||
static void destroy(JSC::JSCell*);
|
||||
|
||||
private:
|
||||
JSC::WriteBarrier<JSWritableStream> m_stream;
|
||||
JSC::WriteBarrier<JSC::JSPromise> m_closedPromise;
|
||||
JSC::WriteBarrier<JSC::JSPromise> m_readyPromise;
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
Reference in New Issue
Block a user