1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6  */
7 
8 /* smart pointer for strong references to nsPresArena-allocated objects
9    that might be held onto until the arena's destruction */
10 
11 #include "mozilla/Assertions.h"
12 #include "mozilla/RefPtr.h"
13 
14 #ifndef mozilla_ArenaRefPtr_h
15 #define mozilla_ArenaRefPtr_h
16 
17 class nsPresArena;
18 
19 namespace mozilla {
20 
21 /**
22  * A class for holding strong references to nsPresArena-allocated
23  * objects.
24  *
25  * Since the arena's lifetime is not related to the refcounts
26  * of the objects allocated within it, it is possible to have a strong
27  * reference to an arena-allocated object that lives until the
28  * destruction of the arena.  An ArenaRefPtr acts like a weak reference
29  * in that it will clear its referent if the arena is about to go away.
30  *
31  * T must be a class that has these two methods:
32  *
33  *   static mozilla::ArenaObjectID ArenaObjectID();
34  *   U* Arena();
35  *
36  * where U is a class that has these two methods:
37  *
38  *   void RegisterArenaRefPtr(ArenaRefPtr<T>*);
39  *   void DeregisterArenaRefPtr(ArenaRefPtr<T>*);
40  *
41  * Currently, both nsPresArena and nsIPresShell can be used as U.
42  *
43  * The ArenaObjectID method must return the mozilla::ArenaObjectID that
44  * uniquely identifies T, and the Arena method must return the nsPresArena
45  * (or a proxy for it) in which the object was allocated.
46  */
47 template <typename T>
48 class ArenaRefPtr {
49   friend class ::nsPresArena;
50 
51  public:
ArenaRefPtr()52   ArenaRefPtr() { AssertValidType(); }
53 
54   template <typename I>
ArenaRefPtr(already_AddRefed<I> & aRhs)55   MOZ_IMPLICIT ArenaRefPtr(already_AddRefed<I>& aRhs) {
56     AssertValidType();
57     assign(aRhs);
58   }
59 
60   template <typename I>
ArenaRefPtr(already_AddRefed<I> && aRhs)61   MOZ_IMPLICIT ArenaRefPtr(already_AddRefed<I>&& aRhs) {
62     AssertValidType();
63     assign(aRhs);
64   }
65 
ArenaRefPtr(T * aRhs)66   MOZ_IMPLICIT ArenaRefPtr(T* aRhs) {
67     AssertValidType();
68     assign(aRhs);
69   }
70 
71   template <typename I>
72   ArenaRefPtr<T>& operator=(already_AddRefed<I>& aRhs) {
73     assign(aRhs);
74     return *this;
75   }
76 
77   template <typename I>
78   ArenaRefPtr<T>& operator=(already_AddRefed<I>&& aRhs) {
79     assign(aRhs);
80     return *this;
81   }
82 
83   ArenaRefPtr<T>& operator=(T* aRhs) {
84     assign(aRhs);
85     return *this;
86   }
87 
~ArenaRefPtr()88   ~ArenaRefPtr() { assign(nullptr); }
89 
90   operator T*() const & { return get(); }
91   operator T*() const && = delete;
92   explicit operator bool() const { return !!mPtr; }
93   bool operator!() const { return !mPtr; }
94   T* operator->() const { return mPtr.operator->(); }
95   T& operator*() const { return *get(); }
96 
get()97   T* get() const { return mPtr; }
98 
99  private:
100   void AssertValidType();
101 
102   /**
103    * Clears the pointer to the arena-allocated object but skips the usual
104    * step of deregistering the ArenaRefPtr from the nsPresArena.  This
105    * method is called by nsPresArena when clearing all registered ArenaRefPtrs
106    * so that it can deregister them all at once, avoiding hash table churn.
107    */
ClearWithoutDeregistering()108   void ClearWithoutDeregistering() { mPtr = nullptr; }
109 
110   template <typename I>
assign(already_AddRefed<I> & aSmartPtr)111   void assign(already_AddRefed<I>& aSmartPtr) {
112     RefPtr<T> newPtr(aSmartPtr);
113     assignFrom(newPtr);
114   }
115 
116   template <typename I>
assign(already_AddRefed<I> && aSmartPtr)117   void assign(already_AddRefed<I>&& aSmartPtr) {
118     RefPtr<T> newPtr(aSmartPtr);
119     assignFrom(newPtr);
120   }
121 
assign(T * aPtr)122   void assign(T* aPtr) { assignFrom(aPtr); }
123 
124   template <typename I>
assignFrom(I & aPtr)125   void assignFrom(I& aPtr) {
126     if (aPtr == mPtr) {
127       return;
128     }
129     bool sameArena = mPtr && aPtr && mPtr->Arena() == aPtr->Arena();
130     if (mPtr && !sameArena) {
131       MOZ_ASSERT(mPtr->Arena());
132       mPtr->Arena()->DeregisterArenaRefPtr(this);
133     }
134     mPtr = Move(aPtr);
135     if (mPtr && !sameArena) {
136       MOZ_ASSERT(mPtr->Arena());
137       mPtr->Arena()->RegisterArenaRefPtr(this);
138     }
139   }
140 
141   RefPtr<T> mPtr;
142 };
143 
144 }  // namespace mozilla
145 
146 #endif  // mozilla_ArenaRefPtr_h
147