Coverage Report

Created: 2025-10-31 09:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/node/deps/v8/include/cppgc/allocation.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_ALLOCATION_H_
6
#define INCLUDE_CPPGC_ALLOCATION_H_
7
8
#include <atomic>
9
#include <cstddef>
10
#include <cstdint>
11
#include <new>
12
#include <type_traits>
13
#include <utility>
14
15
#include "cppgc/custom-space.h"
16
#include "cppgc/internal/api-constants.h"
17
#include "cppgc/internal/gc-info.h"
18
#include "cppgc/type-traits.h"
19
#include "v8config.h"  // NOLINT(build/include_directory)
20
21
#if defined(__has_attribute)
22
#if __has_attribute(assume_aligned)
23
#define CPPGC_DEFAULT_ALIGNED \
24
  __attribute__((assume_aligned(api_constants::kDefaultAlignment)))
25
#define CPPGC_DOUBLE_WORD_ALIGNED \
26
  __attribute__((assume_aligned(2 * api_constants::kDefaultAlignment)))
27
#endif  // __has_attribute(assume_aligned)
28
#endif  // defined(__has_attribute)
29
30
#if !defined(CPPGC_DEFAULT_ALIGNED)
31
#define CPPGC_DEFAULT_ALIGNED
32
#endif
33
34
#if !defined(CPPGC_DOUBLE_WORD_ALIGNED)
35
#define CPPGC_DOUBLE_WORD_ALIGNED
36
#endif
37
38
namespace cppgc {
39
40
/**
41
 * AllocationHandle is used to allocate garbage-collected objects.
42
 */
43
class AllocationHandle;
44
45
namespace internal {
46
47
using AlignVal = std::align_val_t;
48
49
class MakeGarbageCollectedTraitInternal {
50
 protected:
51
0
  static inline void MarkObjectAsFullyConstructed(const void* payload) {
52
    // See api_constants for an explanation of the constants.
53
0
    std::atomic<uint16_t>* atomic_mutable_bitfield =
54
0
        reinterpret_cast<std::atomic<uint16_t>*>(
55
0
            const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(
56
0
                reinterpret_cast<const uint8_t*>(payload) -
57
0
                api_constants::kFullyConstructedBitFieldOffsetFromPayload)));
58
    // It's safe to split use load+store here (instead of a read-modify-write
59
    // operation), since it's guaranteed that this 16-bit bitfield is only
60
    // modified by a single thread. This is cheaper in terms of code bloat (on
61
    // ARM) and performance.
62
0
    uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed);
63
0
    value |= api_constants::kFullyConstructedBitMask;
64
0
    atomic_mutable_bitfield->store(value, std::memory_order_release);
65
0
  }
66
67
  // Dispatch based on compile-time information.
68
  //
69
  // Default implementation is for a custom space with >`kDefaultAlignment` byte
70
  // alignment.
71
  template <typename GCInfoType, typename CustomSpace, size_t alignment>
72
  struct AllocationDispatcher final {
73
    static void* Invoke(AllocationHandle& handle, size_t size) {
74
      static_assert(std::is_base_of_v<CustomSpaceBase, CustomSpace>,
75
                    "Custom space must inherit from CustomSpaceBase.");
76
      static_assert(
77
          !CustomSpace::kSupportsCompaction,
78
          "Custom spaces that support compaction do not support allocating "
79
          "objects with non-default (i.e. word-sized) alignment.");
80
      return MakeGarbageCollectedTraitInternal::Allocate(
81
          handle, size, static_cast<AlignVal>(alignment),
82
          internal::GCInfoTrait<GCInfoType>::Index(), CustomSpace::kSpaceIndex);
83
    }
84
  };
85
86
  // Fast path for regular allocations for the default space with
87
  // `kDefaultAlignment` byte alignment.
88
  template <typename GCInfoType>
89
  struct AllocationDispatcher<GCInfoType, void,
90
                              api_constants::kDefaultAlignment>
91
      final {
92
0
    static void* Invoke(AllocationHandle& handle, size_t size) {
93
0
      return MakeGarbageCollectedTraitInternal::Allocate(
94
0
          handle, size, internal::GCInfoTrait<GCInfoType>::Index());
95
0
    }
96
  };
97
98
  // Default space with >`kDefaultAlignment` byte alignment.
99
  template <typename GCInfoType, size_t alignment>
100
  struct AllocationDispatcher<GCInfoType, void, alignment> final {
101
    static void* Invoke(AllocationHandle& handle, size_t size) {
102
      return MakeGarbageCollectedTraitInternal::Allocate(
103
          handle, size, static_cast<AlignVal>(alignment),
104
          internal::GCInfoTrait<GCInfoType>::Index());
105
    }
106
  };
107
108
  // Custom space with `kDefaultAlignment` byte alignment.
109
  template <typename GCInfoType, typename CustomSpace>
110
  struct AllocationDispatcher<GCInfoType, CustomSpace,
111
                              api_constants::kDefaultAlignment>
112
      final {
113
    static void* Invoke(AllocationHandle& handle, size_t size) {
114
      static_assert(std::is_base_of_v<CustomSpaceBase, CustomSpace>,
115
                    "Custom space must inherit from CustomSpaceBase.");
116
      return MakeGarbageCollectedTraitInternal::Allocate(
117
          handle, size, internal::GCInfoTrait<GCInfoType>::Index(),
118
          CustomSpace::kSpaceIndex);
119
    }
120
  };
121
122
 private:
123
  V8_EXPORT static void* CPPGC_DEFAULT_ALIGNED
124
  Allocate(cppgc::AllocationHandle&, size_t, GCInfoIndex);
125
  V8_EXPORT static void* CPPGC_DOUBLE_WORD_ALIGNED
126
  Allocate(cppgc::AllocationHandle&, size_t, AlignVal, GCInfoIndex);
127
  V8_EXPORT static void* CPPGC_DEFAULT_ALIGNED
128
  Allocate(cppgc::AllocationHandle&, size_t, GCInfoIndex, CustomSpaceIndex);
129
  V8_EXPORT static void* CPPGC_DOUBLE_WORD_ALIGNED
130
  Allocate(cppgc::AllocationHandle&, size_t, AlignVal, GCInfoIndex,
131
           CustomSpaceIndex);
132
133
  friend class HeapObjectHeader;
134
};
135
136
}  // namespace internal
137
138
/**
139
 * Base trait that provides utilities for advancers users that have custom
140
 * allocation needs (e.g., overriding size). It's expected that users override
141
 * MakeGarbageCollectedTrait (see below) and inherit from
142
 * MakeGarbageCollectedTraitBase and make use of the low-level primitives
143
 * offered to allocate and construct an object.
144
 */
