summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/streams/TeeState.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/builtin/streams/TeeState.h155
1 files changed, 155 insertions, 0 deletions
diff --git a/js/src/builtin/streams/TeeState.h b/js/src/builtin/streams/TeeState.h
new file mode 100644
index 0000000000..beb42ff8e6
--- /dev/null
+++ b/js/src/builtin/streams/TeeState.h
@@ -0,0 +1,155 @@
+/* -*- 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/. */
+
+/* Stream teeing state. */
+
+#ifndef builtin_streams_TeeState_h
+#define builtin_streams_TeeState_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <stdint.h> // uint32_t
+
+#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamDefaultController
+#include "js/Class.h" // JSClass
+#include "js/Value.h" // JS::{Int32,Object}Value
+#include "vm/NativeObject.h" // js::NativeObject
+#include "vm/PromiseObject.h" // js::PromiseObject
+
+namespace js {
+
+/**
+ * TeeState objects implement the local variables in Streams spec 3.3.9
+ * ReadableStreamTee, which are accessed by several algorithms.
+ */
+class TeeState : public NativeObject {
+ public:
+ /**
+ * Memory layout for TeeState instances.
+ *
+ * The Reason1 and Reason2 slots store opaque values, which might be
+ * wrapped objects from other compartments. Since we don't treat them as
+ * objects in Streams-specific code, we don't have to worry about that
+ * apart from ensuring that the values are properly wrapped before storing
+ * them.
+ *
+ * CancelPromise is always created in TeeState::create below, so is
+ * guaranteed to be in the same compartment as the TeeState instance
+ * itself.
+ *
+ * Stream can be from another compartment. It is automatically wrapped
+ * before storing it and unwrapped upon retrieval. That means that
+ * TeeState consumers need to be able to deal with unwrapped
+ * ReadableStream instances from non-current compartments.
+ *
+ * Branch1 and Branch2 are always created in the same compartment as the
+ * TeeState instance, so cannot be from another compartment.
+ */
+ enum Slots {
+ Slot_Flags = 0,
+ Slot_Reason1,
+ Slot_Reason2,
+ Slot_CancelPromise,
+ Slot_Stream,
+ Slot_Branch1,
+ Slot_Branch2,
+ SlotCount
+ };
+
+ private:
+ enum Flags {
+ Flag_Reading = 1 << 0,
+ Flag_Canceled1 = 1 << 1,
+ Flag_Canceled2 = 1 << 2,
+
+ // No internal user ever sets the cloneForBranch2 flag to true, and the
+ // streams spec doesn't expose a way to set the flag to true. So for the
+ // moment, don't even reserve flag-space to store it.
+ // Flag_CloneForBranch2 = 1 << 3,
+ };
+ uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); }
+ void setFlags(uint32_t flags) {
+ setFixedSlot(Slot_Flags, JS::Int32Value(flags));
+ }
+
+ public:
+ static const JSClass class_;
+
+ // Consistent with not even storing this always-false flag, expose it as
+ // compile-time constant false.
+ static constexpr bool cloneForBranch2() { return false; }
+
+ bool reading() const { return flags() & Flag_Reading; }
+ void setReading() {
+ MOZ_ASSERT(!(flags() & Flag_Reading));
+ setFlags(flags() | Flag_Reading);
+ }
+ void unsetReading() {
+ MOZ_ASSERT(flags() & Flag_Reading);
+ setFlags(flags() & ~Flag_Reading);
+ }
+
+ bool canceled1() const { return flags() & Flag_Canceled1; }
+ void setCanceled1(HandleValue reason) {
+ MOZ_ASSERT(!(flags() & Flag_Canceled1));
+ setFlags(flags() | Flag_Canceled1);
+ setFixedSlot(Slot_Reason1, reason);
+ }
+
+ bool canceled2() const { return flags() & Flag_Canceled2; }
+ void setCanceled2(HandleValue reason) {
+ MOZ_ASSERT(!(flags() & Flag_Canceled2));
+ setFlags(flags() | Flag_Canceled2);
+ setFixedSlot(Slot_Reason2, reason);
+ }
+
+ JS::Value reason1() const {
+ MOZ_ASSERT(canceled1());
+ return getFixedSlot(Slot_Reason1);
+ }
+
+ JS::Value reason2() const {
+ MOZ_ASSERT(canceled2());
+ return getFixedSlot(Slot_Reason2);
+ }
+
+ PromiseObject* cancelPromise() {
+ return &getFixedSlot(Slot_CancelPromise).toObject().as<PromiseObject>();
+ }
+
+ ReadableStreamDefaultController* branch1() {
+ ReadableStreamDefaultController* controller =
+ &getFixedSlot(Slot_Branch1)
+ .toObject()
+ .as<ReadableStreamDefaultController>();
+ MOZ_ASSERT(controller->isTeeBranch1());
+ return controller;
+ }
+ void setBranch1(ReadableStreamDefaultController* controller) {
+ MOZ_ASSERT(controller->isTeeBranch1());
+ setFixedSlot(Slot_Branch1, JS::ObjectValue(*controller));
+ }
+
+ ReadableStreamDefaultController* branch2() {
+ ReadableStreamDefaultController* controller =
+ &getFixedSlot(Slot_Branch2)
+ .toObject()
+ .as<ReadableStreamDefaultController>();
+ MOZ_ASSERT(controller->isTeeBranch2());
+ return controller;
+ }
+ void setBranch2(ReadableStreamDefaultController* controller) {
+ MOZ_ASSERT(controller->isTeeBranch2());
+ setFixedSlot(Slot_Branch2, JS::ObjectValue(*controller));
+ }
+
+ static TeeState* create(JSContext* cx,
+ Handle<ReadableStream*> unwrappedStream);
+};
+
+} // namespace js
+
+#endif // builtin_streams_TeeState_h