v8  9.4.146 (node 16.15.0)
V8 is Google's open source JavaScript engine
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 "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 
20 namespace cppgc {
21 
22 namespace internal {
23 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
24  typename CheckingPolicy>
26 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
27  typename CheckingPolicy>
28 class BasicPersistent;
29 class ConservativeTracingVisitor;
30 class VisitorBase;
31 class VisitorFactory;
32 } // namespace internal
33 
34 using 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  */
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();
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);
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.
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  */
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>
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_