| // Copyright 2020 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef INCLUDE_CPPGC_VISITOR_H_ |
| #define INCLUDE_CPPGC_VISITOR_H_ |
| |
| #include "cppgc/custom-space.h" |
| #include "cppgc/ephemeron-pair.h" |
| #include "cppgc/garbage-collected.h" |
| #include "cppgc/internal/logging.h" |
| #include "cppgc/internal/pointer-policies.h" |
| #include "cppgc/liveness-broker.h" |
| #include "cppgc/member.h" |
| #include "cppgc/source-location.h" |
| #include "cppgc/trace-trait.h" |
| #include "cppgc/type-traits.h" |
| |
| namespace cppgc { |
| |
| namespace internal { |
| template <typename T, typename WeaknessPolicy, typename LocationPolicy, |
| typename CheckingPolicy> |
| class BasicCrossThreadPersistent; |
| template <typename T, typename WeaknessPolicy, typename LocationPolicy, |
| typename CheckingPolicy> |
| class BasicPersistent; |
| class ConservativeTracingVisitor; |
| class VisitorBase; |
| class VisitorFactory; |
| } // namespace internal |
| |
| using WeakCallback = void (*)(const LivenessBroker&, const void*); |
| |
| /** |
| * Visitor passed to trace methods. All managed pointers must have called the |
| * Visitor's trace method on them. |
| * |
| * \code |
| * class Foo final : public GarbageCollected<Foo> { |
| * public: |
| * void Trace(Visitor* visitor) const { |
| * visitor->Trace(foo_); |
| * visitor->Trace(weak_foo_); |
| * } |
| * private: |
| * Member<Foo> foo_; |
| * WeakMember<Foo> weak_foo_; |
| * }; |
| * \endcode |
| */ |
| class V8_EXPORT Visitor { |
| public: |
| class Key { |
| private: |
| Key() = default; |
| friend class internal::VisitorFactory; |
| }; |
| |
| explicit Visitor(Key) {} |
| |
| virtual ~Visitor() = default; |
| |
| /** |
| * Trace method for Member. |
| * |
| * \param member Member reference retaining an object. |
| */ |
| template <typename T> |
| void Trace(const Member<T>& member) { |
| const T* value = member.GetRawAtomic(); |
| CPPGC_DCHECK(value != kSentinelPointer); |
| Trace(value); |
| } |
| |
| /** |
| * Trace method for WeakMember. |
| * |
| * \param weak_member WeakMember reference weakly retaining an object. |
| */ |
| template <typename T> |
| void Trace(const WeakMember<T>& weak_member) { |
| static_assert(sizeof(T), "Pointee type must be fully defined."); |
| static_assert(internal::IsGarbageCollectedType<T>::value, |
| "T must be GarbageCollected or GarbageCollectedMixin type"); |
| static_assert(!internal::IsAllocatedOnCompactableSpace<T>::value, |
| "Weak references to compactable objects are not allowed"); |
| |
| const T* value = weak_member.GetRawAtomic(); |
| |
| // Bailout assumes that WeakMember emits write barrier. |
| if (!value) { |
| return; |
| } |
| |
| CPPGC_DCHECK(value != kSentinelPointer); |
| VisitWeak(value, TraceTrait<T>::GetTraceDescriptor(value), |
| &HandleWeak<WeakMember<T>>, &weak_member); |
| } |
| |
| /** |
| * Trace method for inlined objects that are not allocated themselves but |
| * otherwise follow managed heap layout and have a Trace() method. |
| * |
| * \param object reference of the inlined object. |
| */ |
| template <typename T> |
| void Trace(const T& object) { |
| #if V8_ENABLE_CHECKS |
| // This object is embedded in potentially multiple nested objects. The |
| // outermost object must not be in construction as such objects are (a) not |
| // processed immediately, and (b) only processed conservatively if not |
| // otherwise possible. |
| CheckObjectNotInConstruction(&object); |
| #endif // V8_ENABLE_CHECKS |
| TraceTrait<T>::Trace(this, &object); |
| } |
| |
| /** |
| * Registers a weak callback method on the object of type T. See |
| * LivenessBroker for an usage example. |
| * |
| * \param object of type T specifying a weak callback method. |
| */ |
| template <typename T, void (T::*method)(const LivenessBroker&)> |
| void RegisterWeakCallbackMethod(const T* object) { |
| RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object); |
| } |
| |
| /** |
| * Trace method for EphemeronPair. |
| * |
| * \param ephemeron_pair EphemeronPair reference weakly retaining a key object |
| * and strongly retaining a value object in case the key object is alive. |
| */ |
| template <typename K, typename V> |
| void Trace(const EphemeronPair<K, V>& ephemeron_pair) { |
| TraceEphemeron(ephemeron_pair.key, ephemeron_pair.value.GetRawAtomic()); |
| } |
| |
| /** |
| * Trace method for ephemerons. Used for tracing raw ephemeron in which the |
| * key and value are kept separately. |
| * |
| * \param key WeakMember reference weakly retaining a key object. |
| * \param value Member reference weakly retaining a value object. |
| */ |
| template <typename K, typename V> |
| void TraceEphemeron(const WeakMember<K>& key, const V* value) { |
| TraceDescriptor value_desc = TraceTrait<V>::GetTraceDescriptor(value); |
| VisitEphemeron(key, value_desc); |
| } |
| |
| /** |
| * Trace method that strongifies a WeakMember. |
| * |
| * \param weak_member WeakMember reference retaining an object. |
| */ |
| template <typename T> |
| void TraceStrongly(const WeakMember<T>& weak_member) { |
| const T* value = weak_member.GetRawAtomic(); |
| CPPGC_DCHECK(value != kSentinelPointer); |
| Trace(value); |
| } |
| |
| /** |
| * Trace method for weak containers. |
| * |
| * \param object reference of the weak container. |
| * \param callback to be invoked. |
| * \param data custom data that is passed to the callback. |
| */ |
| template <typename T> |
| void TraceWeakContainer(const T* object, WeakCallback callback, |
| const void* data) { |
| if (!object) return; |
| VisitWeakContainer(object, TraceTrait<T>::GetTraceDescriptor(object), |
| TraceTrait<T>::GetWeakTraceDescriptor(object), callback, |
| data); |
| } |
| |
| /** |
| * Registers a slot containing a reference to an object allocated on a |
| * compactable space. Such references maybe be arbitrarily moved by the GC. |
| * |
| * \param slot location of reference to object that might be moved by the GC. |
| */ |
| template <typename T> |
| void RegisterMovableReference(const T** slot) { |
| static_assert(internal::IsAllocatedOnCompactableSpace<T>::value, |
| "Only references to objects allocated on compactable spaces " |
| "should be registered as movable slots."); |
| static_assert(!internal::IsGarbageCollectedMixinTypeV<T>, |
| "Mixin types do not support compaction."); |
| HandleMovableReference(reinterpret_cast<const void**>(slot)); |
| } |
| |
| /** |
| * Registers a weak callback that is invoked during garbage collection. |
| * |
| * \param callback to be invoked. |
| * \param data custom data that is passed to the callback. |
| */ |
| virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {} |
| |
| /** |
| * Defers tracing an object from a concurrent thread to the mutator thread. |
| * Should be called by Trace methods of types that are not safe to trace |
| * concurrently. |
| * |
| * \param parameter tells the trace callback which object was deferred. |
| * \param callback to be invoked for tracing on the mutator thread. |
| * \param deferred_size size of deferred object. |
| * |
| * \returns false if the object does not need to be deferred (i.e. currently |
| * traced on the mutator thread) and true otherwise (i.e. currently traced on |
| * a concurrent thread). |
| */ |
| virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent( |
| const void* parameter, TraceCallback callback, size_t deferred_size) { |
| // By default tracing is not deferred. |
| return false; |
| } |
| |
| protected: |
| virtual void Visit(const void* self, TraceDescriptor) {} |
| virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback, |
| const void* weak_member) {} |
| virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {} |
| virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, |
| const void* weak_root, const SourceLocation&) {} |
| virtual void VisitEphemeron(const void* key, TraceDescriptor value_desc) {} |
| virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, |
| TraceDescriptor weak_desc, |
| WeakCallback callback, const void* data) {} |
| virtual void HandleMovableReference(const void**) {} |
| |
| private: |
| template <typename T, void (T::*method)(const LivenessBroker&)> |
| static void WeakCallbackMethodDelegate(const LivenessBroker& info, |
| const void* self) { |
| // Callback is registered through a potential const Trace method but needs |
| // to be able to modify fields. See HandleWeak. |
| (const_cast<T*>(static_cast<const T*>(self))->*method)(info); |
| } |
| |
| template <typename PointerType> |
| static void HandleWeak(const LivenessBroker& info, const void* object) { |
| const PointerType* weak = static_cast<const PointerType*>(object); |
| // Sentinel values are preserved for weak pointers. |
| if (*weak == kSentinelPointer) return; |
| const auto* raw = weak->Get(); |
| if (!info.IsHeapObjectAlive(raw)) { |
| weak->ClearFromGC(); |
| } |
| } |
| |
| template <typename Persistent, |
| std::enable_if_t<Persistent::IsStrongPersistent::value>* = nullptr> |
| void TraceRoot(const Persistent& p, const SourceLocation& loc) { |
| using PointeeType = typename Persistent::PointeeType; |
| static_assert(sizeof(PointeeType), |
| "Persistent's pointee type must be fully defined"); |
| static_assert(internal::IsGarbageCollectedType<PointeeType>::value, |
| "Persistent's pointee type must be GarbageCollected or " |
| "GarbageCollectedMixin"); |
| if (!p.Get()) { |
| return; |
| } |
| VisitRoot(p.Get(), TraceTrait<PointeeType>::GetTraceDescriptor(p.Get()), |
| loc); |
| } |
| |
| template < |
| typename WeakPersistent, |
| std::enable_if_t<!WeakPersistent::IsStrongPersistent::value>* = nullptr> |
| void TraceRoot(const WeakPersistent& p, const SourceLocation& loc) { |
| using PointeeType = typename WeakPersistent::PointeeType; |
| static_assert(sizeof(PointeeType), |
| "Persistent's pointee type must be fully defined"); |
| static_assert(internal::IsGarbageCollectedType<PointeeType>::value, |
| "Persistent's pointee type must be GarbageCollected or " |
| "GarbageCollectedMixin"); |
| static_assert(!internal::IsAllocatedOnCompactableSpace<PointeeType>::value, |
| "Weak references to compactable objects are not allowed"); |
| VisitWeakRoot(p.Get(), TraceTrait<PointeeType>::GetTraceDescriptor(p.Get()), |
| &HandleWeak<WeakPersistent>, &p, loc); |
| } |
| |
| template <typename T> |
| void Trace(const T* t) { |
| static_assert(sizeof(T), "Pointee type must be fully defined."); |
| static_assert(internal::IsGarbageCollectedType<T>::value, |
| "T must be GarbageCollected or GarbageCollectedMixin type"); |
| if (!t) { |
| return; |
| } |
| Visit(t, TraceTrait<T>::GetTraceDescriptor(t)); |
| } |
| |
| #if V8_ENABLE_CHECKS |
| void CheckObjectNotInConstruction(const void* address); |
| #endif // V8_ENABLE_CHECKS |
| |
| template <typename T, typename WeaknessPolicy, typename LocationPolicy, |
| typename CheckingPolicy> |
| friend class internal::BasicCrossThreadPersistent; |
| template <typename T, typename WeaknessPolicy, typename LocationPolicy, |
| typename CheckingPolicy> |
| friend class internal::BasicPersistent; |
| friend class internal::ConservativeTracingVisitor; |
| friend class internal::VisitorBase; |
| }; |
| |
| } // namespace cppgc |
| |
| #endif // INCLUDE_CPPGC_VISITOR_H_ |