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
13namespace v8 {
14
15class Isolate;
16template <class K, class V, class T>
17class PersistentValueMapBase;
18template <class V, class T>
19class PersistentValueVector;
20template <class T>
21class Global;
22template <class T>
23class PersistentBase;
24template <class K, class V, class T>
25class PersistentValueMap;
26class Value;
27
28namespace api_internal {
29V8_EXPORT internal::Address* Eternalize(v8::Isolate* isolate, Value* handle);
30V8_EXPORT internal::Address* CopyGlobalReference(internal::Address* from);
31V8_EXPORT void DisposeGlobal(internal::Address* global_handle);
32V8_EXPORT void MakeWeak(internal::Address** location_addr);
33V8_EXPORT void* ClearWeak(internal::Address* location);
34V8_EXPORT void AnnotateStrongRetainer(internal::Address* location,
35                                      const char* label);
36V8_EXPORT internal::Address* GlobalizeReference(internal::Isolate* isolate,
37                                                internal::Address value);
38V8_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 */
46template <class T>
47class Eternal : public IndirectHandleBase {
48 public:
49  V8_INLINE Eternal() = default;
50
51  template <class S>
52  V8_INLINE Eternal(Isolate* isolate, Local<S> handle) {
53    Set(isolate, handle);
54  }
55
56  // Can only be safely called if already set.
57  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>
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
71namespace api_internal {
72V8_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 */
90template <class T>
91class 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
113  V8_INLINE Local<T> Get(Isolate* isolate) const {
114    return Local<T>::New(isolate, *this);
115  }
116
117  template <class S>
118  V8_INLINE bool operator==(const PersistentBase<S>& that) const {
119    return internal::HandleHelper::EqualHandles(*this, that);
120  }
121
122  template <class S>
123  V8_INLINE bool operator==(const Local<S>& that) const {
124    return internal::HandleHelper::EqualHandles(*this, that);
125  }
126
127  template <class S>
128  V8_INLINE bool operator!=(const PersistentBase<S>& that) const {
129    return !operator==(that);
130  }
131
132  template <class S>
133  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.
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
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 */
226template <class T>
227class NonCopyablePersistentTraits {
228 public:
229  using NonCopyablePersistent = Persistent<T, NonCopyablePersistentTraits<T>>;
230  static const bool kResetInDestructor = false;
231  template <class S, class M>
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 */
243template <class T>
244struct 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>
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 */
262template <class T, class M>
263class 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>
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>
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   */
300  V8_INLINE Persistent(const Persistent& that) : PersistentBase<T>() {
301    Copy(that);
302  }
303  template <class S, class M2>
304  V8_INLINE Persistent(const Persistent<S, M2>& that) : PersistentBase<T>() {
305    Copy(that);
306  }
307  V8_INLINE Persistent& operator=(const Persistent& that) {
308    Copy(that);
309    return *this;
310  }
311  template <class S, class M2>
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   */
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>
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>
340  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 */
363template <class T>
364class 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>
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>
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
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   */
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.
427template <class T>
428using UniquePersistent = Global<T>;
429
430/**
431 * Interface for iterating through all the persistent handles in the heap.
432 */
433class V8_EXPORT PersistentHandleVisitor {
434 public:
435  virtual ~PersistentHandleVisitor() = default;
436  virtual void VisitPersistentHandle(Persistent<Value>* value,
437                                     uint16_t class_id) {}
438};
439
440template <class T>
441internal::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
448template <class T, class M>
449template <class S, class M2>
450void 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
458template <class T>
459bool 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
465template <class T>
466void 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 */
476template <class T>
477template <class S>
478void 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 */
489template <class T>
490template <class S>
491void 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
499template <class T>
500template <typename P>
501V8_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
516template <class T>
517void PersistentBase<T>::SetWeak() {
518  api_internal::MakeWeak(&this->slot());
519}
520
521template <class T>
522template <typename P>
523P* PersistentBase<T>::ClearWeak() {
524  return reinterpret_cast<P*>(api_internal::ClearWeak(this->slot()));
525}
526
527template <class T>
528void PersistentBase<T>::AnnotateStrongRetainer(const char* label) {
529  api_internal::AnnotateStrongRetainer(this->slot(), label);
530}
531
532template <class T>
533void 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
540template <class T>
541uint16_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
548template <class T>
549Global<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
556template <class T>
557template <class S>
558Global<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