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