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