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