gecko-dev/xpcom/base/StaticPtr.h
Kris Maglione c86bc6b1ea Bug 1396676: Return already_AddRefed from cookie service GetSingleton() methods. r=jdm
These methods return an addrefed raw pointer, which makes them easy to use in
ways that cause leaks. If they're to continue returning an addrefed pointer,
they should explicitly return an already_AddRefed.

This also switches to StaticRefPtr with ClearOnShutdown for the cached
pointers for the sake of sanity.

MozReview-Commit-ID: D0lDpU8Hqug

--HG--
extra : rebase_source : 7b199070805fc0472eaf8409932517700ed23d49
2017-09-04 15:05:10 -07:00

286 lines
7.9 KiB
C++

/* -*- 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 T>
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()
{
MOZ_ASSERT(!mRawPtr);
}
#endif
StaticAutoPtr<T>& 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<T>& aOther);
#endif
void Assign(T* aNewPtr)
{
MOZ_ASSERT(!aNewPtr || mRawPtr != aNewPtr);
T* oldPtr = mRawPtr;
mRawPtr = aNewPtr;
delete oldPtr;
}
T* mRawPtr;
};
template<class T>
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()
{
MOZ_ASSERT(!mRawPtr);
}
#endif
StaticRefPtr<T>& operator=(T* aRhs)
{
AssignWithAddref(aRhs);
return *this;
}
StaticRefPtr<T>& operator=(const StaticRefPtr<T>& aRhs)
{
return (this = aRhs.mRawPtr);
}
StaticRefPtr<T>& operator=(already_AddRefed<T>& aRhs)
{
AssignAssumingAddRef(aRhs.take());
return *this;
}
StaticRefPtr<T>& operator=(already_AddRefed<T>&& aRhs)
{
AssignAssumingAddRef(aRhs.take());
return *this;
}
already_AddRefed<T>
forget()
{
T* temp = mRawPtr;
mRawPtr = nullptr;
return already_AddRefed<T>(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<class T, class U>
inline bool
operator==(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
{
return aLhs.get() == aRhs.get();
}
template<class T, class U>
inline bool
operator!=(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
{
return !(aLhs == aRhs);
}
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, const U*,
lhs.get() == rhs, class T, class U)
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, U*,
lhs.get() == rhs, class T, class U)
// Let us compare StaticAutoPtr to 0.
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, StaticPtr_internal::Zero*,
lhs.get() == nullptr, class T)
// StaticRefPtr (in)equality operators
template<class T, class U>
inline bool
operator==(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
{
return aLhs.get() == aRhs.get();
}
template<class T, class U>
inline bool
operator!=(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
{
return !(aLhs == aRhs);
}
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, const U*,
lhs.get() == rhs, class T, class U)
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, U*,
lhs.get() == rhs, class T, class U)
// Let us compare StaticRefPtr to 0.
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, StaticPtr_internal::Zero*,
lhs.get() == nullptr, class T)
#undef REFLEXIVE_EQUALITY_OPERATORS
} // namespace mozilla
// Declared in mozilla/RefPtr.h
template<class T> template<class U>
RefPtr<T>::RefPtr(const mozilla::StaticRefPtr<U>& aOther)
: RefPtr(aOther.get())
{}
template<class T> template<class U>
RefPtr<T>&
RefPtr<T>::operator=(const mozilla::StaticRefPtr<U>& aOther)
{
return operator=(aOther.get());
}
template <class T>
inline already_AddRefed<T>
do_AddRef(const mozilla::StaticRefPtr<T>& aObj)
{
RefPtr<T> ref(aObj);
return ref.forget();
}
#endif