summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/streams/ReadableStreamController.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/streams/ReadableStreamController.h')
-rw-r--r--js/src/builtin/streams/ReadableStreamController.h266
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