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_LOCAL_HANDLE_H_ 6 #define INCLUDE_V8_LOCAL_HANDLE_H_ 7 8 #include <stddef.h> 9 10 #include <type_traits> 11 12 #include "v8-internal.h" // NOLINT(build/include_directory) 13 14 namespace v8 { 15 16 class Boolean; 17 template <class T> 18 class BasicTracedReference; 19 class Context; 20 class EscapableHandleScope; 21 template <class F> 22 class Eternal; 23 template <class F> 24 class FunctionCallbackInfo; 25 class Isolate; 26 template <class F> 27 class MaybeLocal; 28 template <class T> 29 class NonCopyablePersistentTraits; 30 class Object; 31 template <class T, class M = NonCopyablePersistentTraits<T>> 32 class Persistent; 33 template <class T> 34 class PersistentBase; 35 template <class F1, class F2, class F3> 36 class PersistentValueMapBase; 37 template <class F1, class F2> 38 class PersistentValueVector; 39 class Primitive; 40 class Private; 41 template <class F> 42 class PropertyCallbackInfo; 43 template <class F> 44 class ReturnValue; 45 class String; 46 template <class F> 47 class Traced; 48 template <class F> 49 class TracedGlobal; 50 template <class F> 51 class TracedReference; 52 class TracedReferenceBase; 53 class Utils; 54 55 namespace internal { 56 template <typename T> 57 class CustomArguments; 58 } // namespace internal 59 60 namespace api_internal { 61 // Called when ToLocalChecked is called on an empty Local. 62 V8_EXPORT void ToLocalEmpty(); 63 } // namespace api_internal 64 65 /** 66 * A stack-allocated class that governs a number of local handles. 67 * After a handle scope has been created, all local handles will be 68 * allocated within that handle scope until either the handle scope is 69 * deleted or another handle scope is created. If there is already a 70 * handle scope and a new one is created, all allocations will take 71 * place in the new handle scope until it is deleted. After that, 72 * new handles will again be allocated in the original handle scope. 73 * 74 * After the handle scope of a local handle has been deleted the 75 * garbage collector will no longer track the object stored in the 76 * handle and may deallocate it. The behavior of accessing a handle 77 * for which the handle scope has been deleted is undefined. 78 */ 79 class V8_EXPORT V8_NODISCARD HandleScope { 80 public: 81 explicit HandleScope(Isolate* isolate); 82 83 ~HandleScope(); 84 85 /** 86 * Counts the number of allocated handles. 87 */ 88 static int NumberOfHandles(Isolate* isolate); 89 GetIsolate()90 V8_INLINE Isolate* GetIsolate() const { 91 return reinterpret_cast<Isolate*>(isolate_); 92 } 93 94 HandleScope(const HandleScope&) = delete; 95 void operator=(const HandleScope&) = delete; 96 97 protected: 98 V8_INLINE HandleScope() = default; 99 100 void Initialize(Isolate* isolate); 101 102 static internal::Address* CreateHandle(internal::Isolate* isolate, 103 internal::Address value); 104 105 private: 106 // Declaring operator new and delete as deleted is not spec compliant. 107 // Therefore declare them private instead to disable dynamic alloc 108 void* operator new(size_t size); 109 void* operator new[](size_t size); 110 void operator delete(void*, size_t); 111 void operator delete[](void*, size_t); 112 113 internal::Isolate* isolate_; 114 internal::Address* prev_next_; 115 internal::Address* prev_limit_; 116 117 // Local::New uses CreateHandle with an Isolate* parameter. 118 template <class F> 119 friend class Local; 120 121 // Object::GetInternalField and Context::GetEmbedderData use CreateHandle with 122 // a HeapObject in their shortcuts. 123 friend class Object; 124 friend class Context; 125 }; 126 127 /** 128 * An object reference managed by the v8 garbage collector. 129 * 130 * All objects returned from v8 have to be tracked by the garbage collector so 131 * that it knows that the objects are still alive. Also, because the garbage 132 * collector may move objects, it is unsafe to point directly to an object. 133 * Instead, all objects are stored in handles which are known by the garbage 134 * collector and updated whenever an object moves. Handles should always be 135 * passed by value (except in cases like out-parameters) and they should never 136 * be allocated on the heap. 137 * 138 * There are two types of handles: local and persistent handles. 139 * 140 * Local handles are light-weight and transient and typically used in local 141 * operations. They are managed by HandleScopes. That means that a HandleScope 142 * must exist on the stack when they are created and that they are only valid 143 * inside of the HandleScope active during their creation. For passing a local 144 * handle to an outer HandleScope, an EscapableHandleScope and its Escape() 145 * method must be used. 146 * 147 * Persistent handles can be used when storing objects across several 148 * independent operations and have to be explicitly deallocated when they're no 149 * longer used. 150 * 151 * It is safe to extract the object stored in the handle by dereferencing the 152 * handle (for instance, to extract the Object* from a Local<Object>); the value 153 * will still be governed by a handle behind the scenes and the same rules apply 154 * to these values as to their handles. 155 */ 156 template <class T> 157 class Local { 158 public: Local()159 V8_INLINE Local() : val_(nullptr) {} 160 template <class S> Local(Local<S> that)161 V8_INLINE Local(Local<S> that) : val_(reinterpret_cast<T*>(*that)) { 162 /** 163 * This check fails when trying to convert between incompatible 164 * handles. For example, converting from a Local<String> to a 165 * Local<Number>. 166 */ 167 static_assert(std::is_base_of<T, S>::value, "type check"); 168 } 169 170 /** 171 * Returns true if the handle is empty. 172 */ IsEmpty()173 V8_INLINE bool IsEmpty() const { return val_ == nullptr; } 174 175 /** 176 * Sets the handle to be empty. IsEmpty() will then return true. 177 */ Clear()178 V8_INLINE void Clear() { val_ = nullptr; } 179 180 V8_INLINE T* operator->() const { return val_; } 181 182 V8_INLINE T* operator*() const { return val_; } 183 184 /** 185 * Checks whether two handles are the same. 186 * Returns true if both are empty, or if the objects to which they refer 187 * are identical. 188 * 189 * If both handles refer to JS objects, this is the same as strict equality. 190 * For primitives, such as numbers or strings, a `false` return value does not 191 * indicate that the values aren't equal in the JavaScript sense. 192 * Use `Value::StrictEquals()` to check primitives for equality. 193 */ 194 template <class S> 195 V8_INLINE bool operator==(const Local<S>& that) const { 196 internal::Address* a = reinterpret_cast<internal::Address*>(this->val_); 197 internal::Address* b = reinterpret_cast<internal::Address*>(that.val_); 198 if (a == nullptr) return b == nullptr; 199 if (b == nullptr) return false; 200 return *a == *b; 201 } 202 203 template <class S> 204 V8_INLINE bool operator==(const PersistentBase<S>& that) const { 205 internal::Address* a = reinterpret_cast<internal::Address*>(this->val_); 206 internal::Address* b = reinterpret_cast<internal::Address*>(that.val_); 207 if (a == nullptr) return b == nullptr; 208 if (b == nullptr) return false; 209 return *a == *b; 210 } 211 212 /** 213 * Checks whether two handles are different. 214 * Returns true if only one of the handles is empty, or if 215 * the objects to which they refer are different. 216 * 217 * If both handles refer to JS objects, this is the same as strict 218 * non-equality. For primitives, such as numbers or strings, a `true` return 219 * value does not indicate that the values aren't equal in the JavaScript 220 * sense. Use `Value::StrictEquals()` to check primitives for equality. 221 */ 222 template <class S> 223 V8_INLINE bool operator!=(const Local<S>& that) const { 224 return !operator==(that); 225 } 226 227 template <class S> 228 V8_INLINE bool operator!=(const Persistent<S>& that) const { 229 return !operator==(that); 230 } 231 232 /** 233 * Cast a handle to a subclass, e.g. Local<Value> to Local<Object>. 234 * This is only valid if the handle actually refers to a value of the 235 * target type. 236 */ 237 template <class S> Cast(Local<S> that)238 V8_INLINE static Local<T> Cast(Local<S> that) { 239 #ifdef V8_ENABLE_CHECKS 240 // If we're going to perform the type check then we have to check 241 // that the handle isn't empty before doing the checked cast. 242 if (that.IsEmpty()) return Local<T>(); 243 #endif 244 return Local<T>(T::Cast(*that)); 245 } 246 247 /** 248 * Calling this is equivalent to Local<S>::Cast(). 249 * In particular, this is only valid if the handle actually refers to a value 250 * of the target type. 251 */ 252 template <class S> As()253 V8_INLINE Local<S> As() const { 254 return Local<S>::Cast(*this); 255 } 256 257 /** 258 * Create a local handle for the content of another handle. 259 * The referee is kept alive by the local handle even when 260 * the original handle is destroyed/disposed. 261 */ New(Isolate * isolate,Local<T> that)262 V8_INLINE static Local<T> New(Isolate* isolate, Local<T> that) { 263 return New(isolate, that.val_); 264 } 265 New(Isolate * isolate,const PersistentBase<T> & that)266 V8_INLINE static Local<T> New(Isolate* isolate, 267 const PersistentBase<T>& that) { 268 return New(isolate, that.val_); 269 } 270 New(Isolate * isolate,const BasicTracedReference<T> & that)271 V8_INLINE static Local<T> New(Isolate* isolate, 272 const BasicTracedReference<T>& that) { 273 return New(isolate, *that); 274 } 275 276 private: 277 friend class TracedReferenceBase; 278 friend class Utils; 279 template <class F> 280 friend class Eternal; 281 template <class F> 282 friend class PersistentBase; 283 template <class F, class M> 284 friend class Persistent; 285 template <class F> 286 friend class Local; 287 template <class F> 288 friend class MaybeLocal; 289 template <class F> 290 friend class FunctionCallbackInfo; 291 template <class F> 292 friend class PropertyCallbackInfo; 293 friend class String; 294 friend class Object; 295 friend class Context; 296 friend class Isolate; 297 friend class Private; 298 template <class F> 299 friend class internal::CustomArguments; 300 friend Local<Primitive> Undefined(Isolate* isolate); 301 friend Local<Primitive> Null(Isolate* isolate); 302 friend Local<Boolean> True(Isolate* isolate); 303 friend Local<Boolean> False(Isolate* isolate); 304 friend class HandleScope; 305 friend class EscapableHandleScope; 306 template <class F1, class F2, class F3> 307 friend class PersistentValueMapBase; 308 template <class F1, class F2> 309 friend class PersistentValueVector; 310 template <class F> 311 friend class ReturnValue; 312 template <class F> 313 friend class Traced; 314 template <class F> 315 friend class TracedGlobal; 316 template <class F> 317 friend class BasicTracedReference; 318 template <class F> 319 friend class TracedReference; 320 Local(T * that)321 explicit V8_INLINE Local(T* that) : val_(that) {} New(Isolate * isolate,T * that)322 V8_INLINE static Local<T> New(Isolate* isolate, T* that) { 323 if (that == nullptr) return Local<T>(); 324 T* that_ptr = that; 325 internal::Address* p = reinterpret_cast<internal::Address*>(that_ptr); 326 return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle( 327 reinterpret_cast<internal::Isolate*>(isolate), *p))); 328 } 329 T* val_; 330 }; 331 332 #if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) 333 // Handle is an alias for Local for historical reasons. 334 template <class T> 335 using Handle = Local<T>; 336 #endif 337 338 /** 339 * A MaybeLocal<> is a wrapper around Local<> that enforces a check whether 340 * the Local<> is empty before it can be used. 341 * 342 * If an API method returns a MaybeLocal<>, the API method can potentially fail 343 * either because an exception is thrown, or because an exception is pending, 344 * e.g. because a previous API call threw an exception that hasn't been caught 345 * yet, or because a TerminateExecution exception was thrown. In that case, an 346 * empty MaybeLocal is returned. 347 */ 348 template <class T> 349 class MaybeLocal { 350 public: MaybeLocal()351 V8_INLINE MaybeLocal() : val_(nullptr) {} 352 template <class S> MaybeLocal(Local<S> that)353 V8_INLINE MaybeLocal(Local<S> that) : val_(reinterpret_cast<T*>(*that)) { 354 static_assert(std::is_base_of<T, S>::value, "type check"); 355 } 356 IsEmpty()357 V8_INLINE bool IsEmpty() const { return val_ == nullptr; } 358 359 /** 360 * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, 361 * |false| is returned and |out| is left untouched. 362 */ 363 template <class S> ToLocal(Local<S> * out)364 V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local<S>* out) const { 365 out->val_ = IsEmpty() ? nullptr : this->val_; 366 return !IsEmpty(); 367 } 368 369 /** 370 * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, 371 * V8 will crash the process. 372 */ ToLocalChecked()373 V8_INLINE Local<T> ToLocalChecked() { 374 if (V8_UNLIKELY(val_ == nullptr)) api_internal::ToLocalEmpty(); 375 return Local<T>(val_); 376 } 377 378 /** 379 * Converts this MaybeLocal<> to a Local<>, using a default value if this 380 * MaybeLocal<> is empty. 381 */ 382 template <class S> FromMaybe(Local<S> default_value)383 V8_INLINE Local<S> FromMaybe(Local<S> default_value) const { 384 return IsEmpty() ? default_value : Local<S>(val_); 385 } 386 387 private: 388 T* val_; 389 }; 390 391 /** 392 * A HandleScope which first allocates a handle in the current scope 393 * which will be later filled with the escape value. 394 */ 395 class V8_EXPORT V8_NODISCARD EscapableHandleScope : public HandleScope { 396 public: 397 explicit EscapableHandleScope(Isolate* isolate); 398 V8_INLINE ~EscapableHandleScope() = default; 399 400 /** 401 * Pushes the value into the previous scope and returns a handle to it. 402 * Cannot be called twice. 403 */ 404 template <class T> Escape(Local<T> value)405 V8_INLINE Local<T> Escape(Local<T> value) { 406 internal::Address* slot = 407 Escape(reinterpret_cast<internal::Address*>(*value)); 408 return Local<T>(reinterpret_cast<T*>(slot)); 409 } 410 411 template <class T> EscapeMaybe(MaybeLocal<T> value)412 V8_INLINE MaybeLocal<T> EscapeMaybe(MaybeLocal<T> value) { 413 return Escape(value.FromMaybe(Local<T>())); 414 } 415 416 EscapableHandleScope(const EscapableHandleScope&) = delete; 417 void operator=(const EscapableHandleScope&) = delete; 418 419 private: 420 // Declaring operator new and delete as deleted is not spec compliant. 421 // Therefore declare them private instead to disable dynamic alloc 422 void* operator new(size_t size); 423 void* operator new[](size_t size); 424 void operator delete(void*, size_t); 425 void operator delete[](void*, size_t); 426 427 internal::Address* Escape(internal::Address* escape_value); 428 internal::Address* escape_slot_; 429 }; 430 431 /** 432 * A SealHandleScope acts like a handle scope in which no handle allocations 433 * are allowed. It can be useful for debugging handle leaks. 434 * Handles can be allocated within inner normal HandleScopes. 435 */ 436 class V8_EXPORT V8_NODISCARD SealHandleScope { 437 public: 438 explicit SealHandleScope(Isolate* isolate); 439 ~SealHandleScope(); 440 441 SealHandleScope(const SealHandleScope&) = delete; 442 void operator=(const SealHandleScope&) = delete; 443 444 private: 445 // Declaring operator new and delete as deleted is not spec compliant. 446 // Therefore declare them private instead to disable dynamic alloc 447 void* operator new(size_t size); 448 void* operator new[](size_t size); 449 void operator delete(void*, size_t); 450 void operator delete[](void*, size_t); 451 452 internal::Isolate* const isolate_; 453 internal::Address* prev_limit_; 454 int prev_sealed_level_; 455 }; 456 457 } // namespace v8 458 459 #endif // INCLUDE_V8_LOCAL_HANDLE_H_ 460