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_INTERNAL_POINTER_POLICIES_H_
6 #define INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_
7 
8 #include <cstdint>
9 #include <type_traits>
10 
11 #include "cppgc/internal/member-storage.h"
12 #include "cppgc/internal/write-barrier.h"
13 #include "cppgc/sentinel-pointer.h"
14 #include "cppgc/source-location.h"
15 #include "cppgc/type-traits.h"
16 #include "v8config.h"  // NOLINT(build/include_directory)
17 
18 namespace cppgc {
19 namespace internal {
20 
21 class HeapBase;
22 class PersistentRegion;
23 class CrossThreadPersistentRegion;
24 
25 // Tags to distinguish between strong and weak member types.
26 class StrongMemberTag;
27 class WeakMemberTag;
28 class UntracedMemberTag;
29 
30 struct DijkstraWriteBarrierPolicy {
InitializingBarriercppgc::internal::DijkstraWriteBarrierPolicy31   V8_INLINE static void InitializingBarrier(const void*, const void*) {
32     // Since in initializing writes the source object is always white, having no
33     // barrier doesn't break the tri-color invariant.
34   }
35 
36   template <WriteBarrierSlotType SlotType>
AssigningBarriercppgc::internal::DijkstraWriteBarrierPolicy37   V8_INLINE static void AssigningBarrier(const void* slot, const void* value) {
38 #ifdef CPPGC_SLIM_WRITE_BARRIER
39     if (V8_UNLIKELY(WriteBarrier::IsEnabled()))
40       WriteBarrier::CombinedWriteBarrierSlow<SlotType>(slot);
41 #else   // !CPPGC_SLIM_WRITE_BARRIER
42     WriteBarrier::Params params;
43     const WriteBarrier::Type type =
44         WriteBarrier::GetWriteBarrierType(slot, value, params);
45     WriteBarrier(type, params, slot, value);
46 #endif  // !CPPGC_SLIM_WRITE_BARRIER
47   }
48 
49   template <WriteBarrierSlotType SlotType>
AssigningBarriercppgc::internal::DijkstraWriteBarrierPolicy50   V8_INLINE static void AssigningBarrier(const void* slot, RawPointer storage) {
51     static_assert(
52         SlotType == WriteBarrierSlotType::kUncompressed,
53         "Assigning storages of Member and UncompressedMember is not supported");
54 #ifdef CPPGC_SLIM_WRITE_BARRIER
55     if (V8_UNLIKELY(WriteBarrier::IsEnabled()))
56       WriteBarrier::CombinedWriteBarrierSlow<SlotType>(slot);
57 #else   // !CPPGC_SLIM_WRITE_BARRIER
58     WriteBarrier::Params params;
59     const WriteBarrier::Type type =
60         WriteBarrier::GetWriteBarrierType(slot, storage, params);
61     WriteBarrier(type, params, slot, storage.Load());
62 #endif  // !CPPGC_SLIM_WRITE_BARRIER
63   }
64 
65 #if defined(CPPGC_POINTER_COMPRESSION)
66   template <WriteBarrierSlotType SlotType>
AssigningBarriercppgc::internal::DijkstraWriteBarrierPolicy67   V8_INLINE static void AssigningBarrier(const void* slot,
68                                          CompressedPointer storage) {
69     static_assert(
70         SlotType == WriteBarrierSlotType::kCompressed,
71         "Assigning storages of Member and UncompressedMember is not supported");
72 #ifdef CPPGC_SLIM_WRITE_BARRIER
73     if (V8_UNLIKELY(WriteBarrier::IsEnabled()))
74       WriteBarrier::CombinedWriteBarrierSlow<SlotType>(slot);
75 #else   // !CPPGC_SLIM_WRITE_BARRIER
76     WriteBarrier::Params params;
77     const WriteBarrier::Type type =
78         WriteBarrier::GetWriteBarrierType(slot, storage, params);
79     WriteBarrier(type, params, slot, storage.Load());
80 #endif  // !CPPGC_SLIM_WRITE_BARRIER
81   }
82 #endif  // defined(CPPGC_POINTER_COMPRESSION)
83 
84  private:
WriteBarriercppgc::internal::DijkstraWriteBarrierPolicy85   V8_INLINE static void WriteBarrier(WriteBarrier::Type type,
86                                      const WriteBarrier::Params& params,
87                                      const void* slot, const void* value) {
88     switch (type) {
89       case WriteBarrier::Type::kGenerational:
90         WriteBarrier::GenerationalBarrier<
91             WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot);
92         break;
93       case WriteBarrier::Type::kMarking:
94         WriteBarrier::DijkstraMarkingBarrier(params, value);
95         break;
96       case WriteBarrier::Type::kNone:
97         break;
98     }
99   }
100 };
101 
102 struct NoWriteBarrierPolicy {
InitializingBarriercppgc::internal::NoWriteBarrierPolicy103   V8_INLINE static void InitializingBarrier(const void*, const void*) {}
104   template <WriteBarrierSlotType>
AssigningBarriercppgc::internal::NoWriteBarrierPolicy105   V8_INLINE static void AssigningBarrier(const void*, const void*) {}
106   template <WriteBarrierSlotType, typename MemberStorage>
AssigningBarriercppgc::internal::NoWriteBarrierPolicy107   V8_INLINE static void AssigningBarrier(const void*, MemberStorage) {}
108 };
109 
110 class V8_EXPORT SameThreadEnabledCheckingPolicyBase {
111  protected:
112   void CheckPointerImpl(const void* ptr, bool points_to_payload,
113                         bool check_off_heap_assignments);
114 
115   const HeapBase* heap_ = nullptr;
116 };
117 
118 template <bool kCheckOffHeapAssignments>
119 class V8_EXPORT SameThreadEnabledCheckingPolicy
120     : private SameThreadEnabledCheckingPolicyBase {
121  protected:
122   template <typename T>
CheckPointer(const T* ptr)123   void CheckPointer(const T* ptr) {
124     if (!ptr || (kSentinelPointer == ptr)) return;
125 
126     CheckPointersImplTrampoline<T>::Call(this, ptr);
127   }
128 
129  private:
130   template <typename T, bool = IsCompleteV<T>>
131   struct CheckPointersImplTrampoline {
Callcppgc::internal::SameThreadEnabledCheckingPolicy::CheckPointersImplTrampoline132     static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) {
133       policy->CheckPointerImpl(ptr, false, kCheckOffHeapAssignments);
134     }
135   };
136 
137   template <typename T>
138   struct CheckPointersImplTrampoline<T, true> {
Callcppgc::internal::SameThreadEnabledCheckingPolicy::CheckPointersImplTrampoline139     static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) {
140       policy->CheckPointerImpl(ptr, IsGarbageCollectedTypeV<T>,
141                                kCheckOffHeapAssignments);
142     }
143   };
144 };
145 
146 class DisabledCheckingPolicy {
147  protected:
CheckPointer(const void*)148   V8_INLINE void CheckPointer(const void*) {}
149 };
150 
151 #ifdef DEBUG
152 // Off heap members are not connected to object graph and thus cannot ressurect
153 // dead objects.
154 using DefaultMemberCheckingPolicy =
155     SameThreadEnabledCheckingPolicy<false /* kCheckOffHeapAssignments*/>;
156 using DefaultPersistentCheckingPolicy =
157     SameThreadEnabledCheckingPolicy<true /* kCheckOffHeapAssignments*/>;
158 #else   // !DEBUG
159 using DefaultMemberCheckingPolicy = DisabledCheckingPolicy;
160 using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy;
161 #endif  // !DEBUG
162 // For CT(W)P neither marking information (for value), nor objectstart bitmap
163 // (for slot) are guaranteed to be present because there's no synchronization
164 // between heaps after marking.
165 using DefaultCrossThreadPersistentCheckingPolicy = DisabledCheckingPolicy;
166 
167 class KeepLocationPolicy {
168  public:
Location() const169   constexpr const SourceLocation& Location() const { return location_; }
170 
171  protected:
172   constexpr KeepLocationPolicy() = default;
KeepLocationPolicy(const SourceLocation& location)173   constexpr explicit KeepLocationPolicy(const SourceLocation& location)
174       : location_(location) {}
175 
176   // KeepLocationPolicy must not copy underlying source locations.
177   KeepLocationPolicy(const KeepLocationPolicy&) = delete;
178   KeepLocationPolicy& operator=(const KeepLocationPolicy&) = delete;
179 
180   // Location of the original moved from object should be preserved.
181   KeepLocationPolicy(KeepLocationPolicy&&) = default;
182   KeepLocationPolicy& operator=(KeepLocationPolicy&&) = default;
183 
184  private:
185   SourceLocation location_;
186 };
187 
188 class IgnoreLocationPolicy {
189  public:
Location() const190   constexpr SourceLocation Location() const { return {}; }
191 
192  protected:
193   constexpr IgnoreLocationPolicy() = default;
IgnoreLocationPolicy(const SourceLocation&)194   constexpr explicit IgnoreLocationPolicy(const SourceLocation&) {}
195 };
196 
197 #if CPPGC_SUPPORTS_OBJECT_NAMES
198 using DefaultLocationPolicy = KeepLocationPolicy;
199 #else
200 using DefaultLocationPolicy = IgnoreLocationPolicy;
201 #endif
202 
203 struct StrongPersistentPolicy {
204   using IsStrongPersistent = std::true_type;
205   static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object);
206 };
207 
208 struct WeakPersistentPolicy {
209   using IsStrongPersistent = std::false_type;
210   static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object);
211 };
212 
213 struct StrongCrossThreadPersistentPolicy {
214   using IsStrongPersistent = std::true_type;
215   static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion(
216       const void* object);
217 };
218 
219 struct WeakCrossThreadPersistentPolicy {
220   using IsStrongPersistent = std::false_type;
221   static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion(
222       const void* object);
223 };
224 
225 // Forward declarations setting up the default policies.
226 template <typename T, typename WeaknessPolicy,
227           typename LocationPolicy = DefaultLocationPolicy,
228           typename CheckingPolicy = DefaultCrossThreadPersistentCheckingPolicy>
229 class BasicCrossThreadPersistent;
230 template <typename T, typename WeaknessPolicy,
231           typename LocationPolicy = DefaultLocationPolicy,
232           typename CheckingPolicy = DefaultPersistentCheckingPolicy>
233 class BasicPersistent;
234 template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
235           typename CheckingPolicy = DefaultMemberCheckingPolicy,
236           typename StorageType = DefaultMemberStorage>
237 class BasicMember;
238 
239 }  // namespace internal
240 
241 }  // namespace cppgc
242 
243 #endif  // INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_
244