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 Member.
67   *
68   * \param member Member reference retaining an object.
69   */
70  template <typename T>
71  void Trace(const Member<T>& member) {
72    const T* value = member.GetRawAtomic();
73    CPPGC_DCHECK(value != kSentinelPointer);
74    TraceImpl(value);
75  }
76
77  /**
78   * Trace method for WeakMember.
79   *
80   * \param weak_member WeakMember reference weakly retaining an object.
81   */
82  template <typename T>
83  void Trace(const WeakMember<T>& weak_member) {
84    static_assert(sizeof(T), "Pointee type must be fully defined.");
85    static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
86                  "T must be GarbageCollected or GarbageCollectedMixin type");
87    static_assert(!internal::IsAllocatedOnCompactableSpace<T>::value,
88                  "Weak references to compactable objects are not allowed");
89
90    const T* value = weak_member.GetRawAtomic();
91
92    // Bailout assumes that WeakMember emits write barrier.
93    if (!value) {
94      return;
95    }
96
97    CPPGC_DCHECK(value != kSentinelPointer);
98    VisitWeak(value, TraceTrait<T>::GetTraceDescriptor(value),
99              &HandleWeak<WeakMember<T>>, &weak_member);
100  }
101
102#if defined(CPPGC_POINTER_COMPRESSION)
103  /**
104   * Trace method for UncompressedMember.
105   *
106   * \param member UncompressedMember reference retaining an object.
107   */
108  template <typename T>
109  void Trace(const subtle::UncompressedMember<T>& member) {
110    const T* value = member.GetRawAtomic();
111    CPPGC_DCHECK(value != kSentinelPointer);
112    TraceImpl(value);
113  }
114#endif  // defined(CPPGC_POINTER_COMPRESSION)
115
116  /**
117   * Trace method for inlined objects that are not allocated themselves but
118   * otherwise follow managed heap layout and have a Trace() method.
119   *
120   * \param object reference of the inlined object.
121   */
122  template <typename T>
123  void Trace(const T& object) {
124#if V8_ENABLE_CHECKS
125    // This object is embedded in potentially multiple nested objects. The
126    // outermost object must not be in construction as such objects are (a) not
127    // processed immediately, and (b) only processed conservatively if not
128    // otherwise possible.
129    CheckObjectNotInConstruction(&object);
130#endif  // V8_ENABLE_CHECKS
131    TraceTrait<T>::Trace(this, &object);
132  }
133
134  /**
135   * Registers a weak callback method on the object of type T. See
136   * LivenessBroker for an usage example.
137   *
138   * \param object of type T specifying a weak callback method.
139   */
140  template <typename T, void (T::*method)(const LivenessBroker&)>
141  void RegisterWeakCallbackMethod(const T* object) {
142    RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object);
143  }
144
145  /**
146   * Trace method for EphemeronPair.
147   *
148   * \param ephemeron_pair EphemeronPair reference weakly retaining a key object
149   * and strongly retaining a value object in case the key object is alive.
150   */
151  template <typename K, typename V>
152  void Trace(const EphemeronPair<K, V>& ephemeron_pair) {
153    TraceEphemeron(ephemeron_pair.key, &ephemeron_pair.value);
154    RegisterWeakCallbackMethod<EphemeronPair<K, V>,
155                               &EphemeronPair<K, V>::ClearValueIfKeyIsDead>(
156        &ephemeron_pair);
157  }
158
159  /**
160   * Trace method for a single ephemeron. Used for tracing a raw ephemeron in
161   * which the `key` and `value` are kept separately.
162   *
163   * \param weak_member_key WeakMember reference weakly retaining a key object.
164   * \param member_value Member reference with ephemeron semantics.
165   */
166  template <typename KeyType, typename ValueType>
167  void TraceEphemeron(const WeakMember<KeyType>& weak_member_key,
168                      const Member<ValueType>* member_value) {
169    const KeyType* key = weak_member_key.GetRawAtomic();
170    if (!key) return;
171
172    // `value` must always be non-null.
173    CPPGC_DCHECK(member_value);
174    const ValueType* value = member_value->GetRawAtomic();
175    if (!value) return;
176
177    // KeyType and ValueType may refer to GarbageCollectedMixin.
178    TraceDescriptor value_desc =
179        TraceTrait<ValueType>::GetTraceDescriptor(value);
180    CPPGC_DCHECK(value_desc.base_object_payload);
181    const void* key_base_object_payload =
182        TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload;
183    CPPGC_DCHECK(key_base_object_payload);
184
185    VisitEphemeron(key_base_object_payload, value, value_desc);
186  }
187
188  /**
189   * Trace method for a single ephemeron. Used for tracing a raw ephemeron in
190   * which the `key` and `value` are kept separately. Note that this overload
191   * is for non-GarbageCollected `value`s that can be traced though.
192   *
193   * \param key `WeakMember` reference weakly retaining a key object.
194   * \param value Reference weakly retaining a value object. Note that
195   *   `ValueType` here should not be `Member`. It is expected that
196   *   `TraceTrait<ValueType>::GetTraceDescriptor(value)` returns a
197   *   `TraceDescriptor` with a null base pointer but a valid trace method.
198   */
199  template <typename KeyType, typename ValueType>
200  void TraceEphemeron(const WeakMember<KeyType>& weak_member_key,
201                      const ValueType* value) {
202    static_assert(!IsGarbageCollectedOrMixinTypeV<ValueType>,
203                  "garbage-collected types must use WeakMember and Member");
204    const KeyType* key = weak_member_key.GetRawAtomic();
205    if (!key) return;
206
207    // `value` must always be non-null.
208    CPPGC_DCHECK(value);
209    TraceDescriptor value_desc =
210        TraceTrait<ValueType>::GetTraceDescriptor(value);
211    // `value_desc.base_object_payload` must be null as this override is only
212    // taken for non-garbage-collected values.
213    CPPGC_DCHECK(!value_desc.base_object_payload);
214
215    // KeyType might be a GarbageCollectedMixin.
216    const void* key_base_object_payload =
217        TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload;
218    CPPGC_DCHECK(key_base_object_payload);
219
220    VisitEphemeron(key_base_object_payload, value, value_desc);
221  }
222
223  /**
224   * Trace method that strongifies a WeakMember.
225   *
226   * \param weak_member WeakMember reference retaining an object.
227   */
228  template <typename T>
229  void TraceStrongly(const WeakMember<T>& weak_member) {
230    const T* value = weak_member.GetRawAtomic();
231    CPPGC_DCHECK(value != kSentinelPointer);
232    TraceImpl(value);
233  }
234
235  /**
236   * Trace method for retaining containers strongly.
237   *
238   * \param object reference to the container.
239   */
240  template <typename T>
241  void TraceStrongContainer(const T* object) {
242    TraceImpl(object);
243  }
244
245  /**
246   * Trace method for retaining containers weakly. Note that weak containers
247   * should emit write barriers.
248   *
249   * \param object reference to the container.
250   * \param callback to be invoked.
251   * \param callback_data custom data that is passed to the callback.
252   */
253  template <typename T>
254  void TraceWeakContainer(const T* object, WeakCallback callback,
255                          const void* callback_data) {
256    if (!object) return;
257    VisitWeakContainer(object, TraceTrait<T>::GetTraceDescriptor(object),
258                       TraceTrait<T>::GetWeakTraceDescriptor(object), callback,
259                       callback_data);
260  }
261
262  /**
263   * Registers a slot containing a reference to an object allocated on a
264   * compactable space. Such references maybe be arbitrarily moved by the GC.
265   *
266   * \param slot location of reference to object that might be moved by the GC.
267   * The slot must contain an uncompressed pointer.
268   */
269  template <typename T>
270  void RegisterMovableReference(const T** slot) {
271    static_assert(internal::IsAllocatedOnCompactableSpace<T>::value,
272                  "Only references to objects allocated on compactable spaces "
273                  "should be registered as movable slots.");
274    static_assert(!IsGarbageCollectedMixinTypeV<T>,
275                  "Mixin types do not support compaction.");
276    HandleMovableReference(reinterpret_cast<const void**>(slot));
277  }
278
279  /**
280   * Registers a weak callback that is invoked during garbage collection.
281   *
282   * \param callback to be invoked.
283   * \param data custom data that is passed to the callback.
284   */
285  virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {}
286
287  /**
288   * Defers tracing an object from a concurrent thread to the mutator thread.
289   * Should be called by Trace methods of types that are not safe to trace
290   * concurrently.
291   *
292   * \param parameter tells the trace callback which object was deferred.
293   * \param callback to be invoked for tracing on the mutator thread.
294   * \param deferred_size size of deferred object.
295   *
296   * \returns false if the object does not need to be deferred (i.e. currently
297   * traced on the mutator thread) and true otherwise (i.e. currently traced on
298   * a concurrent thread).
299   */
300  virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent(
301      const void* parameter, TraceCallback callback, size_t deferred_size) {
302    // By default tracing is not deferred.
303    return false;
304  }
305
306 protected:
307  virtual void Visit(const void* self, TraceDescriptor) {}
308  virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback,
309                         const void* weak_member) {}
310  virtual void VisitEphemeron(const void* key, const void* value,
311                              TraceDescriptor value_desc) {}
312  virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc,
313                                  TraceDescriptor weak_desc,
314                                  WeakCallback callback, const void* data) {}
315  virtual void HandleMovableReference(const void**) {}
316
317 private:
318  template <typename T, void (T::*method)(const LivenessBroker&)>
319  static void WeakCallbackMethodDelegate(const LivenessBroker& info,
320                                         const void* self) {
321    // Callback is registered through a potential const Trace method but needs
322    // to be able to modify fields. See HandleWeak.
323    (const_cast<T*>(static_cast<const T*>(self))->*method)(info);
324  }
325
326  template <typename PointerType>
327  static void HandleWeak(const LivenessBroker& info, const void* object) {
328    const PointerType* weak = static_cast<const PointerType*>(object);
329    auto* raw_ptr = weak->GetFromGC();
330    if (!info.IsHeapObjectAlive(raw_ptr)) {
331      weak->ClearFromGC();
332    }
333  }
334
335  template <typename T>
336  void TraceImpl(const T* t) {
337    static_assert(sizeof(T), "Pointee type must be fully defined.");
338    static_assert(internal::IsGarbageCollectedOrMixinType<T>::value,
339                  "T must be GarbageCollected or GarbageCollectedMixin type");
340    if (!t) {
341      return;
342    }
343    Visit(t, TraceTrait<T>::GetTraceDescriptor(t));
344  }
345
346#if V8_ENABLE_CHECKS
347  void CheckObjectNotInConstruction(const void* address);
348#endif  // V8_ENABLE_CHECKS
349
350  template <typename T, typename WeaknessPolicy, typename LocationPolicy,
351            typename CheckingPolicy>
352  friend class internal::BasicCrossThreadPersistent;
353  template <typename T, typename WeaknessPolicy, typename LocationPolicy,
354            typename CheckingPolicy>
355  friend class internal::BasicPersistent;
356  friend class internal::ConservativeTracingVisitor;
357  friend class internal::VisitorBase;
358};
359
360namespace internal {
361
362class V8_EXPORT RootVisitor {
363 public:
364  explicit RootVisitor(Visitor::Key) {}
365
366  virtual ~RootVisitor() = default;
367
368  template <typename AnyStrongPersistentType,
369            std::enable_if_t<
370                AnyStrongPersistentType::IsStrongPersistent::value>* = nullptr>
371  void Trace(const AnyStrongPersistentType& p) {
372    using PointeeType = typename AnyStrongPersistentType::PointeeType;
373    const void* object = Extract(p);
374    if (!object) {
375      return;
376    }
377    VisitRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object),
378              p.Location());
379  }
380
381  template <typename AnyWeakPersistentType,
382            std::enable_if_t<
383                !AnyWeakPersistentType::IsStrongPersistent::value>* = nullptr>
384  void Trace(const AnyWeakPersistentType& p) {
385    using PointeeType = typename AnyWeakPersistentType::PointeeType;
386    static_assert(!internal::IsAllocatedOnCompactableSpace<PointeeType>::value,
387                  "Weak references to compactable objects are not allowed");
388    const void* object = Extract(p);
389    if (!object) {
390      return;
391    }
392    VisitWeakRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object),
393                  &HandleWeak<AnyWeakPersistentType>, &p, p.Location());
394  }
395
396 protected:
397  virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {}
398  virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback,
399                             const void* weak_root, const SourceLocation&) {}
400
401 private:
402  template <typename AnyPersistentType>
403  static const void* Extract(AnyPersistentType& p) {
404    using PointeeType = typename AnyPersistentType::PointeeType;
405    static_assert(sizeof(PointeeType),
406                  "Persistent's pointee type must be fully defined");
407    static_assert(internal::IsGarbageCollectedOrMixinType<PointeeType>::value,
408                  "Persistent's pointee type must be GarbageCollected or "
409                  "GarbageCollectedMixin");
410    return p.GetFromGC();
411  }
412
413  template <typename PointerType>
414  static void HandleWeak(const LivenessBroker& info, const void* object) {
415    const PointerType* weak = static_cast<const PointerType*>(object);
416    auto* raw_ptr = weak->GetFromGC();
417    if (!info.IsHeapObjectAlive(raw_ptr)) {
418      weak->ClearFromGC();
419    }
420  }
421};
422
423}  // namespace internal
424}  // namespace cppgc
425
426#endif  // INCLUDE_CPPGC_VISITOR_H_
427