summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/streams/WritableStreamDefaultController.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/builtin/streams/WritableStreamDefaultController.h186
1 files changed, 186 insertions, 0 deletions
diff --git a/js/src/builtin/streams/WritableStreamDefaultController.h b/js/src/builtin/streams/WritableStreamDefaultController.h
new file mode 100644
index 0000000000..44f3d237a9
--- /dev/null
+++ b/js/src/builtin/streams/WritableStreamDefaultController.h
@@ -0,0 +1,186 @@
+/* -*- 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/. */
+
+/* WritableStream controller classes and functions. */
+
+#ifndef builtin_streams_WritableStreamDefaultController_h
+#define builtin_streams_WritableStreamDefaultController_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <stdint.h> // uint32_t
+
+#include "builtin/streams/StreamController.h" // js::StreamController
+#include "builtin/streams/WritableStream.h" // js::WritableStream
+#include "js/Class.h" // JSClass, js::ClassSpec
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/Stream.h" // JS::WritableStreamUnderlyingSink
+#include "js/Value.h" // JS::Value, JS::{Number,Object,Private,Undefined}Value, JS::UndefinedHandleValue
+#include "vm/NativeObject.h" // js::NativeObject
+
+namespace js {
+
+class WritableStreamDefaultController : public StreamController {
+ public:
+ /**
+ * Memory layout for WritableStream default controllers, starting after the
+ * slots reserved for queue container usage. (Note that this is the only
+ * writable stream controller class in the spec: ReadableByteStreamController
+ * exists, but WritableByteStreamController does not.)
+ */
+ enum Slots {
+ /**
+ * The stream that this controller controls. Stream and controller are
+ * initialized at the same time underneath the |WritableStream| constructor,
+ * so they are same-compartment with each other.
+ */
+ Slot_Stream = StreamController::SlotCount,
+
+ /**
+ * The underlying sink object that this controller and its associated stream
+ * write to.
+ *
+ * This is a user-provided value, the first argument passed to
+ * |new WritableStream|, so it may be a cross-compartment wrapper around an
+ * object from another realm.
+ */
+ Slot_UnderlyingSink,
+
+ /** Number stored as DoubleValue. */
+ Slot_StrategyHWM,
+
+ /**
+ * Either undefined if each chunk has size 1, or a callable object to be
+ * invoked on each chunk to determine its size. See
+ * MakeSizeAlgorithmFromSizeFunction.
+ */
+ Slot_StrategySize,
+
+ /**
+ * Slots containing the core of each of the write/close/abort algorithms the
+ * spec creates from the underlying sink passed in when creating a
+ * |WritableStream|. ("core", as in the value produced by
+ * |CreateAlgorithmFromUnderlyingMethod| after validating the user-provided
+ * input.)
+ *
+ * These slots are initialized underneath the |WritableStream| constructor,
+ * so they are same-compartment with both stream and controller. (They
+ * could be wrappers around arbitrary callable objects from other
+ * compartments, tho.)
+ */
+ Slot_WriteMethod,
+ Slot_CloseMethod,
+ Slot_AbortMethod,
+
+ /** Bit field stored as Int32Value. */
+ Slot_Flags,
+
+ SlotCount
+ };
+
+ enum ControllerFlags {
+ Flag_Started = 0b0001,
+ Flag_ExternalSink = 0b0010,
+ };
+
+ WritableStream* stream() const {
+ return &getFixedSlot(Slot_Stream).toObject().as<WritableStream>();
+ }
+ void setStream(WritableStream* stream) {
+ setFixedSlot(Slot_Stream, JS::ObjectValue(*stream));
+ }
+
+ JS::Value underlyingSink() const { return getFixedSlot(Slot_UnderlyingSink); }
+ void setUnderlyingSink(const JS::Value& underlyingSink) {
+ setFixedSlot(Slot_UnderlyingSink, underlyingSink);
+ }
+
+ JS::WritableStreamUnderlyingSink* externalSink() const {
+ static_assert(alignof(JS::WritableStreamUnderlyingSink) >= 2,
+ "external underling sinks are stored as PrivateValues, so "
+ "they must have even addresses");
+ MOZ_ASSERT(hasExternalSink());
+ return static_cast<JS::WritableStreamUnderlyingSink*>(
+ underlyingSink().toPrivate());
+ }
+ void setExternalSink(JS::WritableStreamUnderlyingSink* underlyingSink) {
+ setUnderlyingSink(JS::PrivateValue(underlyingSink));
+ addFlags(Flag_ExternalSink);
+ }
+ static void clearUnderlyingSink(
+ JS::Handle<WritableStreamDefaultController*> controller,
+ bool finalizeSink = true) {
+ if (controller->hasExternalSink()) {
+ if (finalizeSink) {
+ controller->externalSink()->finalize();
+ }
+ controller->setFlags(controller->flags() & ~Flag_ExternalSink);
+ }
+ controller->setUnderlyingSink(JS::UndefinedHandleValue);
+ }
+
+ JS::Value writeMethod() const { return getFixedSlot(Slot_WriteMethod); }
+ void setWriteMethod(const JS::Value& writeMethod) {
+ setFixedSlot(Slot_WriteMethod, writeMethod);
+ }
+ void clearWriteMethod() { setWriteMethod(JS::UndefinedValue()); }
+
+ JS::Value closeMethod() const { return getFixedSlot(Slot_CloseMethod); }
+ void setCloseMethod(const JS::Value& closeMethod) {
+ setFixedSlot(Slot_CloseMethod, closeMethod);
+ }
+ void clearCloseMethod() { setCloseMethod(JS::UndefinedValue()); }
+
+ JS::Value abortMethod() const { return getFixedSlot(Slot_AbortMethod); }
+ void setAbortMethod(const JS::Value& abortMethod) {
+ setFixedSlot(Slot_AbortMethod, abortMethod);
+ }
+ void clearAbortMethod() { setAbortMethod(JS::UndefinedValue()); }
+
+ double strategyHWM() const {
+ return getFixedSlot(Slot_StrategyHWM).toDouble();
+ }
+ void setStrategyHWM(double highWaterMark) {
+ setFixedSlot(Slot_StrategyHWM, DoubleValue(highWaterMark));
+ }
+
+ JS::Value strategySize() const { return getFixedSlot(Slot_StrategySize); }
+ void setStrategySize(const JS::Value& size) {
+ setFixedSlot(Slot_StrategySize, size);
+ }
+ void clearStrategySize() { setStrategySize(JS::UndefinedValue()); }
+
+ 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 hasExternalSink() const { return flags() & Flag_ExternalSink; }
+
+ 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_;
+};
+
+inline WritableStreamDefaultController* WritableStream::controller() const {
+ return &getFixedSlot(Slot_Controller)
+ .toObject()
+ .as<WritableStreamDefaultController>();
+}
+
+inline void WritableStream::setController(
+ WritableStreamDefaultController* controller) {
+ setFixedSlot(Slot_Controller, JS::ObjectValue(*controller));
+}
+
+} // namespace js
+
+#endif // builtin_streams_WritableStreamDefaultController_h