145
template <typename T>
146
class MakeGarbageCollectedTraitBase
147
    : private internal::MakeGarbageCollectedTraitInternal {
148
 private:
149
  static_assert(internal::IsGarbageCollectedType<T>::value,
150
                "T needs to be a garbage collected object");
151
  static_assert(!IsGarbageCollectedWithMixinTypeV<T> ||
152
                    sizeof(T) <=
153
                        internal::api_constants::kLargeObjectSizeThreshold,
154
                "GarbageCollectedMixin may not be a large object");
155
156
 protected:
157
  /**
158
   * Allocates memory for an object of type T.
159
   *
160
   * \param handle AllocationHandle identifying the heap to allocate the object
161
   *   on.
162
   * \param size The size that should be reserved for the object.
163
   * \returns the memory to construct an object of type T on.
164
   */
165
0
  V8_INLINE static void* Allocate(AllocationHandle& handle, size_t size) {
166
0
    static_assert(
167
0
        std::is_base_of_v<typename T::ParentMostGarbageCollectedType, T>,
168
0
        "U of GarbageCollected<U> must be a base of T. Check "
169
0
        "GarbageCollected<T> base class inheritance.");
170
0
    static constexpr size_t kWantedAlignment =
171
0
        alignof(T) < internal::api_constants::kDefaultAlignment
172
0
            ? internal::api_constants::kDefaultAlignment
173
0
            : alignof(T);
174
0
    static_assert(
175
0
        kWantedAlignment <= internal::api_constants::kMaxSupportedAlignment,
176
0
        "Requested alignment larger than alignof(std::max_align_t) bytes. "
177
0
        "Please file a bug to possibly get this restriction lifted.");
178
0
    return AllocationDispatcher<
179
0
        typename internal::GCInfoFolding<
180
0
            T, typename T::ParentMostGarbageCollectedType>::ResultType,
181
0
        typename SpaceTrait<T>::Space, kWantedAlignment>::Invoke(handle, size);
182
0
  }
Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyContext>::Allocate(cppgc::AllocationHandle&, unsigned long)
Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyScript>::Allocate(cppgc::AllocationHandle&, unsigned long)
183
184
  /**
185
   * Marks an object as fully constructed, resulting in precise handling by the
186
   * garbage collector.
187
   *
188
   * \param payload The base pointer the object is allocated at.
189
   */
190
0
  V8_INLINE static void MarkObjectAsFullyConstructed(const void* payload) {
191
0
    internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed(
192
0
        payload);
193
0
  }
Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyContext>::MarkObjectAsFullyConstructed(void const*)
Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyScript>::MarkObjectAsFullyConstructed(void const*)
194
};
195
196
/**
197
 * Passed to MakeGarbageCollected to specify how many bytes should be appended
198
 * to the allocated object.
199
 *
200
 * Example:
201
 * \code
202
 * class InlinedArray final : public GarbageCollected<InlinedArray> {
203
 *  public:
204
 *   explicit InlinedArray(size_t bytes) : size(bytes), byte_array(this + 1) {}
205
 *   void Trace(Visitor*) const {}
206
207
 *   size_t size;
208
 *   char* byte_array;
209
 * };
210
 *
211
 * auto* inlined_array = MakeGarbageCollected<InlinedArray(
212
 *    GetAllocationHandle(), AdditionalBytes(4), 4);
213
 * for (size_t i = 0; i < 4; i++) {
214
 *   Process(inlined_array->byte_array[i]);
215
 * }
216
 * \endcode
217
 */
