/* -*- 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/. */ #ifndef mozilla_ipc_SidedVariant_h #define mozilla_ipc_SidedVariant_h #include #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/ipc/ProtocolUtils.h" #include "ipc/IPCMessageUtils.h" namespace mozilla { namespace ipc { /** * Helper type used by IPDL structs and unions to hold actor pointers with a * dynamic side. * * When sent over IPC, ParentSide will be used for send/recv on parent actors, * and ChildSide will be used for send/recv on child actors. */ template struct SideVariant { public: SideVariant() = default; template , int> = 0> MOZ_IMPLICIT SideVariant(U&& aParent) : mParent(std::forward(aParent)) {} template , int> = 0> MOZ_IMPLICIT SideVariant(U&& aChild) : mChild(std::forward(aChild)) {} MOZ_IMPLICIT SideVariant(std::nullptr_t) {} MOZ_IMPLICIT SideVariant& operator=(ParentSide aParent) { mParent = aParent; mChild = nullptr; return *this; } MOZ_IMPLICIT SideVariant& operator=(ChildSide aChild) { mChild = aChild; mParent = nullptr; return *this; } MOZ_IMPLICIT SideVariant& operator=(std::nullptr_t) { mChild = nullptr; mParent = nullptr; return *this; } MOZ_IMPLICIT operator bool() const { return mParent || mChild; } bool IsNull() const { return !operator bool(); } bool IsParent() const { return mParent; } bool IsChild() const { return mChild; } ParentSide AsParent() const { MOZ_ASSERT(IsNull() || IsParent()); return mParent; } ChildSide AsChild() const { MOZ_ASSERT(IsNull() || IsChild()); return mChild; } private: // As the values are both pointers, this is the same size as a variant would // be, but has less risk of type confusion, and supports an overall `nullptr` // value which is neither parent nor child. ParentSide mParent = nullptr; ChildSide mChild = nullptr; }; } // namespace ipc // NotNull specialization to expose AsChild and AsParent on the NotNull itself // avoiding unnecessary unwrapping. template class NotNull> { template friend constexpr NotNull WrapNotNull(U aBasePtr); template friend constexpr NotNull WrapNotNullUnchecked(U aBasePtr); template friend class NotNull; using BasePtr = mozilla::ipc::SideVariant; BasePtr mBasePtr; // This constructor is only used by WrapNotNull() and MakeNotNull(). template constexpr explicit NotNull(U aBasePtr) : mBasePtr(aBasePtr) {} public: // Disallow default construction. NotNull() = delete; // Construct/assign from another NotNull with a compatible base pointer type. template >> constexpr MOZ_IMPLICIT NotNull(const NotNull& aOther) : mBasePtr(aOther.get()) { static_assert(sizeof(BasePtr) == sizeof(NotNull), "NotNull must have zero space overhead."); static_assert(offsetof(NotNull, mBasePtr) == 0, "mBasePtr must have zero offset."); } template >> constexpr MOZ_IMPLICIT NotNull(MovingNotNull&& aOther) : mBasePtr(NotNull{std::move(aOther)}) {} // Disallow null checks, which are unnecessary for this type. explicit operator bool() const = delete; // Explicit conversion to a base pointer. Use only to resolve ambiguity or to // get a castable pointer. constexpr const BasePtr& get() const { return mBasePtr; } // Implicit conversion to a base pointer. Preferable to get(). constexpr operator const BasePtr&() const { return get(); } bool IsParent() const { return get().IsParent(); } bool IsChild() const { return get().IsChild(); } NotNull AsParent() const { return WrapNotNull(get().AsParent()); } NotNull AsChild() const { return WrapNotNull(get().AsChild()); } }; } // namespace mozilla namespace IPC { template struct ParamTraits> { typedef mozilla::ipc::SideVariant paramType; static void Write(IPC::MessageWriter* aWriter, const paramType& aParam) { if (!aWriter->GetActor()) { aWriter->FatalError("actor required to serialize this type"); return; } if (aWriter->GetActor()->GetSide() == mozilla::ipc::ParentSide) { if (aParam && !aParam.IsParent()) { aWriter->FatalError("invalid side"); return; } WriteParam(aWriter, aParam.AsParent()); } else { if (aParam && !aParam.IsChild()) { aWriter->FatalError("invalid side"); return; } WriteParam(aWriter, aParam.AsChild()); } } static ReadResult Read(IPC::MessageReader* aReader) { if (!aReader->GetActor()) { aReader->FatalError("actor required to deserialize this type"); return {}; } if (aReader->GetActor()->GetSide() == mozilla::ipc::ParentSide) { auto parentSide = ReadParam(aReader); if (!parentSide) { return {}; } return std::move(*parentSide); } auto childSide = ReadParam(aReader); if (!childSide) { return {}; } return std::move(*childSide); } }; } // namespace IPC #endif // mozilla_ipc_SidedVariant_h