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>
EnsureIndexcppgc::internal::final27   V8_INLINE static void EnsureIndex(
28       std::atomic<GCInfoIndex>& registered_index) {
29     EnsureGCInfoIndexTraitDispatch<T>{}(registered_index);
30   }
31 
32  private:
33   template <typename T, bool = std::is_polymorphic<T>::value,
34             bool = FinalizerTrait<T>::HasFinalizer(),
35             bool = NameTrait<T>::HasNonHiddenName()>
36   struct EnsureGCInfoIndexTraitDispatch;
37 
38   static void V8_PRESERVE_MOST
39   EnsureGCInfoIndexPolymorphic(std::atomic<GCInfoIndex>&, TraceCallback,
40                                FinalizationCallback, NameCallback);
41   static void V8_PRESERVE_MOST EnsureGCInfoIndexPolymorphic(
42       std::atomic<GCInfoIndex>&, TraceCallback, FinalizationCallback);
43   static void V8_PRESERVE_MOST EnsureGCInfoIndexPolymorphic(
44       std::atomic<GCInfoIndex>&, TraceCallback, NameCallback);
45   static void V8_PRESERVE_MOST
46   EnsureGCInfoIndexPolymorphic(std::atomic<GCInfoIndex>&, TraceCallback);
47   static void V8_PRESERVE_MOST
48   EnsureGCInfoIndexNonPolymorphic(std::atomic<GCInfoIndex>&, TraceCallback,
49                                   FinalizationCallback, NameCallback);
50   static void V8_PRESERVE_MOST EnsureGCInfoIndexNonPolymorphic(
51       std::atomic<GCInfoIndex>&, TraceCallback, FinalizationCallback);
52   static void V8_PRESERVE_MOST EnsureGCInfoIndexNonPolymorphic(
53       std::atomic<GCInfoIndex>&, TraceCallback, NameCallback);
54   static void V8_PRESERVE_MOST
55   EnsureGCInfoIndexNonPolymorphic(std::atomic<GCInfoIndex>&, TraceCallback);
56 };
57 
58 #define DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function) \
59   template <typename T>                                                        \
60   struct EnsureGCInfoIndexTrait::EnsureGCInfoIndexTraitDispatch<               \
61       T, is_polymorphic, has_finalizer, has_non_hidden_name> {                 \
62     V8_INLINE void operator()(std::atomic<GCInfoIndex>& registered_index) {    \
63       function;                                                                \
64     }                                                                          \
65   };
66 
67 // --------------------------------------------------------------------- //
68 // DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function)
69 // --------------------------------------------------------------------- //
70 DISPATCH(true, true, true,                                               //
71          EnsureGCInfoIndexPolymorphic(registered_index,                  //
72                                       TraceTrait<T>::Trace,              //
73                                       FinalizerTrait<T>::kCallback,      //
74                                       NameTrait<T>::GetName))            //
75 DISPATCH(true, true, false,                                              //
76          EnsureGCInfoIndexPolymorphic(registered_index,                  //
77                                       TraceTrait<T>::Trace,              //
78                                       FinalizerTrait<T>::kCallback))     //
79 DISPATCH(true, false, true,                                              //
80          EnsureGCInfoIndexPolymorphic(registered_index,                  //
81                                       TraceTrait<T>::Trace,              //
82                                       NameTrait<T>::GetName))            //
83 DISPATCH(true, false, false,                                             //
84          EnsureGCInfoIndexPolymorphic(registered_index,                  //
85                                       TraceTrait<T>::Trace))             //
86 DISPATCH(false, true, true,                                              //
87          EnsureGCInfoIndexNonPolymorphic(registered_index,               //
88                                          TraceTrait<T>::Trace,           //
89                                          FinalizerTrait<T>::kCallback,   //
90                                          NameTrait<T>::GetName))         //
91 DISPATCH(false, true, false,                                             //
92          EnsureGCInfoIndexNonPolymorphic(registered_index,               //
93                                          TraceTrait<T>::Trace,           //
94                                          FinalizerTrait<T>::kCallback))  //
95 DISPATCH(false, false, true,                                             //
96          EnsureGCInfoIndexNonPolymorphic(registered_index,               //
97                                          TraceTrait<T>::Trace,           //
98                                          NameTrait<T>::GetName))         //
99 DISPATCH(false, false, false,                                            //
100          EnsureGCInfoIndexNonPolymorphic(registered_index,               //
101                                          TraceTrait<T>::Trace))          //
102 
103 #undef DISPATCH
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 {
110   static constexpr bool kHasVirtualDestructorAtBase =
111       std::has_virtual_destructor<ParentMostGarbageCollectedType>::value;
112   static constexpr bool kBothTypesAreTriviallyDestructible =
113       std::is_trivially_destructible<ParentMostGarbageCollectedType>::value &&
114       std::is_trivially_destructible<T>::value;
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   // Folding would regresses name resolution when deriving names from C++
125   // class names as it would just folds a name to the base class name.
126   using ResultType = std::conditional_t<(kHasVirtualDestructorAtBase ||
127                                          kBothTypesAreTriviallyDestructible ||
128                                          kHasCustomFinalizerDispatchAtBase) &&
129                                             !kWantsDetailedObjectNames,
130                                         ParentMostGarbageCollectedType, T>;
131 };
132 
133 // Trait determines how the garbage collector treats objects wrt. to traversing,
134 // finalization, and naming.
135 template <typename T>
136 struct GCInfoTrait final {
Indexcppgc::internal::final137   V8_INLINE static GCInfoIndex Index() {
138     static_assert(sizeof(T), "T must be fully defined");
139     static std::atomic<GCInfoIndex>
140         registered_index;  // Uses zero initialization.
141     GCInfoIndex index = registered_index.load(std::memory_order_acquire);
142     if (V8_UNLIKELY(!index)) {
143       EnsureGCInfoIndexTrait::EnsureIndex<T>(registered_index);
144       // Slow path call uses V8_PRESERVE_MOST which does not support return
145       // values (also preserves RAX). Avoid out parameter by just reloading the
146       // value here which at this point is guaranteed to be set.
147       index = registered_index.load(std::memory_order_acquire);
148       CPPGC_DCHECK(index != 0);
149     }
150     return index;
151   }
152 };
153 
154 }  // namespace internal
155 }  // namespace cppgc
156 
157 #endif  // INCLUDE_CPPGC_INTERNAL_GC_INFO_H_
158