• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 GlobalHandleStoreMode {
31   kInitializingStore,
32   kAssigningStore,
33 };
34 
35 V8_EXPORT internal::Address* GlobalizeTracedReference(
36     internal::Isolate* isolate, internal::Address* handle,
37     internal::Address* slot, GlobalHandleStoreMode store_mode);
38 V8_EXPORT void MoveTracedReference(internal::Address** from,
39                                    internal::Address** to);
40 V8_EXPORT void CopyTracedReference(const internal::Address* const* from,
41                                    internal::Address** to);
42 V8_EXPORT void DisposeTracedReference(internal::Address* global_handle);
43 
44 }  // namespace internal
45 
46 class TracedReferenceBase {
47  public:
48   /**
49    * Returns true if the reference is empty, i.e., has not been assigned
50    * object.
51    */
IsEmpty()52   bool IsEmpty() const { return val_ == nullptr; }
53 
54   /**
55    * If non-empty, destroy the underlying storage cell. |IsEmpty| will return
56    * true after this call.
57    */
58   V8_INLINE void Reset();
59 
60   /**
61    * Construct a Local<Value> from this handle.
62    */
Get(v8::Isolate * isolate)63   V8_INLINE v8::Local<v8::Value> Get(v8::Isolate* isolate) const {
64     if (IsEmpty()) return Local<Value>();
65     return Local<Value>::New(isolate, reinterpret_cast<Value*>(val_));
66   }
67 
68   /**
69    * Returns true if this TracedReference is empty, i.e., has not been
70    * assigned an object. This version of IsEmpty is thread-safe.
71    */
IsEmptyThreadSafe()72   bool IsEmptyThreadSafe() const {
73     return this->GetSlotThreadSafe() == nullptr;
74   }
75 
76   /**
77    * Assigns a wrapper class ID to the handle.
78    */
79   V8_INLINE void SetWrapperClassId(uint16_t class_id);
80 
81   /**
82    * Returns the class ID previously assigned to this handle or 0 if no class ID
83    * was previously assigned.
84    */
85   V8_INLINE uint16_t WrapperClassId() const;
86 
87  protected:
88   /**
89    * Update this reference in a thread-safe way.
90    */
SetSlotThreadSafe(void * new_val)91   void SetSlotThreadSafe(void* new_val) {
92     reinterpret_cast<std::atomic<void*>*>(&val_)->store(
93         new_val, std::memory_order_relaxed);
94   }
95 
96   /**
97    * Get this reference in a thread-safe way
98    */
GetSlotThreadSafe()99   const void* GetSlotThreadSafe() const {
100     return reinterpret_cast<std::atomic<const void*> const*>(&val_)->load(
101         std::memory_order_relaxed);
102   }
103 
104   V8_EXPORT void CheckValue() const;
105 
106   // val_ points to a GlobalHandles node.
107   internal::Address* val_ = nullptr;
108 
109   friend class internal::BasicTracedReferenceExtractor;
110   template <typename F>
111   friend class Local;
112   template <typename U>
113   friend bool operator==(const TracedReferenceBase&, const Local<U>&);
114   friend bool operator==(const TracedReferenceBase&,
115                          const TracedReferenceBase&);
116 };
117 
118 /**
119  * A traced handle with copy and move semantics. The handle is to be used
120  * together with |v8::EmbedderHeapTracer| or as part of GarbageCollected objects
121  * (see v8-cppgc.h) and specifies edges from C++ objects to JavaScript.
122  *
123  * The exact semantics are:
124  * - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc.
125  * - Non-tracing garbage collections refer to
126  *   |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
127  * be treated as root or not.
128  *
129  * Note that the base class cannot be instantiated itself, use |TracedReference|
130  * instead.
131  */
132 template <typename T>
133 class BasicTracedReference : public TracedReferenceBase {
134  public:
135   /**
136    * Construct a Local<T> from this handle.
137    */
Get(Isolate * isolate)138   Local<T> Get(Isolate* isolate) const { return Local<T>::New(isolate, *this); }
139 
140   template <class S>
As()141   V8_INLINE BasicTracedReference<S>& As() const {
142     return reinterpret_cast<BasicTracedReference<S>&>(
143         const_cast<BasicTracedReference<T>&>(*this));
144   }
145 
146   T* operator->() const {
147 #ifdef V8_ENABLE_CHECKS
148     CheckValue();
149 #endif  // V8_ENABLE_CHECKS
150     return reinterpret_cast<T*>(val_);
151   }
152   T* operator*() const {
153 #ifdef V8_ENABLE_CHECKS
154     CheckValue();
155 #endif  // V8_ENABLE_CHECKS
156     return reinterpret_cast<T*>(val_);
157   }
158 
159  private:
160   /**
161    * An empty BasicTracedReference without storage cell.
162    */
163   BasicTracedReference() = default;
164 
165   V8_INLINE static internal::Address* New(
166       Isolate* isolate, T* that, void* slot,
167       internal::GlobalHandleStoreMode store_mode);
168 
169   friend class EmbedderHeapTracer;
170   template <typename F>
171   friend class Local;
172   friend class Object;
173   template <typename F>
174   friend class TracedReference;
175   template <typename F>
176   friend class BasicTracedReference;
177   template <typename F>
178   friend class ReturnValue;
179 };
180 
181 /**
182  * A traced handle without destructor that clears the handle. The embedder needs
183  * to ensure that the handle is not accessed once the V8 object has been
184  * reclaimed. This can happen when the handle is not passed through the
185  * EmbedderHeapTracer. For more details see BasicTracedReference.
186  *
187  * The reference assumes the embedder has precise knowledge about references at
188  * all times. In case V8 needs to separately handle on-stack references, the
189  * embedder is required to set the stack start through
190  * |EmbedderHeapTracer::SetStackStart|.
191  */
192 template <typename T>
193 class TracedReference : public BasicTracedReference<T> {
194  public:
195   using BasicTracedReference<T>::Reset;
196 
197   /**
198    * An empty TracedReference without storage cell.
199    */
TracedReference()200   TracedReference() : BasicTracedReference<T>() {}
201 
202   /**
203    * Construct a TracedReference from a Local.
204    *
205    * When the Local is non-empty, a new storage cell is created
206    * pointing to the same object.
207    */
208   template <class S>
TracedReference(Isolate * isolate,Local<S> that)209   TracedReference(Isolate* isolate, Local<S> that) : BasicTracedReference<T>() {
210     this->val_ = this->New(isolate, that.val_, &this->val_,
211                            internal::GlobalHandleStoreMode::kInitializingStore);
212     static_assert(std::is_base_of<T, S>::value, "type check");
213   }
214 
215   /**
216    * Move constructor initializing TracedReference from an
217    * existing one.
218    */
TracedReference(TracedReference && other)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>
TracedReference(TracedReference<S> && other)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    */
TracedReference(const TracedReference & other)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>
TracedReference(const TracedReference<S> & other)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    * If non-empty, destroy the underlying storage cell and create a new one with
277    * the contents of other if other is non empty
278    */
279   template <class S>
280   V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
281 
282   template <class S>
As()283   V8_INLINE TracedReference<S>& As() const {
284     return reinterpret_cast<TracedReference<S>&>(
285         const_cast<TracedReference<T>&>(*this));
286   }
287 };
288 
289 // --- Implementation ---
290 template <class T>
New(Isolate * isolate,T * that,void * slot,internal::GlobalHandleStoreMode store_mode)291 internal::Address* BasicTracedReference<T>::New(
292     Isolate* isolate, T* that, void* slot,
293     internal::GlobalHandleStoreMode store_mode) {
294   if (that == nullptr) return nullptr;
295   internal::Address* p = reinterpret_cast<internal::Address*>(that);
296   return internal::GlobalizeTracedReference(
297       reinterpret_cast<internal::Isolate*>(isolate), p,
298       reinterpret_cast<internal::Address*>(slot), store_mode);
299 }
300 
Reset()301 void TracedReferenceBase::Reset() {
302   if (IsEmpty()) return;
303   internal::DisposeTracedReference(reinterpret_cast<internal::Address*>(val_));
304   SetSlotThreadSafe(nullptr);
305 }
306 
307 V8_INLINE bool operator==(const TracedReferenceBase& lhs,
308                           const TracedReferenceBase& rhs) {
309   v8::internal::Address* a = reinterpret_cast<v8::internal::Address*>(lhs.val_);
310   v8::internal::Address* b = reinterpret_cast<v8::internal::Address*>(rhs.val_);
311   if (a == nullptr) return b == nullptr;
312   if (b == nullptr) return false;
313   return *a == *b;
314 }
315 
316 template <typename U>
317 V8_INLINE bool operator==(const TracedReferenceBase& lhs,
318                           const v8::Local<U>& rhs) {
319   v8::internal::Address* a = reinterpret_cast<v8::internal::Address*>(lhs.val_);
320   v8::internal::Address* b = reinterpret_cast<v8::internal::Address*>(*rhs);
321   if (a == nullptr) return b == nullptr;
322   if (b == nullptr) return false;
323   return *a == *b;
324 }
325 
326 template <typename U>
327 V8_INLINE bool operator==(const v8::Local<U>& lhs,
328                           const TracedReferenceBase& rhs) {
329   return rhs == lhs;
330 }
331 
332 V8_INLINE bool operator!=(const TracedReferenceBase& lhs,
333                           const TracedReferenceBase& rhs) {
334   return !(lhs == rhs);
335 }
336 
337 template <typename U>
338 V8_INLINE bool operator!=(const TracedReferenceBase& lhs,
339                           const v8::Local<U>& rhs) {
340   return !(lhs == rhs);
341 }
342 
343 template <typename U>
344 V8_INLINE bool operator!=(const v8::Local<U>& lhs,
345                           const TracedReferenceBase& rhs) {
346   return !(rhs == lhs);
347 }
348 
349 template <class T>
350 template <class S>
Reset(Isolate * isolate,const Local<S> & other)351 void TracedReference<T>::Reset(Isolate* isolate, const Local<S>& other) {
352   static_assert(std::is_base_of<T, S>::value, "type check");
353   this->Reset();
354   if (other.IsEmpty()) return;
355   this->SetSlotThreadSafe(
356       this->New(isolate, other.val_, &this->val_,
357                 internal::GlobalHandleStoreMode::kAssigningStore));
358 }
359 
360 template <class T>
361 template <class S>
362 TracedReference<T>& TracedReference<T>::operator=(
363     TracedReference<S>&& rhs) noexcept {
364   static_assert(std::is_base_of<T, S>::value, "type check");
365   *this = std::move(rhs.template As<T>());
366   return *this;
367 }
368 
369 template <class T>
370 template <class S>
371 TracedReference<T>& TracedReference<T>::operator=(
372     const TracedReference<S>& rhs) {
373   static_assert(std::is_base_of<T, S>::value, "type check");
374   *this = rhs.template As<T>();
375   return *this;
376 }
377 
378 template <class T>
379 TracedReference<T>& TracedReference<T>::operator=(
380     TracedReference&& rhs) noexcept {
381   if (this != &rhs) {
382     internal::MoveTracedReference(
383         reinterpret_cast<internal::Address**>(&rhs.val_),
384         reinterpret_cast<internal::Address**>(&this->val_));
385   }
386   return *this;
387 }
388 
389 template <class T>
390 TracedReference<T>& TracedReference<T>::operator=(const TracedReference& rhs) {
391   if (this != &rhs) {
392     this->Reset();
393     if (rhs.val_ != nullptr) {
394       internal::CopyTracedReference(
395           reinterpret_cast<const internal::Address* const*>(&rhs.val_),
396           reinterpret_cast<internal::Address**>(&this->val_));
397     }
398   }
399   return *this;
400 }
401 
SetWrapperClassId(uint16_t class_id)402 void TracedReferenceBase::SetWrapperClassId(uint16_t class_id) {
403   using I = internal::Internals;
404   if (IsEmpty()) return;
405   internal::Address* obj = reinterpret_cast<internal::Address*>(val_);
406   uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
407   *reinterpret_cast<uint16_t*>(addr) = class_id;
408 }
409 
WrapperClassId()410 uint16_t TracedReferenceBase::WrapperClassId() const {
411   using I = internal::Internals;
412   if (IsEmpty()) return 0;
413   internal::Address* obj = reinterpret_cast<internal::Address*>(val_);
414   uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
415   return *reinterpret_cast<uint16_t*>(addr);
416 }
417 
418 }  // namespace v8
419 
420 #endif  // INCLUDE_V8_TRACED_HANDLE_H_
421