v8  10.1.124 (node 18.2.0)
V8 is Google's open source JavaScript engine
v8-local-handle.h
Go to the documentation of this file.
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>
19 class Context;
21 template <class F>
22 class Eternal;
23 template <class F>
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>
37 template <class F1, class F2>
39 class Primitive;
40 class Private;
41 template <class F>
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;
53 class Utils;
54 
55 namespace internal {
56 template <typename T>
58 } // namespace internal
59 
60 namespace api_internal {
61 // Called when ToLocalChecked is called on an empty Local.
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  */
80  public:
81  explicit HandleScope(Isolate* isolate);
82 
84 
85  /**
86  * Counts the number of allocated handles.
87  */
88  static int NumberOfHandles(Isolate* isolate);
89 
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:
159  V8_INLINE Local() : val_(nullptr) {}
160  template <class S>
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  */
173  V8_INLINE bool IsEmpty() const { return val_ == nullptr; }
174 
175  /**
176  * Sets the handle to be empty. IsEmpty() will then return true.
177  */
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>
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>
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  */
262  V8_INLINE static Local<T> New(Isolate* isolate, Local<T> that) {
263  return New(isolate, that.val_);
264  }
265 
266  V8_INLINE static Local<T> New(Isolate* isolate,
267  const PersistentBase<T>& that) {
268  return New(isolate, that.val_);
269  }
270 
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>
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 
321  explicit V8_INLINE Local(T* that) : val_(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:
351  V8_INLINE MaybeLocal() : val_(nullptr) {}
352  template <class S>
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 
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>
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  */
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>
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  */
396  public:
397  explicit EscapableHandleScope(Isolate* isolate);
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>
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>
413  return Escape(value.FromMaybe(Local<T>()));
414  }
415 
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  */
437  public:
438  explicit SealHandleScope(Isolate* isolate);
440 
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_