187 lines
5.9 KiB
C++
187 lines
5.9 KiB
C++
/* -*- 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 <variant>
|
|
#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 <typename ParentSide, typename ChildSide>
|
|
struct SideVariant {
|
|
public:
|
|
SideVariant() = default;
|
|
template <typename U,
|
|
std::enable_if_t<std::is_convertible_v<U&&, ParentSide>, int> = 0>
|
|
MOZ_IMPLICIT SideVariant(U&& aParent) : mParent(std::forward<U>(aParent)) {}
|
|
template <typename U,
|
|
std::enable_if_t<std::is_convertible_v<U&&, ChildSide>, int> = 0>
|
|
MOZ_IMPLICIT SideVariant(U&& aChild) : mChild(std::forward<U>(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 <typename ParentSide, typename ChildSide>
|
|
class NotNull<mozilla::ipc::SideVariant<ParentSide, ChildSide>> {
|
|
template <typename U>
|
|
friend constexpr NotNull<U> WrapNotNull(U aBasePtr);
|
|
template <typename U>
|
|
friend constexpr NotNull<U> WrapNotNullUnchecked(U aBasePtr);
|
|
template <typename U>
|
|
friend class NotNull;
|
|
|
|
using BasePtr = mozilla::ipc::SideVariant<ParentSide, ChildSide>;
|
|
|
|
BasePtr mBasePtr;
|
|
|
|
// This constructor is only used by WrapNotNull() and MakeNotNull<U>().
|
|
template <typename U>
|
|
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 <typename U, typename = std::enable_if_t<
|
|
std::is_convertible_v<const U&, BasePtr>>>
|
|
constexpr MOZ_IMPLICIT NotNull(const NotNull<U>& aOther)
|
|
: mBasePtr(aOther.get()) {
|
|
static_assert(sizeof(BasePtr) == sizeof(NotNull<BasePtr>),
|
|
"NotNull must have zero space overhead.");
|
|
static_assert(offsetof(NotNull<BasePtr>, mBasePtr) == 0,
|
|
"mBasePtr must have zero offset.");
|
|
}
|
|
|
|
template <typename U,
|
|
typename = std::enable_if_t<std::is_convertible_v<U&&, BasePtr>>>
|
|
constexpr MOZ_IMPLICIT NotNull(MovingNotNull<U>&& 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<ParentSide> AsParent() const { return WrapNotNull(get().AsParent()); }
|
|
NotNull<ChildSide> AsChild() const { return WrapNotNull(get().AsChild()); }
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
namespace IPC {
|
|
|
|
template <typename ParentSide, typename ChildSide>
|
|
struct ParamTraits<mozilla::ipc::SideVariant<ParentSide, ChildSide>> {
|
|
typedef mozilla::ipc::SideVariant<ParentSide, ChildSide> 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<paramType> 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<ParentSide>(aReader);
|
|
if (!parentSide) {
|
|
return {};
|
|
}
|
|
return std::move(*parentSide);
|
|
}
|
|
auto childSide = ReadParam<ChildSide>(aReader);
|
|
if (!childSide) {
|
|
return {};
|
|
}
|
|
return std::move(*childSide);
|
|
}
|
|
};
|
|
|
|
} // namespace IPC
|
|
|
|
#endif // mozilla_ipc_SidedVariant_h
|