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_PERSISTENT_HANDLE_H_
6 #define INCLUDE_V8_PERSISTENT_HANDLE_H_
7
8 #include "v8-internal.h" // NOLINT(build/include_directory)
9 #include "v8-local-handle.h" // NOLINT(build/include_directory)
10 #include "v8-weak-callback-info.h" // NOLINT(build/include_directory)
11 #include "v8config.h" // NOLINT(build/include_directory)
12
13 namespace v8 {
14
15 class Isolate;
16 template <class K, class V, class T>
17 class PersistentValueMapBase;
18 template <class V, class T>
19 class PersistentValueVector;
20 template <class T>
21 class Global;
22 template <class T>
23 class PersistentBase;
24 template <class K, class V, class T>
25 class PersistentValueMap;
26 class Value;
27
28 namespace api_internal {
29 V8_EXPORT internal::Address* Eternalize(v8::Isolate* isolate, Value* handle);
30 V8_EXPORT internal::Address* CopyGlobalReference(internal::Address* from);
31 V8_EXPORT void DisposeGlobal(internal::Address* global_handle);
32 V8_EXPORT void MakeWeak(internal::Address** location_addr);
33 V8_EXPORT void* ClearWeak(internal::Address* location);
34 V8_EXPORT void AnnotateStrongRetainer(internal::Address* location,
35 const char* label);
36 V8_EXPORT internal::Address* GlobalizeReference(internal::Isolate* isolate,
37 internal::Address value);
38 V8_EXPORT void MoveGlobalReference(internal::Address** from,
39 internal::Address** to);
40 } // namespace api_internal
41
42 /**
43 * Eternal handles are set-once handles that live for the lifetime of the
44 * isolate.
45 */
46 template <class T>
47 class Eternal : public IndirectHandleBase {
48 public:
49 V8_INLINE Eternal() = default;
50
51 template <class S>
Eternal(Isolate* isolate, Local<S> handle)52 V8_INLINE Eternal(Isolate* isolate, Local<S> handle) {
53 Set(isolate, handle);
54 }
55
56 // Can only be safely called if already set.
Get(Isolate* isolate) const57 V8_INLINE Local<T> Get(Isolate* isolate) const {
58 // The eternal handle will never go away, so as with the roots, we don't
59 // even need to open a handle.
60 return Local<T>::FromSlot(slot());
61 }
62
63 template <class S>
Set(Isolate* isolate, Local<S> handle)64 void Set(Isolate* isolate, Local<S> handle) {
65 static_assert(std::is_base_of<T, S>::value, "type check");
66 slot() =
67 api_internal::Eternalize(isolate, *handle.template UnsafeAs<Value>());
68 }
69 };
70
71 namespace api_internal {
72 V8_EXPORT void MakeWeak(internal::Address* location, void* data,
73 WeakCallbackInfo<void>::Callback weak_callback,
74 WeakCallbackType type);
75 } // namespace api_internal
76
77 /**
78 * An object reference that is independent of any handle scope. Where
79 * a Local handle only lives as long as the HandleScope in which it was
80 * allocated, a PersistentBase handle remains valid until it is explicitly
81 * disposed using Reset().
82 *
83 * A persistent handle contains a reference to a storage cell within
84 * the V8 engine which holds an object value and which is updated by
85 * the garbage collector whenever the object is moved. A new storage
86 * cell can be created using the constructor or PersistentBase::Reset and
87 * existing handles can be disposed using PersistentBase::Reset.
88 *
89 */
90 template <class T>
91 class PersistentBase : public IndirectHandleBase {
92 public:
93 /**
94 * If non-empty, destroy the underlying storage cell
95 * IsEmpty() will return true after this call.
96 */
97 V8_INLINE void Reset();
98
99 /**
100 * If non-empty, destroy the underlying storage cell
101 * and create a new one with the contents of other if other is non empty
102 */
103 template <class S>
104 V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
105
106 /**
107 * If non-empty, destroy the underlying storage cell
108 * and create a new one with the contents of other if other is non empty
109 */
110 template <class S>
111 V8_INLINE void Reset(Isolate* isolate, const PersistentBase<S>& other);
112
Get(Isolate* isolate) const113 V8_INLINE Local<T> Get(Isolate* isolate) const {
114 return Local<T>::New(isolate, *this);
115 }
116
117 template <class S>
operator ==(const PersistentBase<S>& that) const118 V8_INLINE bool operator==(const PersistentBase<S>& that) const {
119 return internal::HandleHelper::EqualHandles(*this, that);
120 }
121
122 template <class S>
operator ==(const Local<S>& that) const123 V8_INLINE bool operator==(const Local<S>& that) const {
124 return internal::HandleHelper::EqualHandles(*this, that);
125 }
126
127 template <class S>
operator !=(const PersistentBase<S>& that) const128 V8_INLINE bool operator!=(const PersistentBase<S>& that) const {
129 return !operator==(that);
130 }
131
132 template <class S>
operator !=(const Local<S>& that) const133 V8_INLINE bool operator!=(const Local<S>& that) const {
134 return !operator==(that);
135 }
136
137 /**
138 * Install a finalization callback on this object.
139 * NOTE: There is no guarantee as to *when* or even *if* the callback is
140 * invoked. The invocation is performed solely on a best effort basis.
141 * As always, GC-based finalization should *not* be relied upon for any
142 * critical form of resource management!
143 *
144 * The callback is supposed to reset the handle. No further V8 API may be
145 * called in this callback. In case additional work involving V8 needs to be
146 * done, a second callback can be scheduled using
147 * WeakCallbackInfo<void>::SetSecondPassCallback.
148 */
149 template <typename P>
150 V8_INLINE void SetWeak(P* parameter,
151 typename WeakCallbackInfo<P>::Callback callback,
152 WeakCallbackType type);
153
154 /**
155 * Turns this handle into a weak phantom handle without finalization callback.
156 * The handle will be reset automatically when the garbage collector detects
157 * that the object is no longer reachable.
158 */
159 V8_INLINE void SetWeak();
160
161 template <typename P>
162 V8_INLINE P* ClearWeak();
163
164 // TODO(dcarney): remove this.
ClearWeak()165 V8_INLINE void ClearWeak() { ClearWeak<void>(); }
166
167 /**
168 * Annotates the strong handle with the given label, which is then used by the
169 * heap snapshot generator as a name of the edge from the root to the handle.
170 * The function does not take ownership of the label and assumes that the
171 * label is valid as long as the handle is valid.
172 */
173 V8_INLINE void AnnotateStrongRetainer(const char* label);
174
175 /** Returns true if the handle's reference is weak. */
176 V8_INLINE bool IsWeak() const;
177
178 /**
179 * Assigns a wrapper class ID to the handle.
180 */
181 V8_INLINE void SetWrapperClassId(uint16_t class_id);
182
183 /**
184 * Returns the class ID previously assigned to this handle or 0 if no class ID
185 * was previously assigned.
186 */
187 V8_INLINE uint16_t WrapperClassId() const;
188
189 PersistentBase(const PersistentBase& other) = delete;
190 void operator=(const PersistentBase&) = delete;
191
192 private:
193 friend class Isolate;
194 friend class Utils;
195 template <class F>
196 friend class Local;
197 template <class F1, class F2>
198 friend class Persistent;
199 template <class F>
200 friend class Global;
201 template <class F>
202 friend class PersistentBase;
203 template <class F>
204 friend class ReturnValue;
205 template <class F1, class F2, class F3>
206 friend class PersistentValueMapBase;
207 template <class F1, class F2>
208 friend class PersistentValueVector;
209 friend class Object;
210 friend class internal::ValueHelper;
211
212 V8_INLINE PersistentBase() = default;
213
PersistentBase(internal::Address* location)214 V8_INLINE explicit PersistentBase(internal::Address* location)
215 : IndirectHandleBase(location) {}
216
217 V8_INLINE static internal::Address* New(Isolate* isolate, T* that);
218 };
219
220 /**
221 * Default traits for Persistent. This class does not allow
222 * use of the copy constructor or assignment operator.
223 * At present kResetInDestructor is not set, but that will change in a future
224 * version.
225 */
226 template <class T>
227 class NonCopyablePersistentTraits {
228 public:
229 using NonCopyablePersistent = Persistent<T, NonCopyablePersistentTraits<T>>;
230 static const bool kResetInDestructor = false;
231 template <class S, class M>
Copy(const Persistent<S, M>& source, NonCopyablePersistent* dest)232 V8_INLINE static void Copy(const Persistent<S, M>& source,
233 NonCopyablePersistent* dest) {
234 static_assert(sizeof(S) < 0,
235 "NonCopyablePersistentTraits::Copy is not instantiable");
236 }
237 };
238
239 /**
240 * Helper class traits to allow copying and assignment of Persistent.
241 * This will clone the contents of storage cell, but not any of the flags, etc.
242 */
243 template <class T>
244 struct V8_DEPRECATED("Use v8::Global instead") CopyablePersistentTraits {
245 using CopyablePersistent = Persistent<T, CopyablePersistentTraits<T>>;
246 static const bool kResetInDestructor = true;
247 template <class S, class M>
Copy(const Persistent<S, M>& source, CopyablePersistent* dest)248 static V8_INLINE void Copy(const Persistent<S, M>& source,
249 CopyablePersistent* dest) {
250 // do nothing, just allow copy
251 }
252 };
253
254 /**
255 * A PersistentBase which allows copy and assignment.
256 *
257 * Copy, assignment and destructor behavior is controlled by the traits
258 * class M.
259 *
260 * Note: Persistent class hierarchy is subject to future changes.
261 */
262 template <class T, class M>
263 class Persistent : public PersistentBase<T> {
264 public:
265 /**
266 * A Persistent with no storage cell.
267 */
268 V8_INLINE Persistent() = default;
269
270 /**
271 * Construct a Persistent from a Local.
272 * When the Local is non-empty, a new storage cell is created
273 * pointing to the same object, and no flags are set.
274 */
275 template <class S>
Persistent(Isolate* isolate, Local<S> that)276 V8_INLINE Persistent(Isolate* isolate, Local<S> that)
277 : PersistentBase<T>(
278 PersistentBase<T>::New(isolate, that.template value<S>())) {
279 static_assert(std::is_base_of<T, S>::value, "type check");
280 }
281
282 /**
283 * Construct a Persistent from a Persistent.
284 * When the Persistent is non-empty, a new storage cell is created
285 * pointing to the same object, and no flags are set.
286 */
287 template <class S, class M2>
Persistent(Isolate* isolate, const Persistent<S, M2>& that)288 V8_INLINE Persistent(Isolate* isolate, const Persistent<S, M2>& that)
289 : PersistentBase<T>(
290 PersistentBase<T>::New(isolate, that.template value<S>())) {
291 static_assert(std::is_base_of<T, S>::value, "type check");
292 }
293
294 /**
295 * The copy constructors and assignment operator create a Persistent
296 * exactly as the Persistent constructor, but the Copy function from the
297 * traits class is called, allowing the setting of flags based on the
298 * copied Persistent.
299 */
Persistent(const Persistent& that)300 V8_INLINE Persistent(const Persistent& that) : PersistentBase<T>() {
301 Copy(that);
302 }
303 template <class S, class M2>
Persistent(const Persistent<S, M2>& that)304 V8_INLINE Persistent(const Persistent<S, M2>& that) : PersistentBase<T>() {
305 Copy(that);
306 }
operator =(const Persistent& that)307 V8_INLINE Persistent& operator=(const Persistent& that) {
308 Copy(that);
309 return *this;
310 }
311 template <class S, class M2>
operator =(const Persistent<S, M2>& that)312 V8_INLINE Persistent& operator=(const Persistent<S, M2>& that) {
313 Copy(that);
314 return *this;
315 }
316
317 /**
318 * The destructor will dispose the Persistent based on the
319 * kResetInDestructor flags in the traits class. Since not calling dispose
320 * can result in a memory leak, it is recommended to always set this flag.
321 */
~Persistent()322 V8_INLINE ~Persistent() {
323 if (M::kResetInDestructor) this->Reset();
324 }
325
326 // TODO(dcarney): this is pretty useless, fix or remove
327 template <class S, class M2>
Cast(const Persistent<S, M2>& that)328 V8_INLINE static Persistent<T, M>& Cast(const Persistent<S, M2>& that) {
329 #ifdef V8_ENABLE_CHECKS
330 // If we're going to perform the type check then we have to check
331 // that the handle isn't empty before doing the checked cast.
332 if (!that.IsEmpty()) T::Cast(that.template value<S>());
333 #endif
334 return reinterpret_cast<Persistent<T, M>&>(
335 const_cast<Persistent<S, M2>&>(that));
336 }
337
338 // TODO(dcarney): this is pretty useless, fix or remove
339 template <class S, class M2>
As() const340 V8_INLINE Persistent<S, M2>& As() const {
341 return Persistent<S, M2>::Cast(*this);
342 }
343
344 private:
345 friend class Isolate;
346 friend class Utils;
347 template <class F>
348 friend class Local;
349 template <class F1, class F2>
350 friend class Persistent;
351 template <class F>
352 friend class ReturnValue;
353
354 template <class S, class M2>
355 V8_INLINE void Copy(const Persistent<S, M2>& that);
356 };
357
358 /**
359 * A PersistentBase which has move semantics.
360 *
361 * Note: Persistent class hierarchy is subject to future changes.
362 */
363 template <class T>
364 class Global : public PersistentBase<T> {
365 public:
366 /**
367 * A Global with no storage cell.
368 */
369 V8_INLINE Global() = default;
370
371 /**
372 * Construct a Global from a Local.
373 * When the Local is non-empty, a new storage cell is created
374 * pointing to the same object, and no flags are set.
375 */
376 template <class S>
Global(Isolate* isolate, Local<S> that)377 V8_INLINE Global(Isolate* isolate, Local<S> that)
378 : PersistentBase<T>(
379 PersistentBase<T>::New(isolate, that.template value<S>())) {
380 static_assert(std::is_base_of<T, S>::value, "type check");
381 }
382
383 /**
384 * Construct a Global from a PersistentBase.
385 * When the Persistent is non-empty, a new storage cell is created
386 * pointing to the same object, and no flags are set.
387 */
388 template <class S>
Global(Isolate* isolate, const PersistentBase<S>& that)389 V8_INLINE Global(Isolate* isolate, const PersistentBase<S>& that)
390 : PersistentBase<T>(
391 PersistentBase<T>::New(isolate, that.template value<S>())) {
392 static_assert(std::is_base_of<T, S>::value, "type check");
393 }
394
395 /**
396 * Move constructor.
397 */
398 V8_INLINE Global(Global&& other);
399
~Global()400 V8_INLINE ~Global() { this->Reset(); }
401
402 /**
403 * Move via assignment.
404 */
405 template <class S>
406 V8_INLINE Global& operator=(Global<S>&& rhs);
407
408 /**
409 * Pass allows returning uniques from functions, etc.
410 */
Pass()411 Global Pass() { return static_cast<Global&&>(*this); }
412
413 /*
414 * For compatibility with Chromium's base::Bind (base::Passed).
415 */
416 using MoveOnlyTypeForCPP03 = void;
417
418 Global(const Global&) = delete;
419 void operator=(const Global&) = delete;
420
421 private:
422 template <class F>
423 friend class ReturnValue;
424 };
425
426 // UniquePersistent is an alias for Global for historical reason.
427 template <class T>
428 using UniquePersistent = Global<T>;
429
430 /**
431 * Interface for iterating through all the persistent handles in the heap.
432 */
433 class V8_EXPORT PersistentHandleVisitor {
434 public:
435 virtual ~PersistentHandleVisitor() = default;
VisitPersistentHandle(Persistent<Value>* value, uint16_t class_id)436 virtual void VisitPersistentHandle(Persistent<Value>* value,
437 uint16_t class_id) {}
438 };
439
440 template <class T>
New(Isolate* isolate, T* that)441 internal::Address* PersistentBase<T>::New(Isolate* isolate, T* that) {
442 if (internal::ValueHelper::IsEmpty(that)) return nullptr;
443 return api_internal::GlobalizeReference(
444 reinterpret_cast<internal::Isolate*>(isolate),
445 internal::ValueHelper::ValueAsAddress(that));
446 }
447
448 template <class T, class M>
449 template <class S, class M2>
Copy(const Persistent<S, M2>& that)450 void Persistent<T, M>::Copy(const Persistent<S, M2>& that) {
451 static_assert(std::is_base_of<T, S>::value, "type check");
452 this->Reset();
453 if (that.IsEmpty()) return;
454 this->slot() = api_internal::CopyGlobalReference(that.slot());
455 M::Copy(that, this);
456 }
457
458 template <class T>
IsWeak() const459 bool PersistentBase<T>::IsWeak() const {
460 using I = internal::Internals;
461 if (this->IsEmpty()) return false;
462 return I::GetNodeState(this->slot()) == I::kNodeStateIsWeakValue;
463 }
464
465 template <class T>
Reset()466 void PersistentBase<T>::Reset() {
467 if (this->IsEmpty()) return;
468 api_internal::DisposeGlobal(this->slot());
469 this->Clear();
470 }
471
472 /**
473 * If non-empty, destroy the underlying storage cell
474 * and create a new one with the contents of other if other is non empty
475 */
476 template <class T>
477 template <class S>
Reset(Isolate* isolate, const Local<S>& other)478 void PersistentBase<T>::Reset(Isolate* isolate, const Local<S>& other) {
479 static_assert(std::is_base_of<T, S>::value, "type check");
480 Reset();
481 if (other.IsEmpty()) return;
482 this->slot() = New(isolate, *other);
483 }
484
485 /**
486 * If non-empty, destroy the underlying storage cell
487 * and create a new one with the contents of other if other is non empty
488 */
489 template <class T>
490 template <class S>
Reset(Isolate* isolate, const PersistentBase<S>& other)491 void PersistentBase<T>::Reset(Isolate* isolate,
492 const PersistentBase<S>& other) {
493 static_assert(std::is_base_of<T, S>::value, "type check");
494 Reset();
495 if (other.IsEmpty()) return;
496 this->slot() = New(isolate, other.template value<S>());
497 }
498
499 template <class T>
500 template <typename P>
SetWeak( P* parameter, typename WeakCallbackInfo<P>::Callback callback, WeakCallbackType type)501 V8_INLINE void PersistentBase<T>::SetWeak(
502 P* parameter, typename WeakCallbackInfo<P>::Callback callback,
503 WeakCallbackType type) {
504 using Callback = WeakCallbackInfo<void>::Callback;
505 #if (__GNUC__ >= 8) && !defined(__clang__)
506 #pragma GCC diagnostic push
507 #pragma GCC diagnostic ignored "-Wcast-function-type"
508 #endif
509 api_internal::MakeWeak(this->slot(), parameter,
510 reinterpret_cast<Callback>(callback), type);
511 #if (__GNUC__ >= 8) && !defined(__clang__)
512 #pragma GCC diagnostic pop
513 #endif
514 }
515
516 template <class T>
SetWeak()517 void PersistentBase<T>::SetWeak() {
518 api_internal::MakeWeak(&this->slot());
519 }
520
521 template <class T>
522 template <typename P>
ClearWeak()523 P* PersistentBase<T>::ClearWeak() {
524 return reinterpret_cast<P*>(api_internal::ClearWeak(this->slot()));
525 }
526
527 template <class T>
AnnotateStrongRetainer(const char* label)528 void PersistentBase<T>::AnnotateStrongRetainer(const char* label) {
529 api_internal::AnnotateStrongRetainer(this->slot(), label);
530 }
531
532 template <class T>
SetWrapperClassId(uint16_t class_id)533 void PersistentBase<T>::SetWrapperClassId(uint16_t class_id) {
534 using I = internal::Internals;
535 if (this->IsEmpty()) return;
536 uint8_t* addr = reinterpret_cast<uint8_t*>(slot()) + I::kNodeClassIdOffset;
537 *reinterpret_cast<uint16_t*>(addr) = class_id;
538 }
539
540 template <class T>
WrapperClassId() const541 uint16_t PersistentBase<T>::WrapperClassId() const {
542 using I = internal::Internals;
543 if (this->IsEmpty()) return 0;
544 uint8_t* addr = reinterpret_cast<uint8_t*>(slot()) + I::kNodeClassIdOffset;
545 return *reinterpret_cast<uint16_t*>(addr);
546 }
547
548 template <class T>
Global(Global&& other)549 Global<T>::Global(Global&& other) : PersistentBase<T>(other.slot()) {
550 if (!other.IsEmpty()) {
551 api_internal::MoveGlobalReference(&other.slot(), &this->slot());
552 other.Clear();
553 }
554 }
555
556 template <class T>
557 template <class S>
operator =(Global<S>&& rhs)558 Global<T>& Global<T>::operator=(Global<S>&& rhs) {
559 static_assert(std::is_base_of<T, S>::value, "type check");
560 if (this != &rhs) {
561 this->Reset();
562 if (!rhs.IsEmpty()) {
563 this->slot() = rhs.slot();
564 api_internal::MoveGlobalReference(&rhs.slot(), &this->slot());
565 rhs.Clear();
566 }
567 }
568 return *this;
569 }
570
571 } // namespace v8
572
573 #endif // INCLUDE_V8_PERSISTENT_HANDLE_H_
574