v8  10.1.124 (node 18.2.0)
V8 is Google's open source JavaScript engine
v8-traced-handle.h
Go to the documentation of this file.
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 
31 
35 };
36 
38  internal::Isolate* isolate, internal::Address* handle,
39  internal::Address* slot, GlobalHandleDestructionMode destruction_mode,
40  GlobalHandleStoreMode store_mode);
42  internal::Address** to);
44  internal::Address** to);
47  internal::Address* location, void* parameter,
48  WeakCallbackInfo<void>::Callback callback);
49 
50 } // namespace internal
51 
52 /**
53  * Deprecated. Use |TracedReference<T>| instead.
54  */
55 template <typename T>
57 
59  public:
60  /**
61  * Returns true if the reference is empty, i.e., has not been assigned
62  * object.
63  */
64  bool IsEmpty() const { return val_ == nullptr; }
65 
66  /**
67  * If non-empty, destroy the underlying storage cell. |IsEmpty| will return
68  * true after this call.
69  */
70  V8_INLINE void Reset();
71 
72  /**
73  * Construct a Local<Value> from this handle.
74  */
75  V8_INLINE v8::Local<v8::Value> Get(v8::Isolate* isolate) const {
76  if (IsEmpty()) return Local<Value>();
77  return Local<Value>::New(isolate, reinterpret_cast<Value*>(val_));
78  }
79 
80  /**
81  * Returns true if this TracedReference is empty, i.e., has not been
82  * assigned an object. This version of IsEmpty is thread-safe.
83  */
84  bool IsEmptyThreadSafe() const {
85  return this->GetSlotThreadSafe() == nullptr;
86  }
87 
88  /**
89  * Assigns a wrapper class ID to the handle.
90  */
91  V8_INLINE void SetWrapperClassId(uint16_t class_id);
92 
93  /**
94  * Returns the class ID previously assigned to this handle or 0 if no class ID
95  * was previously assigned.
96  */
97  V8_INLINE uint16_t WrapperClassId() const;
98 
99  protected:
100  /**
101  * Update this reference in a thread-safe way.
102  */
103  void SetSlotThreadSafe(void* new_val) {
104  reinterpret_cast<std::atomic<void*>*>(&val_)->store(
105  new_val, std::memory_order_relaxed);
106  }
107 
108  /**
109  * Get this reference in a thread-safe way
110  */
111  const void* GetSlotThreadSafe() const {
112  return reinterpret_cast<std::atomic<const void*> const*>(&val_)->load(
113  std::memory_order_relaxed);
114  }
115 
116  V8_EXPORT void CheckValue() const;
117 
118  // val_ points to a GlobalHandles node.
119  internal::Address* val_ = nullptr;
120 
121  friend class internal::BasicTracedReferenceExtractor;
122  template <typename F>
123  friend class Local;
124  template <typename U>
125  friend bool operator==(const TracedReferenceBase&, const Local<U>&);
126  friend bool operator==(const TracedReferenceBase&,
127  const TracedReferenceBase&);
128 };
129 
130 /**
131  * A traced handle with copy and move semantics. The handle is to be used
132  * together with |v8::EmbedderHeapTracer| or as part of GarbageCollected objects
133  * (see v8-cppgc.h) and specifies edges from C++ objects to JavaScript.
134  *
135  * The exact semantics are:
136  * - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc.
137  * - Non-tracing garbage collections refer to
138  * |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
139  * be treated as root or not.
140  *
141  * Note that the base class cannot be instantiated itself. Choose from
142  * - TracedGlobal
143  * - TracedReference
144  */
145 template <typename T>
147  public:
148  /**
149  * Construct a Local<T> from this handle.
150  */
151  Local<T> Get(Isolate* isolate) const { return Local<T>::New(isolate, *this); }
152 
153  template <class S>
155  return reinterpret_cast<BasicTracedReference<S>&>(
156  const_cast<BasicTracedReference<T>&>(*this));
157  }
158 
159  T* operator->() const {
160 #ifdef V8_ENABLE_CHECKS
161  CheckValue();
162 #endif // V8_ENABLE_CHECKS
163  return reinterpret_cast<T*>(val_);
164  }
165  T* operator*() const {
166 #ifdef V8_ENABLE_CHECKS
167  CheckValue();
168 #endif // V8_ENABLE_CHECKS
169  return reinterpret_cast<T*>(val_);
170  }
171 
172  private:
173  /**
174  * An empty BasicTracedReference without storage cell.
175  */
176  BasicTracedReference() = default;
177 
178  V8_INLINE static internal::Address* New(
179  Isolate* isolate, T* that, void* slot,
180  internal::GlobalHandleDestructionMode destruction_mode,
181  internal::GlobalHandleStoreMode store_mode);
182 
183  friend class EmbedderHeapTracer;
184  template <typename F>
185  friend class Local;
186  friend class Object;
187  template <typename F>
188  friend class TracedGlobal;
189  template <typename F>
190  friend class TracedReference;
191  template <typename F>
192  friend class BasicTracedReference;
193  template <typename F>
194  friend class ReturnValue;
195 };
196 
197 /**
198  * A traced handle with destructor that clears the handle. For more details see
199  * BasicTracedReference.
200  *
201  * This type is being deprecated and embedders are encouraged to use
202  * `v8::TracedReference` in combination with `v8::CppHeap`. If this is not
203  * possible, the following provides feature parity:
204  *
205  * \code
206  * template <typename T>
207  * struct TracedGlobalPolyfill {
208  * v8::TracedReference<T> traced_reference;
209  * v8::Global<T> weak_reference_for_callback;
210  * };
211  * \endcode
212  *
213  * In this example, `weak_reference_for_callback` can be used to emulate
214  * `SetFinalizationCallback()`.
215  */
216 template <typename T>
218  public:
219  using BasicTracedReference<T>::Reset;
220 
221  /**
222  * Destructor resetting the handle.Is
223  */
224  ~TracedGlobal() { this->Reset(); }
225 
226  /**
227  * An empty TracedGlobal without storage cell.
228  */
229  V8_DEPRECATED("See class comment.")
231 
232  /**
233  * Construct a TracedGlobal from a Local.
234  *
235  * When the Local is non-empty, a new storage cell is created
236  * pointing to the same object.
237  */
238  template <class S>
239  V8_DEPRECATED("See class comment.")
240  TracedGlobal(Isolate* isolate, Local<S> that) : BasicTracedReference<T>() {
241  this->val_ =
242  this->New(isolate, that.val_, &this->val_,
245  static_assert(std::is_base_of<T, S>::value, "type check");
246  }
247 
248  /**
249  * Move constructor initializing TracedGlobal from an existing one.
250  */
251  V8_INLINE TracedGlobal(TracedGlobal&& other) noexcept {
252  // Forward to operator=.
253  *this = std::move(other);
254  }
255 
256  /**
257  * Move constructor initializing TracedGlobal from an existing one.
258  */
259  template <typename S>
260  V8_INLINE TracedGlobal(TracedGlobal<S>&& other) noexcept {
261  // Forward to operator=.
262  *this = std::move(other);
263  }
264 
265  /**
266  * Copy constructor initializing TracedGlobal from an existing one.
267  */
269  // Forward to operator=;
270  *this = other;
271  }
272 
273  /**
274  * Copy constructor initializing TracedGlobal from an existing one.
275  */
276  template <typename S>
278  // Forward to operator=;
279  *this = other;
280  }
281 
282  /**
283  * Move assignment operator initializing TracedGlobal from an existing one.
284  */
285  V8_INLINE TracedGlobal& operator=(TracedGlobal&& rhs) noexcept;
286 
287  /**
288  * Move assignment operator initializing TracedGlobal from an existing one.
289  */
290  template <class S>
292 
293  /**
294  * Copy assignment operator initializing TracedGlobal from an existing one.
295  *
296  * Note: Prohibited when |other| has a finalization callback set through
297  * |SetFinalizationCallback|.
298  */
300 
301  /**
302  * Copy assignment operator initializing TracedGlobal from an existing one.
303  *
304  * Note: Prohibited when |other| has a finalization callback set through
305  * |SetFinalizationCallback|.
306  */
307  template <class S>
309 
310  /**
311  * If non-empty, destroy the underlying storage cell and create a new one with
312  * the contents of other if other is non empty
313  */
314  template <class S>
315  V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
316 
317  template <class S>
318  V8_INLINE TracedGlobal<S>& As() const {
319  return reinterpret_cast<TracedGlobal<S>&>(
320  const_cast<TracedGlobal<T>&>(*this));
321  }
322 
323  /**
324  * Adds a finalization callback to the handle. The type of this callback is
325  * similar to WeakCallbackType::kInternalFields, i.e., it will pass the
326  * parameter and the first two internal fields of the object.
327  *
328  * The callback is then supposed to reset the handle in the callback. No
329  * further V8 API may be called in this callback. In case additional work
330  * involving V8 needs to be done, a second callback can be scheduled using
331  * WeakCallbackInfo<void>::SetSecondPassCallback.
332  */
334  void* parameter, WeakCallbackInfo<void>::Callback callback);
335 };
336 
337 /**
338  * A traced handle without destructor that clears the handle. The embedder needs
339  * to ensure that the handle is not accessed once the V8 object has been
340  * reclaimed. This can happen when the handle is not passed through the
341  * EmbedderHeapTracer. For more details see BasicTracedReference.
342  *
343  * The reference assumes the embedder has precise knowledge about references at
344  * all times. In case V8 needs to separately handle on-stack references, the
345  * embedder is required to set the stack start through
346  * |EmbedderHeapTracer::SetStackStart|.
347  */
348 template <typename T>
350  public:
351  using BasicTracedReference<T>::Reset;
352 
353  /**
354  * An empty TracedReference without storage cell.
355  */
357 
358  /**
359  * Construct a TracedReference from a Local.
360  *
361  * When the Local is non-empty, a new storage cell is created
362  * pointing to the same object.
363  */
364  template <class S>
365  TracedReference(Isolate* isolate, Local<S> that) : BasicTracedReference<T>() {
366  this->val_ =
367  this->New(isolate, that.val_, &this->val_,
370  static_assert(std::is_base_of<T, S>::value, "type check");
371  }
372 
373  /**
374  * Move constructor initializing TracedReference from an
375  * existing one.
376  */
378  // Forward to operator=.
379  *this = std::move(other);
380  }
381 
382  /**
383  * Move constructor initializing TracedReference from an
384  * existing one.
385  */
386  template <typename S>
388  // Forward to operator=.
389  *this = std::move(other);
390  }
391 
392  /**
393  * Copy constructor initializing TracedReference from an
394  * existing one.
395  */
397  // Forward to operator=;
398  *this = other;
399  }
400 
401  /**
402  * Copy constructor initializing TracedReference from an
403  * existing one.
404  */
405  template <typename S>
407  // Forward to operator=;
408  *this = other;
409  }
410 
411  /**
412  * Move assignment operator initializing TracedGlobal from an existing one.
413  */
415 
416  /**
417  * Move assignment operator initializing TracedGlobal from an existing one.
418  */
419  template <class S>
421 
422  /**
423  * Copy assignment operator initializing TracedGlobal from an existing one.
424  */
426 
427  /**
428  * Copy assignment operator initializing TracedGlobal from an existing one.
429  */
430  template <class S>
432 
433  /**
434  * If non-empty, destroy the underlying storage cell and create a new one with
435  * the contents of other if other is non empty
436  */
437  template <class S>
438  V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
439 
440  template <class S>
442  return reinterpret_cast<TracedReference<S>&>(
443  const_cast<TracedReference<T>&>(*this));
444  }
445 };
446 
447 // --- Implementation ---
448 template <class T>
450  Isolate* isolate, T* that, void* slot,
451  internal::GlobalHandleDestructionMode destruction_mode,
452  internal::GlobalHandleStoreMode store_mode) {
453  if (that == nullptr) return nullptr;
454  internal::Address* p = reinterpret_cast<internal::Address*>(that);
456  reinterpret_cast<internal::Isolate*>(isolate), p,
457  reinterpret_cast<internal::Address*>(slot), destruction_mode, store_mode);
458 }
459 
461  if (IsEmpty()) return;
463  SetSlotThreadSafe(nullptr);
464 }
465 
467  const TracedReferenceBase& rhs) {
468  v8::internal::Address* a = reinterpret_cast<v8::internal::Address*>(lhs.val_);
469  v8::internal::Address* b = reinterpret_cast<v8::internal::Address*>(rhs.val_);
470  if (a == nullptr) return b == nullptr;
471  if (b == nullptr) return false;
472  return *a == *b;
473 }
474 
475 template <typename U>
477  const v8::Local<U>& rhs) {
478  v8::internal::Address* a = reinterpret_cast<v8::internal::Address*>(lhs.val_);
479  v8::internal::Address* b = reinterpret_cast<v8::internal::Address*>(*rhs);
480  if (a == nullptr) return b == nullptr;
481  if (b == nullptr) return false;
482  return *a == *b;
483 }
484 
485 template <typename U>
486 V8_INLINE bool operator==(const v8::Local<U>& lhs,
487  const TracedReferenceBase& rhs) {
488  return rhs == lhs;
489 }
490 
492  const TracedReferenceBase& rhs) {
493  return !(lhs == rhs);
494 }
495 
496 template <typename U>
498  const v8::Local<U>& rhs) {
499  return !(lhs == rhs);
500 }
501 
502 template <typename U>
503 V8_INLINE bool operator!=(const v8::Local<U>& lhs,
504  const TracedReferenceBase& rhs) {
505  return !(rhs == lhs);
506 }
507 
508 template <class T>
509 template <class S>
510 void TracedReference<T>::Reset(Isolate* isolate, const Local<S>& other) {
511  static_assert(std::is_base_of<T, S>::value, "type check");
512  this->Reset();
513  if (other.IsEmpty()) return;
514  this->SetSlotThreadSafe(
515  this->New(isolate, other.val_, &this->val_,
518 }
519 
520 template <class T>
521 template <class S>
523  TracedReference<S>&& rhs) noexcept {
524  static_assert(std::is_base_of<T, S>::value, "type check");
525  *this = std::move(rhs.template As<T>());
526  return *this;
527 }
528 
529 template <class T>
530 template <class S>
532  const TracedReference<S>& rhs) {
533  static_assert(std::is_base_of<T, S>::value, "type check");
534  *this = rhs.template As<T>();
535  return *this;
536 }
537 
538 template <class T>
540  TracedReference&& rhs) noexcept {
541  if (this != &rhs) {
543  reinterpret_cast<internal::Address**>(&rhs.val_),
544  reinterpret_cast<internal::Address**>(&this->val_));
545  }
546  return *this;
547 }
548 
549 template <class T>
551  if (this != &rhs) {
552  this->Reset();
553  if (rhs.val_ != nullptr) {
555  reinterpret_cast<const internal::Address* const*>(&rhs.val_),
556  reinterpret_cast<internal::Address**>(&this->val_));
557  }
558  }
559  return *this;
560 }
561 
562 void TracedReferenceBase::SetWrapperClassId(uint16_t class_id) {
563  using I = internal::Internals;
564  if (IsEmpty()) return;
565  internal::Address* obj = reinterpret_cast<internal::Address*>(val_);
566  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
567  *reinterpret_cast<uint16_t*>(addr) = class_id;
568 }
569 
571  using I = internal::Internals;
572  if (IsEmpty()) return 0;
573  internal::Address* obj = reinterpret_cast<internal::Address*>(val_);
574  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
575  return *reinterpret_cast<uint16_t*>(addr);
576 }
577 
578 template <class T>
579 template <class S>
580 void TracedGlobal<T>::Reset(Isolate* isolate, const Local<S>& other) {
581  static_assert(std::is_base_of<T, S>::value, "type check");
582  Reset();
583  if (other.IsEmpty()) return;
584  this->val_ = this->New(isolate, other.val_, &this->val_,
587 }
588 
589 template <class T>
590 template <class S>
591 TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal<S>&& rhs) noexcept {
592  static_assert(std::is_base_of<T, S>::value, "type check");
593  *this = std::move(rhs.template As<T>());
594  return *this;
595 }
596 
597 template <class T>
598 template <class S>
600  static_assert(std::is_base_of<T, S>::value, "type check");
601  *this = rhs.template As<T>();
602  return *this;
603 }
604 
605 template <class T>
606 TracedGlobal<T>& TracedGlobal<T>::operator=(TracedGlobal&& rhs) noexcept {
607  if (this != &rhs) {
609  reinterpret_cast<internal::Address**>(&rhs.val_),
610  reinterpret_cast<internal::Address**>(&this->val_));
611  }
612  return *this;
613 }
614 
615 template <class T>
617  if (this != &rhs) {
618  this->Reset();
619  if (rhs.val_ != nullptr) {
621  reinterpret_cast<const internal::Address* const*>(&rhs.val_),
622  reinterpret_cast<internal::Address**>(&this->val_));
623  }
624  }
625  return *this;
626 }
627 
628 template <class T>
630  void* parameter, typename WeakCallbackInfo<void>::Callback callback) {
632  reinterpret_cast<internal::Address*>(this->val_), parameter, callback);
633 }
634 
635 } // namespace v8
636 
637 #endif // INCLUDE_V8_TRACED_HANDLE_H_