/src/node/deps/v8/include/cppgc/internal/gc-info.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_INTERNAL_GC_INFO_H_ |
6 | | #define INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ |
7 | | |
8 | | #include <atomic> |
9 | | #include <cstdint> |
10 | | #include <type_traits> |
11 | | |
12 | | #include "cppgc/internal/finalizer-trait.h" |
13 | | #include "cppgc/internal/logging.h" |
14 | | #include "cppgc/internal/name-trait.h" |
15 | | #include "cppgc/trace-trait.h" |
16 | | #include "v8config.h" // NOLINT(build/include_directory) |
17 | | |
18 | | namespace cppgc { |
19 | | namespace internal { |
20 | | |
21 | | using GCInfoIndex = uint16_t; |
22 | | |
23 | | struct V8_EXPORT EnsureGCInfoIndexTrait final { |
24 | | // Acquires a new GC info object and updates `registered_index` with the index |
25 | | // that identifies that new info accordingly. |
26 | | template <typename T> |
27 | | V8_INLINE static GCInfoIndex EnsureIndex( |
28 | 0 | std::atomic<GCInfoIndex>& registered_index) { |
29 | 0 | return EnsureGCInfoIndexTraitDispatch<T>{}(registered_index); |
30 | 0 | } |
31 | | |
32 | | private: |
33 | | template <typename T, bool = FinalizerTrait<T>::HasFinalizer(), |
34 | | bool = NameTrait<T>::HasNonHiddenName()> |
35 | | struct EnsureGCInfoIndexTraitDispatch; |
36 | | |
37 | | static GCInfoIndex V8_PRESERVE_MOST |
38 | | EnsureGCInfoIndex(std::atomic<GCInfoIndex>&, TraceCallback, |
39 | | FinalizationCallback, NameCallback); |
40 | | static GCInfoIndex V8_PRESERVE_MOST EnsureGCInfoIndex( |
41 | | std::atomic<GCInfoIndex>&, TraceCallback, FinalizationCallback); |
42 | | static GCInfoIndex V8_PRESERVE_MOST |
43 | | EnsureGCInfoIndex(std::atomic<GCInfoIndex>&, TraceCallback, NameCallback); |
44 | | static GCInfoIndex V8_PRESERVE_MOST |
45 | | EnsureGCInfoIndex(std::atomic<GCInfoIndex>&, TraceCallback); |
46 | | }; |
47 | | |
48 | | #define DISPATCH(has_finalizer, has_non_hidden_name, function) \ |
49 | | template <typename T> \ |
50 | | struct EnsureGCInfoIndexTrait::EnsureGCInfoIndexTraitDispatch< \ |
51 | | T, has_finalizer, has_non_hidden_name> { \ |
52 | | V8_INLINE GCInfoIndex \ |
53 | 0 | operator()(std::atomic<GCInfoIndex>& registered_index) { \ |
54 | 0 | return function; \ |
55 | 0 | } \ |
56 | | }; |
57 | | |
58 | | // ------------------------------------------------------- // |
59 | | // DISPATCH(has_finalizer, has_non_hidden_name, function) // |
60 | | // ------------------------------------------------------- // |
61 | | DISPATCH(true, true, // |
62 | | EnsureGCInfoIndex(registered_index, // |
63 | | TraceTrait<T>::Trace, // |
64 | | FinalizerTrait<T>::kCallback, // |
65 | | NameTrait<T>::GetName)) // |
66 | | DISPATCH(true, false, // |
67 | | EnsureGCInfoIndex(registered_index, // |
68 | | TraceTrait<T>::Trace, // |
69 | | FinalizerTrait<T>::kCallback)) // |
70 | | DISPATCH(false, true, // |
71 | | EnsureGCInfoIndex(registered_index, // |
72 | | TraceTrait<T>::Trace, // |
73 | | NameTrait<T>::GetName)) // |
74 | | DISPATCH(false, false, // |
75 | | EnsureGCInfoIndex(registered_index, // |
76 | | TraceTrait<T>::Trace)) // |
77 | | |
78 | | #undef DISPATCH |
79 | | |
80 | | // Trait determines how the garbage collector treats objects wrt. to traversing, |
81 | | // finalization, and naming. |
82 | | template <typename T> |
83 | | struct GCInfoTrait final { |
84 | 0 | V8_INLINE static GCInfoIndex Index() { |
85 | 0 | static_assert(sizeof(T), "T must be fully defined"); |
86 | 0 | static std::atomic<GCInfoIndex> |
87 | 0 | registered_index; // Uses zero initialization. |
88 | 0 | GCInfoIndex index = registered_index.load(std::memory_order_acquire); |
89 | 0 | if (V8_UNLIKELY(!index)) { |
90 | 0 | index = EnsureGCInfoIndexTrait::EnsureIndex<T>(registered_index); |
91 | 0 | CPPGC_DCHECK(index != 0); |
92 | 0 | CPPGC_DCHECK(index == registered_index.load(std::memory_order_acquire)); |
93 | 0 | } |
94 | 0 | return index; |
95 | 0 | } |
96 | | |
97 | 0 | static constexpr void CheckCallbacksAreDefined() { |
98 | 0 | // No USE() macro available. |
99 | 0 | (void)static_cast<TraceCallback>(TraceTrait<T>::Trace); |
100 | 0 | (void)static_cast<FinalizationCallback>(FinalizerTrait<T>::kCallback); |
101 | 0 | (void)static_cast<NameCallback>(NameTrait<T>::GetName); |
102 | 0 | } Unexecuted instantiation: cppgc::internal::GCInfoTrait<node::contextify::ContextifyContext>::CheckCallbacksAreDefined() Unexecuted instantiation: cppgc::internal::GCInfoTrait<v8::Object::Wrappable>::CheckCallbacksAreDefined() Unexecuted instantiation: cppgc::internal::GCInfoTrait<node::contextify::ContextifyScript>::CheckCallbacksAreDefined() |
103 | | }; |
104 | | |
105 | | // Fold types based on finalizer behavior. Note that finalizer characteristics |
106 | | // align with trace behavior, i.e., destructors are virtual when trace methods |
107 | | // are and vice versa. |
108 | | template <typename T, typename ParentMostGarbageCollectedType> |
109 | | struct GCInfoFolding final { |
110 | | static constexpr bool kHasVirtualDestructorAtBase = |
111 | | std::has_virtual_destructor_v<ParentMostGarbageCollectedType>; |
112 | | static constexpr bool kBothTypesAreTriviallyDestructible = |
113 | | std::is_trivially_destructible_v<ParentMostGarbageCollectedType> && |
114 | | std::is_trivially_destructible_v<T>; |
115 | | static constexpr bool kHasCustomFinalizerDispatchAtBase = |
116 | | internal::HasFinalizeGarbageCollectedObject< |
117 | | ParentMostGarbageCollectedType>::value; |
118 | | #ifdef CPPGC_SUPPORTS_OBJECT_NAMES |
119 | | static constexpr bool kWantsDetailedObjectNames = true; |
120 | | #else // !CPPGC_SUPPORTS_OBJECT_NAMES |
121 | | static constexpr bool kWantsDetailedObjectNames = false; |
122 | | #endif // !CPPGC_SUPPORTS_OBJECT_NAMES |
123 | | |
124 | | // Always true. Forces the compiler to resolve callbacks which ensures that |
125 | | // both modes don't break without requiring compiling a separate |
126 | | // configuration. Only a single GCInfo (for `ResultType` below) will actually |
127 | | // be instantiated but existence (and well-formedness) of all callbacks is |
128 | | // checked. |
129 | 0 | static constexpr bool WantToFold() { |
130 | 0 | if constexpr ((kHasVirtualDestructorAtBase || |
131 | 0 | kBothTypesAreTriviallyDestructible || |
132 | 0 | kHasCustomFinalizerDispatchAtBase) && |
133 | 0 | !kWantsDetailedObjectNames) { |
134 | 0 | GCInfoTrait<T>::CheckCallbacksAreDefined(); |
135 | 0 | GCInfoTrait<ParentMostGarbageCollectedType>::CheckCallbacksAreDefined(); |
136 | 0 | return true; |
137 | 0 | } |
138 | 0 | return false; |
139 | 0 | } Unexecuted instantiation: cppgc::internal::GCInfoFolding<node::contextify::ContextifyContext, v8::Object::Wrappable>::WantToFold() Unexecuted instantiation: cppgc::internal::GCInfoFolding<node::contextify::ContextifyScript, v8::Object::Wrappable>::WantToFold() |
140 | | |
141 | | // Folding would regress name resolution when deriving names from C++ |
142 | | // class names as it would just folds a name to the base class name. |
143 | | using ResultType = |
144 | | std::conditional_t<WantToFold(), ParentMostGarbageCollectedType, T>; |
145 | | }; |
146 | | |
147 | | } // namespace internal |
148 | | } // namespace cppgc |
149 | | |
150 | | #endif // INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ |