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