• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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