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