xref: /third_party/node/deps/v8/include/cppgc/visitor.h (revision 1cb0ef41)
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