#ifndef builtin_Stream_h
#define builtin_Stream_h
#include "js/Stream.h"
#include "builtin/Promise.h"
#include "vm/List.h"
#include "vm/NativeObject.h"
namespace js {
class ReadableStreamReader;
class ReadableStreamController;
class ReadableStream : public NativeObject {
public:
enum Slots {
Slot_Controller,
Slot_Reader,
Slot_State,
Slot_StoredError,
SlotCount
};
private:
enum StateBits {
Readable = 0,
Closed = 1,
Errored = 2,
StateMask = 0x000000ff,
Disturbed = 0x00000100
};
uint32_t stateBits() const { return getFixedSlot(Slot_State).toInt32(); }
void initStateBits(uint32_t stateBits) {
MOZ_ASSERT((stateBits & ~Disturbed) <= Errored);
setFixedSlot(Slot_State, Int32Value(stateBits));
}
void setStateBits(uint32_t stateBits) {
#ifdef DEBUG
bool wasDisturbed = disturbed();
bool wasClosedOrErrored = closed() || errored();
#endif
initStateBits(stateBits);
MOZ_ASSERT_IF(wasDisturbed, disturbed());
MOZ_ASSERT_IF(wasClosedOrErrored, !readable());
}
StateBits state() const { return StateBits(stateBits() & StateMask); }
void setState(StateBits state) {
MOZ_ASSERT(state <= Errored);
uint32_t current = stateBits() & ~StateMask;
setStateBits(current | state);
}
public:
bool readable() const { return state() == Readable; }
bool closed() const { return state() == Closed; }
void setClosed() { setState(Closed); }
bool errored() const { return state() == Errored; }
void setErrored() { setState(Errored); }
bool disturbed() const { return stateBits() & Disturbed; }
void setDisturbed() { setStateBits(stateBits() | Disturbed); }
bool hasController() const {
return !getFixedSlot(Slot_Controller).isUndefined();
}
inline ReadableStreamController* controller() const;
inline void setController(ReadableStreamController* controller);
void clearController() {
setFixedSlot(Slot_Controller, JS::UndefinedValue());
}
bool hasReader() const { return !getFixedSlot(Slot_Reader).isUndefined(); }
void setReader(JSObject* reader) {
setFixedSlot(Slot_Reader, JS::ObjectValue(*reader));
}
void clearReader() { setFixedSlot(Slot_Reader, JS::UndefinedValue()); }
Value storedError() const { return getFixedSlot(Slot_StoredError); }
void setStoredError(HandleValue value) {
setFixedSlot(Slot_StoredError, value);
}
JS::ReadableStreamMode mode() const;
bool locked() const;
static MOZ_MUST_USE ReadableStream* create(JSContext* cx,
HandleObject proto = nullptr);
static ReadableStream* createExternalSourceStream(
JSContext* cx, JS::ReadableStreamUnderlyingSource* source,
HandleObject proto = nullptr);
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
enum class ForAuthorCodeBool { No, Yes };
class ReadableStreamReader : public NativeObject {
public:
enum Slots {
Slot_Stream,
Slot_Requests,
Slot_ClosedPromise,
Slot_ForAuthorCode,
SlotCount,
};
bool hasStream() const { return !getFixedSlot(Slot_Stream).isUndefined(); }
void setStream(JSObject* stream) {
setFixedSlot(Slot_Stream, ObjectValue(*stream));
}
void clearStream() { setFixedSlot(Slot_Stream, UndefinedValue()); }
bool isClosed() { return !hasStream(); }
ForAuthorCodeBool forAuthorCode() const {
return getFixedSlot(Slot_ForAuthorCode).toBoolean() ? ForAuthorCodeBool::Yes
: ForAuthorCodeBool::No;
}
void setForAuthorCode(ForAuthorCodeBool value) {
setFixedSlot(Slot_ForAuthorCode,
BooleanValue(value == ForAuthorCodeBool::Yes));
}
ListObject* requests() const {
return &getFixedSlot(Slot_Requests).toObject().as<ListObject>();
}
void clearRequests() { setFixedSlot(Slot_Requests, UndefinedValue()); }
JSObject* closedPromise() const {
return &getFixedSlot(Slot_ClosedPromise).toObject();
}
void setClosedPromise(JSObject* wrappedPromise) {
setFixedSlot(Slot_ClosedPromise, ObjectValue(*wrappedPromise));
}
static const Class class_;
};
class ReadableStreamDefaultReader : public ReadableStreamReader {
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class StreamController : public NativeObject {
public:
enum Slots { Slot_Queue, Slot_TotalSize, SlotCount };
ListObject* queue() const {
return &getFixedSlot(Slot_Queue).toObject().as<ListObject>();
}
double queueTotalSize() const {
return getFixedSlot(Slot_TotalSize).toNumber();
}
void setQueueTotalSize(double size) {
setFixedSlot(Slot_TotalSize, NumberValue(size));
}
};
class ReadableStreamController : public StreamController {
public:
enum Slots {
Slot_Stream = StreamController::SlotCount,
Slot_UnderlyingSource,
Slot_PullMethod,
Slot_CancelMethod,
Slot_StrategyHWM,
Slot_Flags,
SlotCount
};
enum ControllerFlags {
Flag_Started = 1 << 0,
Flag_Pulling = 1 << 1,
Flag_PullAgain = 1 << 2,
Flag_CloseRequested = 1 << 3,
Flag_TeeBranch1 = 1 << 4,
Flag_TeeBranch2 = 1 << 5,
Flag_ExternalSource = 1 << 6,
Flag_SourceLocked = 1 << 7,
};
ReadableStream* stream() const {
return &getFixedSlot(Slot_Stream).toObject().as<ReadableStream>();
}
void setStream(ReadableStream* stream) {
setFixedSlot(Slot_Stream, ObjectValue(*stream));
}
Value underlyingSource() const { return getFixedSlot(Slot_UnderlyingSource); }
void setUnderlyingSource(const Value& underlyingSource) {
setFixedSlot(Slot_UnderlyingSource, underlyingSource);
}
Value pullMethod() const { return getFixedSlot(Slot_PullMethod); }
void setPullMethod(const Value& pullMethod) {
setFixedSlot(Slot_PullMethod, pullMethod);
}
Value cancelMethod() const { return getFixedSlot(Slot_CancelMethod); }
void setCancelMethod(const Value& cancelMethod) {
setFixedSlot(Slot_CancelMethod, cancelMethod);
}
JS::ReadableStreamUnderlyingSource* externalSource() const {
static_assert(alignof(JS::ReadableStreamUnderlyingSource) >= 2,
"External underling sources are stored as PrivateValues, "
"so they must have even addresses");
MOZ_ASSERT(hasExternalSource());
return static_cast<JS::ReadableStreamUnderlyingSource*>(
underlyingSource().toPrivate());
}
void setExternalSource(JS::ReadableStreamUnderlyingSource* underlyingSource) {
setUnderlyingSource(JS::PrivateValue(underlyingSource));
addFlags(Flag_ExternalSource);
}
static void clearUnderlyingSource(
JS::Handle<ReadableStreamController*> controller) {
if (controller->hasExternalSource()) {
controller->externalSource()->finalize();
controller->setFlags(controller->flags() & ~Flag_ExternalSource);
}
controller->setUnderlyingSource(JS::UndefinedHandleValue);
}
double strategyHWM() const {
return getFixedSlot(Slot_StrategyHWM).toNumber();
}
void setStrategyHWM(double highWaterMark) {
setFixedSlot(Slot_StrategyHWM, NumberValue(highWaterMark));
}
uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); }
void setFlags(uint32_t flags) { setFixedSlot(Slot_Flags, Int32Value(flags)); }
void addFlags(uint32_t flags) { setFlags(this->flags() | flags); }
void removeFlags(uint32_t flags) { setFlags(this->flags() & ~flags); }
bool started() const { return flags() & Flag_Started; }
void setStarted() { addFlags(Flag_Started); }
bool pulling() const { return flags() & Flag_Pulling; }
void setPulling() { addFlags(Flag_Pulling); }
void clearPullFlags() { removeFlags(Flag_Pulling | Flag_PullAgain); }
bool pullAgain() const { return flags() & Flag_PullAgain; }
void setPullAgain() { addFlags(Flag_PullAgain); }
bool closeRequested() const { return flags() & Flag_CloseRequested; }
void setCloseRequested() { addFlags(Flag_CloseRequested); }
bool isTeeBranch1() const { return flags() & Flag_TeeBranch1; }
void setTeeBranch1() {
MOZ_ASSERT(!isTeeBranch2());
addFlags(Flag_TeeBranch1);
}
bool isTeeBranch2() const { return flags() & Flag_TeeBranch2; }
void setTeeBranch2() {
MOZ_ASSERT(!isTeeBranch1());
addFlags(Flag_TeeBranch2);
}
bool hasExternalSource() const { return flags() & Flag_ExternalSource; }
bool sourceLocked() const { return flags() & Flag_SourceLocked; }
void setSourceLocked() { addFlags(Flag_SourceLocked); }
void clearSourceLocked() { removeFlags(Flag_SourceLocked); }
};
class ReadableStreamDefaultController : public ReadableStreamController {
private:
enum Slots {
Slot_StrategySize = ReadableStreamController::SlotCount,
SlotCount
};
public:
Value strategySize() const { return getFixedSlot(Slot_StrategySize); }
void setStrategySize(const Value& size) {
setFixedSlot(Slot_StrategySize, size);
}
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableByteStreamController : public ReadableStreamController {
public:
enum Slots {
Slot_BYOBRequest = ReadableStreamController::SlotCount,
Slot_PendingPullIntos,
Slot_AutoAllocateSize,
SlotCount
};
Value byobRequest() const { return getFixedSlot(Slot_BYOBRequest); }
void clearBYOBRequest() {
setFixedSlot(Slot_BYOBRequest, JS::UndefinedValue());
}
ListObject* pendingPullIntos() const {
return &getFixedSlot(Slot_PendingPullIntos).toObject().as<ListObject>();
}
Value autoAllocateChunkSize() const {
return getFixedSlot(Slot_AutoAllocateSize);
}
void setAutoAllocateChunkSize(const Value& size) {
setFixedSlot(Slot_AutoAllocateSize, size);
}
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ByteLengthQueuingStrategy : public NativeObject {
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class CountQueuingStrategy : public NativeObject {
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
}
template <>
inline bool JSObject::is<js::StreamController>() const {
return is<js::ReadableStreamDefaultController>() ||
is<js::ReadableByteStreamController>();
}
template <>
inline bool JSObject::is<js::ReadableStreamController>() const {
return is<js::ReadableStreamDefaultController>() ||
is<js::ReadableByteStreamController>();
}
template <>
inline bool JSObject::is<js::ReadableStreamReader>() const {
return is<js::ReadableStreamDefaultReader>();
}
namespace js {
inline ReadableStreamController* ReadableStream::controller() const {
return &getFixedSlot(Slot_Controller)
.toObject()
.as<ReadableStreamController>();
}
inline void ReadableStream::setController(
ReadableStreamController* controller) {
setFixedSlot(Slot_Controller, JS::ObjectValue(*controller));
}
}
#endif