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_NAME_TRAIT_H_
6 #define INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_
7 
8 #include <cstddef>
9 #include <cstdint>
10 #include <type_traits>
11 
12 #include "cppgc/name-provider.h"
13 #include "v8config.h"  // NOLINT(build/include_directory)
14 
15 namespace cppgc {
16 namespace internal {
17 
18 #if CPPGC_SUPPORTS_OBJECT_NAMES && defined(__clang__)
19 #define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 1
20 
21 // Provides constexpr c-string storage for a name of fixed |Size| characters.
22 // Automatically appends terminating 0 byte.
23 template <size_t Size>
24 struct NameBuffer {
25   char name[Size + 1]{};
26 
FromCStringcppgc::internal::NameBuffer27   static constexpr NameBuffer FromCString(const char* str) {
28     NameBuffer result;
29     for (size_t i = 0; i < Size; ++i) result.name[i] = str[i];
30     result.name[Size] = 0;
31     return result;
32   }
33 };
34 
35 template <typename T>
GetTypename()36 const char* GetTypename() {
37   static constexpr char kSelfPrefix[] =
38       "const char *cppgc::internal::GetTypename() [T =";
39   static_assert(__builtin_strncmp(__PRETTY_FUNCTION__, kSelfPrefix,
40                                   sizeof(kSelfPrefix) - 1) == 0,
41                 "The prefix must match");
42   static constexpr const char* kTypenameStart =
43       __PRETTY_FUNCTION__ + sizeof(kSelfPrefix);
44   static constexpr size_t kTypenameSize =
45       __builtin_strlen(__PRETTY_FUNCTION__) - sizeof(kSelfPrefix) - 1;
46   // NameBuffer is an indirection that is needed to make sure that only a
47   // substring of __PRETTY_FUNCTION__ gets materialized in the binary.
48   static constexpr auto buffer =
49       NameBuffer<kTypenameSize>::FromCString(kTypenameStart);
50   return buffer.name;
51 }
52 
53 #else
54 #define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 0
55 #endif
56 
57 struct HeapObjectName {
58   const char* value;
59   bool name_was_hidden;
60 };
61 
62 enum class HeapObjectNameForUnnamedObject : uint8_t {
63   kUseClassNameIfSupported,
64   kUseHiddenName,
65 };
66 
67 class V8_EXPORT NameTraitBase {
68  protected:
69   static HeapObjectName GetNameFromTypeSignature(const char*);
70 };
71 
72 // Trait that specifies how the garbage collector retrieves the name for a
73 // given object.
74 template <typename T>
75 class NameTrait final : public NameTraitBase {
76  public:
HasNonHiddenName()77   static constexpr bool HasNonHiddenName() {
78 #if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME
79     return true;
80 #elif CPPGC_SUPPORTS_OBJECT_NAMES
81     return true;
82 #else   // !CPPGC_SUPPORTS_OBJECT_NAMES
83     return std::is_base_of<NameProvider, T>::value;
84 #endif  // !CPPGC_SUPPORTS_OBJECT_NAMES
85   }
86 
GetName( const void* obj, HeapObjectNameForUnnamedObject name_retrieval_mode)87   static HeapObjectName GetName(
88       const void* obj, HeapObjectNameForUnnamedObject name_retrieval_mode) {
89     return GetNameFor(static_cast<const T*>(obj), name_retrieval_mode);
90   }
91 
92  private:
GetNameFor(const NameProvider* name_provider, HeapObjectNameForUnnamedObject)93   static HeapObjectName GetNameFor(const NameProvider* name_provider,
94                                    HeapObjectNameForUnnamedObject) {
95     // Objects inheriting from `NameProvider` are not considered unnamed as
96     // users already provided a name for them.
97     return {name_provider->GetHumanReadableName(), false};
98   }
99 
GetNameFor( const void*, HeapObjectNameForUnnamedObject name_retrieval_mode)100   static HeapObjectName GetNameFor(
101       const void*, HeapObjectNameForUnnamedObject name_retrieval_mode) {
102     if (name_retrieval_mode == HeapObjectNameForUnnamedObject::kUseHiddenName)
103       return {NameProvider::kHiddenName, true};
104 
105 #if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME
106     return {GetTypename<T>(), false};
107 #elif CPPGC_SUPPORTS_OBJECT_NAMES
108 
109 #if defined(V8_CC_GNU)
110 #define PRETTY_FUNCTION_VALUE __PRETTY_FUNCTION__
111 #elif defined(V8_CC_MSVC)
112 #define PRETTY_FUNCTION_VALUE __FUNCSIG__
113 #else
114 #define PRETTY_FUNCTION_VALUE nullptr
115 #endif
116 
117     static const HeapObjectName leaky_name =
118         GetNameFromTypeSignature(PRETTY_FUNCTION_VALUE);
119     return leaky_name;
120 
121 #undef PRETTY_FUNCTION_VALUE
122 
123 #else   // !CPPGC_SUPPORTS_OBJECT_NAMES
124     return {NameProvider::kHiddenName, true};
125 #endif  // !CPPGC_SUPPORTS_OBJECT_NAMES
126   }
127 };
128 
129 using NameCallback = HeapObjectName (*)(const void*,
130                                         HeapObjectNameForUnnamedObject);
131 
132 }  // namespace internal
133 }  // namespace cppgc
134 
135 #undef CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME
136 
137 #endif  // INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_
138