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 <type_traits> 15#include <utility> 16 17#include "v8-internal.h" // NOLINT(build/include_directory) 18#include "v8-local-handle.h" // NOLINT(build/include_directory) 19#include "v8-weak-callback-info.h" // NOLINT(build/include_directory) 20#include "v8config.h" // NOLINT(build/include_directory) 21 22namespace v8 { 23 24class Value; 25 26namespace internal { 27 28class BasicTracedReferenceExtractor; 29 30enum class GlobalHandleStoreMode { 31 kInitializingStore, 32 kAssigningStore, 33}; 34 35V8_EXPORT internal::Address* GlobalizeTracedReference( 36 internal::Isolate* isolate, internal::Address value, 37 internal::Address* slot, GlobalHandleStoreMode store_mode); 38V8_EXPORT void MoveTracedReference(internal::Address** from, 39 internal::Address** to); 40V8_EXPORT void CopyTracedReference(const internal::Address* const* from, 41 internal::Address** to); 42V8_EXPORT void DisposeTracedReference(internal::Address* global_handle); 43 44} // namespace internal 45 46/** 47 * An indirect handle, where the indirect pointer points to a GlobalHandles 48 * node. 49 */ 50class TracedReferenceBase : public IndirectHandleBase { 51 public: 52 /** 53 * If non-empty, destroy the underlying storage cell. |IsEmpty| will return 54 * true after this call. 55 */ 56 V8_INLINE void Reset(); 57 58 /** 59 * Construct a Local<Value> from this handle. 60 */ 61 V8_INLINE Local<Value> Get(Isolate* isolate) const { 62 if (IsEmpty()) return Local<Value>(); 63 return Local<Value>::New(isolate, this->value<Value>()); 64 } 65 66 /** 67 * Returns true if this TracedReference is empty, i.e., has not been 68 * assigned an object. This version of IsEmpty is thread-safe. 69 */ 70 bool IsEmptyThreadSafe() const { 71 return this->GetSlotThreadSafe() == nullptr; 72 } 73 74 /** 75 * Assigns a wrapper class ID to the handle. 76 */ 77 V8_INLINE void SetWrapperClassId(uint16_t class_id); 78 79 /** 80 * Returns the class ID previously assigned to this handle or 0 if no class ID 81 * was previously assigned. 82 */ 83 V8_INLINE uint16_t WrapperClassId() const; 84 85 protected: 86 V8_INLINE TracedReferenceBase() = default; 87 88 /** 89 * Update this reference in a thread-safe way. 90 */ 91 void SetSlotThreadSafe(void* new_val) { 92 reinterpret_cast<std::atomic<void*>*>(&slot())->store( 93 new_val, std::memory_order_relaxed); 94 } 95 96 /** 97 * Get this reference in a thread-safe way 98 */ 99 const void* GetSlotThreadSafe() const { 100 return reinterpret_cast<std::atomic<const void*> const*>(&slot())->load( 101 std::memory_order_relaxed); 102 } 103 104 V8_EXPORT void CheckValue() const; 105 106 friend class internal::BasicTracedReferenceExtractor; 107 template <typename F> 108 friend class Local; 109 template <typename U> 110 friend bool operator==(const TracedReferenceBase&, const Local<U>&); 111 friend bool operator==(const TracedReferenceBase&, 112 const TracedReferenceBase&); 113}; 114 115/** 116 * A traced handle with copy and move semantics. The handle is to be used 117 * together as part of GarbageCollected objects (see v8-cppgc.h) or from stack 118 * and specifies edges from C++ objects to JavaScript. 119 * 120 * The exact semantics are: 121 * - Tracing garbage collections using CppHeap. 122 * - Non-tracing garbage collections refer to 123 * |v8::EmbedderRootsHandler::IsRoot()| whether the handle should 124 * be treated as root or not. 125 * 126 * Note that the base class cannot be instantiated itself, use |TracedReference| 127 * instead. 128 */ 129template <typename T> 130class BasicTracedReference : public TracedReferenceBase { 131 public: 132 /** 133 * Construct a Local<T> from this handle. 134 */ 135 Local<T> Get(Isolate* isolate) const { return Local<T>::New(isolate, *this); } 136 137 template <class S> 138 V8_INLINE BasicTracedReference<S>& As() const { 139 return reinterpret_cast<BasicTracedReference<S>&>( 140 const_cast<BasicTracedReference<T>&>(*this)); 141 } 142 143 V8_DEPRECATE_SOON("Use Get to convert to Local instead") 144 V8_INLINE T* operator->() const { 145#ifdef V8_ENABLE_CHECKS 146 CheckValue(); 147#endif // V8_ENABLE_CHECKS 148 return this->template value<T>(); 149 } 150 151 V8_DEPRECATE_SOON("Use Get to convert to Local instead") 152 V8_INLINE T* operator*() const { return this->operator->(); } 153 154 private: 155 /** 156 * An empty BasicTracedReference without storage cell. 157 */ 158 BasicTracedReference() = default; 159 160 V8_INLINE static internal::Address* New( 161 Isolate* isolate, T* that, internal::Address** slot, 162 internal::GlobalHandleStoreMode store_mode); 163 164 template <typename F> 165 friend class Local; 166 friend class Object; 167 template <typename F> 168 friend class TracedReference; 169 template <typename F> 170 friend class BasicTracedReference; 171 template <typename F> 172 friend class ReturnValue; 173}; 174 175/** 176 * A traced handle without destructor that clears the handle. The embedder needs 177 * to ensure that the handle is not accessed once the V8 object has been 178 * reclaimed. For more details see BasicTracedReference. 179 */ 180template <typename T> 181class TracedReference : public BasicTracedReference<T> { 182 public: 183 using BasicTracedReference<T>::Reset; 184 185 /** 186 * An empty TracedReference without storage cell. 187 */ 188 V8_INLINE TracedReference() = default; 189 190 /** 191 * Construct a TracedReference from a Local. 192 * 193 * When the Local is non-empty, a new storage cell is created 194 * pointing to the same object. 195 */ 196 template <class S> 197 TracedReference(Isolate* isolate, Local<S> that) : BasicTracedReference<T>() { 198 this->slot() = 199 this->New(isolate, *that, &this->slot(), 200 internal::GlobalHandleStoreMode::kInitializingStore); 201 static_assert(std::is_base_of<T, S>::value, "type check"); 202 } 203 204 /** 205 * Move constructor initializing TracedReference from an 206 * existing one. 207 */ 208 V8_INLINE TracedReference(TracedReference&& other) noexcept { 209 // Forward to operator=. 210 *this = std::move(other); 211 } 212 213 /** 214 * Move constructor initializing TracedReference from an 215 * existing one. 216 */ 217 template <typename S> 218 V8_INLINE TracedReference(TracedReference<S>&& other) noexcept { 219 // Forward to operator=. 220 *this = std::move(other); 221 } 222 223 /** 224 * Copy constructor initializing TracedReference from an 225 * existing one. 226 */ 227 V8_INLINE TracedReference(const TracedReference& other) { 228 // Forward to operator=; 229 *this = other; 230 } 231 232 /** 233 * Copy constructor initializing TracedReference from an 234 * existing one. 235 */ 236 template <typename S> 237 V8_INLINE TracedReference(const TracedReference<S>& other) { 238 // Forward to operator=; 239 *this = other; 240 } 241 242 /** 243 * Move assignment operator initializing TracedReference from an existing one. 244 */ 245 V8_INLINE TracedReference& operator=(TracedReference&& rhs) noexcept; 246 247 /** 248 * Move assignment operator initializing TracedReference from an existing one. 249 */ 250 template <class S> 251 V8_INLINE TracedReference& operator=(TracedReference<S>&& rhs) noexcept; 252 253 /** 254 * Copy assignment operator initializing TracedReference from an existing one. 255 */ 256 V8_INLINE TracedReference& operator=(const TracedReference& rhs); 257 258 /** 259 * Copy assignment operator initializing TracedReference from an existing one. 260 */ 261 template <class S> 262 V8_INLINE TracedReference& operator=(const TracedReference<S>& rhs); 263 264 /** 265 * If non-empty, destroy the underlying storage cell and create a new one with 266 * the contents of other if other is non empty 267 */ 268 template <class S> 269 V8_INLINE void Reset(Isolate* isolate, const Local<S>& other); 270 271 template <class S> 272 V8_INLINE TracedReference<S>& As() const { 273 return reinterpret_cast<TracedReference<S>&>( 274 const_cast<TracedReference<T>&>(*this)); 275 } 276}; 277 278// --- Implementation --- 279template <class T> 280internal::Address* BasicTracedReference<T>::New( 281 Isolate* isolate, T* that, internal::Address** slot, 282 internal::GlobalHandleStoreMode store_mode) { 283 if (internal::ValueHelper::IsEmpty(that)) return nullptr; 284 return internal::GlobalizeTracedReference( 285 reinterpret_cast<internal::Isolate*>(isolate), 286 internal::ValueHelper::ValueAsAddress(that), 287 reinterpret_cast<internal::Address*>(slot), store_mode); 288} 289 290void TracedReferenceBase::Reset() { 291 if (IsEmpty()) return; 292 internal::DisposeTracedReference(slot()); 293 SetSlotThreadSafe(nullptr); 294} 295 296V8_INLINE bool operator==(const TracedReferenceBase& lhs, 297 const TracedReferenceBase& rhs) { 298 return internal::HandleHelper::EqualHandles(lhs, rhs); 299} 300 301template <typename U> 302V8_INLINE bool operator==(const TracedReferenceBase& lhs, 303 const v8::Local<U>& rhs) { 304 return internal::HandleHelper::EqualHandles(lhs, rhs); 305} 306 307template <typename U> 308V8_INLINE bool operator==(const v8::Local<U>& lhs, 309 const TracedReferenceBase& rhs) { 310 return rhs == lhs; 311} 312 313V8_INLINE bool operator!=(const TracedReferenceBase& lhs, 314 const TracedReferenceBase& rhs) { 315 return !(lhs == rhs); 316} 317 318template <typename U> 319V8_INLINE bool operator!=(const TracedReferenceBase& lhs, 320 const v8::Local<U>& rhs) { 321 return !(lhs == rhs); 322} 323 324template <typename U> 325V8_INLINE bool operator!=(const v8::Local<U>& lhs, 326 const TracedReferenceBase& rhs) { 327 return !(rhs == lhs); 328} 329 330template <class T> 331template <class S> 332void TracedReference<T>::Reset(Isolate* isolate, const Local<S>& other) { 333 static_assert(std::is_base_of<T, S>::value, "type check"); 334 this->Reset(); 335 if (other.IsEmpty()) return; 336 this->SetSlotThreadSafe( 337 this->New(isolate, *other, &this->slot(), 338 internal::GlobalHandleStoreMode::kAssigningStore)); 339} 340 341template <class T> 342template <class S> 343TracedReference<T>& TracedReference<T>::operator=( 344 TracedReference<S>&& rhs) noexcept { 345 static_assert(std::is_base_of<T, S>::value, "type check"); 346 *this = std::move(rhs.template As<T>()); 347 return *this; 348} 349 350template <class T> 351template <class S> 352TracedReference<T>& TracedReference<T>::operator=( 353 const TracedReference<S>& rhs) { 354 static_assert(std::is_base_of<T, S>::value, "type check"); 355 *this = rhs.template As<T>(); 356 return *this; 357} 358 359template <class T> 360TracedReference<T>& TracedReference<T>::operator=( 361 TracedReference&& rhs) noexcept { 362 if (this != &rhs) { 363 internal::MoveTracedReference(&rhs.slot(), &this->slot()); 364 } 365 return *this; 366} 367 368template <class T> 369TracedReference<T>& TracedReference<T>::operator=(const TracedReference& rhs) { 370 if (this != &rhs) { 371 this->Reset(); 372 if (!rhs.IsEmpty()) { 373 internal::CopyTracedReference(&rhs.slot(), &this->slot()); 374 } 375 } 376 return *this; 377} 378 379void TracedReferenceBase::SetWrapperClassId(uint16_t class_id) { 380 using I = internal::Internals; 381 if (IsEmpty()) return; 382 uint8_t* addr = 383 reinterpret_cast<uint8_t*>(slot()) + I::kTracedNodeClassIdOffset; 384 *reinterpret_cast<uint16_t*>(addr) = class_id; 385} 386 387uint16_t TracedReferenceBase::WrapperClassId() const { 388 using I = internal::Internals; 389 if (IsEmpty()) return 0; 390 uint8_t* addr = 391 reinterpret_cast<uint8_t*>(slot()) + I::kTracedNodeClassIdOffset; 392 return *reinterpret_cast<uint16_t*>(addr); 393} 394 395} // namespace v8 396 397#endif // INCLUDE_V8_TRACED_HANDLE_H_ 398