Coverage Report

Created: 2025-07-04 09:33

/src/node/deps/v8/include/cppgc/visitor.h
Line
Count
Source (jump to first uncovered line)
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 <type_traits>
9
10
#include "cppgc/custom-space.h"
11
#include "cppgc/ephemeron-pair.h"
12
#include "cppgc/garbage-collected.h"
13
#include "cppgc/internal/logging.h"
14
#include "cppgc/internal/member-storage.h"
15
#include "cppgc/internal/pointer-policies.h"
16
#include "cppgc/liveness-broker.h"
17
#include "cppgc/member.h"
18
#include "cppgc/sentinel-pointer.h"
19
#include "cppgc/source-location.h"
20
#include "cppgc/trace-trait.h"
21
#include "cppgc/type-traits.h"
22
23
namespace cppgc {
24
25
namespace internal {
26
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
27
          typename CheckingPolicy>
28
class BasicCrossThreadPersistent;
29
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
30
          typename CheckingPolicy>
31
class BasicPersistent;
32
class ConservativeTracingVisitor;
33
class VisitorBase;
34
class VisitorFactory;
35
}  // namespace internal
36
37
using WeakCallback = void (*)(const LivenessBroker&, const void*);
38
39
/**
40
 * Visitor passed to trace methods. All managed pointers must have called the
41
 * Visitor's trace method on them.
42
 *
43
 * \code
44
 * class Foo final : public GarbageCollected<Foo> {
45
 *  public:
46
 *   void Trace(Visitor* visitor) const {
47
 *     visitor->Trace(foo_);
48
 *     visitor->Trace(weak_foo_);
49
 *   }
50
 *  private:
51
 *   Member<Foo> foo_;
52
 *   WeakMember<Foo> weak_foo_;
53
 * };
54
 * \endcode
55
 */
56
class V8_EXPORT Visitor {
57
 public:
58
  class Key {
59
   private:
60
    Key() = default;
61
    friend class internal::VisitorFactory;
62
  };
63
64
0
  explicit Visitor(Key) {}
65
66
  virtual ~Visitor() = default;
67
68
  /**
69
   * Trace method for Member.
70
   *
71
   * \param member Member reference retaining an object.
72
   */
73
  template <typename T>
74
  void Trace(const Member<T>& member) {
75
    const T* value = member.GetRawAtomic();
76
    CPPGC_DCHECK(value != kSentinelPointer);
77
    TraceImpl(value);
78
  }
79
80
  /**
81
   * Trace method for WeakMember.
82
   *
83
   * \param weak_member WeakMember reference weakly retaining an object.
84
   */
85
  template <typename T>
86
  void Trace(const WeakMember<T>& weak_member) {
87
    static_assert(sizeof(T), "Pointee type must be fully defined.");
88
    static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
89
                  "T must be GarbageCollected or GarbageCollectedMixin type");
90
    static_assert(!internal::IsAllocatedOnCompactableSpace<T>::value,
91
                  "Weak references to compactable objects are not allowed");
92
93
    const T* value = weak_member.GetRawAtomic();
94
95
    // Bailout assumes that WeakMember emits write barrier.
96
    if (!value) {
97
      return;
98
    }
99
100
    CPPGC_DCHECK(value != kSentinelPointer);
101
    VisitWeak(value, TraceTrait<T>::GetTraceDescriptor(value),
102
              &HandleWeak<WeakMember<T>>, &weak_member);
103
  }
104
105
#if defined(CPPGC_POINTER_COMPRESSION)
106
  /**
107
   * Trace method for UncompressedMember.
108
   *
109
   * \param member UncompressedMember reference retaining an object.
110
   */
111
  template <typename T>
112
  void Trace(const subtle::UncompressedMember<T>& member) {
113
    const T* value = member.GetRawAtomic();
114
    CPPGC_DCHECK(value != kSentinelPointer);
115
    TraceImpl(value);
116
  }
117
#endif  // defined(CPPGC_POINTER_COMPRESSION)
118
119
  template <typename T>
120
  void TraceMultiple(const subtle::UncompressedMember<T>* start, size_t len) {
121
    static_assert(sizeof(T), "Pointee type must be fully defined.");
122
    static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
123
                  "T must be GarbageCollected or GarbageCollectedMixin type");
124
    VisitMultipleUncompressedMember(start, len,
125
                                    &TraceTrait<T>::GetTraceDescriptor);
126
  }
