1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef INCLUDE_CPPGC_PERSISTENT_H_
6 #define INCLUDE_CPPGC_PERSISTENT_H_
7 
8 #include <type_traits>
9 
10 #include "cppgc/internal/persistent-node.h"
11 #include "cppgc/internal/pointer-policies.h"
12 #include "cppgc/sentinel-pointer.h"
13 #include "cppgc/source-location.h"
14 #include "cppgc/type-traits.h"
15 #include "cppgc/visitor.h"
16 #include "v8config.h"  // NOLINT(build/include_directory)
17 
18 namespace cppgc {
19 namespace internal {
20 
21 // PersistentBase always refers to the object as const object and defers to
22 // BasicPersistent on casting to the right type as needed.
23 class PersistentBase {
24  protected:
25   PersistentBase() = default;
PersistentBase(const void* raw)26   explicit PersistentBase(const void* raw) : raw_(raw) {}
27 
GetValue() const28   const void* GetValue() const { return raw_; }
SetValue(const void* value)29   void SetValue(const void* value) { raw_ = value; }
30 
GetNode() const31   PersistentNode* GetNode() const { return node_; }
SetNode(PersistentNode* node)32   void SetNode(PersistentNode* node) { node_ = node; }
33 
34   // Performs a shallow clear which assumes that internal persistent nodes are
35   // destroyed elsewhere.
ClearFromGC() const36   void ClearFromGC() const {
37     raw_ = nullptr;
38     node_ = nullptr;
39   }
40 
41  protected:
42   mutable const void* raw_ = nullptr;
43   mutable PersistentNode* node_ = nullptr;
44 
45   friend class PersistentRegionBase;
46 };
47 
48 // The basic class from which all Persistent classes are generated.
49 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
50           typename CheckingPolicy>
51 class BasicPersistent final : public PersistentBase,
52                               public LocationPolicy,
53                               private WeaknessPolicy,
54                               private CheckingPolicy {
55  public:
56   using typename WeaknessPolicy::IsStrongPersistent;
57   using PointeeType = T;
58 
59   // Null-state/sentinel constructors.
BasicPersistent( const SourceLocation& loc = SourceLocation::Current())60   BasicPersistent(  // NOLINT
61       const SourceLocation& loc = SourceLocation::Current())
62       : LocationPolicy(loc) {}
63 
BasicPersistent(std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())64   BasicPersistent(std::nullptr_t,  // NOLINT
65                   const SourceLocation& loc = SourceLocation::Current())
66       : LocationPolicy(loc) {}
67 
BasicPersistent( SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())68   BasicPersistent(  // NOLINT
69       SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
70       : PersistentBase(s), LocationPolicy(loc) {}
71 
72   // Raw value constructors.
BasicPersistent(T* raw, const SourceLocation& loc = SourceLocation::Current())73   BasicPersistent(T* raw,  // NOLINT
74                   const SourceLocation& loc = SourceLocation::Current())
75       : PersistentBase(raw), LocationPolicy(loc) {
76     if (!IsValid()) return;
77     SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
78                 .AllocateNode(this, &TraceAsRoot));
79     this->CheckPointer(Get());
80   }
81 
BasicPersistent(T& raw, const SourceLocation& loc = SourceLocation::Current())82   BasicPersistent(T& raw,  // NOLINT
83                   const SourceLocation& loc = SourceLocation::Current())
84       : BasicPersistent(&raw, loc) {}
85 
86   // Copy ctor.
BasicPersistent(const BasicPersistent& other, const SourceLocation& loc = SourceLocation::Current())87   BasicPersistent(const BasicPersistent& other,
88                   const SourceLocation& loc = SourceLocation::Current())
89       : BasicPersistent(other.Get(), loc) {}
90 
91   // Heterogeneous ctor.
92   template <typename U, typename OtherWeaknessPolicy,
93             typename OtherLocationPolicy, typename OtherCheckingPolicy,
94             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent( const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy>& other, const SourceLocation& loc = SourceLocation::Current())95   BasicPersistent(
96       const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
97                             OtherCheckingPolicy>& other,
98       const SourceLocation& loc = SourceLocation::Current())
99       : BasicPersistent(other.Get(), loc) {}
100 
101   // Move ctor. The heterogeneous move ctor is not supported since e.g.
102   // persistent can't reuse persistent node from weak persistent.
103   BasicPersistent(
104       BasicPersistent&& other,
105       const SourceLocation& loc = SourceLocation::Current()) noexcept
move(other)106       : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) {
107     if (!IsValid()) return;
108     GetNode()->UpdateOwner(this);
109     other.SetValue(nullptr);
110     other.SetNode(nullptr);
111     this->CheckPointer(Get());
112   }
113 
114   // Constructor from member.
115   template <typename U, typename MemberBarrierPolicy,
116             typename MemberWeaknessTag, typename MemberCheckingPolicy,
117             typename MemberStorageType,
118             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent(const internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType>& member, const SourceLocation& loc = SourceLocation::Current())119   BasicPersistent(const internal::BasicMember<
120                       U, MemberBarrierPolicy, MemberWeaknessTag,
121                       MemberCheckingPolicy, MemberStorageType>& member,
122                   const SourceLocation& loc = SourceLocation::Current())
123       : BasicPersistent(member.Get(), loc) {}
124 
~BasicPersistent()125   ~BasicPersistent() { Clear(); }
126 
127   // Copy assignment.
operator =(const BasicPersistent& other)128   BasicPersistent& operator=(const BasicPersistent& other) {
129     return operator=(other.Get());
130   }
131 
132   template <typename U, typename OtherWeaknessPolicy,
133             typename OtherLocationPolicy, typename OtherCheckingPolicy,
134             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy>& other)135   BasicPersistent& operator=(
136       const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
137                             OtherCheckingPolicy>& other) {
138     return operator=(other.Get());
139   }
140 
141   // Move assignment.
142   BasicPersistent& operator=(BasicPersistent&& other) noexcept {
143     if (this == &other) return *this;
144     Clear();
145     PersistentBase::operator=(std::move(other));
146     LocationPolicy::operator=(std::move(other));
147     if (!IsValid()) return *this;
148     GetNode()->UpdateOwner(this);
149     other.SetValue(nullptr);
150     other.SetNode(nullptr);
151     this->CheckPointer(Get());
152     return *this;
153   }
154 
155   // Assignment from member.
156   template <typename U, typename MemberBarrierPolicy,
157             typename MemberWeaknessTag, typename MemberCheckingPolicy,
158             typename MemberStorageType,
159             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType>& member)160   BasicPersistent& operator=(
161       const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
162                                   MemberCheckingPolicy, MemberStorageType>&
163           member) {
164     return operator=(member.Get());
165   }
166 
operator =(T* other)167   BasicPersistent& operator=(T* other) {
168     Assign(other);
169     return *this;
170   }
171 
operator =(std::nullptr_t)172   BasicPersistent& operator=(std::nullptr_t) {
173     Clear();
174     return *this;
175   }
176 
operator =(SentinelPointer s)177   BasicPersistent& operator=(SentinelPointer s) {
178     Assign(s);
179     return *this;
180   }
181 
operator bool() const182   explicit operator bool() const { return Get(); }
operator T*() const183   operator T*() const { return Get(); }
operator ->() const184   T* operator->() const { return Get(); }
operator *() const185   T& operator*() const { return *Get(); }
186 
187   // CFI cast exemption to allow passing SentinelPointer through T* and support
188   // heterogeneous assignments between different Member and Persistent handles
189   // based on their actual types.
Get() const190   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
191     // The const_cast below removes the constness from PersistentBase storage.
192     // The following static_cast re-adds any constness if specified through the
193     // user-visible template parameter T.
194     return static_cast<T*>(const_cast<void*>(GetValue()));
195   }
196 
Clear()197   void Clear() {
198     // Simplified version of `Assign()` to allow calling without a complete type
199     // `T`.
200     if (IsValid()) {
201       WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
202       SetNode(nullptr);
203     }
204     SetValue(nullptr);
205   }
206 
Release()207   T* Release() {
208     T* result = Get();
209     Clear();
210     return result;
211   }
212 
213   template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
214             typename OtherLocationPolicy = LocationPolicy,
215             typename OtherCheckingPolicy = CheckingPolicy>
216   BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
217                   OtherCheckingPolicy>
To() const218   To() const {
219     return BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
220                            OtherCheckingPolicy>(static_cast<U*>(Get()));
221   }
222 
223  private:
TraceAsRoot(RootVisitor& root_visitor, const void* ptr)224   static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
225     root_visitor.Trace(*static_cast<const BasicPersistent*>(ptr));
226   }
227 
IsValid() const228   bool IsValid() const {
229     // Ideally, handling kSentinelPointer would be done by the embedder. On the
230     // other hand, having Persistent aware of it is beneficial since no node
231     // gets wasted.
232     return GetValue() != nullptr && GetValue() != kSentinelPointer;
233   }
234 
Assign(T* ptr)235   void Assign(T* ptr) {
236     if (IsValid()) {
237       if (ptr && ptr != kSentinelPointer) {
238         // Simply assign the pointer reusing the existing node.
239         SetValue(ptr);
240         this->CheckPointer(ptr);
241         return;
242       }
243       WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
244       SetNode(nullptr);
245     }
246     SetValue(ptr);
247     if (!IsValid()) return;
248     SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
249                 .AllocateNode(this, &TraceAsRoot));
250     this->CheckPointer(Get());
251   }
252 
ClearFromGC() const253   void ClearFromGC() const {
254     if (IsValid()) {
255       WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
256       PersistentBase::ClearFromGC();
257     }
258   }
259 
260   // Set Get() for details.
261   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
GetFromGC() const262   T* GetFromGC() const {
263     return static_cast<T*>(const_cast<void*>(GetValue()));
264   }
265 
266   friend class internal::RootVisitor;
267 };
268 
269 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
270           typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
271           typename LocationPolicy2, typename CheckingPolicy2>
operator ==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)272 bool operator==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
273                                       CheckingPolicy1>& p1,
274                 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
275                                       CheckingPolicy2>& p2) {
276   return p1.Get() == p2.Get();
277 }
278 
279 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
280           typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
281           typename LocationPolicy2, typename CheckingPolicy2>
operator !=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)282 bool operator!=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
283                                       CheckingPolicy1>& p1,
284                 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
285                                       CheckingPolicy2>& p2) {
286   return !(p1 == p2);
287 }
288 
289 template <typename T1, typename PersistentWeaknessPolicy,
290           typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
291           typename T2, typename MemberWriteBarrierPolicy,
292           typename MemberWeaknessTag, typename MemberCheckingPolicy,
293           typename MemberStorageType>
operator ==( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m)294 bool operator==(
295     const BasicPersistent<T1, PersistentWeaknessPolicy,
296                           PersistentLocationPolicy, PersistentCheckingPolicy>&
297         p,
298     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
299                       MemberCheckingPolicy, MemberStorageType>& m) {
300   return p.Get() == m.Get();
301 }
302 
303 template <typename T1, typename PersistentWeaknessPolicy,
304           typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
305           typename T2, typename MemberWriteBarrierPolicy,
306           typename MemberWeaknessTag, typename MemberCheckingPolicy,
307           typename MemberStorageType>
operator !=( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m)308 bool operator!=(
309     const BasicPersistent<T1, PersistentWeaknessPolicy,
310                           PersistentLocationPolicy, PersistentCheckingPolicy>&
311         p,
312     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
313                       MemberCheckingPolicy, MemberStorageType>& m) {
314   return !(p == m);
315 }
316 
317 template <typename T1, typename MemberWriteBarrierPolicy,
318           typename MemberWeaknessTag, typename MemberCheckingPolicy,
319           typename MemberStorageType, typename T2,
320           typename PersistentWeaknessPolicy, typename PersistentLocationPolicy,
321           typename PersistentCheckingPolicy>
operator ==( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)322 bool operator==(
323     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
324                       MemberCheckingPolicy, MemberStorageType>& m,
325     const BasicPersistent<T1, PersistentWeaknessPolicy,
326                           PersistentLocationPolicy, PersistentCheckingPolicy>&
327         p) {
328   return m.Get() == p.Get();
329 }
330 
331 template <typename T1, typename MemberWriteBarrierPolicy,
332           typename MemberWeaknessTag, typename MemberCheckingPolicy,
333           typename MemberStorageType, typename T2,
334           typename PersistentWeaknessPolicy, typename PersistentLocationPolicy,
335           typename PersistentCheckingPolicy>
operator !=( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)336 bool operator!=(
337     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
338                       MemberCheckingPolicy, MemberStorageType>& m,
339     const BasicPersistent<T1, PersistentWeaknessPolicy,
340                           PersistentLocationPolicy, PersistentCheckingPolicy>&
341         p) {
342   return !(m == p);
343 }
344 
345 template <typename T, typename LocationPolicy, typename CheckingPolicy>
346 struct IsWeak<BasicPersistent<T, internal::WeakPersistentPolicy, LocationPolicy,
347                               CheckingPolicy>> : std::true_type {};
348 }  // namespace internal
349 
350 /**
351  * Persistent is a way to create a strong pointer from an off-heap object to
352  * another on-heap object. As long as the Persistent handle is alive the GC will
353  * keep the object pointed to alive. The Persistent handle is always a GC root
354  * from the point of view of the GC. Persistent must be constructed and
355  * destructed in the same thread.
356  */
357 template <typename T>
358 using Persistent =
359     internal::BasicPersistent<T, internal::StrongPersistentPolicy>;
360 
361 /**
362  * WeakPersistent is a way to create a weak pointer from an off-heap object to
363  * an on-heap object. The pointer is automatically cleared when the pointee gets
364  * collected. WeakPersistent must be constructed and destructed in the same
365  * thread.
366  */
367 template <typename T>
368 using WeakPersistent =
369     internal::BasicPersistent<T, internal::WeakPersistentPolicy>;
370 
371 }  // namespace cppgc
372 
373 #endif  // INCLUDE_CPPGC_PERSISTENT_H_
374