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>
25 class BasicCrossThreadPersistent;
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  */
53 class V8_EXPORT Visitor {
54  public:
55   class Key {
56    private:
57     Key() = default;
58     friend class internal::VisitorFactory;
59   };
60 
Visitor(Key)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>
Trace(const T* 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>
Trace(const Member<T>& member)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>
Trace(const WeakMember<T>& weak_member)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>
Trace(const T& object)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&)>
RegisterWeakCallbackMethod(const T* object)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>
Trace(const EphemeronPair<K, V>& ephemeron_pair)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>
TraceEphemeron(const WeakMember<KeyType>& weak_member_key, const Member<ValueType>* member_value)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>
TraceEphemeron(const WeakMember<KeyType>& weak_member_key, const ValueType* value)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>
TraceStrongly(const WeakMember<T>& weak_member)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>
TraceWeakContainer(const T* object, WeakCallback callback, const void* data)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>
RegisterMovableReference(const T** slot)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    */
RegisterWeakCallback(WeakCallback callback, const void* data)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    */
DeferTraceToMutatorThreadIfConcurrent( const void* parameter, TraceCallback callback, size_t deferred_size)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:
Visit(const void* self, TraceDescriptor)297   virtual void Visit(const void* self, TraceDescriptor) {}
VisitWeak(const void* self, TraceDescriptor, WeakCallback, const void* weak_member)298   virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback,
299                          const void* weak_member) {}
VisitRoot(const void*, TraceDescriptor, const SourceLocation&)300   virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {}
VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, const void* weak_root, const SourceLocation&)301   virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback,
302                              const void* weak_root, const SourceLocation&) {}
VisitEphemeron(const void* key, const void* value, TraceDescriptor value_desc)303   virtual void VisitEphemeron(const void* key, const void* value,
304                               TraceDescriptor value_desc) {}
VisitWeakContainer(const void* self, TraceDescriptor strong_desc, TraceDescriptor weak_desc, WeakCallback callback, const void* data)305   virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc,
306                                   TraceDescriptor weak_desc,
307                                   WeakCallback callback, const void* data) {}
HandleMovableReference(const void**)308   virtual void HandleMovableReference(const void**) {}
309 
310  private:
311   template <typename T, void (T::*method)(const LivenessBroker&)>
WeakCallbackMethodDelegate(const LivenessBroker& info, const void* self)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>
HandleWeak(const LivenessBroker& info, const void* object)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>
TraceRoot(const Persistent& p, const SourceLocation& loc)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>
TraceRoot(const WeakPersistent& p, const SourceLocation& loc)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