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
22namespace v8 {
23
24class Value;
25
26namespace internal {
27
28class BasicTracedReferenceExtractor;
29
30enum class GlobalHandleStoreMode {
31  kInitializingStore,
32  kAssigningStore,
33};
34
35V8_EXPORT internal::Address* GlobalizeTracedReference(
36    internal::Isolate* isolate, internal::Address value,
37    internal::Address* slot, GlobalHandleStoreMode store_mode);
38V8_EXPORT void MoveTracedReference(internal::Address** from,
39                                   internal::Address** to);
40V8_EXPORT void CopyTracedReference(const internal::Address* const* from,
41                                   internal::Address** to);
42V8_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 */
50class 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   */
61  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   */
70  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   */
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   */
99  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 */
129template <typename T>
130class BasicTracedReference : public TracedReferenceBase {
131 public:
132  /**
133   * Construct a Local<T> from this handle.
134   */
135  Local<T> Get(Isolate* isolate) const { return Local<T>::New(isolate, *this); }
136
137  template <class S>
138  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")
144  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")
152  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 */
180template <typename T>
181class 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>
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   */
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>
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>
272  V8_INLINE TracedReference<S>& As() const {
273    return reinterpret_cast<TracedReference<S>&>(
274        const_cast<TracedReference<T>&>(*this));
275  }
276};
277
278// --- Implementation ---
279template <class T>
280internal::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
290void TracedReferenceBase::Reset() {
291  if (IsEmpty()) return;
292  internal::DisposeTracedReference(slot());
293  SetSlotThreadSafe(nullptr);
294}
295
296V8_INLINE bool operator==(const TracedReferenceBase& lhs,
297                          const TracedReferenceBase& rhs) {
298  return internal::HandleHelper::EqualHandles(lhs, rhs);
299}
300
301template <typename U>
302V8_INLINE bool operator==(const TracedReferenceBase& lhs,
303                          const v8::Local<U>& rhs) {
304  return internal::HandleHelper::EqualHandles(lhs, rhs);
305}
306
307template <typename U>
308V8_INLINE bool operator==(const v8::Local<U>& lhs,
309                          const TracedReferenceBase& rhs) {
310  return rhs == lhs;
311}
312
313V8_INLINE bool operator!=(const TracedReferenceBase& lhs,
314                          const TracedReferenceBase& rhs) {
315  return !(lhs == rhs);
316}
317
318template <typename U>
319V8_INLINE bool operator!=(const TracedReferenceBase& lhs,
320                          const v8::Local<U>& rhs) {
321  return !(lhs == rhs);
322}
323
324template <typename U>
325V8_INLINE bool operator!=(const v8::Local<U>& lhs,
326                          const TracedReferenceBase& rhs) {
327  return !(rhs == lhs);
328}
329
330template <class T>
331template <class S>
332void 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
341template <class T>
342template <class S>
343TracedReference<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
350template <class T>
351template <class S>
352TracedReference<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
359template <class T>
360TracedReference<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
368template <class T>
369TracedReference<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
379void 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
387uint16_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