/src/node/deps/v8/include/cppgc/visitor.h
Line | Count | Source |
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 | | |
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 | | * An ephemeron pair is used to conditionally retain an object. |
41 | | * The `value` will be kept alive only if the `key` is alive. |
42 | | */ |
43 | | template <typename K, typename V> |
44 | | struct EphemeronPair { |
45 | | CPPGC_DISALLOW_NEW(); |
46 | | |
47 | | EphemeronPair(K* k, V* v) : key(k), value(v) {} |
48 | | WeakMember<K> key; |
49 | | Member<V> value; |
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 | | */ |
75 | | class V8_EXPORT Visitor { |
76 | | public: |
77 | | class Key { |
78 | | private: |
79 | | Key() = default; |
80 | | friend class internal::VisitorFactory; |
81 | | }; |
82 | | |
83 | 0 | 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(); |
133 | | CPPGC_DCHECK(value != kSentinelPointer); |
134 | | TraceImpl(value); |
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, |
148 | | std::enable_if_t<!std::is_same_v< |
149 | | Member<T>, subtle::UncompressedMember<T>>>* = nullptr> |
150 | | void TraceMultiple(const Member<T>* start, size_t len) { |
151 | | static_assert(sizeof(T), "Pointee type must be fully defined."); |
152 | | static_assert(internal::IsGarbageCollectedOrMixinType<T>::value, |
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."); |
157 | | VisitMultipleCompressedMember(start, len, |
158 | | &TraceTrait<T>::GetTraceDescriptor); |
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 | 0 | void Trace(const T& object) { |
170 | 0 | static_assert(!IsGarbageCollectedOrMixinTypeV<T>); |
171 | | #if V8_ENABLE_CHECKS |
172 | | // This object is embedded in potentially multiple nested objects. The |
173 | | // outermost object must not be in construction as such objects are (a) not |
174 | | // processed immediately, and (b) only processed conservatively if not |
175 | | // otherwise possible. |
176 | | CheckObjectNotInConstruction(&object); |
177 | | #endif // V8_ENABLE_CHECKS |
178 | 0 | TraceTrait<T>::Trace(this, &object); |
179 | 0 | } Unexecuted instantiation: void cppgc::Visitor::Trace<v8::TracedReference<v8::Object> >(v8::TracedReference<v8::Object> const&) Unexecuted instantiation: void cppgc::Visitor::Trace<v8::TracedReference<v8::Context> >(v8::TracedReference<v8::Context> const&) Unexecuted instantiation: void cppgc::Visitor::Trace<v8::TracedReference<v8::UnboundScript> >(v8::TracedReference<v8::UnboundScript> const&) |
180 | | |
181 | | template <typename T> |
182 | | void TraceMultiple(const T* start, size_t len) { |
183 | | #if V8_ENABLE_CHECKS |
184 | | // This object is embedded in potentially multiple nested objects. The |
185 | | // outermost object must not be in construction as such objects are (a) not |
186 | | // processed immediately, and (b) only processed conservatively if not |
187 | | // otherwise possible. |
188 | | CheckObjectNotInConstruction(start); |
189 | | #endif // V8_ENABLE_CHECKS |
190 | | for (size_t i = 0; i < len; ++i) { |
191 | | const T* object = &start[i]; |
192 | | if constexpr (std::is_polymorphic_v<T>) { |
193 | | // The object's vtable may be uninitialized in which case the object is |
194 | | // not traced. |
195 | | if (*reinterpret_cast<const uintptr_t*>(object) == 0) continue; |
196 | | } |
197 | | TraceTrait<T>::Trace(this, object); |
198 | | } |
199 | | } |
200 | | |
201 | | /** |
202 | | * Registers a weak callback method on the object of type T. See |
203 | | * LivenessBroker for an usage example. |
204 | | * |
205 | | * \param object of type T specifying a weak callback method. |
206 | | */ |
207 | | template <typename T, void (T::*method)(const LivenessBroker&)> |
208 | | void RegisterWeakCallbackMethod(const T* object) { |
209 | | RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object); |
210 | | } |
211 | | |
212 | | /** |
213 | | * Trace method for EphemeronPair. |
214 | | * |
215 | | * \param ephemeron_pair EphemeronPair reference weakly retaining a key object |
216 | | * and strongly retaining a value object in case the key object is alive. |
217 | | */ |
218 | | template <typename K, typename V> |
219 | | void Trace(const EphemeronPair<K, V>& ephemeron_pair) { |
220 | | TraceEphemeron(ephemeron_pair.key, &ephemeron_pair.value); |
221 | | RegisterWeakCallbackMethod<EphemeronPair<K, V>, |
222 | | &EphemeronPair<K, V>::ClearValueIfKeyIsDead>( |
223 | | &ephemeron_pair); |
224 | | } |
225 | | |
226 | | /** |
227 | | * Trace method for a single ephemeron. Used for tracing a raw ephemeron in |
228 | | * which the `key` and `value` are kept separately. |
229 | | * |
230 | | * \param weak_member_key WeakMember reference weakly retaining a key object. |
231 | | * \param member_value Member reference with ephemeron semantics. |
232 | | */ |
233 | | template <typename KeyType, typename ValueType> |
234 | | void TraceEphemeron(const WeakMember<KeyType>& weak_member_key, |
235 | | const Member<ValueType>* member_value) { |
236 | | const KeyType* key = weak_member_key.GetAtomic(); |
237 | | if (!key) return; |
238 | | |
239 | | // `value` must always be non-null. |
240 | | CPPGC_DCHECK(member_value); |
241 | | const ValueType* value = member_value->GetAtomic(); |
242 | | if (!value) return; |
243 | | |
244 | | // KeyType and ValueType may refer to GarbageCollectedMixin. |
245 | | TraceDescriptor value_desc = |
246 | | TraceTrait<ValueType>::GetTraceDescriptor(value); |
247 | | CPPGC_DCHECK(value_desc.base_object_payload); |
248 | | const void* key_base_object_payload = |
249 | | TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload; |
250 | | CPPGC_DCHECK(key_base_object_payload); |
251 | | |
252 | | VisitEphemeron(key_base_object_payload, value, value_desc); |
253 | | } |
254 | | |
255 | | /** |
256 | | * Trace method for a single ephemeron. Used for tracing a raw ephemeron in |
257 | | * which the `key` and `value` are kept separately. Note that this overload |
258 | | * is for non-GarbageCollected `value`s that can be traced though. |
259 | | * |
260 | | * \param key `WeakMember` reference weakly retaining a key object. |
261 | | * \param value Reference weakly retaining a value object. Note that |
262 | | * `ValueType` here should not be `Member`. It is expected that |
263 | | * `TraceTrait<ValueType>::GetTraceDescriptor(value)` returns a |
264 | | * `TraceDescriptor` with a null base pointer but a valid trace method. |
265 | | */ |
266 | | template <typename KeyType, typename ValueType> |
267 | | void TraceEphemeron(const WeakMember<KeyType>& weak_member_key, |
268 | | const ValueType* value) { |
269 | | static_assert(!IsGarbageCollectedOrMixinTypeV<ValueType>, |
270 | | "garbage-collected types must use WeakMember and Member"); |
271 | | const KeyType* key = weak_member_key.GetAtomic(); |
272 | | if (!key) return; |
273 | | |
274 | | // `value` must always be non-null. |
275 | | CPPGC_DCHECK(value); |
276 | | TraceDescriptor value_desc = |
277 | | TraceTrait<ValueType>::GetTraceDescriptor(value); |
278 | | // `value_desc.base_object_payload` must be null as this override is only |
279 | | // taken for non-garbage-collected values. |
280 | | CPPGC_DCHECK(!value_desc.base_object_payload); |
281 | | |
282 | | // KeyType might be a GarbageCollectedMixin. |
283 | | const void* key_base_object_payload = |
284 | | TraceTrait<KeyType>::GetTraceDescriptor(key).base_object_payload; |
285 | | CPPGC_DCHECK(key_base_object_payload); |
286 | | |
287 | | VisitEphemeron(key_base_object_payload, value, value_desc); |
288 | | } |
289 | | |
290 | | /** |
291 | | * Trace method that strongifies a WeakMember. |
292 | | * |
293 | | * \param weak_member WeakMember reference retaining an object. |
294 | | */ |
295 | | template <typename T> |
296 | | void TraceStrongly(const WeakMember<T>& weak_member) { |
297 | | const T* value = weak_member.GetAtomic(); |
298 | | CPPGC_DCHECK(value != kSentinelPointer); |
299 | | TraceImpl(value); |
300 | | } |
301 | | |
302 | | /** |
303 | | * Trace method for retaining containers strongly. |
304 | | * |
305 | | * \param object reference to the container. |
306 | | */ |
307 | | template <typename T> |
308 | | void TraceStrongContainer(const T* object) { |
309 | | TraceImpl(object); |
310 | | } |
311 | | |
312 | | /** |
313 | | * Trace method for retaining containers weakly. Note that weak containers |
314 | | * should emit write barriers. |
315 | | * |
316 | | * \param object reference to the container. |
317 | | * \param callback to be invoked. |
318 | | * \param callback_data custom data that is passed to the callback. |
319 | | */ |
320 | | template <typename T> |
321 | | void TraceWeakContainer(const T* object, WeakCallback callback, |
322 | | const void* callback_data) { |
323 | | if (!object) return; |
324 | | VisitWeakContainer(object, TraceTrait<T>::GetTraceDescriptor(object), |
325 | | TraceTrait<T>::GetWeakTraceDescriptor(object), callback, |
326 | | callback_data); |
327 | | } |
328 | | |
329 | | /** |
330 | | * Registers a slot containing a reference to an object allocated on a |
331 | | * compactable space. Such references maybe be arbitrarily moved by the GC. |
332 | | * |
333 | | * \param slot location of reference to object that might be moved by the GC. |
334 | | * The slot must contain an uncompressed pointer. |
335 | | */ |
336 | | template <typename T> |
337 | | void RegisterMovableReference(const T** slot) { |
338 | | static_assert(internal::IsAllocatedOnCompactableSpace<T>::value, |
339 | | "Only references to objects allocated on compactable spaces " |
340 | | "should be registered as movable slots."); |
341 | | static_assert(!IsGarbageCollectedMixinTypeV<T>, |
342 | | "Mixin types do not support compaction."); |
343 | | HandleMovableReference(reinterpret_cast<const void**>(slot)); |
344 | | } |
345 | | |
346 | | /** |
347 | | * Registers a weak callback that is invoked during garbage collection. |
348 | | * |
349 | | * \param callback to be invoked. |
350 | | * \param data custom data that is passed to the callback. |
351 | | */ |
352 | 0 | virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {} |
353 | | |
354 | | /** |
355 | | * Defers tracing an object from a concurrent thread to the mutator thread. |
356 | | * Should be called by Trace methods of types that are not safe to trace |
357 | | * concurrently. |
358 | | * |
359 | | * \param parameter tells the trace callback which object was deferred. |
360 | | * \param callback to be invoked for tracing on the mutator thread. |
361 | | * \param deferred_size size of deferred object. |
362 | | * |
363 | | * \returns false if the object does not need to be deferred (i.e. currently |
364 | | * traced on the mutator thread) and true otherwise (i.e. currently traced on |
365 | | * a concurrent thread). |
366 | | */ |
367 | | virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent( |
368 | 0 | const void* parameter, TraceCallback callback, size_t deferred_size) { |
369 | 0 | // By default tracing is not deferred. |
370 | 0 | return false; |
371 | 0 | } |
372 | | |
373 | | /** |
374 | | * Checks whether the visitor is running concurrently to the mutator or not. |
375 | | */ |
376 | 0 | virtual bool IsConcurrent() const { return false; } |
377 | | |
378 | | protected: |
379 | 0 | virtual void Visit(const void* self, TraceDescriptor) {} |
380 | | virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback, |
381 | 0 | const void* weak_member) {} |
382 | | virtual void VisitEphemeron(const void* key, const void* value, |
383 | 0 | TraceDescriptor value_desc) {} |
384 | | virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, |
385 | | TraceDescriptor weak_desc, |
386 | 0 | WeakCallback callback, const void* data) {} |
387 | 0 | virtual void HandleMovableReference(const void**) {} |
388 | | |
389 | | virtual void VisitMultipleUncompressedMember( |
390 | | const void* start, size_t len, |
391 | 0 | TraceDescriptorCallback get_trace_descriptor) { |
392 | 0 | // Default implementation merely delegates to Visit(). |
393 | 0 | const char* it = static_cast<const char*>(start); |
394 | 0 | const char* end = it + len * internal::kSizeOfUncompressedMember; |
395 | 0 | for (; it < end; it += internal::kSizeOfUncompressedMember) { |
396 | 0 | const auto* current = reinterpret_cast<const internal::RawPointer*>(it); |
397 | 0 | const void* object = current->LoadAtomic(); |
398 | 0 | if (!object) continue; |
399 | 0 |
|
400 | 0 | Visit(object, get_trace_descriptor(object)); |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | | #if defined(CPPGC_POINTER_COMPRESSION) |
405 | | virtual void VisitMultipleCompressedMember( |
406 | | const void* start, size_t len, |
407 | | TraceDescriptorCallback get_trace_descriptor) { |
408 | | // Default implementation merely delegates to Visit(). |
409 | | const char* it = static_cast<const char*>(start); |
410 | | const char* end = it + len * internal::kSizeofCompressedMember; |
411 | | for (; it < end; it += internal::kSizeofCompressedMember) { |
412 | | const auto* current = |
413 | | reinterpret_cast<const internal::CompressedPointer*>(it); |
414 | | const void* object = current->LoadAtomic(); |
415 | | if (!object) continue; |
416 | | |
417 | | Visit(object, get_trace_descriptor(object)); |
418 | | } |
419 | | } |
420 | | #endif // defined(CPPGC_POINTER_COMPRESSION) |
421 | | |
422 | | private: |
423 | | template <typename T, void (T::*method)(const LivenessBroker&)> |
424 | | static void WeakCallbackMethodDelegate(const LivenessBroker& info, |
425 | | const void* self) { |
426 | | // Callback is registered through a potential const Trace method but needs |
427 | | // to be able to modify fields. See HandleWeak. |
428 | | (const_cast<T*>(static_cast<const T*>(self))->*method)(info); |
429 | | } |
430 | | |
431 | | template <typename PointerType> |
432 | | static void HandleWeak(const LivenessBroker& info, const void* object) { |
433 | | const PointerType* weak = static_cast<const PointerType*>(object); |
434 | | if (!info.IsHeapObjectAlive(weak->GetFromGC())) { |
435 | | weak->ClearFromGC(); |
436 | | } |
437 | | } |
438 | | |
439 | | template <typename T> |
440 | | void TraceImpl(const T* t) { |
441 | | static_assert(sizeof(T), "Pointee type must be fully defined."); |
442 | | static_assert(internal::IsGarbageCollectedOrMixinType<T>::value, |
443 | | "T must be GarbageCollected or GarbageCollectedMixin type"); |
444 | | if (!t) { |
445 | | return; |
446 | | } |
447 | | Visit(t, TraceTrait<T>::GetTraceDescriptor(t)); |
448 | | } |
449 | | |
450 | | #if V8_ENABLE_CHECKS |
451 | | void CheckObjectNotInConstruction(const void* address); |
452 | | #endif // V8_ENABLE_CHECKS |
453 | | |
454 | | template <typename T, typename WeaknessPolicy, typename LocationPolicy, |
455 | | typename CheckingPolicy> |
456 | | friend class internal::BasicCrossThreadPersistent; |
457 | | template <typename T, typename WeaknessPolicy, typename LocationPolicy, |
458 | | typename CheckingPolicy> |
459 | | friend class internal::BasicPersistent; |
460 | | friend class internal::ConservativeTracingVisitor; |
461 | | friend class internal::VisitorBase; |
462 | | }; |
463 | | |
464 | | template <typename K, typename V> |
465 | | void EphemeronPair<K, V>::Trace(Visitor* visitor) const { |
466 | | visitor->TraceEphemeron(key, value); |
467 | | } |
468 | | |
469 | | namespace internal { |
470 | | |
471 | | class V8_EXPORT RootVisitor { |
472 | | public: |
473 | 0 | explicit RootVisitor(Visitor::Key) {} |
474 | | |
475 | | virtual ~RootVisitor() = default; |
476 | | |
477 | | template <typename AnyStrongPersistentType, |
478 | | std::enable_if_t< |
479 | | AnyStrongPersistentType::IsStrongPersistent::value>* = nullptr> |
480 | | void Trace(const AnyStrongPersistentType& p) { |
481 | | using PointeeType = typename AnyStrongPersistentType::PointeeType; |
482 | | const void* object = Extract(p); |
483 | | if (!object) { |
484 | | return; |
485 | | } |
486 | | VisitRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object), |
487 | | p.Location()); |
488 | | } |
489 | | |
490 | | template <typename AnyWeakPersistentType, |
491 | | std::enable_if_t< |
492 | | !AnyWeakPersistentType::IsStrongPersistent::value>* = nullptr> |
493 | 0 | void Trace(const AnyWeakPersistentType& p) { |
494 | 0 | using PointeeType = typename AnyWeakPersistentType::PointeeType; |
495 | 0 | static_assert(!internal::IsAllocatedOnCompactableSpace<PointeeType>::value, |
496 | 0 | "Weak references to compactable objects are not allowed"); |
497 | 0 | const void* object = Extract(p); |
498 | 0 | if (!object) { |
499 | 0 | return; |
500 | 0 | } |
501 | 0 | VisitWeakRoot(object, TraceTrait<PointeeType>::GetTraceDescriptor(object), |
502 | 0 | &HandleWeak<AnyWeakPersistentType>, &p, p.Location()); |
503 | 0 | } |
504 | | |
505 | | protected: |
506 | 0 | virtual void VisitRoot(const void*, TraceDescriptor, SourceLocation) {} |
507 | | virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, |
508 | 0 | const void* weak_root, SourceLocation) {} |
509 | | |
510 | | private: |
511 | | template <typename AnyPersistentType> |
512 | 0 | static const void* Extract(AnyPersistentType& p) { |
513 | 0 | using PointeeType = typename AnyPersistentType::PointeeType; |
514 | 0 | static_assert(sizeof(PointeeType), |
515 | 0 | "Persistent's pointee type must be fully defined"); |
516 | 0 | static_assert(internal::IsGarbageCollectedOrMixinType<PointeeType>::value, |
517 | 0 | "Persistent's pointee type must be GarbageCollected or " |
518 | 0 | "GarbageCollectedMixin"); |
519 | 0 | return p.GetFromGC(); |
520 | 0 | } |
521 | | |
522 | | template <typename PointerType> |
523 | 0 | static void HandleWeak(const LivenessBroker& info, const void* object) { |
524 | 0 | const PointerType* weak = static_cast<const PointerType*>(object); |
525 | 0 | if (!info.IsHeapObjectAlive(weak->GetFromGC())) { |
526 | 0 | weak->ClearFromGC(); |
527 | 0 | } |
528 | 0 | } |
529 | | }; |
530 | | |
531 | | } // namespace internal |
532 | | } // namespace cppgc |
533 | | |
534 | | #endif // INCLUDE_CPPGC_VISITOR_H_ |