Coverage Report

Created: 2025-07-04 09:33

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