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