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