summaryrefslogtreecommitdiffstats
path: root/ipc/glue/SideVariant.h
blob: 3082feebde3043ad597350d16b132500b51058c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* -*- 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