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