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 
20 class Visitor;
21 
22 namespace internal {
23 
24 // PersistentBase always refers to the object as const object and defers to
25 // BasicPersistent on casting to the right type as needed.
26 class PersistentBase {
27  protected:
28   PersistentBase() = default;
PersistentBase(const void* raw)29   explicit PersistentBase(const void* raw) : raw_(raw) {}
30 
GetValue() const31   const void* GetValue() const { return raw_; }
SetValue(const void* value)32   void SetValue(const void* value) { raw_ = value; }
33 
GetNode() const34   PersistentNode* GetNode() const { return node_; }
SetNode(PersistentNode* node)35   void SetNode(PersistentNode* node) { node_ = node; }
36 
37   // Performs a shallow clear which assumes that internal persistent nodes are
38   // destroyed elsewhere.
ClearFromGC() const39   void ClearFromGC() const {
40     raw_ = nullptr;
41     node_ = nullptr;
42   }
43 
44  protected:
45   mutable const void* raw_ = nullptr;
46   mutable PersistentNode* node_ = nullptr;
47 
48   friend class PersistentRegionBase;
49 };
50 
51 // The basic class from which all Persistent classes are generated.
52 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
53           typename CheckingPolicy>
54 class BasicPersistent final : public PersistentBase,
55                               public LocationPolicy,
56                               private WeaknessPolicy,
57                               private CheckingPolicy {
58  public:
59   using typename WeaknessPolicy::IsStrongPersistent;
60   using PointeeType = T;
61 
62   // Null-state/sentinel constructors.
BasicPersistent( const SourceLocation& loc = SourceLocation::Current())63   BasicPersistent(  // NOLINT
64       const SourceLocation& loc = SourceLocation::Current())
65       : LocationPolicy(loc) {}
66 
BasicPersistent(std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())67   BasicPersistent(std::nullptr_t,  // NOLINT
68                   const SourceLocation& loc = SourceLocation::Current())
69       : LocationPolicy(loc) {}
70 
BasicPersistent( SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())71   BasicPersistent(  // NOLINT
72       SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
73       : PersistentBase(s), LocationPolicy(loc) {}
74 
75   // Raw value constructors.
BasicPersistent(T* raw, const SourceLocation& loc = SourceLocation::Current())76   BasicPersistent(T* raw,  // NOLINT
77                   const SourceLocation& loc = SourceLocation::Current())
78       : PersistentBase(raw), LocationPolicy(loc) {
79     if (!IsValid()) return;
80     SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
81                 .AllocateNode(this, &BasicPersistent::Trace));
82     this->CheckPointer(Get());
83   }
84 
BasicPersistent(T& raw, const SourceLocation& loc = SourceLocation::Current())85   BasicPersistent(T& raw,  // NOLINT
86                   const SourceLocation& loc = SourceLocation::Current())
87       : BasicPersistent(&raw, loc) {}
88 
89   // Copy ctor.
BasicPersistent(const BasicPersistent& other, const SourceLocation& loc = SourceLocation::Current())90   BasicPersistent(const BasicPersistent& other,
91                   const SourceLocation& loc = SourceLocation::Current())
92       : BasicPersistent(other.Get(), loc) {}
93 
94   // Heterogeneous ctor.
95   template <typename U, typename OtherWeaknessPolicy,
96             typename OtherLocationPolicy, typename OtherCheckingPolicy,
97             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())98   BasicPersistent(
99       const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
100                             OtherCheckingPolicy>& other,
101       const SourceLocation& loc = SourceLocation::Current())
102       : BasicPersistent(other.Get(), loc) {}
103 
104   // Move ctor. The heterogeneous move ctor is not supported since e.g.
105   // persistent can't reuse persistent node from weak persistent.
106   BasicPersistent(
107       BasicPersistent&& other,
108       const SourceLocation& loc = SourceLocation::Current()) noexcept
move(other)109       : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) {
110     if (!IsValid()) return;
111     GetNode()->UpdateOwner(this);
112     other.SetValue(nullptr);
113     other.SetNode(nullptr);
114     this->CheckPointer(Get());
115   }
116 
117   // Constructor from member.
118   template <typename U, typename MemberBarrierPolicy,
119             typename MemberWeaknessTag, typename MemberCheckingPolicy,
120             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent( const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy>& member, const SourceLocation& loc = SourceLocation::Current())121   BasicPersistent(
122       const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
123                                   MemberCheckingPolicy>& member,
124       const SourceLocation& loc = SourceLocation::Current())
125       : BasicPersistent(member.Get(), loc) {}
126 
~BasicPersistent()127   ~BasicPersistent() { Clear(); }
128 
129   // Copy assignment.
operator =(const BasicPersistent& other)130   BasicPersistent& operator=(const BasicPersistent& other) {
131     return operator=(other.Get());
132   }
133 
134   template <typename U, typename OtherWeaknessPolicy,
135             typename OtherLocationPolicy, typename OtherCheckingPolicy,
136             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy>& other)137   BasicPersistent& operator=(
138       const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
139                             OtherCheckingPolicy>& other) {
140     return operator=(other.Get());
141   }
142 
143   // Move assignment.
144   BasicPersistent& operator=(BasicPersistent&& other) noexcept {
145     if (this == &other) return *this;
146     Clear();
147     PersistentBase::operator=(std::move(other));
148     LocationPolicy::operator=(std::move(other));
149     if (!IsValid()) return *this;
150     GetNode()->UpdateOwner(this);
151     other.SetValue(nullptr);
152     other.SetNode(nullptr);
153     this->CheckPointer(Get());
154     return *this;
155   }
156 
157   // Assignment from member.
158   template <typename U, typename MemberBarrierPolicy,
159             typename MemberWeaknessTag, typename MemberCheckingPolicy,
160             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy>& member)161   BasicPersistent& operator=(
162       const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
163                                   MemberCheckingPolicy>& 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:
Trace(Visitor* v, const void* ptr)224   static void Trace(Visitor* v, const void* ptr) {
225     const auto* persistent = static_cast<const BasicPersistent*>(ptr);
226     v->TraceRoot(*persistent, persistent->Location());
227   }
228 
IsValid() const229   bool IsValid() const {
230     // Ideally, handling kSentinelPointer would be done by the embedder. On the
231     // other hand, having Persistent aware of it is beneficial since no node
232     // gets wasted.
233     return GetValue() != nullptr && GetValue() != kSentinelPointer;
234   }
235 
Assign(T* ptr)236   void Assign(T* ptr) {
237     if (IsValid()) {
238       if (ptr && ptr != kSentinelPointer) {
239         // Simply assign the pointer reusing the existing node.
240         SetValue(ptr);
241         this->CheckPointer(ptr);
242         return;
243       }
244       WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
245       SetNode(nullptr);
246     }
247     SetValue(ptr);
248     if (!IsValid()) return;
249     SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
250                 .AllocateNode(this, &BasicPersistent::Trace));
251     this->CheckPointer(Get());
252   }
253 
ClearFromGC() const254   void ClearFromGC() const {
255     if (IsValid()) {
256       WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
257       PersistentBase::ClearFromGC();
258     }
259   }
260 
261   // Set Get() for details.
262   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
GetFromGC() const263   T* GetFromGC() const {
264     return static_cast<T*>(const_cast<void*>(GetValue()));
265   }
266 
267   friend class cppgc::Visitor;
268 };
269 
270 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
271           typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
272           typename LocationPolicy2, typename CheckingPolicy2>
operator ==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)273 bool operator==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
274                                       CheckingPolicy1>& p1,
275                 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
276                                       CheckingPolicy2>& p2) {
277   return p1.Get() == p2.Get();
278 }
279 
280 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
281           typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
282           typename LocationPolicy2, typename CheckingPolicy2>
operator !=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)283 bool operator!=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
284                                       CheckingPolicy1>& p1,
285                 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
286                                       CheckingPolicy2>& p2) {
287   return !(p1 == p2);
288 }
289 
290 template <typename T1, typename PersistentWeaknessPolicy,
291           typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
292           typename T2, typename MemberWriteBarrierPolicy,
293           typename MemberWeaknessTag, typename MemberCheckingPolicy>
operator ==( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m)294 bool operator==(
295     const BasicPersistent<T1, PersistentWeaknessPolicy,
296                           PersistentLocationPolicy, PersistentCheckingPolicy>&
297         p,
298     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
299                       MemberCheckingPolicy>& 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>
operator !=( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m)307 bool operator!=(
308     const BasicPersistent<T1, PersistentWeaknessPolicy,
309                           PersistentLocationPolicy, PersistentCheckingPolicy>&
310         p,
311     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
312                       MemberCheckingPolicy>& m) {
313   return !(p == m);
314 }
315 
316 template <typename T1, typename MemberWriteBarrierPolicy,
317           typename MemberWeaknessTag, typename MemberCheckingPolicy,
318           typename T2, typename PersistentWeaknessPolicy,
319           typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
operator ==( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)320 bool operator==(
321     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
322                       MemberCheckingPolicy>& m,
323     const BasicPersistent<T1, PersistentWeaknessPolicy,
324                           PersistentLocationPolicy, PersistentCheckingPolicy>&
325         p) {
326   return m.Get() == p.Get();
327 }
328 
329 template <typename T1, typename MemberWriteBarrierPolicy,
330           typename MemberWeaknessTag, typename MemberCheckingPolicy,
331           typename T2, typename PersistentWeaknessPolicy,
332           typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
operator !=( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)333 bool operator!=(
334     const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
335                       MemberCheckingPolicy>& m,
336     const BasicPersistent<T1, PersistentWeaknessPolicy,
337                           PersistentLocationPolicy, PersistentCheckingPolicy>&
338         p) {
339   return !(m == p);
340 }
341 
342 template <typename T, typename LocationPolicy, typename CheckingPolicy>
343 struct IsWeak<BasicPersistent<T, internal::WeakPersistentPolicy, LocationPolicy,
344                               CheckingPolicy>> : std::true_type {};
345 }  // namespace internal
346 
347 /**
348  * Persistent is a way to create a strong pointer from an off-heap object to
349  * another on-heap object. As long as the Persistent handle is alive the GC will
350  * keep the object pointed to alive. The Persistent handle is always a GC root
351  * from the point of view of the GC. Persistent must be constructed and
352  * destructed in the same thread.
353  */
354 template <typename T>
355 using Persistent =
356     internal::BasicPersistent<T, internal::StrongPersistentPolicy>;
357 
358 /**
359  * WeakPersistent is a way to create a weak pointer from an off-heap object to
360  * an on-heap object. The pointer is automatically cleared when the pointee gets
361  * collected. WeakPersistent must be constructed and destructed in the same
362  * thread.
363  */
364 template <typename T>
365 using WeakPersistent =
366     internal::BasicPersistent<T, internal::WeakPersistentPolicy>;
367 
368 }  // namespace cppgc
369 
370 #endif  // INCLUDE_CPPGC_PERSISTENT_H_
371