127
128
  template <typename T,
129
            std::enable_if_t<!std::is_same_v<
130
                Member<T>, subtle::UncompressedMember<T>>>* = nullptr>
131
  void TraceMultiple(const Member<T>* start, size_t len) {
132
    static_assert(sizeof(T), "Pointee type must be fully defined.");
133
    static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
134
                  "T must be GarbageCollected or GarbageCollectedMixin type");
135
#if defined(CPPGC_POINTER_COMPRESSION)
136
    static_assert(std::is_same_v<Member<T>, subtle::CompressedMember<T>>,
137
                  "Member and CompressedMember must be the same.");
138
    VisitMultipleCompressedMember(start, len,
139
                                  &TraceTrait<T>::GetTraceDescriptor);
140
#endif  // defined(CPPGC_POINTER_COMPRESSION)
141
  }
142
143
  /**
144
   * Trace method for inlined objects that are not allocated themselves but
145
   * otherwise follow managed heap layout and have a Trace() method.
146
   *
147
   * \param object reference of the inlined object.
148
   */
149
  template <typename T>
150
  void Trace(const T& object) {
151
#if V8_ENABLE_CHECKS
152
    // This object is embedded in potentially multiple nested objects. The
153
    // outermost object must not be in construction as such objects are (a) not
154
    // processed immediately, and (b) only processed conservatively if not
155
    // otherwise possible.
156
    CheckObjectNotInConstruction(&object);
157
#endif  // V8_ENABLE_CHECKS
158
    TraceTrait<T>::Trace(this, &object);
159
  }
160
161
  template <typename T>
162
  void TraceMultiple(const T* start, size_t len) {
163
#if V8_ENABLE_CHECKS
164
    // This object is embedded in potentially multiple nested objects. The
165
    // outermost object must not be in construction as such objects are (a) not
166
    // processed immediately, and (b) only processed conservatively if not
167
    // otherwise possible.
168
    CheckObjectNotInConstruction(start);
169
#endif  // V8_ENABLE_CHECKS
170
    for (size_t i = 0; i < len; ++i) {
171
      const T* object = &start[i];
172
      if constexpr (std::is_polymorphic_v<T>) {
173
        // The object's vtable may be uninitialized in which case the object is
174
        // not traced.
175
        if (*reinterpret_cast<const uintptr_t*>(object) == 0) continue;
176
      }
177
      TraceTrait<T>::Trace(this, object);
178
    }
179
  }
180
181
  /**
182
   * Registers a weak callback method on the object of type T. See
183
   * LivenessBroker for an usage example.
184
   *
185
   * \param object of type T specifying a weak callback method.
186
   */
187
  template <typename T, void (T::*method)(const LivenessBroker&)>
188
  void RegisterWeakCallbackMethod(const T* object) {
189
    RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object);
190
  }
191
192
  /**
193
   * Trace method for EphemeronPair.
194
   *
195
   * \param ephemeron_pair EphemeronPair reference weakly retaining a key object
196
   * and strongly retaining a value object in case the key object is alive.
197
   */
198
  template <typename K, typename V>
199
  void Trace(const EphemeronPair<K, V>& ephemeron_pair) {
200
    TraceEphemeron(ephemeron_pair.key, &ephemeron_pair.value);
201
    RegisterWeakCallbackMethod<EphemeronPair<K, V>,
202
                               &EphemeronPair<K, V>::ClearValueIfKeyIsDead>(
203
        &ephemeron_pair);
204
  }
205
206
  /**
207
   * Trace method for a single ephemeron. Used for tracing a raw ephemeron in
208
   * which the `key` and `value` are kept separately.
209
   *
210
   * \param weak_member_key WeakMember reference weakly retaining a key object.
211
   * \param member_value Member reference with ephemeron semantics.
212
   */
