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_V8_CPPGC_H_ 6 #define INCLUDE_V8_CPPGC_H_ 7 8 #include <cstdint> 9 #include <memory> 10 #include <vector> 11 12 #include "cppgc/common.h" 13 #include "cppgc/custom-space.h" 14 #include "cppgc/heap-statistics.h" 15 #include "cppgc/internal/write-barrier.h" 16 #include "cppgc/visitor.h" 17 #include "v8-internal.h" // NOLINT(build/include_directory) 18 #include "v8-platform.h" // NOLINT(build/include_directory) 19 #include "v8-traced-handle.h" // NOLINT(build/include_directory) 20 21 namespace cppgc { 22 class AllocationHandle; 23 class HeapHandle; 24 } // namespace cppgc 25 26 namespace v8 { 27 28 class Object; 29 30 namespace internal { 31 class CppHeap; 32 } // namespace internal 33 34 class CustomSpaceStatisticsReceiver; 35 36 /** 37 * Describes how V8 wrapper objects maintain references to garbage-collected C++ 38 * objects. 39 */ 40 struct WrapperDescriptor final { 41 /** 42 * The index used on `v8::Ojbect::SetAlignedPointerFromInternalField()` and 43 * related APIs to add additional data to an object which is used to identify 44 * JS->C++ references. 45 */ 46 using InternalFieldIndex = int; 47 48 /** 49 * Unknown embedder id. The value is reserved for internal usages and must not 50 * be used with `CppHeap`. 51 */ 52 static constexpr uint16_t kUnknownEmbedderId = UINT16_MAX; 53 WrapperDescriptorfinal54 constexpr WrapperDescriptor(InternalFieldIndex wrappable_type_index, 55 InternalFieldIndex wrappable_instance_index, 56 uint16_t embedder_id_for_garbage_collected) 57 : wrappable_type_index(wrappable_type_index), 58 wrappable_instance_index(wrappable_instance_index), 59 embedder_id_for_garbage_collected(embedder_id_for_garbage_collected) {} 60 61 /** 62 * Index of the wrappable type. 63 */ 64 InternalFieldIndex wrappable_type_index; 65 66 /** 67 * Index of the wrappable instance. 68 */ 69 InternalFieldIndex wrappable_instance_index; 70 71 /** 72 * Embedder id identifying instances of garbage-collected objects. It is 73 * expected that the first field of the wrappable type is a uint16_t holding 74 * the id. Only references to instances of wrappables types with an id of 75 * `embedder_id_for_garbage_collected` will be considered by CppHeap. 76 */ 77 uint16_t embedder_id_for_garbage_collected; 78 }; 79 80 struct V8_EXPORT CppHeapCreateParams { 81 CppHeapCreateParams(const CppHeapCreateParams&) = delete; 82 CppHeapCreateParams& operator=(const CppHeapCreateParams&) = delete; 83 84 std::vector<std::unique_ptr<cppgc::CustomSpaceBase>> custom_spaces; 85 WrapperDescriptor wrapper_descriptor; 86 }; 87 88 /** 89 * A heap for allocating managed C++ objects. 90 */ 91 class V8_EXPORT CppHeap { 92 public: 93 static std::unique_ptr<CppHeap> Create(v8::Platform* platform, 94 const CppHeapCreateParams& params); 95 96 virtual ~CppHeap() = default; 97 98 /** 99 * \returns the opaque handle for allocating objects using 100 * `MakeGarbageCollected()`. 101 */ 102 cppgc::AllocationHandle& GetAllocationHandle(); 103 104 /** 105 * \returns the opaque heap handle which may be used to refer to this heap in 106 * other APIs. Valid as long as the underlying `CppHeap` is alive. 107 */ 108 cppgc::HeapHandle& GetHeapHandle(); 109 110 /** 111 * Terminate clears all roots and performs multiple garbage collections to 112 * reclaim potentially newly created objects in destructors. 113 * 114 * After this call, object allocation is prohibited. 115 */ 116 void Terminate(); 117 118 /** 119 * \param detail_level specifies whether should return detailed 120 * statistics or only brief summary statistics. 121 * \returns current CppHeap statistics regarding memory consumption 122 * and utilization. 123 */ 124 cppgc::HeapStatistics CollectStatistics( 125 cppgc::HeapStatistics::DetailLevel detail_level); 126 127 /** 128 * Collects statistics for the given spaces and reports them to the receiver. 129 * 130 * \param custom_spaces a collection of custom space indicies. 131 * \param receiver an object that gets the results. 132 */ 133 void CollectCustomSpaceStatisticsAtLastGC( 134 std::vector<cppgc::CustomSpaceIndex> custom_spaces, 135 std::unique_ptr<CustomSpaceStatisticsReceiver> receiver); 136 137 /** 138 * Enables a detached mode that allows testing garbage collection using 139 * `cppgc::testing` APIs. Once used, the heap cannot be attached to an 140 * `Isolate` anymore. 141 */ 142 void EnableDetachedGarbageCollectionsForTesting(); 143 144 /** 145 * Performs a stop-the-world garbage collection for testing purposes. 146 * 147 * \param stack_state The stack state to assume for the garbage collection. 148 */ 149 void CollectGarbageForTesting(cppgc::EmbedderStackState stack_state); 150 151 private: 152 CppHeap() = default; 153 154 friend class internal::CppHeap; 155 }; 156 157 class JSVisitor : public cppgc::Visitor { 158 public: JSVisitor(cppgc::Visitor::Key key)159 explicit JSVisitor(cppgc::Visitor::Key key) : cppgc::Visitor(key) {} 160 Trace(const TracedReferenceBase & ref)161 void Trace(const TracedReferenceBase& ref) { 162 if (ref.IsEmptyThreadSafe()) return; 163 Visit(ref); 164 } 165 166 protected: 167 using cppgc::Visitor::Visit; 168 Visit(const TracedReferenceBase & ref)169 virtual void Visit(const TracedReferenceBase& ref) {} 170 }; 171 172 /** 173 * **DO NOT USE: Use the appropriate managed types.** 174 * 175 * Consistency helpers that aid in maintaining a consistent internal state of 176 * the garbage collector. 177 */ 178 class V8_EXPORT JSHeapConsistency final { 179 public: 180 using WriteBarrierParams = cppgc::internal::WriteBarrier::Params; 181 using WriteBarrierType = cppgc::internal::WriteBarrier::Type; 182 183 /** 184 * Gets the required write barrier type for a specific write. 185 * 186 * Note: Handling for C++ to JS references. 187 * 188 * \param ref The reference being written to. 189 * \param params Parameters that may be used for actual write barrier calls. 190 * Only filled if return value indicates that a write barrier is needed. The 191 * contents of the `params` are an implementation detail. 192 * \param callback Callback returning the corresponding heap handle. The 193 * callback is only invoked if the heap cannot otherwise be figured out. The 194 * callback must not allocate. 195 * \returns whether a write barrier is needed and which barrier to invoke. 196 */ 197 template <typename HeapHandleCallback> 198 static V8_INLINE WriteBarrierType GetWriteBarrierType(const TracedReferenceBase & ref,WriteBarrierParams & params,HeapHandleCallback callback)199 GetWriteBarrierType(const TracedReferenceBase& ref, 200 WriteBarrierParams& params, HeapHandleCallback callback) { 201 if (ref.IsEmpty()) return WriteBarrierType::kNone; 202 203 if (V8_LIKELY(!cppgc::internal::WriteBarrier:: 204 IsAnyIncrementalOrConcurrentMarking())) { 205 return cppgc::internal::WriteBarrier::Type::kNone; 206 } 207 cppgc::HeapHandle& handle = callback(); 208 if (!cppgc::subtle::HeapState::IsMarking(handle)) { 209 return cppgc::internal::WriteBarrier::Type::kNone; 210 } 211 params.heap = &handle; 212 #if V8_ENABLE_CHECKS 213 params.type = cppgc::internal::WriteBarrier::Type::kMarking; 214 #endif // !V8_ENABLE_CHECKS 215 return cppgc::internal::WriteBarrier::Type::kMarking; 216 } 217 218 /** 219 * Gets the required write barrier type for a specific write. 220 * 221 * Note: Handling for JS to C++ references. 222 * 223 * \param wrapper The wrapper that has been written into. 224 * \param wrapper_index The wrapper index in `wrapper` that has been written 225 * into. 226 * \param wrappable The value that was written. 227 * \param params Parameters that may be used for actual write barrier calls. 228 * Only filled if return value indicates that a write barrier is needed. The 229 * contents of the `params` are an implementation detail. 230 * \param callback Callback returning the corresponding heap handle. The 231 * callback is only invoked if the heap cannot otherwise be figured out. The 232 * callback must not allocate. 233 * \returns whether a write barrier is needed and which barrier to invoke. 234 */ 235 template <typename HeapHandleCallback> GetWriteBarrierType(v8::Local<v8::Object> & wrapper,int wrapper_index,const void * wrappable,WriteBarrierParams & params,HeapHandleCallback callback)236 static V8_INLINE WriteBarrierType GetWriteBarrierType( 237 v8::Local<v8::Object>& wrapper, int wrapper_index, const void* wrappable, 238 WriteBarrierParams& params, HeapHandleCallback callback) { 239 #if V8_ENABLE_CHECKS 240 CheckWrapper(wrapper, wrapper_index, wrappable); 241 #endif // V8_ENABLE_CHECKS 242 return cppgc::internal::WriteBarrier:: 243 GetWriteBarrierTypeForExternallyReferencedObject(wrappable, params, 244 callback); 245 } 246 247 /** 248 * Conservative Dijkstra-style write barrier that processes an object if it 249 * has not yet been processed. 250 * 251 * \param params The parameters retrieved from `GetWriteBarrierType()`. 252 * \param ref The reference being written to. 253 */ DijkstraMarkingBarrier(const WriteBarrierParams & params,cppgc::HeapHandle & heap_handle,const TracedReferenceBase & ref)254 static V8_INLINE void DijkstraMarkingBarrier(const WriteBarrierParams& params, 255 cppgc::HeapHandle& heap_handle, 256 const TracedReferenceBase& ref) { 257 cppgc::internal::WriteBarrier::CheckParams(WriteBarrierType::kMarking, 258 params); 259 DijkstraMarkingBarrierSlow(heap_handle, ref); 260 } 261 262 /** 263 * Conservative Dijkstra-style write barrier that processes an object if it 264 * has not yet been processed. 265 * 266 * \param params The parameters retrieved from `GetWriteBarrierType()`. 267 * \param object The pointer to the object. May be an interior pointer to a 268 * an interface of the actual object. 269 */ DijkstraMarkingBarrier(const WriteBarrierParams & params,cppgc::HeapHandle & heap_handle,const void * object)270 static V8_INLINE void DijkstraMarkingBarrier(const WriteBarrierParams& params, 271 cppgc::HeapHandle& heap_handle, 272 const void* object) { 273 cppgc::internal::WriteBarrier::DijkstraMarkingBarrier(params, object); 274 } 275 276 /** 277 * Generational barrier for maintaining consistency when running with multiple 278 * generations. 279 * 280 * \param params The parameters retrieved from `GetWriteBarrierType()`. 281 * \param ref The reference being written to. 282 */ GenerationalBarrier(const WriteBarrierParams & params,const TracedReferenceBase & ref)283 static V8_INLINE void GenerationalBarrier(const WriteBarrierParams& params, 284 const TracedReferenceBase& ref) {} 285 286 private: 287 JSHeapConsistency() = delete; 288 289 static void CheckWrapper(v8::Local<v8::Object>&, int, const void*); 290 291 static void DijkstraMarkingBarrierSlow(cppgc::HeapHandle&, 292 const TracedReferenceBase& ref); 293 }; 294 295 /** 296 * Provided as input to `CppHeap::CollectCustomSpaceStatisticsAtLastGC()`. 297 * 298 * Its method is invoked with the results of the statistic collection. 299 */ 300 class CustomSpaceStatisticsReceiver { 301 public: 302 virtual ~CustomSpaceStatisticsReceiver() = default; 303 /** 304 * Reports the size of a space at the last GC. It is called for each space 305 * that was requested in `CollectCustomSpaceStatisticsAtLastGC()`. 306 * 307 * \param space_index The index of the space. 308 * \param bytes The total size of live objects in the space at the last GC. 309 * It is zero if there was no GC yet. 310 */ 311 virtual void AllocatedBytes(cppgc::CustomSpaceIndex space_index, 312 size_t bytes) = 0; 313 }; 314 315 } // namespace v8 316 317 namespace cppgc { 318 319 template <typename T> 320 struct TraceTrait<v8::TracedReference<T>> { 321 static void Trace(Visitor* visitor, const v8::TracedReference<T>* self) { 322 static_cast<v8::JSVisitor*>(visitor)->Trace(*self); 323 } 324 }; 325 326 } // namespace cppgc 327 328 #endif // INCLUDE_V8_CPPGC_H_ 329