/* -*- 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 // 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(); } ReadableStreamDefaultController* branch1() { ReadableStreamDefaultController* controller = &getFixedSlot(Slot_Branch1) .toObject() .as(); 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(); 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 unwrappedStream); }; } // namespace js #endif // builtin_streams_TeeState_h