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