/* -*- 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 class SafeRefPtr; template SafeRefPtr MakeSafeRefPtr(Args&&... aArgs); namespace detail { struct InitialConstructionTag {}; class SafeRefCountedBase { template friend SafeRefPtr mozilla::MakeSafeRefPtr(Args&&... aArgs); template 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 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(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(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(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 SafeRefPtrFromThis(); private: mutable RC mRefCnt = RC{1}; }; } // namespace detail template class SafeRefCounted : public detail::SafeRefCounted { public: ~SafeRefCounted() { static_assert(std::is_base_of::value, "T must derive from SafeRefCounted"); } }; template class AtomicSafeRefCounted : public detail::SafeRefCounted { public: ~AtomicSafeRefCounted() { static_assert(std::is_base_of::value, "T must derive from AtomicSafeRefCounted"); } }; 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, 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 class MOZ_IS_REFPTR MOZ_TRIVIAL_ABI SafeRefPtr { template friend class SafeRefPtr; template friend SafeRefPtr 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::AddRef(aRawPtr); } assign_assuming_AddRef(aRawPtr); } void assign_assuming_AddRef(T* aNewPtr) { T* oldPtr = mRawPtr; mRawPtr = aNewPtr; if (oldPtr) { ConstRemovingRefPtrTraits::Release(oldPtr); } } template struct ConstRemovingRefPtrTraits { static void AddRef(U* aPtr) { mozilla::RefPtrTraits::AddRef(aPtr); } static void Release(U* aPtr) { mozilla::RefPtrTraits::Release(aPtr); } }; template struct ConstRemovingRefPtrTraits { static void AddRef(const U* aPtr) { mozilla::RefPtrTraits::AddRef(const_cast(aPtr)); } static void Release(const U* aPtr) { mozilla::RefPtrTraits::Release(const_cast(aPtr)); } }; // END Some things copied from RefPtr. SafeRefPtr(T* aRawPtr, mozilla::detail::InitialConstructionTag); public: SafeRefPtr() = default; template >> MOZ_IMPLICIT SafeRefPtr(SafeRefPtr&& aSrc) : mRawPtr(aSrc.mRawPtr) { aSrc.mRawPtr = nullptr; } explicit SafeRefPtr(RefPtr&& 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.mRawPtr); aOther.mRawPtr = nullptr; return *this; } ~SafeRefPtr() { static_assert(!std::is_copy_constructible_v); static_assert(!std::is_copy_assignable_v); static_assert(!std::is_move_constructible_v); static_assert(!std::is_move_assignable_v); if (mRawPtr) { ConstRemovingRefPtrTraits::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 maybeDeref() const { return ToMaybeRef(mRawPtr); } T* unsafeGetRawPtr() const { return mRawPtr; } SafeRefPtr clonePtr() const { return SafeRefPtr{mRawPtr, AcquireStrongRefFromRawPtr{}}; } already_AddRefed forget() { auto* const res = mRawPtr; mRawPtr = nullptr; return dont_AddRef(res); } bool operator==(const SafeRefPtr& aOther) const { return mRawPtr == aOther.mRawPtr; } bool operator!=(const SafeRefPtr& aOther) const { return mRawPtr != aOther.mRawPtr; } template >> SafeRefPtr downcast() && { SafeRefPtr res; res.mRawPtr = static_cast(mRawPtr); mRawPtr = nullptr; return res; } template friend RefPtr AsRefPtr(SafeRefPtr&& aSafeRefPtr); }; template SafeRefPtr(RefPtr&&) -> SafeRefPtr; template SafeRefPtr(already_AddRefed&&) -> SafeRefPtr; template class CheckedUnsafePtr; template SafeRefPtr(const CheckedUnsafePtr&, const AcquireStrongRefFromRawPtr&) -> SafeRefPtr; template SafeRefPtr::SafeRefPtr(T* aRawPtr, detail::InitialConstructionTag) : mRawPtr(aRawPtr) { if (!std::is_base_of_v && mRawPtr) { ConstRemovingRefPtrTraits::AddRef(mRawPtr); } } template bool operator==(std::nullptr_t aLhs, const SafeRefPtr& aRhs) { return !aRhs; } template bool operator!=(std::nullptr_t aLhs, const SafeRefPtr& aRhs) { return static_cast(aRhs); } template bool operator==(const SafeRefPtr& aLhs, std::nullptr_t aRhs) { return !aLhs; } template bool operator!=(const SafeRefPtr& aLhs, std::nullptr_t aRhs) { return static_cast(aLhs); } template > bool operator==(T* const aLhs, const SafeRefPtr& aRhs) { return aLhs == aRhs.unsafeGetRawPtr(); } template > bool operator!=(T* const aLhs, const SafeRefPtr& aRhs) { return !(aLhs == aRhs); } template > bool operator==(const SafeRefPtr& aLhs, U* const aRhs) { return aRhs == aLhs; } template > bool operator!=(const SafeRefPtr& aLhs, U* const aRhs) { return aRhs != aLhs; } template > bool operator==(const Maybe aLhs, const SafeRefPtr& aRhs) { return &aLhs.ref() == aRhs.unsafeGetRawPtr(); } template > bool operator!=(const Maybe aLhs, const SafeRefPtr& aRhs) { return !(aLhs == aRhs); } template > bool operator==(const SafeRefPtr& aLhs, const Maybe aRhs) { return aRhs == aLhs; } template > bool operator!=(const SafeRefPtr& aLhs, const Maybe aRhs) { return aRhs != aLhs; } template RefPtr AsRefPtr(SafeRefPtr&& aSafeRefPtr) { return aSafeRefPtr.forget(); } template SafeRefPtr MakeSafeRefPtr(Args&&... aArgs) { return SafeRefPtr{new T(std::forward(aArgs)...), detail::InitialConstructionTag{}}; } template void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, const SafeRefPtr& aField, const char* aName, uint32_t aFlags = 0) { CycleCollectionNoteChild(aCallback, aField.unsafeGetRawPtr(), aName, aFlags); } template void ImplCycleCollectionUnlink(SafeRefPtr& aField) { aField = nullptr; } namespace detail { template SafeRefPtr SafeRefCounted::SafeRefPtrFromThis() { // this actually is safe return {static_cast(this), AcquireStrongRefFromRawPtr{}}; } template struct CopyablePtr> { SafeRefPtr mPtr; explicit CopyablePtr(SafeRefPtr 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 inline RefPtr StrongOrRawPtr(SafeRefPtr&& aPtr) { return AsRefPtr(std::move(aPtr)); } } // namespace dom } // namespace mozilla template class nsTObserverArray> : public nsAutoTObserverArray, 0> { public: using base_type = nsAutoTObserverArray, 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 \ 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 \ friend class ::mozilla::detail::SafeRefCounted; \ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \ return Super::AddRef(); \ } \ NS_IMETHOD_(MozExternalRefCountType) Release() override { \ return Super::Release(); \ } #endif #endif