Bug 1700051: part 46) Const-qualify `mozInlineSpellStatus::mAnchorRange`. r=smaug
[gecko.git] / dom / quota / CheckedUnsafePtr.h
blob660cd00fd29b741c2216d30b07bd4ba04c8e7272
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // Diagnostic class template that helps finding dangling pointers.
7 #ifndef mozilla_CheckedUnsafePtr_h
8 #define mozilla_CheckedUnsafePtr_h
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/DataMutex.h"
13 #include "nsTArray.h"
15 #include <cstddef>
16 #include <type_traits>
17 #include <utility>
19 namespace mozilla {
20 enum class CheckingSupport {
21 Disabled,
22 Enabled,
25 template <typename T>
26 class CheckedUnsafePtr;
28 namespace detail {
29 class CheckedUnsafePtrBaseCheckingEnabled;
31 struct CheckedUnsafePtrCheckData {
32 using Data = nsTArray<CheckedUnsafePtrBaseCheckingEnabled*>;
34 DataMutex<Data> mPtrs{"mozilla::SupportsCheckedUnsafePtr"};
37 class CheckedUnsafePtrBaseCheckingEnabled {
38 friend class CheckedUnsafePtrBaseAccess;
40 protected:
41 constexpr CheckedUnsafePtrBaseCheckingEnabled() = default;
42 CheckedUnsafePtrBaseCheckingEnabled(
43 const CheckedUnsafePtrBaseCheckingEnabled& aOther) = default;
45 // When copying an CheckedUnsafePtr, its mIsDangling member must be copied as
46 // well; otherwise the new copy might try to dereference a dangling pointer
47 // when destructed.
48 void CopyDanglingFlagIfAvailableFrom(
49 const CheckedUnsafePtrBaseCheckingEnabled& aOther) {
50 mIsDangling = aOther.mIsDangling;
53 template <typename Ptr>
54 using DisableForCheckedUnsafePtr = std::enable_if_t<
55 !std::is_base_of<CheckedUnsafePtrBaseCheckingEnabled, Ptr>::value>;
57 // When constructing an CheckedUnsafePtr from a different kind of pointer it's
58 // not possible to determine whether it's dangling; therefore it's undefined
59 // behavior to construct one from a dangling pointer, and we assume that any
60 // CheckedUnsafePtr thus constructed is not dangling.
61 template <typename Ptr>
62 DisableForCheckedUnsafePtr<Ptr> CopyDanglingFlagIfAvailableFrom(const Ptr&) {}
64 template <typename F>
65 void WithCheckedUnsafePtrsImpl(CheckedUnsafePtrCheckData* const aRawPtr,
66 F&& aClosure) {
67 if (!mIsDangling && aRawPtr) {
68 const auto CheckedUnsafePtrs = aRawPtr->mPtrs.Lock();
69 aClosure(this, *CheckedUnsafePtrs);
73 private:
74 bool mIsDangling = false;
77 class CheckedUnsafePtrBaseAccess {
78 protected:
79 static void SetDanglingFlag(CheckedUnsafePtrBaseCheckingEnabled& aBase) {
80 aBase.mIsDangling = true;
84 template <typename T, CheckingSupport = T::SupportsChecking::value>
85 class CheckedUnsafePtrBase;
87 template <typename T, typename U, typename S = std::nullptr_t>
88 using EnableIfCompatible =
89 std::enable_if_t<std::is_base_of<T, std::remove_reference_t<decltype(
90 *std::declval<U>())>>::value,
91 S>;
93 template <typename T>
94 class CheckedUnsafePtrBase<T, CheckingSupport::Enabled>
95 : detail::CheckedUnsafePtrBaseCheckingEnabled {
96 public:
97 MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const std::nullptr_t = nullptr)
98 : mRawPtr(nullptr) {}
100 template <typename U, typename = EnableIfCompatible<T, U>>
101 MOZ_IMPLICIT CheckedUnsafePtrBase(const U& aPtr) {
102 Set(aPtr);
105 CheckedUnsafePtrBase(const CheckedUnsafePtrBase& aOther) {
106 Set(aOther.Downcast());
109 ~CheckedUnsafePtrBase() { Reset(); }
111 CheckedUnsafePtr<T>& operator=(const std::nullptr_t) {
112 Reset();
113 return Downcast();
116 template <typename U>
117 EnableIfCompatible<T, U, CheckedUnsafePtr<T>&> operator=(const U& aPtr) {
118 Replace(aPtr);
119 return Downcast();
122 CheckedUnsafePtrBase& operator=(const CheckedUnsafePtrBase& aOther) {
123 if (&aOther != this) {
124 Replace(aOther.Downcast());
126 return Downcast();
129 constexpr T* get() const { return mRawPtr; }
131 private:
132 template <typename U, CheckingSupport>
133 friend class CheckedUnsafePtrBase;
135 CheckedUnsafePtr<T>& Downcast() {
136 return static_cast<CheckedUnsafePtr<T>&>(*this);
138 const CheckedUnsafePtr<T>& Downcast() const {
139 return static_cast<const CheckedUnsafePtr<T>&>(*this);
142 using Base = detail::CheckedUnsafePtrBaseCheckingEnabled;
144 template <typename U>
145 void Replace(const U& aPtr) {
146 Reset();
147 Set(aPtr);
150 void Reset() {
151 WithCheckedUnsafePtrs(
152 [](Base* const aSelf,
153 detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) {
154 const auto index = aCheckedUnsafePtrs.IndexOf(aSelf);
155 aCheckedUnsafePtrs.UnorderedRemoveElementAt(index);
157 mRawPtr = nullptr;
160 template <typename U>
161 void Set(const U& aPtr) {
162 this->CopyDanglingFlagIfAvailableFrom(aPtr);
163 mRawPtr = &*aPtr;
164 WithCheckedUnsafePtrs(
165 [](Base* const aSelf,
166 detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) {
167 aCheckedUnsafePtrs.AppendElement(aSelf);
171 template <typename F>
172 void WithCheckedUnsafePtrs(F&& aClosure) {
173 this->WithCheckedUnsafePtrsImpl(mRawPtr, std::forward<F>(aClosure));
176 T* mRawPtr;
179 template <typename T>
180 class CheckedUnsafePtrBase<T, CheckingSupport::Disabled> {
181 public:
182 MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const std::nullptr_t = nullptr)
183 : mRawPtr(nullptr) {}
185 template <typename U, typename = EnableIfCompatible<T, U>>
186 MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const U& aPtr) : mRawPtr(aPtr) {}
188 constexpr CheckedUnsafePtr<T>& operator=(const std::nullptr_t) {
189 mRawPtr = nullptr;
190 return Downcast();
193 template <typename U>
194 constexpr EnableIfCompatible<T, U, CheckedUnsafePtr<T>&> operator=(
195 const U& aPtr) {
196 mRawPtr = aPtr;
197 return Downcast();
200 constexpr T* get() const { return mRawPtr; }
202 private:
203 constexpr CheckedUnsafePtr<T>& Downcast() {
204 return static_cast<CheckedUnsafePtr<T>&>(*this);
207 T* mRawPtr;
209 } // namespace detail
211 class CheckingPolicyAccess {
212 protected:
213 template <typename CheckingPolicy>
214 static void NotifyCheckFailure(CheckingPolicy& aPolicy) {
215 aPolicy.NotifyCheckFailure();
219 template <typename Derived>
220 class CheckCheckedUnsafePtrs : private CheckingPolicyAccess,
221 private detail::CheckedUnsafePtrBaseAccess {
222 public:
223 using SupportsChecking =
224 std::integral_constant<CheckingSupport, CheckingSupport::Enabled>;
226 protected:
227 static constexpr bool ShouldCheck() {
228 static_assert(
229 std::is_base_of<CheckCheckedUnsafePtrs, Derived>::value,
230 "cannot instantiate with a type that's not a subclass of this class");
231 return true;
234 void Check(detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) {
235 if (!aCheckedUnsafePtrs.IsEmpty()) {
236 for (auto* const aCheckedUnsafePtrBase : aCheckedUnsafePtrs) {
237 SetDanglingFlag(*aCheckedUnsafePtrBase);
239 NotifyCheckFailure(*static_cast<Derived*>(this));
244 class CrashOnDanglingCheckedUnsafePtr
245 : public CheckCheckedUnsafePtrs<CrashOnDanglingCheckedUnsafePtr> {
246 friend class mozilla::CheckingPolicyAccess;
247 void NotifyCheckFailure() { MOZ_CRASH("Found dangling CheckedUnsafePtr"); }
250 struct DoNotCheckCheckedUnsafePtrs {
251 using SupportsChecking =
252 std::integral_constant<CheckingSupport, CheckingSupport::Disabled>;
255 namespace detail {
256 // Template parameter CheckingSupport controls the inclusion of
257 // CheckedUnsafePtrCheckData as a subobject of instantiations of
258 // SupportsCheckedUnsafePtr, ensuring that choosing a policy without checking
259 // support incurs no size overhead.
260 template <typename CheckingPolicy,
261 CheckingSupport = CheckingPolicy::SupportsChecking::value>
262 class SupportCheckedUnsafePtrImpl;
264 template <typename CheckingPolicy>
265 class SupportCheckedUnsafePtrImpl<CheckingPolicy, CheckingSupport::Disabled>
266 : public CheckingPolicy {
267 protected:
268 template <typename... Args>
269 explicit SupportCheckedUnsafePtrImpl(Args&&... aArgs)
270 : CheckingPolicy(std::forward<Args>(aArgs)...) {}
273 template <typename CheckingPolicy>
274 class SupportCheckedUnsafePtrImpl<CheckingPolicy, CheckingSupport::Enabled>
275 : public CheckedUnsafePtrCheckData, public CheckingPolicy {
276 template <typename T>
277 friend class CheckedUnsafePtr;
279 protected:
280 template <typename... Args>
281 explicit SupportCheckedUnsafePtrImpl(Args&&... aArgs)
282 : CheckingPolicy(std::forward<Args>(aArgs)...) {}
284 ~SupportCheckedUnsafePtrImpl() {
285 if (this->ShouldCheck()) {
286 const auto ptrs = mPtrs.Lock();
287 this->Check(*ptrs);
292 struct SupportsCheckedUnsafePtrTag {};
293 } // namespace detail
295 template <typename Condition,
296 typename CheckingPolicy = CrashOnDanglingCheckedUnsafePtr>
297 using CheckIf = std::conditional_t<Condition::value, CheckingPolicy,
298 DoNotCheckCheckedUnsafePtrs>;
300 using DiagnosticAssertEnabled = std::integral_constant<bool,
301 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
302 true
303 #else
304 false
305 #endif
308 // A T class that publicly inherits from an instantiation of
309 // SupportsCheckedUnsafePtr and its subclasses can be pointed to by smart
310 // pointers of type CheckedUnsafePtr<T>. Whenever such a smart pointer is
311 // created, its existence is tracked by the pointee according to its
312 // CheckingPolicy. When the pointee goes out of scope it then uses the its
313 // CheckingPolicy to verify that no CheckedUnsafePtr pointers are left pointing
314 // to it.
316 // The CheckingPolicy type is used to control the kind of verification that
317 // happen at the end of the object's lifetime. By default, debug builds always
318 // check for dangling CheckedUnsafePtr pointers and assert that none are found,
319 // while release builds forgo all checks. (Release builds incur no size or
320 // runtime penalties compared to bare pointers.)
321 template <typename CheckingPolicy>
322 class SupportsCheckedUnsafePtr
323 : public detail::SupportCheckedUnsafePtrImpl<CheckingPolicy>,
324 public detail::SupportsCheckedUnsafePtrTag {
325 public:
326 template <typename... Args>
327 explicit SupportsCheckedUnsafePtr(Args&&... aArgs)
328 : detail::SupportCheckedUnsafePtrImpl<CheckingPolicy>(
329 std::forward<Args>(aArgs)...) {}
332 // CheckedUnsafePtr<T> is a smart pointer class that helps detect dangling
333 // pointers in cases where such pointers are not allowed. In order to use it,
334 // the pointee T must publicly inherit from an instantiation of
335 // SupportsCheckedUnsafePtr. An CheckedUnsafePtr<T> can be used anywhere a T*
336 // can be used, has the same size, and imposes no additional thread-safety
337 // restrictions.
338 template <typename T>
339 class CheckedUnsafePtr : public detail::CheckedUnsafePtrBase<T> {
340 static_assert(
341 std::is_base_of<detail::SupportsCheckedUnsafePtrTag, T>::value,
342 "type T must be derived from instantiation of SupportsCheckedUnsafePtr");
344 public:
345 using detail::CheckedUnsafePtrBase<T>::CheckedUnsafePtrBase;
346 using detail::CheckedUnsafePtrBase<T>::get;
348 constexpr T* operator->() const { return get(); }
350 constexpr T& operator*() const { return *get(); }
352 MOZ_IMPLICIT constexpr operator T*() const { return get(); }
354 template <typename U>
355 constexpr bool operator==(
356 detail::EnableIfCompatible<T, U, const U&> aRhs) const {
357 return get() == aRhs.get();
360 template <typename U>
361 friend constexpr bool operator==(
362 detail::EnableIfCompatible<T, U, const U&> aLhs,
363 const CheckedUnsafePtr& aRhs) {
364 return aRhs == aLhs;
367 template <typename U>
368 constexpr bool operator!=(
369 detail::EnableIfCompatible<T, U, const U&> aRhs) const {
370 return !(*this == aRhs);
373 template <typename U>
374 friend constexpr bool operator!=(
375 detail::EnableIfCompatible<T, U, const U&> aLhs,
376 const CheckedUnsafePtr& aRhs) {
377 return aRhs != aLhs;
381 } // namespace mozilla
383 // nsTArray<T> requires by default that T can be safely moved with std::memmove.
384 // Since CheckedUnsafePtr<T> has a non-trivial copy constructor, it has to opt
385 // into nsTArray<T> using them.
386 template <typename T>
387 struct nsTArray_RelocationStrategy<mozilla::CheckedUnsafePtr<T>> {
388 using Type = std::conditional_t<
389 T::SupportsChecking::value == mozilla::CheckingSupport::Enabled,
390 nsTArray_RelocateUsingMoveConstructor<mozilla::CheckedUnsafePtr<T>>,
391 nsTArray_RelocateUsingMemutils>;
394 template <typename T>
395 struct nsTArray_RelocationStrategy<
396 mozilla::NotNull<mozilla::CheckedUnsafePtr<T>>> {
397 using Type =
398 std::conditional_t<T::SupportsChecking::value ==
399 mozilla::CheckingSupport::Enabled,
400 nsTArray_RelocateUsingMoveConstructor<
401 mozilla::NotNull<mozilla::CheckedUnsafePtr<T>>>,
402 nsTArray_RelocateUsingMemutils>;
405 #endif // mozilla_CheckedUnsafePtr_h