diff options
Diffstat (limited to 'js/src/builtin/streams/ReadableStreamController.h')
-rw-r--r-- | js/src/builtin/streams/ReadableStreamController.h | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/js/src/builtin/streams/ReadableStreamController.h b/js/src/builtin/streams/ReadableStreamController.h new file mode 100644 index 0000000000..2cb1fc9e2a --- /dev/null +++ b/js/src/builtin/streams/ReadableStreamController.h @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* ReadableStream controller classes and functions. */ + +#ifndef builtin_streams_ReadableStreamController_h +#define builtin_streams_ReadableStreamController_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include <stdint.h> // uint32_t + +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/StreamController.h" // js::StreamController +#include "js/Class.h" // JSClass, js::ClassSpec +#include "js/RootingAPI.h" // JS::Handle +#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource +#include "js/Value.h" // JS::Value, JS::{Number,Object,Private,Undefined}Value, JS::UndefinedHandleValue +#include "vm/List.h" // js::ListObject +#include "vm/NativeObject.h" // js::NativeObject + +namespace js { + +class PromiseObject; + +class ReadableStreamController : public StreamController { + public: + /** + * Memory layout for ReadableStream controllers, starting after the slots + * reserved for queue container usage. + * + * Storage of the internal slots listed in the standard is fairly + * straightforward except for [[pullAlgorithm]] and [[cancelAlgorithm]]. + * These algorithms are not stored as JSFunction objects. Rather, there are + * three cases: + * + * - Streams created with `new ReadableStream`: The methods are stored + * in Slot_PullMethod and Slot_CancelMethod. The underlying source + * object (`this` for these methods) is in Slot_UnderlyingSource. + * + * - External source streams. Slot_UnderlyingSource is a PrivateValue + * pointing to the JS::ReadableStreamUnderlyingSource object. The + * algorithms are implemented using the .pull() and .cancel() methods + * of that object. Slot_Pull/CancelMethod are undefined. + * + * - Tee streams. Slot_UnderlyingSource is a TeeState object. The + * pull/cancel algorithms are implemented as separate functions in + * Stream.cpp. Slot_Pull/CancelMethod are undefined. + * + * UnderlyingSource, PullMethod, and CancelMethod can be wrappers to objects + * in other compartments. + * + * StrategyHWM and Flags are both primitive (numeric) values. + */ + 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, JS::ObjectValue(*stream)); + } + JS::Value underlyingSource() const { + return getFixedSlot(Slot_UnderlyingSource); + } + void setUnderlyingSource(const JS::Value& underlyingSource) { + setFixedSlot(Slot_UnderlyingSource, underlyingSource); + } + JS::Value pullMethod() const { return getFixedSlot(Slot_PullMethod); } + void setPullMethod(const JS::Value& pullMethod) { + setFixedSlot(Slot_PullMethod, pullMethod); + } + JS::Value cancelMethod() const { return getFixedSlot(Slot_CancelMethod); } + void setCancelMethod(const JS::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, + bool finalizeSource = true) { + if (controller->hasExternalSource()) { + if (finalizeSource) { + 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: + /** + * Memory layout for ReadableStreamDefaultControllers, starting after the + * slots shared among all types of controllers. + * + * StrategySize is treated as an opaque value when stored. The only use site + * ensures that it's wrapped into the current cx compartment. + */ + enum Slots { + Slot_StrategySize = ReadableStreamController::SlotCount, + SlotCount + }; + + public: + JS::Value strategySize() const { return getFixedSlot(Slot_StrategySize); } + void setStrategySize(const JS::Value& size) { + setFixedSlot(Slot_StrategySize, size); + } + + static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +class ReadableByteStreamController : public ReadableStreamController { + public: + /** + * Memory layout for ReadableByteStreamControllers, starting after the + * slots shared among all types of controllers. + * + * PendingPullIntos is guaranteed to be in the same compartment as the + * controller, but might contain wrappers for objects from other + * compartments. + * + * AutoAllocateSize is a primitive (numeric) value. + */ + enum Slots { + Slot_BYOBRequest = ReadableStreamController::SlotCount, + Slot_PendingPullIntos, + Slot_AutoAllocateSize, + SlotCount + }; + + JS::Value byobRequest() const { return getFixedSlot(Slot_BYOBRequest); } + void clearBYOBRequest() { + setFixedSlot(Slot_BYOBRequest, JS::UndefinedValue()); + } + ListObject* pendingPullIntos() const { + return &getFixedSlot(Slot_PendingPullIntos).toObject().as<ListObject>(); + } + JS::Value autoAllocateChunkSize() const { + return getFixedSlot(Slot_AutoAllocateSize); + } + void setAutoAllocateChunkSize(const JS::Value& size) { + setFixedSlot(Slot_AutoAllocateSize, size); + } + + static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +extern MOZ_MUST_USE bool CheckReadableStreamControllerCanCloseOrEnqueue( + JSContext* cx, JS::Handle<ReadableStreamController*> unwrappedController, + const char* action); + +extern MOZ_MUST_USE JSObject* ReadableStreamControllerCancelSteps( + JSContext* cx, JS::Handle<ReadableStreamController*> unwrappedController, + JS::Handle<JS::Value> reason); + +extern PromiseObject* ReadableStreamDefaultControllerPullSteps( + JSContext* cx, + JS::Handle<ReadableStreamDefaultController*> unwrappedController); + +extern bool ReadableStreamControllerStartHandler(JSContext* cx, unsigned argc, + JS::Value* vp); + +extern bool ReadableStreamControllerStartFailedHandler(JSContext* cx, + unsigned argc, + JS::Value* vp); + +} // namespace js + +template <> +inline bool JSObject::is<js::ReadableStreamController>() const { + return is<js::ReadableStreamDefaultController>() || + is<js::ReadableByteStreamController>(); +} + +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)); +} + +} // namespace js + +#endif // builtin_streams_ReadableStreamController_h |