213
  template <typename KeyType, typename ValueType>
214
  void TraceEphemeron(const WeakMember<KeyType>& weak_member_key,
215
                      const Member<ValueType>* member_value) {
216
    const KeyType* key = weak_member_key.GetRawAtomic();
217
    if (!key) return;
218
219
    // `value` must always be non-null.
220
    CPPGC_DCHECK(member_value);
221
    const ValueType* value = member_value->GetRawAtomic();
222
    if (!value) return;
223
224
    // KeyType and ValueType may refer to GarbageCollectedMixin.
225
    TraceDescriptor value_desc =
226
        TraceTrait<ValueType>::GetTraceDescriptor(value);
227
    CPPGC_DCHECK(value_desc.base_object_payload);
228
    const void* key_base_object_payload =
229
        TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload;
230
    CPPGC_DCHECK(key_base_object_payload);
231
232
    VisitEphemeron(key_base_object_payload, value, value_desc);
233
  }
234
235
  /**
236
   * Trace method for a single ephemeron. Used for tracing a raw ephemeron in
237
   * which the `key` and `value` are kept separately. Note that this overload
238
   * is for non-GarbageCollected `value`s that can be traced though.
239
   *
240
   * \param key `WeakMember` reference weakly retaining a key object.
241
   * \param value Reference weakly retaining a value object. Note that
242
   *   `ValueType` here should not be `Member`. It is expected that
243
   *   `TraceTrait<ValueType>::GetTraceDescriptor(value)` returns a
244
   *   `TraceDescriptor` with a null base pointer but a valid trace method.
245
   */
246
  template <typename KeyType, typename ValueType>
247
  void TraceEphemeron(const WeakMember<KeyType>& weak_member_key,
248
                      const ValueType* value) {
249
    static_assert(!IsGarbageCollectedOrMixinTypeV<ValueType>,
250
                  "garbage-collected types must use WeakMember and Member");
251
    const KeyType* key = weak_member_key.GetRawAtomic();
252
    if (!key) return;
253
254
    // `value` must always be non-null.
255
    CPPGC_DCHECK(value);
256
    TraceDescriptor value_desc =
257
        TraceTrait<ValueType>::GetTraceDescriptor(value);
258
    // `value_desc.base_object_payload` must be null as this override is only
259
    // taken for non-garbage-collected values.
260
    CPPGC_DCHECK(!value_desc.base_object_payload);
261
262
    // KeyType might be a GarbageCollectedMixin.
263
    const void* key_base_object_payload =
264
        TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload;
265
    CPPGC_DCHECK(key_base_object_payload);
266
267
    VisitEphemeron(key_base_object_payload, value, value_desc);
268
  }
269
270
  /**
271
   * Trace method that strongifies a WeakMember.
272
   *
273
   * \param weak_member WeakMember reference retaining an object.
274
   */
275
  template <typename T>
276
  void TraceStrongly(const WeakMember<T>& weak_member) {
277
    const T* value = weak_member.GetRawAtomic();
278
    CPPGC_DCHECK(value != kSentinelPointer);
279
    TraceImpl(value);
280
  }
281
282
  /**
283
   * Trace method for retaining containers strongly.
284
   *
285
   * \param object reference to the container.
286
   */
287
  template <typename T>
288
  void TraceStrongContainer(const T* object) {
289
    TraceImpl(object);
290
  }
291
292
  /**
293
   * Trace method for retaining containers weakly. Note that weak containers
294
   * should emit write barriers.
295
   *
296
   * \param object reference to the container.
297
   * \param callback to be invoked.
298
   * \param callback_data custom data that is passed to the callback.
299
   */
300
  template <typename T>
301
  void TraceWeakContainer(const T* object, WeakCallback callback,
302
                          const void* callback_data) {
303
    if (!object) return;
304
    VisitWeakContainer(object, TraceTrait<T>::GetTraceDescriptor(object),
305
                       TraceTrait<T>::GetWeakTraceDescriptor(object), callback,
306
                       callback_data);
307
  }
