// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ #define INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ #include #include #include #include "cppgc/internal/finalizer-trait.h" #include "cppgc/internal/logging.h" #include "cppgc/internal/name-trait.h" #include "cppgc/trace-trait.h" #include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { namespace internal { using GCInfoIndex = uint16_t; struct V8_EXPORT EnsureGCInfoIndexTrait final { // Acquires a new GC info object and updates `registered_index` with the index // that identifies that new info accordingly. template V8_INLINE static GCInfoIndex EnsureIndex( std::atomic& registered_index) { return EnsureGCInfoIndexTraitDispatch{}(registered_index); } private: template ::HasFinalizer(), bool = NameTrait::HasNonHiddenName()> struct EnsureGCInfoIndexTraitDispatch; static GCInfoIndex V8_PRESERVE_MOST EnsureGCInfoIndex(std::atomic&, TraceCallback, FinalizationCallback, NameCallback); static GCInfoIndex V8_PRESERVE_MOST EnsureGCInfoIndex( std::atomic&, TraceCallback, FinalizationCallback); static GCInfoIndex V8_PRESERVE_MOST EnsureGCInfoIndex(std::atomic&, TraceCallback, NameCallback); static GCInfoIndex V8_PRESERVE_MOST EnsureGCInfoIndex(std::atomic&, TraceCallback); }; #define DISPATCH(has_finalizer, has_non_hidden_name, function) \ template \ struct EnsureGCInfoIndexTrait::EnsureGCInfoIndexTraitDispatch< \ T, has_finalizer, has_non_hidden_name> { \ V8_INLINE GCInfoIndex \ operator()(std::atomic& registered_index) { \ return function; \ } \ }; // ------------------------------------------------------- // // DISPATCH(has_finalizer, has_non_hidden_name, function) // // ------------------------------------------------------- // DISPATCH(true, true, // EnsureGCInfoIndex(registered_index, // TraceTrait::Trace, // FinalizerTrait::kCallback, // NameTrait::GetName)) // DISPATCH(true, false, // EnsureGCInfoIndex(registered_index, // TraceTrait::Trace, // FinalizerTrait::kCallback)) // DISPATCH(false, true, // EnsureGCInfoIndex(registered_index, // TraceTrait::Trace, // NameTrait::GetName)) // DISPATCH(false, false, // EnsureGCInfoIndex(registered_index, // TraceTrait::Trace)) // #undef DISPATCH // Trait determines how the garbage collector treats objects wrt. to traversing, // finalization, and naming. template struct GCInfoTrait final { V8_INLINE static GCInfoIndex Index() { static_assert(sizeof(T), "T must be fully defined"); static std::atomic registered_index; // Uses zero initialization. GCInfoIndex index = registered_index.load(std::memory_order_acquire); if (V8_UNLIKELY(!index)) { index = EnsureGCInfoIndexTrait::EnsureIndex(registered_index); CPPGC_DCHECK(index != 0); CPPGC_DCHECK(index == registered_index.load(std::memory_order_acquire)); } return index; } static constexpr void CheckCallbacksAreDefined() { // No USE() macro available. (void)static_cast(TraceTrait::Trace); (void)static_cast(FinalizerTrait::kCallback); (void)static_cast(NameTrait::GetName); } }; // Fold types based on finalizer behavior. Note that finalizer characteristics // align with trace behavior, i.e., destructors are virtual when trace methods // are and vice versa. template struct GCInfoFolding final { static constexpr bool kHasVirtualDestructorAtBase = std::has_virtual_destructor::value; static constexpr bool kBothTypesAreTriviallyDestructible = std::is_trivially_destructible::value && std::is_trivially_destructible::value; static constexpr bool kHasCustomFinalizerDispatchAtBase = internal::HasFinalizeGarbageCollectedObject< ParentMostGarbageCollectedType>::value; #ifdef CPPGC_SUPPORTS_OBJECT_NAMES static constexpr bool kWantsDetailedObjectNames = true; #else // !CPPGC_SUPPORTS_OBJECT_NAMES static constexpr bool kWantsDetailedObjectNames = false; #endif // !CPPGC_SUPPORTS_OBJECT_NAMES // Always true. Forces the compiler to resolve callbacks which ensures that // both modes don't break without requiring compiling a separate // configuration. Only a single GCInfo (for `ResultType` below) will actually // be instantiated but existence (and well-formedness) of all callbacks is // checked. static constexpr bool WantToFold() { if constexpr ((kHasVirtualDestructorAtBase || kBothTypesAreTriviallyDestructible || kHasCustomFinalizerDispatchAtBase) && !kWantsDetailedObjectNames) { GCInfoTrait::CheckCallbacksAreDefined(); GCInfoTrait::CheckCallbacksAreDefined(); return true; } return false; } // Folding would regress name resolution when deriving names from C++ // class names as it would just folds a name to the base class name. using ResultType = std::conditional_t; }; } // namespace internal } // namespace cppgc #endif // INCLUDE_CPPGC_INTERNAL_GC_INFO_H_