218
struct AdditionalBytes {
219
0
  constexpr explicit AdditionalBytes(size_t bytes) : value(bytes) {}
220
  const size_t value;
221
};
222
223
/**
224
 * Default trait class that specifies how to construct an object of type T.
225
 * Advanced users may override how an object is constructed using the utilities
226
 * that are provided through MakeGarbageCollectedTraitBase.
227
 *
228
 * Any trait overriding construction must
229
 * - allocate through `MakeGarbageCollectedTraitBase<T>::Allocate`;
230
 * - mark the object as fully constructed using
231
 *   `MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed`;
232
 */
233
template <typename T>
234
class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<T> {
235
 public:
236
  template <typename... Args>
237
0
  static T* Call(AllocationHandle& handle, Args&&... args) {
238
0
    void* memory =
239
0
        MakeGarbageCollectedTraitBase<T>::Allocate(handle, sizeof(T));
240
0
    T* object = ::new (memory) T(std::forward<Args>(args)...);
241
0
    MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object);
242
0
    return object;
243
0
  }
Unexecuted instantiation: node::contextify::ContextifyContext* cppgc::MakeGarbageCollectedTrait<node::contextify::ContextifyContext>::Call<node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&)
Unexecuted instantiation: node::contextify::ContextifyScript* cppgc::MakeGarbageCollectedTrait<node::contextify::ContextifyScript>::Call<node::Environment*&, v8::Local<v8::Object>&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&)
244
245
  template <typename... Args>
246
  static T* Call(AllocationHandle& handle, AdditionalBytes additional_bytes,
247
                 Args&&... args) {
248
    void* memory = MakeGarbageCollectedTraitBase<T>::Allocate(
249
        handle, sizeof(T) + additional_bytes.value);
250
    T* object = ::new (memory) T(std::forward<Args>(args)...);
251
    MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object);
252
    return object;
253
  }
254
};
255
256
/**
257
 * Allows users to specify a post-construction callback for specific types. The
258
 * callback is invoked on the instance of type T right after it has been
259
 * constructed. This can be useful when the callback requires a
260
 * fully-constructed object to be able to dispatch to virtual methods.
261
 */
262
template <typename T, typename = void>
263
struct PostConstructionCallbackTrait {
264
0
  static void Call(T*) {}
Unexecuted instantiation: cppgc::PostConstructionCallbackTrait<node::contextify::ContextifyContext, void>::Call(node::contextify::ContextifyContext*)
Unexecuted instantiation: cppgc::PostConstructionCallbackTrait<node::contextify::ContextifyScript, void>::Call(node::contextify::ContextifyScript*)
265
};
266
267
/**
268
 * Constructs a managed object of type T where T transitively inherits from
269
 * GarbageCollected.
270
 *
271
 * \param args List of arguments with which an instance of T will be
272
 *   constructed.
273
 * \returns an instance of type T.
274
 */
275
template <typename T, typename... Args>
276
0
V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) {
277
0
  T* object =
278
0
      MakeGarbageCollectedTrait<T>::Call(handle, std::forward<Args>(args)...);
279
0
  PostConstructionCallbackTrait<T>::Call(object);
280
0
  return object;
281
0
}
Unexecuted instantiation: node::contextify::ContextifyContext* cppgc::MakeGarbageCollected<node::contextify::ContextifyContext, node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&)
Unexecuted instantiation: node::contextify::ContextifyScript* cppgc::MakeGarbageCollected<node::contextify::ContextifyScript, node::Environment*&, v8::Local<v8::Object>&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&)
282
283
/**
284
 * Constructs a managed object of type T where T transitively inherits from
285
 * GarbageCollected. Created objects will have additional bytes appended to
286
 * it. Allocated memory would suffice for `sizeof(T) + additional_bytes`.
287
 *
288
 * \param additional_bytes Denotes how many bytes to append to T.
289
 * \param args List of arguments with which an instance of T will be
290
 *   constructed.
291
 * \returns an instance of type T.
292
 */
293
template <typename T, typename... Args>
294
V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle,
295
                                  AdditionalBytes additional_bytes,
296
                                  Args&&... args) {
297
  T* object = MakeGarbageCollectedTrait<T>::Call(handle, additional_bytes,
298
                                                 std::forward<Args>(args)...);
299
  PostConstructionCallbackTrait<T>::Call(object);
300
  return object;
301
}
302
303
}  // namespace cppgc
304
305
#undef CPPGC_DEFAULT_ALIGNED
306
#undef CPPGC_DOUBLE_WORD_ALIGNED
307
308
#endif  // INCLUDE_CPPGC_ALLOCATION_H_