summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/SafeRefPtr.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/SafeRefPtr.h')
-rw-r--r--dom/indexedDB/SafeRefPtr.h480
1 files changed, 480 insertions, 0 deletions
diff --git a/dom/indexedDB/SafeRefPtr.h b/dom/indexedDB/SafeRefPtr.h
new file mode 100644
index 0000000000..91fa4dded1
--- /dev/null
+++ b/dom/indexedDB/SafeRefPtr.h
@@ -0,0 +1,480 @@
+/* -*- 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_saferefptr_h__
+#define mozilla_saferefptr_h__
+
+#include "mozilla/ArrayAlgorithm.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/RefCounted.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTObserverArray.h"
+
+namespace mozilla {
+template <typename T>
+class SafeRefPtr;
+
+template <typename T, typename... Args>
+SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs);
+
+namespace detail {
+struct InitialConstructionTag {};
+
+class SafeRefCountedBase {
+ template <typename U, typename... Args>
+ friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs);
+
+ template <typename T>
+ friend class SafeRefPtr;
+
+ void* operator new(size_t aSize) { return ::operator new(aSize); }
+
+ protected:
+ void operator delete(void* aPtr) { ::operator delete(aPtr); }
+
+ public:
+ void* operator new[](size_t) = delete;
+};
+
+// SafeRefCounted is similar to RefCounted, but they differ in their initial
+// refcount (here 1), and the visibility of operator new (here private). The
+// rest is mostly a copy of RefCounted.
+template <typename T, RefCountAtomicity Atomicity>
+class SafeRefCounted : public SafeRefCountedBase {
+ protected:
+ SafeRefCounted() = default;
+#ifdef DEBUG
+ ~SafeRefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); }
+#endif
+
+ public:
+ // Compatibility with nsRefPtr.
+ MozRefCountType AddRef() const {
+ // Note: this method must be thread safe for AtomicRefCounted.
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0);
+ const MozRefCountType cnt = ++mRefCnt;
+ detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt);
+ return cnt;
+ }
+
+ MozRefCountType Release() const {
+ // Note: this method must be thread safe for AtomicRefCounted.
+ MOZ_ASSERT(int32_t(mRefCnt) > 0);
+ detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this));
+ const MozRefCountType cnt = --mRefCnt;
+ // Note: it's not safe to touch |this| after decrementing the refcount,
+ // except for below.
+ logger.logRelease(cnt);
+ if (0 == cnt) {
+ // Because we have atomically decremented the refcount above, only
+ // one thread can get a 0 count here, so as long as we can assume that
+ // everything else in the system is accessing this object through
+ // RefPtrs, it's safe to access |this| here.
+#ifdef DEBUG
+ mRefCnt = detail::DEAD;
+#endif
+ delete static_cast<const T*>(this);
+ }
+ return cnt;
+ }
+
+ // Compatibility with wtf::RefPtr.
+ void ref() { AddRef(); }
+ void deref() { Release(); }
+ MozRefCountType refCount() const { return mRefCnt; }
+ bool hasOneRef() const {
+ MOZ_ASSERT(mRefCnt > 0);
+ return mRefCnt == 1;
+ }
+
+ protected:
+ SafeRefPtr<T> SafeRefPtrFromThis();
+
+ private:
+ mutable RC<MozRefCountType, Atomicity> mRefCnt =
+ RC<MozRefCountType, Atomicity>{1};
+};
+} // namespace detail
+
+template <typename T>
+class SafeRefCounted
+ : public detail::SafeRefCounted<T, detail::NonAtomicRefCount> {
+ public:
+ ~SafeRefCounted() {
+ static_assert(std::is_base_of<SafeRefCounted, T>::value,
+ "T must derive from SafeRefCounted<T>");
+ }
+};
+
+template <typename T>
+class AtomicSafeRefCounted
+ : public detail::SafeRefCounted<T, detail::AtomicRefCount> {
+ public:
+ ~AtomicSafeRefCounted() {
+ static_assert(std::is_base_of<AtomicSafeRefCounted, T>::value,
+ "T must derive from AtomicSafeRefCounted<T>");
+ }
+};
+
+struct AcquireStrongRefFromRawPtr {};
+
+// XXX for Apple, clang::trivial_abi is probably also supported, but we need to
+// find out the correct version number
+#if defined(__clang__) && !defined(__apple_build_version__) && \
+ __clang_major__ >= 7
+# define MOZ_TRIVIAL_ABI [[clang::trivial_abi]]
+#else
+# define MOZ_TRIVIAL_ABI
+#endif
+
+// A restricted variant of mozilla::RefPtr<T>, which prohibits some unsafe or
+// unperformant misuses, in particular:
+// * It is not implicitly convertible from a raw pointer. Unsafe acquisitions
+// from a raw pointer must be made using the verbose
+// AcquireStrongRefFromRawPtr. To create a new object on the heap, use
+// MakeSafeRefPtr.
+// * It does not implicitly decay to a raw pointer. unsafeGetRawPtr() must be
+// called
+// explicitly.
+// * It is not copyable, but must be explicitly copied using clonePtr().
+// * Temporaries cannot be dereferenced using operator* or operator->.
+template <typename T>
+class MOZ_IS_REFPTR MOZ_TRIVIAL_ABI SafeRefPtr {
+ template <typename U>
+ friend class SafeRefPtr;
+
+ template <typename U, typename... Args>
+ friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs);
+
+ T* MOZ_OWNING_REF mRawPtr = nullptr;
+
+ // BEGIN Some things copied from RefPtr.
+ // We cannot simply use a RefPtr member because we want to be trivial_abi,
+ // which RefPtr is not.
+ void assign_with_AddRef(T* aRawPtr) {
+ if (aRawPtr) {
+ ConstRemovingRefPtrTraits<T>::AddRef(aRawPtr);
+ }
+ assign_assuming_AddRef(aRawPtr);
+ }
+
+ void assign_assuming_AddRef(T* aNewPtr) {
+ T* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ if (oldPtr) {
+ ConstRemovingRefPtrTraits<T>::Release(oldPtr);
+ }
+ }
+
+ template <class U>
+ struct ConstRemovingRefPtrTraits {
+ static void AddRef(U* aPtr) { mozilla::RefPtrTraits<U>::AddRef(aPtr); }
+ static void Release(U* aPtr) { mozilla::RefPtrTraits<U>::Release(aPtr); }
+ };
+ template <class U>
+ struct ConstRemovingRefPtrTraits<const U> {
+ static void AddRef(const U* aPtr) {
+ mozilla::RefPtrTraits<U>::AddRef(const_cast<U*>(aPtr));
+ }
+ static void Release(const U* aPtr) {
+ mozilla::RefPtrTraits<U>::Release(const_cast<U*>(aPtr));
+ }
+ };
+ // END Some things copied from RefPtr.
+
+ SafeRefPtr(T* aRawPtr, mozilla::detail::InitialConstructionTag);
+
+ public:
+ SafeRefPtr() = default;
+
+ template <typename U,
+ typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
+ MOZ_IMPLICIT SafeRefPtr(SafeRefPtr<U>&& aSrc) : mRawPtr(aSrc.mRawPtr) {
+ aSrc.mRawPtr = nullptr;
+ }
+
+ explicit SafeRefPtr(RefPtr<T>&& aRefPtr) : mRawPtr(aRefPtr.forget().take()) {}
+
+ // To prevent implicit conversion of raw pointer to RefPtr and then
+ // calling the previous overload.
+ SafeRefPtr(T* const aRawPtr) = delete;
+
+ SafeRefPtr(T* const aRawPtr, const AcquireStrongRefFromRawPtr&) {
+ assign_with_AddRef(aRawPtr);
+ }
+
+ MOZ_IMPLICIT SafeRefPtr(std::nullptr_t) {}
+
+ // Prevent implicit copying, use clonePtr() instead.
+ SafeRefPtr(const SafeRefPtr&) = delete;
+ SafeRefPtr& operator=(const SafeRefPtr&) = delete;
+
+ // Allow moving.
+ SafeRefPtr(SafeRefPtr&& aOther) noexcept : mRawPtr(aOther.mRawPtr) {
+ aOther.mRawPtr = nullptr;
+ }
+ SafeRefPtr& operator=(SafeRefPtr&& aOther) noexcept {
+ assign_assuming_AddRef(aOther.forget().take());
+ return *this;
+ }
+
+ ~SafeRefPtr() {
+ static_assert(!std::is_copy_constructible_v<T>);
+ static_assert(!std::is_copy_assignable_v<T>);
+ static_assert(!std::is_move_constructible_v<T>);
+ static_assert(!std::is_move_assignable_v<T>);
+
+ if (mRawPtr) {
+ ConstRemovingRefPtrTraits<T>::Release(mRawPtr);
+ }
+ }
+
+ typedef T element_type;
+
+ explicit operator bool() const { return mRawPtr; }
+ bool operator!() const { return !mRawPtr; }
+
+ T& operator*() const&& = delete;
+
+ T& operator*() const& {
+ MOZ_ASSERT(mRawPtr);
+ return *mRawPtr;
+ }
+
+ T* operator->() const&& = delete;
+
+ T* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN {
+ MOZ_ASSERT(mRawPtr);
+ return mRawPtr;
+ }
+
+ Maybe<T&> maybeDeref() const { return ToMaybeRef(mRawPtr); }
+
+ T* unsafeGetRawPtr() const { return mRawPtr; }
+
+ SafeRefPtr<T> clonePtr() const {
+ return SafeRefPtr{mRawPtr, AcquireStrongRefFromRawPtr{}};
+ }
+
+ already_AddRefed<T> forget() {
+ auto* const res = mRawPtr;
+ mRawPtr = nullptr;
+ return dont_AddRef(res);
+ }
+
+ bool operator==(const SafeRefPtr<T>& aOther) const {
+ return mRawPtr == aOther.mRawPtr;
+ }
+
+ bool operator!=(const SafeRefPtr<T>& aOther) const {
+ return mRawPtr != aOther.mRawPtr;
+ }
+
+ template <typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>>
+ SafeRefPtr<U> downcast() && {
+ SafeRefPtr<U> res;
+ res.mRawPtr = static_cast<U*>(mRawPtr);
+ mRawPtr = nullptr;
+ return res;
+ }
+
+ template <typename U>
+ friend RefPtr<U> AsRefPtr(SafeRefPtr<U>&& aSafeRefPtr);
+};
+
+template <typename T>
+SafeRefPtr(RefPtr<T>&&) -> SafeRefPtr<T>;
+
+template <typename T>
+SafeRefPtr(already_AddRefed<T>&&) -> SafeRefPtr<T>;
+
+template <typename T>
+class CheckedUnsafePtr;
+
+template <typename T>
+SafeRefPtr(const CheckedUnsafePtr<T>&, const AcquireStrongRefFromRawPtr&)
+ -> SafeRefPtr<T>;
+
+template <typename T>
+SafeRefPtr<T>::SafeRefPtr(T* aRawPtr, detail::InitialConstructionTag)
+ : mRawPtr(aRawPtr) {
+ if (!std::is_base_of_v<detail::SafeRefCountedBase, T> && mRawPtr) {
+ ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
+ }
+}
+
+template <typename T>
+bool operator==(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) {
+ return !aRhs;
+}
+
+template <typename T>
+bool operator!=(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) {
+ return static_cast<bool>(aRhs);
+}
+
+template <typename T>
+bool operator==(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) {
+ return !aLhs;
+}
+
+template <typename T>
+bool operator!=(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) {
+ return static_cast<bool>(aLhs);
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator==(T* const aLhs, const SafeRefPtr<U>& aRhs) {
+ return aLhs == aRhs.unsafeGetRawPtr();
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator!=(T* const aLhs, const SafeRefPtr<U>& aRhs) {
+ return !(aLhs == aRhs);
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator==(const SafeRefPtr<T>& aLhs, U* const aRhs) {
+ return aRhs == aLhs;
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator!=(const SafeRefPtr<T>& aLhs, U* const aRhs) {
+ return aRhs != aLhs;
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator==(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) {
+ return &aLhs.ref() == aRhs.unsafeGetRawPtr();
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator!=(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) {
+ return !(aLhs == aRhs);
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator==(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) {
+ return aRhs == aLhs;
+}
+
+template <typename T, typename U, typename = std::common_type_t<T*, U*>>
+bool operator!=(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) {
+ return aRhs != aLhs;
+}
+
+template <typename T>
+RefPtr<T> AsRefPtr(SafeRefPtr<T>&& aSafeRefPtr) {
+ return aSafeRefPtr.forget();
+}
+
+template <typename T, typename... Args>
+SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs) {
+ return SafeRefPtr{new T(std::forward<Args>(aArgs)...),
+ detail::InitialConstructionTag{}};
+}
+
+template <typename T>
+void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ const SafeRefPtr<T>& aField, const char* aName,
+ uint32_t aFlags = 0) {
+ CycleCollectionNoteChild(aCallback, aField.unsafeGetRawPtr(), aName, aFlags);
+}
+
+template <typename T>
+void ImplCycleCollectionUnlink(SafeRefPtr<T>& aField) {
+ aField = nullptr;
+}
+
+namespace detail {
+
+template <typename T, RefCountAtomicity Atomicity>
+SafeRefPtr<T> SafeRefCounted<T, Atomicity>::SafeRefPtrFromThis() {
+ // this actually is safe
+ return {static_cast<T*>(this), AcquireStrongRefFromRawPtr{}};
+}
+
+template <typename T>
+struct CopyablePtr<SafeRefPtr<T>> {
+ SafeRefPtr<T> mPtr;
+
+ explicit CopyablePtr(SafeRefPtr<T> aPtr) : mPtr{std::move(aPtr)} {}
+
+ CopyablePtr(const CopyablePtr& aOther) : mPtr{aOther.mPtr.clonePtr()} {}
+ CopyablePtr& operator=(const CopyablePtr& aOther) {
+ if (this != &aOther) {
+ mPtr = aOther.mPtr.clonePtr();
+ }
+ return *this;
+ }
+ CopyablePtr(CopyablePtr&&) = default;
+ CopyablePtr& operator=(CopyablePtr&&) = default;
+};
+
+} // namespace detail
+
+namespace dom {
+/// XXX Move this to BindingUtils.h later on
+template <class T, class S>
+inline RefPtr<T> StrongOrRawPtr(SafeRefPtr<S>&& aPtr) {
+ return AsRefPtr(std::move(aPtr));
+}
+
+} // namespace dom
+
+} // namespace mozilla
+
+template <class T>
+class nsTObserverArray<mozilla::SafeRefPtr<T>>
+ : public nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0> {
+ public:
+ using base_type = nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0>;
+ using size_type = nsTObserverArray_base::size_type;
+
+ // Initialization methods
+ nsTObserverArray() = default;
+
+ // Initialize this array and pre-allocate some number of elements.
+ explicit nsTObserverArray(size_type aCapacity) {
+ base_type::mArray.SetCapacity(aCapacity);
+ }
+
+ nsTObserverArray Clone() const {
+ auto result = nsTObserverArray{};
+ result.mArray = mozilla::TransformIntoNewArray(
+ this->mArray, [](const auto& ptr) { return ptr.clonePtr(); });
+ return result;
+ }
+};
+
+// Use MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED in a 'Class' derived from a
+// 'Super' class which derives from (Atomic)SafeRefCounted, and from some other
+// class using NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING.
+#if defined(NS_BUILD_REFCNT_LOGGING)
+# define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \
+ template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \
+ friend class ::mozilla::detail::SafeRefCounted; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \
+ NS_IMPL_ADDREF_INHERITED_GUTS(Class, Super); \
+ } \
+ NS_IMETHOD_(MozExternalRefCountType) Release() override { \
+ NS_IMPL_RELEASE_INHERITED_GUTS(Class, Super); \
+ }
+#else // NS_BUILD_REFCNT_LOGGING
+# define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \
+ template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \
+ friend class ::mozilla::detail::SafeRefCounted; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \
+ return Super::AddRef(); \
+ } \
+ NS_IMETHOD_(MozExternalRefCountType) Release() override { \
+ return Super::Release(); \
+ }
+#endif
+
+#endif