/* -*- 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_StaticPtr_h #define mozilla_StaticPtr_h #include "mozilla/AlreadyAddRefed.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/RefPtr.h" namespace mozilla { /** * StaticAutoPtr and StaticRefPtr are like nsAutoPtr and nsRefPtr, except they * are suitable for use as global variables. * * In particular, a global instance of Static{Auto,Ref}Ptr doesn't cause the * compiler to emit a static initializer (in release builds, anyway). * * In order to accomplish this, Static{Auto,Ref}Ptr must have a trivial * constructor and destructor. As a consequence, it cannot initialize its raw * pointer to 0 on construction, and it cannot delete/release its raw pointer * upon destruction. * * Since the compiler guarantees that all global variables are initialized to * 0, these trivial constructors are safe. Since we rely on this, the clang * plugin, run as part of our "static analysis" builds, makes it a compile-time * error to use Static{Auto,Ref}Ptr as anything except a global variable. * * Static{Auto,Ref}Ptr have a limited interface as compared to ns{Auto,Ref}Ptr; * this is intentional, since their range of acceptable uses is smaller. */ template class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticAutoPtr { public: // In debug builds, check that mRawPtr is initialized for us as we expect // by the compiler. In non-debug builds, don't declare a constructor // so that the compiler can see that the constructor is trivial. #ifdef DEBUG StaticAutoPtr() { # ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wuninitialized" // False positive with gcc. See bug 1430729 # endif MOZ_ASSERT(!mRawPtr); # ifdef __GNUC__ # pragma GCC diagnostic pop # endif } #endif StaticAutoPtr& operator=(T* aRhs) { Assign(aRhs); return *this; } T* get() const { return mRawPtr; } operator T*() const { return get(); } T* operator->() const { MOZ_ASSERT(mRawPtr); return get(); } T& operator*() const { return *get(); } T* forget() { T* temp = mRawPtr; mRawPtr = nullptr; return temp; } private: // Disallow copy constructor, but only in debug mode. We only define // a default constructor in debug mode (see above); if we declared // this constructor always, the compiler wouldn't generate a trivial // default constructor for us in non-debug mode. #ifdef DEBUG StaticAutoPtr(StaticAutoPtr& aOther); #endif void Assign(T* aNewPtr) { MOZ_ASSERT(!aNewPtr || mRawPtr != aNewPtr); T* oldPtr = mRawPtr; mRawPtr = aNewPtr; delete oldPtr; } T* mRawPtr; }; template class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticRefPtr { public: // In debug builds, check that mRawPtr is initialized for us as we expect // by the compiler. In non-debug builds, don't declare a constructor // so that the compiler can see that the constructor is trivial. #ifdef DEBUG StaticRefPtr() { # ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wuninitialized" // False positive with gcc. See bug 1430729 # endif MOZ_ASSERT(!mRawPtr); # ifdef __GNUC__ # pragma GCC diagnostic pop # endif } #endif StaticRefPtr& operator=(T* aRhs) { AssignWithAddref(aRhs); return *this; } StaticRefPtr& operator=(const StaticRefPtr& aRhs) { return (this = aRhs.mRawPtr); } StaticRefPtr& operator=(already_AddRefed& aRhs) { AssignAssumingAddRef(aRhs.take()); return *this; } StaticRefPtr& operator=(already_AddRefed&& aRhs) { AssignAssumingAddRef(aRhs.take()); return *this; } already_AddRefed forget() { T* temp = mRawPtr; mRawPtr = nullptr; return already_AddRefed(temp); } T* get() const { return mRawPtr; } operator T*() const { return get(); } T* operator->() const { MOZ_ASSERT(mRawPtr); return get(); } T& operator*() const { return *get(); } private: void AssignWithAddref(T* aNewPtr) { if (aNewPtr) { aNewPtr->AddRef(); } AssignAssumingAddRef(aNewPtr); } void AssignAssumingAddRef(T* aNewPtr) { T* oldPtr = mRawPtr; mRawPtr = aNewPtr; if (oldPtr) { oldPtr->Release(); } } T* MOZ_OWNING_REF mRawPtr; }; namespace StaticPtr_internal { class Zero; } // namespace StaticPtr_internal #define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \ template <__VA_ARGS__> \ inline bool operator==(type1 lhs, type2 rhs) { \ return eq_fn; \ } \ \ template <__VA_ARGS__> \ inline bool operator==(type2 lhs, type1 rhs) { \ return rhs == lhs; \ } \ \ template <__VA_ARGS__> \ inline bool operator!=(type1 lhs, type2 rhs) { \ return !(lhs == rhs); \ } \ \ template <__VA_ARGS__> \ inline bool operator!=(type2 lhs, type1 rhs) { \ return !(lhs == rhs); \ } // StaticAutoPtr (in)equality operators template inline bool operator==(const StaticAutoPtr& aLhs, const StaticAutoPtr& aRhs) { return aLhs.get() == aRhs.get(); } template inline bool operator!=(const StaticAutoPtr& aLhs, const StaticAutoPtr& aRhs) { return !(aLhs == aRhs); } REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr&, const U*, lhs.get() == rhs, class T, class U) REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr&, U*, lhs.get() == rhs, class T, class U) // Let us compare StaticAutoPtr to 0. REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr&, StaticPtr_internal::Zero*, lhs.get() == nullptr, class T) // StaticRefPtr (in)equality operators template inline bool operator==(const StaticRefPtr& aLhs, const StaticRefPtr& aRhs) { return aLhs.get() == aRhs.get(); } template inline bool operator!=(const StaticRefPtr& aLhs, const StaticRefPtr& aRhs) { return !(aLhs == aRhs); } REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr&, const U*, lhs.get() == rhs, class T, class U) REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr&, U*, lhs.get() == rhs, class T, class U) // Let us compare StaticRefPtr to 0. REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr&, StaticPtr_internal::Zero*, lhs.get() == nullptr, class T) #undef REFLEXIVE_EQUALITY_OPERATORS } // namespace mozilla // Declared in mozilla/RefPtr.h template template RefPtr::RefPtr(const mozilla::StaticRefPtr& aOther) : RefPtr(aOther.get()) {} template template RefPtr& RefPtr::operator=(const mozilla::StaticRefPtr& aOther) { return operator=(aOther.get()); } template inline already_AddRefed do_AddRef(const mozilla::StaticRefPtr& aObj) { RefPtr ref(aObj); return ref.forget(); } #endif