Bug 1876289 [wpt PR 44165] - Unskip loaf-source-location-redirect and deflake, a...
[gecko.git] / mfbt / UniquePtrExtensions.h
blob2679440e501ee5800b315cd7455cee2c84cffe8d
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* Useful extensions to UniquePtr. */
9 #ifndef mozilla_UniquePtrExtensions_h
10 #define mozilla_UniquePtrExtensions_h
12 #include <type_traits>
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/DebugOnly.h"
17 #include "mozilla/fallible.h"
18 #include "mozilla/UniquePtr.h"
20 #ifdef XP_WIN
21 # include <cstdint>
22 #endif
23 #if defined(XP_DARWIN) && !defined(RUST_BINDGEN)
24 # include <mach/mach.h>
25 #endif
27 namespace mozilla {
29 /**
30 * MakeUniqueFallible works exactly like MakeUnique, except that the memory
31 * allocation performed is done fallibly, i.e. it can return nullptr.
33 template <typename T, typename... Args>
34 typename detail::UniqueSelector<T>::SingleObject MakeUniqueFallible(
35 Args&&... aArgs) {
36 return UniquePtr<T>(new (fallible) T(std::forward<Args>(aArgs)...));
39 template <typename T>
40 typename detail::UniqueSelector<T>::UnknownBound MakeUniqueFallible(
41 decltype(sizeof(int)) aN) {
42 using ArrayType = std::remove_extent_t<T>;
43 return UniquePtr<T>(new (fallible) ArrayType[aN]());
46 template <typename T, typename... Args>
47 typename detail::UniqueSelector<T>::KnownBound MakeUniqueFallible(
48 Args&&... aArgs) = delete;
50 /**
51 * MakeUniqueForOverwrite and MakeUniqueFallibleForOverwrite are like MakeUnique
52 * and MakeUniqueFallible except they use default-initialization. This is
53 * useful, for example, when you have a POD type array that will be overwritten
54 * directly after construction and so zero-initialization is a waste.
56 template <typename T, typename... Args>
57 typename detail::UniqueSelector<T>::SingleObject MakeUniqueForOverwrite() {
58 return UniquePtr<T>(new T);
61 template <typename T>
62 typename detail::UniqueSelector<T>::UnknownBound MakeUniqueForOverwrite(
63 decltype(sizeof(int)) aN) {
64 using ArrayType = std::remove_extent_t<T>;
65 return UniquePtr<T>(new ArrayType[aN]);
68 template <typename T, typename... Args>
69 typename detail::UniqueSelector<T>::KnownBound MakeUniqueForOverwrite(
70 Args&&... aArgs) = delete;
72 template <typename T, typename... Args>
73 typename detail::UniqueSelector<T>::SingleObject
74 MakeUniqueForOverwriteFallible() {
75 return UniquePtr<T>(new (fallible) T);
78 template <typename T>
79 typename detail::UniqueSelector<T>::UnknownBound MakeUniqueForOverwriteFallible(
80 decltype(sizeof(int)) aN) {
81 using ArrayType = std::remove_extent_t<T>;
82 return UniquePtr<T>(new (fallible) ArrayType[aN]);
85 template <typename T, typename... Args>
86 typename detail::UniqueSelector<T>::KnownBound MakeUniqueForOverwriteFallible(
87 Args&&... aArgs) = delete;
89 namespace detail {
91 template <typename T>
92 struct FreePolicy {
93 void operator()(const void* ptr) { free(const_cast<void*>(ptr)); }
96 #if defined(XP_WIN)
97 // Can't include <windows.h> to get the actual definition of HANDLE
98 // because of namespace pollution.
99 typedef void* FileHandleType;
100 #elif defined(XP_UNIX)
101 typedef int FileHandleType;
102 #else
103 # error "Unsupported OS?"
104 #endif
106 struct FileHandleHelper {
107 MOZ_IMPLICIT FileHandleHelper(FileHandleType aHandle) : mHandle(aHandle) {
108 #if defined(XP_UNIX) && (defined(DEBUG) || defined(FUZZING))
109 MOZ_RELEASE_ASSERT(aHandle == kInvalidHandle || aHandle > 2);
110 #endif
113 MOZ_IMPLICIT constexpr FileHandleHelper(std::nullptr_t)
114 : mHandle(kInvalidHandle) {}
116 bool operator!=(std::nullptr_t) const {
117 #ifdef XP_WIN
118 // Windows uses both nullptr and INVALID_HANDLE_VALUE (-1 cast to
119 // HANDLE) in different situations, but nullptr is more reliably
120 // null while -1 is also valid input to some calls that take
121 // handles. So class considers both to be null (since neither
122 // should be closed) but default-constructs as nullptr.
123 if (mHandle == (void*)-1) {
124 return false;
126 #endif
127 return mHandle != kInvalidHandle;
130 operator FileHandleType() const { return mHandle; }
132 #ifdef XP_WIN
133 // NSPR uses an integer type for PROsfd, so this conversion is
134 // provided for working with it without needing reinterpret casts
135 // everywhere.
136 operator std::intptr_t() const {
137 return reinterpret_cast<std::intptr_t>(mHandle);
139 #endif
141 // When there's only one user-defined conversion operator, the
142 // compiler will use that to derive equality, but that doesn't work
143 // when the conversion is ambiguoug (the XP_WIN case above).
144 bool operator==(const FileHandleHelper& aOther) const {
145 return mHandle == aOther.mHandle;
148 private:
149 FileHandleType mHandle;
151 #ifdef XP_WIN
152 // See above for why this is nullptr. (Also, INVALID_HANDLE_VALUE
153 // can't be expressed as a constexpr.)
154 static constexpr FileHandleType kInvalidHandle = nullptr;
155 #else
156 static constexpr FileHandleType kInvalidHandle = -1;
157 #endif
160 struct FileHandleDeleter {
161 using pointer = FileHandleHelper;
162 using receiver = FileHandleType;
163 MFBT_API void operator()(FileHandleHelper aHelper);
166 #if defined(XP_DARWIN) && !defined(RUST_BINDGEN)
167 struct MachPortHelper {
168 MOZ_IMPLICIT MachPortHelper(mach_port_t aPort) : mPort(aPort) {}
170 MOZ_IMPLICIT constexpr MachPortHelper(std::nullptr_t)
171 : mPort(MACH_PORT_NULL) {}
173 bool operator!=(std::nullptr_t) const { return mPort != MACH_PORT_NULL; }
175 operator const mach_port_t&() const { return mPort; }
176 operator mach_port_t&() { return mPort; }
178 private:
179 mach_port_t mPort;
182 struct MachSendRightDeleter {
183 using pointer = MachPortHelper;
184 using receiver = mach_port_t;
185 MFBT_API void operator()(MachPortHelper aHelper) {
186 DebugOnly<kern_return_t> kr =
187 mach_port_deallocate(mach_task_self(), aHelper);
188 MOZ_ASSERT(kr == KERN_SUCCESS, "failed to deallocate mach send right");
192 struct MachReceiveRightDeleter {
193 using pointer = MachPortHelper;
194 using receiver = mach_port_t;
195 MFBT_API void operator()(MachPortHelper aHelper) {
196 DebugOnly<kern_return_t> kr = mach_port_mod_refs(
197 mach_task_self(), aHelper, MACH_PORT_RIGHT_RECEIVE, -1);
198 MOZ_ASSERT(kr == KERN_SUCCESS, "failed to release mach receive right");
202 struct MachPortSetDeleter {
203 using pointer = MachPortHelper;
204 using receiver = mach_port_t;
205 MFBT_API void operator()(MachPortHelper aHelper) {
206 DebugOnly<kern_return_t> kr = mach_port_mod_refs(
207 mach_task_self(), aHelper, MACH_PORT_RIGHT_PORT_SET, -1);
208 MOZ_ASSERT(kr == KERN_SUCCESS, "failed to release mach port set");
211 #endif
213 } // namespace detail
215 template <typename T>
216 using UniqueFreePtr = UniquePtr<T, detail::FreePolicy<T>>;
218 // A RAII class for the OS construct used for open files and similar
219 // objects: a file descriptor on Unix or a handle on Windows.
220 using UniqueFileHandle =
221 UniquePtr<detail::FileHandleType, detail::FileHandleDeleter>;
223 #if defined(XP_DARWIN) && !defined(RUST_BINDGEN)
224 // A RAII class for a Mach port that names a send right.
225 using UniqueMachSendRight =
226 UniquePtr<mach_port_t, detail::MachSendRightDeleter>;
227 // A RAII class for a Mach port that names a receive right.
228 using UniqueMachReceiveRight =
229 UniquePtr<mach_port_t, detail::MachReceiveRightDeleter>;
230 // A RAII class for a Mach port set.
231 using UniqueMachPortSet = UniquePtr<mach_port_t, detail::MachPortSetDeleter>;
233 // Increases the user reference count for MACH_PORT_RIGHT_SEND by 1 and returns
234 // a new UniqueMachSendRight to manage the additional right.
235 inline UniqueMachSendRight RetainMachSendRight(mach_port_t aPort) {
236 kern_return_t kr =
237 mach_port_mod_refs(mach_task_self(), aPort, MACH_PORT_RIGHT_SEND, 1);
238 if (kr == KERN_SUCCESS) {
239 return UniqueMachSendRight(aPort);
241 return nullptr;
243 #endif
245 namespace detail {
247 struct HasReceiverTypeHelper {
248 template <class U>
249 static double Test(...);
250 template <class U>
251 static char Test(typename U::receiver* = 0);
254 template <class T>
255 class HasReceiverType
256 : public std::integral_constant<bool, sizeof(HasReceiverTypeHelper::Test<T>(
257 0)) == 1> {};
259 template <class T, class D, bool = HasReceiverType<D>::value>
260 struct ReceiverTypeImpl {
261 using Type = typename D::receiver;
264 template <class T, class D>
265 struct ReceiverTypeImpl<T, D, false> {
266 using Type = typename PointerType<T, D>::Type;
269 template <class T, class D>
270 struct ReceiverType {
271 using Type = typename ReceiverTypeImpl<T, std::remove_reference_t<D>>::Type;
274 template <typename T, typename D>
275 class MOZ_TEMPORARY_CLASS UniquePtrGetterTransfers {
276 public:
277 using Ptr = UniquePtr<T, D>;
278 using Receiver = typename detail::ReceiverType<T, D>::Type;
280 explicit UniquePtrGetterTransfers(Ptr& p)
281 : mPtr(p), mReceiver(typename Ptr::Pointer(nullptr)) {}
282 ~UniquePtrGetterTransfers() { mPtr.reset(mReceiver); }
284 operator Receiver*() { return &mReceiver; }
285 Receiver& operator*() { return mReceiver; }
287 // operator void** is conditionally enabled if `Receiver` is a pointer.
288 template <typename U = Receiver,
289 std::enable_if_t<
290 std::is_pointer_v<U> && std::is_same_v<U, Receiver>, int> = 0>
291 operator void**() {
292 return reinterpret_cast<void**>(&mReceiver);
295 private:
296 Ptr& mPtr;
297 Receiver mReceiver;
300 } // namespace detail
302 // Helper for passing a UniquePtr to an old-style function that uses raw
303 // pointers for out params. Example usage:
305 // void AllocateFoo(Foo** out) { *out = new Foo(); }
306 // UniquePtr<Foo> foo;
307 // AllocateFoo(getter_Transfers(foo));
308 template <typename T, typename D>
309 auto getter_Transfers(UniquePtr<T, D>& up) {
310 return detail::UniquePtrGetterTransfers<T, D>(up);
313 } // namespace mozilla
315 #endif // mozilla_UniquePtrExtensions_h