308
309
  /**
310
   * Registers a slot containing a reference to an object allocated on a
311
   * compactable space. Such references maybe be arbitrarily moved by the GC.
312
   *
313
   * \param slot location of reference to object that might be moved by the GC.
314
   * The slot must contain an uncompressed pointer.
315
   */
316
  template <typename T>
317
  void RegisterMovableReference(const T** slot) {
318
    static_assert(internal::IsAllocatedOnCompactableSpace<T>::value,
319
                  "Only references to objects allocated on compactable spaces "
320
                  "should be registered as movable slots.");
321
    static_assert(!IsGarbageCollectedMixinTypeV<T>,
322
                  "Mixin types do not support compaction.");
323
    HandleMovableReference(reinterpret_cast<const void**>(slot));
324
  }
325
326
  /**
327
   * Registers a weak callback that is invoked during garbage collection.
328
   *
329
   * \param callback to be invoked.
330
   * \param data custom data that is passed to the callback.
331
   */
332
0
  virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {}
333
334
  /**
335
   * Defers tracing an object from a concurrent thread to the mutator thread.
336
   * Should be called by Trace methods of types that are not safe to trace
337
   * concurrently.
338
   *
339
   * \param parameter tells the trace callback which object was deferred.
340
   * \param callback to be invoked for tracing on the mutator thread.
341
   * \param deferred_size size of deferred object.
342
   *
343
   * \returns false if the object does not need to be deferred (i.e. currently
344
   * traced on the mutator thread) and true otherwise (i.e. currently traced on
345
   * a concurrent thread).
346
   */
347
  virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent(
348
0
      const void* parameter, TraceCallback callback, size_t deferred_size) {
349
0
    // By default tracing is not deferred.
350
0
    return false;
351
0
  }
352
353
 protected:
354
0
  virtual void Visit(const void* self, TraceDescriptor) {}
355
  virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback,
356
0
                         const void* weak_member) {}
357
  virtual void VisitEphemeron(const void* key, const void* value,
358
0
                              TraceDescriptor value_desc) {}
359
  virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc,
360
                                  TraceDescriptor weak_desc,
361
0
                                  WeakCallback callback, const void* data) {}
362
0
  virtual void HandleMovableReference(const void**) {}
363
364
  virtual void VisitMultipleUncompressedMember(
365
      const void* start, size_t len,
366
0
      TraceDescriptorCallback get_trace_descriptor) {
367
0
    // Default implementation merely delegates to Visit().
368
0
    const char* it = static_cast<const char*>(start);
369
0
    const char* end = it + len * internal::kSizeOfUncompressedMember;
370
0
    for (; it < end; it += internal::kSizeOfUncompressedMember) {
371
0
      const auto* current = reinterpret_cast<const internal::RawPointer*>(it);
372
0
      const void* object = current->LoadAtomic();
373
0
      if (!object) continue;
374
0
375
0
      Visit(object, get_trace_descriptor(object));
376
0
    }
377
0
  }
378
379
#if defined(CPPGC_POINTER_COMPRESSION)
380
  virtual void VisitMultipleCompressedMember(
381
      const void* start, size_t len,
382
      TraceDescriptorCallback get_trace_descriptor) {
383
    // Default implementation merely delegates to Visit().
384
    const char* it = static_cast<const char*>(start);
385
    const char* end = it + len * internal::kSizeofCompressedMember;
386
    for (; it < end; it += internal::kSizeofCompressedMember) {
387
      const auto* current =
388
          reinterpret_cast<const internal::CompressedPointer*>(it);
389
      const void* object = current->LoadAtomic();
390
      if (!object) continue;
391
392
      Visit(object, get_trace_descriptor(object));
393
    }
394
  }
395
#endif  // defined(CPPGC_POINTER_COMPRESSION)
396
397
 private:
398
  template <typename T, void (T::*method)(const LivenessBroker&)>
399
  static void WeakCallbackMethodDelegate(const LivenessBroker& info,
400
                                         const void* self) {
401
    // Callback is registered through a potential const Trace method but needs
402
    // to be able to modify fields. See HandleWeak.
403
    (const_cast<T*>(static_cast<const T*>(self))->*method)(info);
404
  }
