SideVariant.h (6068B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_ipc_SidedVariant_h 8 #define mozilla_ipc_SidedVariant_h 9 10 #include <variant> 11 #include "mozilla/Assertions.h" 12 #include "mozilla/Attributes.h" 13 #include "mozilla/ipc/ProtocolUtils.h" 14 #include "ipc/IPCMessageUtils.h" 15 16 namespace mozilla { 17 namespace ipc { 18 19 /** 20 * Helper type used by IPDL structs and unions to hold actor pointers with a 21 * dynamic side. 22 * 23 * When sent over IPC, ParentSide will be used for send/recv on parent actors, 24 * and ChildSide will be used for send/recv on child actors. 25 */ 26 template <typename ParentSide, typename ChildSide> 27 struct SideVariant { 28 public: 29 SideVariant() = default; 30 template <typename U, 31 std::enable_if_t<std::is_convertible_v<U&&, ParentSide>, int> = 0> 32 MOZ_IMPLICIT SideVariant(U&& aParent) : mParent(std::forward<U>(aParent)) {} 33 template <typename U, 34 std::enable_if_t<std::is_convertible_v<U&&, ChildSide>, int> = 0> 35 MOZ_IMPLICIT SideVariant(U&& aChild) : mChild(std::forward<U>(aChild)) {} 36 MOZ_IMPLICIT SideVariant(std::nullptr_t) {} 37 38 MOZ_IMPLICIT SideVariant& operator=(ParentSide aParent) { 39 mParent = aParent; 40 mChild = nullptr; 41 return *this; 42 } 43 MOZ_IMPLICIT SideVariant& operator=(ChildSide aChild) { 44 mChild = aChild; 45 mParent = nullptr; 46 return *this; 47 } 48 MOZ_IMPLICIT SideVariant& operator=(std::nullptr_t) { 49 mChild = nullptr; 50 mParent = nullptr; 51 return *this; 52 } 53 54 MOZ_IMPLICIT operator bool() const { return mParent || mChild; } 55 56 bool IsNull() const { return !operator bool(); } 57 bool IsParent() const { return mParent; } 58 bool IsChild() const { return mChild; } 59 60 ParentSide AsParent() const { 61 MOZ_ASSERT(IsNull() || IsParent()); 62 return mParent; 63 } 64 ChildSide AsChild() const { 65 MOZ_ASSERT(IsNull() || IsChild()); 66 return mChild; 67 } 68 69 private: 70 // As the values are both pointers, this is the same size as a variant would 71 // be, but has less risk of type confusion, and supports an overall `nullptr` 72 // value which is neither parent nor child. 73 ParentSide mParent = nullptr; 74 ChildSide mChild = nullptr; 75 }; 76 77 } // namespace ipc 78 79 // NotNull specialization to expose AsChild and AsParent on the NotNull itself 80 // avoiding unnecessary unwrapping. 81 template <typename ParentSide, typename ChildSide> 82 class NotNull<mozilla::ipc::SideVariant<ParentSide, ChildSide>> { 83 template <typename U> 84 friend constexpr NotNull<U> WrapNotNull(U aBasePtr); 85 template <typename U> 86 friend constexpr NotNull<U> WrapNotNullUnchecked(U aBasePtr); 87 template <typename U> 88 friend class NotNull; 89 90 using BasePtr = mozilla::ipc::SideVariant<ParentSide, ChildSide>; 91 92 BasePtr mBasePtr; 93 94 // This constructor is only used by WrapNotNull() and MakeNotNull<U>(). 95 template <typename U> 96 constexpr explicit NotNull(U aBasePtr) : mBasePtr(aBasePtr) {} 97 98 public: 99 // Disallow default construction. 100 NotNull() = delete; 101 102 // Construct/assign from another NotNull with a compatible base pointer type. 103 template <typename U, typename = std::enable_if_t< 104 std::is_convertible_v<const U&, BasePtr>>> 105 constexpr MOZ_IMPLICIT NotNull(const NotNull<U>& aOther) 106 : mBasePtr(aOther.get()) { 107 static_assert(sizeof(BasePtr) == sizeof(NotNull<BasePtr>), 108 "NotNull must have zero space overhead."); 109 static_assert(offsetof(NotNull<BasePtr>, mBasePtr) == 0, 110 "mBasePtr must have zero offset."); 111 } 112 113 template <typename U, 114 typename = std::enable_if_t<std::is_convertible_v<U&&, BasePtr>>> 115 constexpr MOZ_IMPLICIT NotNull(MovingNotNull<U>&& aOther) 116 : mBasePtr(NotNull{std::move(aOther)}) {} 117 118 // Disallow null checks, which are unnecessary for this type. 119 explicit operator bool() const = delete; 120 121 // Explicit conversion to a base pointer. Use only to resolve ambiguity or to 122 // get a castable pointer. 123 constexpr const BasePtr& get() const { return mBasePtr; } 124 125 // Implicit conversion to a base pointer. Preferable to get(). 126 constexpr operator const BasePtr&() const { return get(); } 127 128 bool IsParent() const { return get().IsParent(); } 129 bool IsChild() const { return get().IsChild(); } 130 131 NotNull<ParentSide> AsParent() const { return WrapNotNull(get().AsParent()); } 132 NotNull<ChildSide> AsChild() const { return WrapNotNull(get().AsChild()); } 133 }; 134 135 } // namespace mozilla 136 137 namespace IPC { 138 139 template <typename ParentSide, typename ChildSide> 140 struct ParamTraits<mozilla::ipc::SideVariant<ParentSide, ChildSide>> { 141 typedef mozilla::ipc::SideVariant<ParentSide, ChildSide> paramType; 142 143 static void Write(IPC::MessageWriter* aWriter, const paramType& aParam) { 144 if (!aWriter->GetActor()) { 145 aWriter->FatalError("actor required to serialize this type"); 146 return; 147 } 148 149 if (aWriter->GetActor()->GetSide() == mozilla::ipc::ParentSide) { 150 if (aParam && !aParam.IsParent()) { 151 aWriter->FatalError("invalid side"); 152 return; 153 } 154 WriteParam(aWriter, aParam.AsParent()); 155 } else { 156 if (aParam && !aParam.IsChild()) { 157 aWriter->FatalError("invalid side"); 158 return; 159 } 160 WriteParam(aWriter, aParam.AsChild()); 161 } 162 } 163 164 static ReadResult<paramType> Read(IPC::MessageReader* aReader) { 165 if (!aReader->GetActor()) { 166 aReader->FatalError("actor required to deserialize this type"); 167 return {}; 168 } 169 170 if (aReader->GetActor()->GetSide() == mozilla::ipc::ParentSide) { 171 auto parentSide = ReadParam<ParentSide>(aReader); 172 if (!parentSide) { 173 return {}; 174 } 175 return std::move(*parentSide); 176 } 177 auto childSide = ReadParam<ChildSide>(aReader); 178 if (!childSide) { 179 return {}; 180 } 181 return std::move(*childSide); 182 } 183 }; 184 185 } // namespace IPC 186 187 #endif // mozilla_ipc_SidedVariant_h