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/v8-traced-handle.h
Line
Count
Source
1
// Copyright 2021 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_TRACED_HANDLE_H_
6
#define INCLUDE_V8_TRACED_HANDLE_H_
7
8
#include <stddef.h>
9
#include <stdint.h>
10
#include <stdio.h>
11
12
#include <atomic>
13
#include <memory>
14
#include <type_traits>
15
#include <utility>
16
17
#include "v8-internal.h"            // NOLINT(build/include_directory)
18
#include "v8-local-handle.h"        // NOLINT(build/include_directory)
19
#include "v8-weak-callback-info.h"  // NOLINT(build/include_directory)
20
#include "v8config.h"               // NOLINT(build/include_directory)
21
22
namespace v8 {
23
24
class Value;
25
26
namespace internal {
27
28
class BasicTracedReferenceExtractor;
29
30
enum class TracedReferenceStoreMode {
31
  kInitializingStore,
32
  kAssigningStore,
33
};
34
35
enum class TracedReferenceHandling {
36
  kDefault,  // See EmbedderRootsHandler::IsRoot().
37
  kDroppable
38
};
39
40
V8_EXPORT Address* GlobalizeTracedReference(
41
    Isolate* isolate, Address value, Address* slot,
42
    TracedReferenceStoreMode store_mode,
43
    TracedReferenceHandling reference_handling);
44
V8_EXPORT void MoveTracedReference(Address** from, Address** to);
45
V8_EXPORT void CopyTracedReference(const Address* const* from, Address** to);
46
V8_EXPORT void DisposeTracedReference(Address* global_handle);
47
48
}  // namespace internal
49
50
/**
51
 * An indirect handle, where the indirect pointer points to a GlobalHandles
52
 * node.
53
 */
54
class TracedReferenceBase : public api_internal::IndirectHandleBase {
55
 public:
56
  static_assert(sizeof(std::atomic<internal::Address*>) ==
57
                sizeof(internal::Address*));
58
59
  /**
60
   * If non-empty, destroy the underlying storage cell. |IsEmpty| will return
61
   * true after this call.
62
   */
63
  V8_INLINE void Reset();
64
65
  /**
66
   * Construct a Local<Data> from this handle.
67
   */
68
0
  V8_INLINE Local<Data> Get(Isolate* isolate) const {
69
0
    if (IsEmpty()) return Local<Data>();
70
0
    return Local<Data>::New(isolate, this->value<Data>());
71
0
  }
72
73
  /**
74
   * Returns true if this TracedReference is empty, i.e., has not been
75
   * assigned an object. This version of IsEmpty is thread-safe.
76
   */
77
0
  bool IsEmptyThreadSafe() const { return GetSlotThreadSafe() == nullptr; }
78
79
 protected:
80
0
  V8_INLINE TracedReferenceBase() = default;
81
82
  /**
83
   * Update this reference in a thread-safe way.
84
   */
85
0
  void SetSlotThreadSafe(internal::Address* new_val) {
86
0
    reinterpret_cast<std::atomic<internal::Address*>*>(&slot())->store(
87
0
        new_val, std::memory_order_relaxed);
88
0
  }
89
90
  /**
91
   * Get this reference in a thread-safe way
92
   */
93
0
  const internal::Address* GetSlotThreadSafe() const {
94
0
    return reinterpret_cast<const std::atomic<internal::Address*>*>(&slot())
95
0
        ->load(std::memory_order_relaxed);
96
0
  }
97
98
  V8_EXPORT void CheckValue() const;
99
100
  friend class internal::BasicTracedReferenceExtractor;
101
  template <typename F>
102
  friend class Local;
103
  template <typename U>
104
  friend bool operator==(const TracedReferenceBase&, const Local<U>&);
105
  friend bool operator==(const TracedReferenceBase&,
106
                         const TracedReferenceBase&);
107
};
108
109
/**
110
 * A traced handle with copy and move semantics. The handle is to be used
111
 * together as part of GarbageCollected objects (see v8-cppgc.h) or from stack
112
 * and specifies edges from C++ objects to JavaScript.
113
 *
114
 * The exact semantics are:
115
 * - Tracing garbage collections using CppHeap.
116
 * - Non-tracing garbage collections refer to
117
 *   |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
118
 * be treated as root or not.
119
 *
120
 * Note that the base class cannot be instantiated itself, use |TracedReference|
121
 * instead.
122
 */
123
template <typename T>
124
class BasicTracedReference : public TracedReferenceBase {
125
 public:
126
  /**
127
   * Construct a Local<T> from this handle.
128
   */
129
0
  Local<T> Get(Isolate* isolate) const { return Local<T>::New(isolate, *this); }
Unexecuted instantiation: v8::BasicTracedReference<v8::Object>::Get(v8::Isolate*) const
Unexecuted instantiation: v8::BasicTracedReference<v8::Context>::Get(v8::Isolate*) const
Unexecuted instantiation: v8::BasicTracedReference<v8::UnboundScript>::Get(v8::Isolate*) const
130
131
  template <class S>
132
  V8_INLINE BasicTracedReference<S>& As() const {
133
    return reinterpret_cast<BasicTracedReference<S>&>(
134
        const_cast<BasicTracedReference<T>&>(*this));
135
  }
136
137
 private:
138
  /**
139
   * An empty BasicTracedReference without storage cell.
140
   */
141
0
  BasicTracedReference() = default;
Unexecuted instantiation: v8::BasicTracedReference<v8::Object>::BasicTracedReference()
Unexecuted instantiation: v8::BasicTracedReference<v8::Context>::BasicTracedReference()
Unexecuted instantiation: v8::BasicTracedReference<v8::UnboundScript>::BasicTracedReference()
142
143
  V8_INLINE static internal::Address* NewFromNonEmptyValue(
144
      Isolate* isolate, T* that, internal::Address** slot,
145
      internal::TracedReferenceStoreMode store_mode,
146
      internal::TracedReferenceHandling reference_handling);
147
148
  template <typename F>
149
  friend class Local;
150
  friend class Object;
151
  template <typename F>
152
  friend class TracedReference;
153
  template <typename F>
154
  friend class BasicTracedReference;
155
  template <typename F>
156
  friend class ReturnValue;
157
};
158
159
/**
160
 * A traced handle without destructor that clears the handle. The embedder needs
161
 * to ensure that the handle is not accessed once the V8 object has been
162
 * reclaimed. For more details see BasicTracedReference.
163
 */
164
template <typename T>
165
class TracedReference : public BasicTracedReference<T> {
166
 public:
167
  struct IsDroppable {};
168
169
  using BasicTracedReference<T>::Reset;
170
171
  /**
172
   * An empty TracedReference without storage cell.
173
   */
174
0
  V8_INLINE TracedReference() = default;
Unexecuted instantiation: v8::TracedReference<v8::Object>::TracedReference()
Unexecuted instantiation: v8::TracedReference<v8::Context>::TracedReference()
Unexecuted instantiation: v8::TracedReference<v8::UnboundScript>::TracedReference()
175
176
  /**
177
   * Construct a TracedReference from a Local.
178
   *
179
   * When the Local is non-empty, a new storage cell is created
180
   * pointing to the same object.
181
   */
182
  template <class S>
183
0
  TracedReference(Isolate* isolate, Local<S> that) : BasicTracedReference<T>() {
184
0
    static_assert(std::is_base_of_v<T, S>, "type check");
185
0
    if (V8_UNLIKELY(that.IsEmpty())) {
186
0
      return;
187
0
    }
188
0
    this->slot() = this->NewFromNonEmptyValue(
189
0
        isolate, *that, &this->slot(),
190
0
        internal::TracedReferenceStoreMode::kInitializingStore,
191
0
        internal::TracedReferenceHandling::kDefault);
192
0
  }
193
194
  /**
195
   * Construct a droppable TracedReference from a Local. Droppable means that V8
196
   * is free to reclaim the pointee if it is unmodified and otherwise
197
   * unreachable
198
   *
199
   * When the Local is non-empty, a new storage cell is created
200
   * pointing to the same object.
201
   */
202
  template <class S>
203
  TracedReference(Isolate* isolate, Local<S> that, IsDroppable)
204
      : BasicTracedReference<T>() {
205
    static_assert(std::is_base_of_v<T, S>, "type check");
206
    if (V8_UNLIKELY(that.IsEmpty())) {
207
      return;
208
    }
209
    this->slot() = this->NewFromNonEmptyValue(
210
        isolate, *that, &this->slot(),
211
        internal::TracedReferenceStoreMode::kInitializingStore,
212
        internal::TracedReferenceHandling::kDroppable);
213
  }
214
215
  /**
216
   * Move constructor initializing TracedReference from an
217
   * existing one.
218
   */
219
  V8_INLINE TracedReference(TracedReference&& other) noexcept {
220
    // Forward to operator=.
221
    *this = std::move(other);
222
  }
223
224
  /**
225
   * Move constructor initializing TracedReference from an
226
   * existing one.
227
   */
228
  template <typename S>
229
  V8_INLINE TracedReference(TracedReference<S>&& other) noexcept {
230
    // Forward to operator=.
231
    *this = std::move(other);
232
  }
233
234
  /**
235
   * Copy constructor initializing TracedReference from an
236
   * existing one.
237
   */
238
  V8_INLINE TracedReference(const TracedReference& other) {
239
    // Forward to operator=;
240
    *this = other;
241
  }
242
243
  /**
244
   * Copy constructor initializing TracedReference from an
245
   * existing one.
246
   */
247
  template <typename S>
248
  V8_INLINE TracedReference(const TracedReference<S>& other) {
249
    // Forward to operator=;
250
    *this = other;
251
  }
252
253
  /**
254
   * Move assignment operator initializing TracedReference from an existing one.
255
   */
256
  V8_INLINE TracedReference& operator=(TracedReference&& rhs) noexcept;
257
258
  /**
259
   * Move assignment operator initializing TracedReference from an existing one.
260
   */
261
  template <class S>
262
  V8_INLINE TracedReference& operator=(TracedReference<S>&& rhs) noexcept;
263
264
  /**
265
   * Copy assignment operator initializing TracedReference from an existing one.
266
   */
267
  V8_INLINE TracedReference& operator=(const TracedReference& rhs);
268
269
  /**
270
   * Copy assignment operator initializing TracedReference from an existing one.
271
   */
272
  template <class S>
273
  V8_INLINE TracedReference& operator=(const TracedReference<S>& rhs);
274
275
  /**
276
   * Always resets the reference. Creates a new reference from `other` if it is
277
   * non-empty.
278
   */
279
  template <class S>
280
  V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
281
282
  /**
283
   * Always resets the reference. Creates a new reference from `other` if it is
284
   * non-empty. The new reference is droppable, see constructor.
285
   */
286
  template <class S>
287
  V8_INLINE void Reset(Isolate* isolate, const Local<S>& other, IsDroppable);
288
289
  template <class S>
290
  V8_INLINE TracedReference<S>& As() const {
291
    return reinterpret_cast<TracedReference<S>&>(
292
        const_cast<TracedReference<T>&>(*this));
293
  }
294
};
295
296
// --- Implementation ---
297
template <class T>
298
internal::Address* BasicTracedReference<T>::NewFromNonEmptyValue(
299
    Isolate* isolate, T* that, internal::Address** slot,
300
    internal::TracedReferenceStoreMode store_mode,
301
0
    internal::TracedReferenceHandling reference_handling) {
302
0
  return internal::GlobalizeTracedReference(
303
0
      reinterpret_cast<internal::Isolate*>(isolate),
304
0
      internal::ValueHelper::ValueAsAddress(that),
305
0
      reinterpret_cast<internal::Address*>(slot), store_mode,
306
0
      reference_handling);
307
0
}
Unexecuted instantiation: v8::BasicTracedReference<v8::Object>::NewFromNonEmptyValue(v8::Isolate*, v8::Object*, unsigned long**, v8::internal::TracedReferenceStoreMode, v8::internal::TracedReferenceHandling)
Unexecuted instantiation: v8::BasicTracedReference<v8::Context>::NewFromNonEmptyValue(v8::Isolate*, v8::Context*, unsigned long**, v8::internal::TracedReferenceStoreMode, v8::internal::TracedReferenceHandling)
Unexecuted instantiation: v8::BasicTracedReference<v8::UnboundScript>::NewFromNonEmptyValue(v8::Isolate*, v8::UnboundScript*, unsigned long**, v8::internal::TracedReferenceStoreMode, v8::internal::TracedReferenceHandling)
308
309
0
void TracedReferenceBase::Reset() {
310
0
  if (V8_UNLIKELY(IsEmpty())) {
311
0
    return;
312
0
  }
313
0
  internal::DisposeTracedReference(slot());
314
0
  SetSlotThreadSafe(nullptr);
315
0
}
316
317
V8_INLINE bool operator==(const TracedReferenceBase& lhs,
318
0
                          const TracedReferenceBase& rhs) {
319
0
  return internal::HandleHelper::EqualHandles(lhs, rhs);
320
0
}
321
322
template <typename U>
323
V8_INLINE bool operator==(const TracedReferenceBase& lhs,
324
                          const v8::Local<U>& rhs) {
325
  return internal::HandleHelper::EqualHandles(lhs, rhs);
326
}
327
328
template <typename U>
329
V8_INLINE bool operator==(const v8::Local<U>& lhs,
330
                          const TracedReferenceBase& rhs) {
331
  return rhs == lhs;
332
}
333
334
V8_INLINE bool operator!=(const TracedReferenceBase& lhs,
335
0
                          const TracedReferenceBase& rhs) {
336
0
  return !(lhs == rhs);
337
0
}
338
339
template <typename U>
340
V8_INLINE bool operator!=(const TracedReferenceBase& lhs,
341
                          const v8::Local<U>& rhs) {
342
  return !(lhs == rhs);
343
}
344
345
template <typename U>
346
V8_INLINE bool operator!=(const v8::Local<U>& lhs,
347
                          const TracedReferenceBase& rhs) {
348
  return !(rhs == lhs);
349
}
350
351
template <class T>
352
template <class S>
353
0
void TracedReference<T>::Reset(Isolate* isolate, const Local<S>& other) {
354
0
  static_assert(std::is_base_of_v<T, S>, "type check");
355
0
  this->Reset();
356
0
  if (V8_UNLIKELY(other.IsEmpty())) {
357
0
    return;
358
0
  }
359
0
  this->SetSlotThreadSafe(this->NewFromNonEmptyValue(
360
0
      isolate, *other, &this->slot(),
361
0
      internal::TracedReferenceStoreMode::kAssigningStore,
362
0
      internal::TracedReferenceHandling::kDefault));
363
0
}
Unexecuted instantiation: void v8::TracedReference<v8::Context>::Reset<v8::Context>(v8::Isolate*, v8::Local<v8::Context> const&)
Unexecuted instantiation: void v8::TracedReference<v8::UnboundScript>::Reset<v8::UnboundScript>(v8::Isolate*, v8::Local<v8::UnboundScript> const&)
364
365
template <class T>
366
template <class S>
367
void TracedReference<T>::Reset(Isolate* isolate, const Local<S>& other,
368
                               IsDroppable) {
369
  static_assert(std::is_base_of_v<T, S>, "type check");
370
  this->Reset();
371
  if (V8_UNLIKELY(other.IsEmpty())) {
372
    return;
373
  }
374
  this->SetSlotThreadSafe(this->NewFromNonEmptyValue(
375
      isolate, *other, &this->slot(),
376
      internal::TracedReferenceStoreMode::kAssigningStore,
377
      internal::TracedReferenceHandling::kDroppable));
378
}
379
380
template <class T>
381
template <class S>
382
TracedReference<T>& TracedReference<T>::operator=(
383
    TracedReference<S>&& rhs) noexcept {
384
  static_assert(std::is_base_of_v<T, S>, "type check");
385
  *this = std::move(rhs.template As<T>());
386
  return *this;
387
}
388
389
template <class T>
390
template <class S>
391
TracedReference<T>& TracedReference<T>::operator=(
392
    const TracedReference<S>& rhs) {
393
  static_assert(std::is_base_of_v<T, S>, "type check");
394
  *this = rhs.template As<T>();
395
  return *this;
396
}
397
398
template <class T>
399
TracedReference<T>& TracedReference<T>::operator=(
400
0
    TracedReference&& rhs) noexcept {
401
0
  if (this != &rhs) {
402
0
    internal::MoveTracedReference(&rhs.slot(), &this->slot());
403
0
  }
404
0
  return *this;
405
0
}
406
407
template <class T>
408
TracedReference<T>& TracedReference<T>::operator=(const TracedReference& rhs) {
409
  if (this != &rhs) {
410
    this->Reset();
411
    if (!rhs.IsEmpty()) {
412
      internal::CopyTracedReference(&rhs.slot(), &this->slot());
413
    }
414
  }
415
  return *this;
416
}
417
418
}  // namespace v8
419
420
#endif  // INCLUDE_V8_TRACED_HANDLE_H_