405
406
  template <typename PointerType>
407
  static void HandleWeak(const LivenessBroker& info, const void* object) {
408
    const PointerType* weak = static_cast<const PointerType*>(object);
409
    if (!info.IsHeapObjectAlive(weak->GetFromGC())) {
410
      weak->ClearFromGC();
411
    }
412
  }
413
414
  template <typename T>
415
  void TraceImpl(const T* t) {
416
    static_assert(sizeof(T), "Pointee type must be fully defined.");
417
    static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
418
                  "T must be GarbageCollected or GarbageCollectedMixin type");
419
    if (!t) {
420
      return;
421
    }
422
    Visit(t, TraceTrait<T>::GetTraceDescriptor(t));
423
  }
424
425
#if V8_ENABLE_CHECKS
426
  void CheckObjectNotInConstruction(const void* address);
427
#endif  // V8_ENABLE_CHECKS
428
429
  template <typename T, typename WeaknessPolicy, typename LocationPolicy,
430
            typename CheckingPolicy>
431
  friend class internal::BasicCrossThreadPersistent;
432
  template <typename T, typename WeaknessPolicy, typename LocationPolicy,
433
            typename CheckingPolicy>
434
  friend class internal::BasicPersistent;
435
  friend class internal::ConservativeTracingVisitor;
436
  friend class internal::VisitorBase;
437
};
438
439
namespace internal {
440
441
class V8_EXPORT RootVisitor {
442
 public:
443
0
  explicit RootVisitor(Visitor::Key) {}
444
445
  virtual ~RootVisitor() = default;
446
447
  template <typename AnyStrongPersistentType,
448
            std::enable_if_t<
449
                AnyStrongPersistentType::IsStrongPersistent::value>* = nullptr>
450
  void Trace(const AnyStrongPersistentType& p) {
451
    using PointeeType = typename AnyStrongPersistentType::PointeeType;
452
    const void* object = Extract(p);
453
    if (!object) {
454
      return;
455
    }
456
    VisitRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object),
457
              p.Location());
458
  }
459
460
  template <typename AnyWeakPersistentType,
461
            std::enable_if_t<
462
                !AnyWeakPersistentType::IsStrongPersistent::value>* = nullptr>
463
  void Trace(const AnyWeakPersistentType& p) {
464
    using PointeeType = typename AnyWeakPersistentType::PointeeType;
465
    static_assert(!internal::IsAllocatedOnCompactableSpace<PointeeType>::value,
466
                  "Weak references to compactable objects are not allowed");
467
    const void* object = Extract(p);
468
    if (!object) {
469
      return;
470
    }
471
    VisitWeakRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object),
472
                  &HandleWeak<AnyWeakPersistentType>, &p, p.Location());
473
  }
474
475
 protected:
476
0
  virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {}
477
  virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback,
478
0
                             const void* weak_root, const SourceLocation&) {}
479
480
 private:
481
  template <typename AnyPersistentType>
482
  static const void* Extract(AnyPersistentType& p) {
483
    using PointeeType = typename AnyPersistentType::PointeeType;
484
    static_assert(sizeof(PointeeType),
485
                  "Persistent's pointee type must be fully defined");
486
    static_assert(internal::IsGarbageCollectedOrMixinType<PointeeType>::value,
487
                  "Persistent's pointee type must be GarbageCollected or "
488
                  "GarbageCollectedMixin");
489
    return p.GetFromGC();
490
  }
491
492
  template <typename PointerType>
493
  static void HandleWeak(const LivenessBroker& info, const void* object) {
494
    const PointerType* weak = static_cast<const PointerType*>(object);
495
    if (!info.IsHeapObjectAlive(weak->GetFromGC())) {
496
      weak->ClearFromGC();
497
    }
498
  }
499
};
500
501
}  // namespace internal
502
}  // namespace cppgc
503
504
#endif  // INCLUDE_CPPGC_VISITOR_H_