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_VISITOR_H_ 6#define INCLUDE_CPPGC_VISITOR_H_ 7 8#include "cppgc/custom-space.h" 9#include "cppgc/ephemeron-pair.h" 10#include "cppgc/garbage-collected.h" 11#include "cppgc/internal/logging.h" 12#include "cppgc/internal/pointer-policies.h" 13#include "cppgc/liveness-broker.h" 14#include "cppgc/member.h" 15#include "cppgc/sentinel-pointer.h" 16#include "cppgc/source-location.h" 17#include "cppgc/trace-trait.h" 18#include "cppgc/type-traits.h" 19 20namespace cppgc { 21 22namespace internal { 23template <typename T, typename WeaknessPolicy, typename LocationPolicy, 24 typename CheckingPolicy> 25class BasicCrossThreadPersistent; 26template <typename T, typename WeaknessPolicy, typename LocationPolicy, 27 typename CheckingPolicy> 28class BasicPersistent; 29class ConservativeTracingVisitor; 30class VisitorBase; 31class VisitorFactory; 32} // namespace internal 33 34using WeakCallback = void (*)(const LivenessBroker&, const void*); 35 36/** 37 * Visitor passed to trace methods. All managed pointers must have called the 38 * Visitor's trace method on them. 39 * 40 * \code 41 * class Foo final : public GarbageCollected<Foo> { 42 * public: 43 * void Trace(Visitor* visitor) const { 44 * visitor->Trace(foo_); 45 * visitor->Trace(weak_foo_); 46 * } 47 * private: 48 * Member<Foo> foo_; 49 * WeakMember<Foo> weak_foo_; 50 * }; 51 * \endcode 52 */ 53class V8_EXPORT Visitor { 54 public: 55 class Key { 56 private: 57 Key() = default; 58 friend class internal::VisitorFactory; 59 }; 60 61 explicit Visitor(Key) {} 62 63 virtual ~Visitor() = default; 64 65 /** 66 * Trace method for raw pointers. Prefer the versions for managed pointers. 67 * 68 * \param member Reference retaining an object. 69 */ 70 template <typename T> 71 void Trace(const T* t) { 72 static_assert(sizeof(T), "Pointee type must be fully defined."); 73 static_assert(internal::IsGarbageCollectedOrMixinType<T>::value, 74 "T must be GarbageCollected or GarbageCollectedMixin type"); 75 if (!t) { 76 return; 77 } 78 Visit(t, TraceTrait<T>::GetTraceDescriptor(t)); 79 } 80 81 /** 82 * Trace method for Member. 83 * 84 * \param member Member reference retaining an object. 85 */ 86 template <typename T> 87 void Trace(const Member<T>& member) { 88 const T* value = member.GetRawAtomic(); 89 CPPGC_DCHECK(value != kSentinelPointer); 90 Trace(value); 91 } 92 93 /** 94 * Trace method for WeakMember. 95 * 96 * \param weak_member WeakMember reference weakly retaining an object. 97 */ 98 template <typename T> 99 void Trace(const WeakMember<T>& weak_member) { 100 static_assert(sizeof(T), "Pointee type must be fully defined."); 101 static_assert(internal::IsGarbageCollectedOrMixinType<T>::value, 102 "T must be GarbageCollected or GarbageCollectedMixin type"); 103 static_assert(!internal::IsAllocatedOnCompactableSpace<T>::value, 104 "Weak references to compactable objects are not allowed"); 105 106 const T* value = weak_member.GetRawAtomic(); 107 108 // Bailout assumes that WeakMember emits write barrier. 109 if (!value) { 110 return; 111 } 112 113 CPPGC_DCHECK(value != kSentinelPointer); 114 VisitWeak(value, TraceTrait<T>::GetTraceDescriptor(value), 115 &HandleWeak<WeakMember<T>>, &weak_member); 116 } 117 118 /** 119 * Trace method for inlined objects that are not allocated themselves but 120 * otherwise follow managed heap layout and have a Trace() method. 121 * 122 * \param object reference of the inlined object. 123 */ 124 template <typename T> 125 void Trace(const T& object) { 126#if V8_ENABLE_CHECKS 127 // This object is embedded in potentially multiple nested objects. The 128 // outermost object must not be in construction as such objects are (a) not 129 // processed immediately, and (b) only processed conservatively if not 130 // otherwise possible. 131 CheckObjectNotInConstruction(&object); 132#endif // V8_ENABLE_CHECKS 133 TraceTrait<T>::Trace(this, &object); 134 } 135 136 /** 137 * Registers a weak callback method on the object of type T. See 138 * LivenessBroker for an usage example. 139 * 140 * \param object of type T specifying a weak callback method. 141 */ 142 template <typename T, void (T::*method)(const LivenessBroker&)> 143 void RegisterWeakCallbackMethod(const T* object) { 144 RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object); 145 } 146 147 /** 148 * Trace method for EphemeronPair. 149 * 150 * \param ephemeron_pair EphemeronPair reference weakly retaining a key object 151 * and strongly retaining a value object in case the key object is alive. 152 */ 153 template <typename K, typename V> 154 void Trace(const EphemeronPair<K, V>& ephemeron_pair) { 155 TraceEphemeron(ephemeron_pair.key, &ephemeron_pair.value); 156 RegisterWeakCallbackMethod<EphemeronPair<K, V>, 157 &EphemeronPair<K, V>::ClearValueIfKeyIsDead>( 158 &ephemeron_pair); 159 } 160 161 /** 162 * Trace method for a single ephemeron. Used for tracing a raw ephemeron in 163 * which the `key` and `value` are kept separately. 164 * 165 * \param weak_member_key WeakMember reference weakly retaining a key object. 166 * \param member_value Member reference with ephemeron semantics. 167 */ 168 template <typename KeyType, typename ValueType> 169 void TraceEphemeron(const WeakMember<KeyType>& weak_member_key, 170 const Member<ValueType>* member_value) { 171 const KeyType* key = weak_member_key.GetRawAtomic(); 172 if (!key) return; 173 174 // `value` must always be non-null. 175 CPPGC_DCHECK(member_value); 176 const ValueType* value = member_value->GetRawAtomic(); 177 if (!value) return; 178 179 // KeyType and ValueType may refer to GarbageCollectedMixin. 180 TraceDescriptor value_desc = 181 TraceTrait<ValueType>::GetTraceDescriptor(value); 182 CPPGC_DCHECK(value_desc.base_object_payload); 183 const void* key_base_object_payload = 184 TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload; 185 CPPGC_DCHECK(key_base_object_payload); 186 187 VisitEphemeron(key_base_object_payload, value, value_desc); 188 } 189 190 /** 191 * Trace method for a single ephemeron. Used for tracing a raw ephemeron in 192 * which the `key` and `value` are kept separately. Note that this overload 193 * is for non-GarbageCollected `value`s that can be traced though. 194 * 195 * \param key `WeakMember` reference weakly retaining a key object. 196 * \param value Reference weakly retaining a value object. Note that 197 * `ValueType` here should not be `Member`. It is expected that 198 * `TraceTrait<ValueType>::GetTraceDescriptor(value)` returns a 199 * `TraceDescriptor` with a null base pointer but a valid trace method. 200 */ 201 template <typename KeyType, typename ValueType> 202 void TraceEphemeron(const WeakMember<KeyType>& weak_member_key, 203 const ValueType* value) { 204 static_assert(!IsGarbageCollectedOrMixinTypeV<ValueType>, 205 "garbage-collected types must use WeakMember and Member"); 206 const KeyType* key = weak_member_key.GetRawAtomic(); 207 if (!key) return; 208 209 // `value` must always be non-null. 210 CPPGC_DCHECK(value); 211 TraceDescriptor value_desc = 212 TraceTrait<ValueType>::GetTraceDescriptor(value); 213 // `value_desc.base_object_payload` must be null as this override is only 214 // taken for non-garbage-collected values. 215 CPPGC_DCHECK(!value_desc.base_object_payload); 216 217 // KeyType might be a GarbageCollectedMixin. 218 const void* key_base_object_payload = 219 TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload; 220 CPPGC_DCHECK(key_base_object_payload); 221 222 VisitEphemeron(key_base_object_payload, value, value_desc); 223 } 224 225 /** 226 * Trace method that strongifies a WeakMember. 227 * 228 * \param weak_member WeakMember reference retaining an object. 229 */ 230 template <typename T> 231 void TraceStrongly(const WeakMember<T>& weak_member) { 232 const T* value = weak_member.GetRawAtomic(); 233 CPPGC_DCHECK(value != kSentinelPointer); 234 Trace(value); 235 } 236 237 /** 238 * Trace method for weak containers. 239 * 240 * \param object reference of the weak container. 241 * \param callback to be invoked. 242 * \param data custom data that is passed to the callback. 243 */ 244 template <typename T> 245 void TraceWeakContainer(const T* object, WeakCallback callback, 246 const void* data) { 247 if (!object) return; 248 VisitWeakContainer(object, TraceTrait<T>::GetTraceDescriptor(object), 249 TraceTrait<T>::GetWeakTraceDescriptor(object), callback, 250 data); 251 } 252 253 /** 254 * Registers a slot containing a reference to an object allocated on a 255 * compactable space. Such references maybe be arbitrarily moved by the GC. 256 * 257 * \param slot location of reference to object that might be moved by the GC. 258 */ 259 template <typename T> 260 void RegisterMovableReference(const T** slot) { 261 static_assert(internal::IsAllocatedOnCompactableSpace<T>::value, 262 "Only references to objects allocated on compactable spaces " 263 "should be registered as movable slots."); 264 static_assert(!IsGarbageCollectedMixinTypeV<T>, 265 "Mixin types do not support compaction."); 266 HandleMovableReference(reinterpret_cast<const void**>(slot)); 267 } 268 269 /** 270 * Registers a weak callback that is invoked during garbage collection. 271 * 272 * \param callback to be invoked. 273 * \param data custom data that is passed to the callback. 274 */ 275 virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {} 276 277 /** 278 * Defers tracing an object from a concurrent thread to the mutator thread. 279 * Should be called by Trace methods of types that are not safe to trace 280 * concurrently. 281 * 282 * \param parameter tells the trace callback which object was deferred. 283 * \param callback to be invoked for tracing on the mutator thread. 284 * \param deferred_size size of deferred object. 285 * 286 * \returns false if the object does not need to be deferred (i.e. currently 287 * traced on the mutator thread) and true otherwise (i.e. currently traced on 288 * a concurrent thread). 289 */ 290 virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent( 291 const void* parameter, TraceCallback callback, size_t deferred_size) { 292 // By default tracing is not deferred. 293 return false; 294 } 295 296 protected: 297 virtual void Visit(const void* self, TraceDescriptor) {} 298 virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback, 299 const void* weak_member) {} 300 virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {} 301 virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, 302 const void* weak_root, const SourceLocation&) {} 303 virtual void VisitEphemeron(const void* key, const void* value, 304 TraceDescriptor value_desc) {} 305 virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, 306 TraceDescriptor weak_desc, 307 WeakCallback callback, const void* data) {} 308 virtual void HandleMovableReference(const void**) {} 309 310 private: 311 template <typename T, void (T::*method)(const LivenessBroker&)> 312 static void WeakCallbackMethodDelegate(const LivenessBroker& info, 313 const void* self) { 314 // Callback is registered through a potential const Trace method but needs 315 // to be able to modify fields. See HandleWeak. 316 (const_cast<T*>(static_cast<const T*>(self))->*method)(info); 317 } 318 319 template <typename PointerType> 320 static void HandleWeak(const LivenessBroker& info, const void* object) { 321 const PointerType* weak = static_cast<const PointerType*>(object); 322 auto* raw_ptr = weak->GetFromGC(); 323 // Sentinel values are preserved for weak pointers. 324 if (raw_ptr == kSentinelPointer) return; 325 if (!info.IsHeapObjectAlive(raw_ptr)) { 326 weak->ClearFromGC(); 327 } 328 } 329 330 template <typename Persistent, 331 std::enable_if_t<Persistent::IsStrongPersistent::value>* = nullptr> 332 void TraceRoot(const Persistent& p, const SourceLocation& loc) { 333 using PointeeType = typename Persistent::PointeeType; 334 static_assert(sizeof(PointeeType), 335 "Persistent's pointee type must be fully defined"); 336 static_assert(internal::IsGarbageCollectedOrMixinType<PointeeType>::value, 337 "Persistent's pointee type must be GarbageCollected or " 338 "GarbageCollectedMixin"); 339 auto* ptr = p.GetFromGC(); 340 if (!ptr) { 341 return; 342 } 343 VisitRoot(ptr, TraceTrait<PointeeType>::GetTraceDescriptor(ptr), loc); 344 } 345 346 template < 347 typename WeakPersistent, 348 std::enable_if_t<!WeakPersistent::IsStrongPersistent::value>* = nullptr> 349 void TraceRoot(const WeakPersistent& p, const SourceLocation& loc) { 350 using PointeeType = typename WeakPersistent::PointeeType; 351 static_assert(sizeof(PointeeType), 352 "Persistent's pointee type must be fully defined"); 353 static_assert(internal::IsGarbageCollectedOrMixinType<PointeeType>::value, 354 "Persistent's pointee type must be GarbageCollected or " 355 "GarbageCollectedMixin"); 356 static_assert(!internal::IsAllocatedOnCompactableSpace<PointeeType>::value, 357 "Weak references to compactable objects are not allowed"); 358 auto* ptr = p.GetFromGC(); 359 VisitWeakRoot(ptr, TraceTrait<PointeeType>::GetTraceDescriptor(ptr), 360 &HandleWeak<WeakPersistent>, &p, loc); 361 } 362 363#if V8_ENABLE_CHECKS 364 void CheckObjectNotInConstruction(const void* address); 365#endif // V8_ENABLE_CHECKS 366 367 template <typename T, typename WeaknessPolicy, typename LocationPolicy, 368 typename CheckingPolicy> 369 friend class internal::BasicCrossThreadPersistent; 370 template <typename T, typename WeaknessPolicy, typename LocationPolicy, 371 typename CheckingPolicy> 372 friend class internal::BasicPersistent; 373 friend class internal::ConservativeTracingVisitor; 374 friend class internal::VisitorBase; 375}; 376 377} // namespace cppgc 378 379#endif // INCLUDE_CPPGC_VISITOR_H_ 380