Viewing file: v8-local-handle.h (24.35 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file.
#ifndef INCLUDE_V8_LOCAL_HANDLE_H_ #define INCLUDE_V8_LOCAL_HANDLE_H_
#include <stddef.h>
#include <type_traits> #include <vector>
#include "v8-handle-base.h" // NOLINT(build/include_directory) #include "v8-internal.h" // NOLINT(build/include_directory)
namespace v8 {
template <class T> class LocalBase; template <class T> class Local; template <class T> class LocalVector; template <class F> class MaybeLocal;
template <class T> class Eternal; template <class T> class Global;
template <class T> class NonCopyablePersistentTraits; template <class T> class PersistentBase; template <class T, class M = NonCopyablePersistentTraits<T>> class Persistent;
class TracedReferenceBase; template <class T> class BasicTracedReference; template <class F> class TracedReference;
class Boolean; class Context; class EscapableHandleScope; template <class F> class FunctionCallbackInfo; class Isolate; class Object; template <class F1, class F2, class F3> class PersistentValueMapBase; class Primitive; class Private; template <class F> class PropertyCallbackInfo; template <class F> class ReturnValue; class String; template <class F> class Traced; class TypecheckWitness; class Utils;
namespace debug { class ConsoleCallArguments; }
namespace internal { template <typename T> class CustomArguments; template <typename T> class LocalUnchecked; class SamplingHeapProfiler; } // namespace internal
namespace api_internal { // Called when ToLocalChecked is called on an empty Local. V8_EXPORT void ToLocalEmpty(); } // namespace api_internal
/** * A stack-allocated class that governs a number of local handles. * After a handle scope has been created, all local handles will be * allocated within that handle scope until either the handle scope is * deleted or another handle scope is created. If there is already a * handle scope and a new one is created, all allocations will take * place in the new handle scope until it is deleted. After that, * new handles will again be allocated in the original handle scope. * * After the handle scope of a local handle has been deleted the * garbage collector will no longer track the object stored in the * handle and may deallocate it. The behavior of accessing a handle * for which the handle scope has been deleted is undefined. */ class V8_EXPORT V8_NODISCARD HandleScope { public: explicit HandleScope(Isolate* isolate);
~HandleScope();
/** * Counts the number of allocated handles. */ static int NumberOfHandles(Isolate* isolate);
V8_INLINE Isolate* GetIsolate() const { return reinterpret_cast<Isolate*>(i_isolate_); }
HandleScope(const HandleScope&) = delete; void operator=(const HandleScope&) = delete;
static internal::Address* CreateHandleForCurrentIsolate( internal::Address value);
protected: V8_INLINE HandleScope() = default;
void Initialize(Isolate* isolate);
static internal::Address* CreateHandle(internal::Isolate* i_isolate, internal::Address value);
private: // Declaring operator new and delete as deleted is not spec compliant. // Therefore declare them private instead to disable dynamic alloc void* operator new(size_t size); void* operator new[](size_t size); void operator delete(void*, size_t); void operator delete[](void*, size_t);
internal::Isolate* i_isolate_; internal::Address* prev_next_; internal::Address* prev_limit_; #ifdef V8_ENABLE_CHECKS int scope_level_ = 0; #endif
// LocalBase<T>::New uses CreateHandle with an Isolate* parameter. template <typename T> friend class LocalBase;
// Object::GetInternalField and Context::GetEmbedderData use CreateHandle with // a HeapObject in their shortcuts. friend class Object; friend class Context; };
/** * A base class for local handles. * Its implementation depends on whether direct handle support is enabled. * When it is, a local handle contains a direct pointer to the referenced * object, otherwise it contains an indirect pointer. */ #ifdef V8_ENABLE_DIRECT_HANDLE
template <typename T> class LocalBase : public api_internal::DirectHandleBase { protected: template <class F> friend class Local;
V8_INLINE LocalBase() = default;
V8_INLINE explicit LocalBase(internal::Address ptr) : DirectHandleBase(ptr) {}
template <typename S> V8_INLINE LocalBase(const LocalBase<S>& other) : DirectHandleBase(other) {}
V8_INLINE static LocalBase<T> New(Isolate* isolate, internal::Address value) { return LocalBase<T>(value); }
V8_INLINE static LocalBase<T> New(Isolate* isolate, T* that) { return LocalBase<T>::New(isolate, internal::ValueHelper::ValueAsAddress(that)); }
V8_INLINE static LocalBase<T> FromSlot(internal::Address* slot) { if (slot == nullptr) return LocalBase<T>(); return LocalBase<T>(*slot); }
V8_INLINE static LocalBase<T> FromRepr( internal::ValueHelper::InternalRepresentationType repr) { return LocalBase<T>(repr); } };
#else // !V8_ENABLE_DIRECT_HANDLE
template <typename T> class LocalBase : public api_internal::IndirectHandleBase { protected: template <class F> friend class Local;
V8_INLINE LocalBase() = default;
V8_INLINE explicit LocalBase(internal::Address* location) : IndirectHandleBase(location) {}
template <typename S> V8_INLINE LocalBase(const LocalBase<S>& other) : IndirectHandleBase(other) {}
V8_INLINE static LocalBase<T> New(Isolate* isolate, internal::Address value) { return LocalBase(HandleScope::CreateHandle( reinterpret_cast<internal::Isolate*>(isolate), value)); }
V8_INLINE static LocalBase<T> New(Isolate* isolate, T* that) { if (internal::ValueHelper::IsEmpty(that)) return LocalBase<T>(); return LocalBase<T>::New(isolate, internal::ValueHelper::ValueAsAddress(that)); }
V8_INLINE static LocalBase<T> FromSlot(internal::Address* slot) { return LocalBase<T>(slot); }
V8_INLINE static LocalBase<T> FromRepr( internal::ValueHelper::InternalRepresentationType repr) { return LocalBase<T>(repr); } };
#endif // V8_ENABLE_DIRECT_HANDLE
/** * An object reference managed by the v8 garbage collector. * * All objects returned from v8 have to be tracked by the garbage collector so * that it knows that the objects are still alive. Also, because the garbage * collector may move objects, it is unsafe to point directly to an object. * Instead, all objects are stored in handles which are known by the garbage * collector and updated whenever an object moves. Handles should always be * passed by value (except in cases like out-parameters) and they should never * be allocated on the heap. * * There are two types of handles: local and persistent handles. * * Local handles are light-weight and transient and typically used in local * operations. They are managed by HandleScopes. That means that a HandleScope * must exist on the stack when they are created and that they are only valid * inside of the HandleScope active during their creation. For passing a local * handle to an outer HandleScope, an EscapableHandleScope and its Escape() * method must be used. * * Persistent handles can be used when storing objects across several * independent operations and have to be explicitly deallocated when they're no * longer used. * * It is safe to extract the object stored in the handle by dereferencing the * handle (for instance, to extract the Object* from a Local<Object>); the value * will still be governed by a handle behind the scenes and the same rules apply * to these values as to their handles. */ template <class T> class V8_TRIVIAL_ABI Local : public LocalBase<T>, #ifdef V8_ENABLE_LOCAL_OFF_STACK_CHECK public api_internal::StackAllocated<true> #else public api_internal::StackAllocated<false> #endif { public: /** * Default constructor: Returns an empty handle. */ V8_INLINE Local() = default;
/** * Constructor for handling automatic up casting. * Ex. Local<Object> can be passed when Local<Value> is expected but not * the other way round. */ template <class S> requires std::is_base_of_v<T, S> V8_INLINE Local(Local<S> that) : LocalBase<T>(that) {}
V8_INLINE T* operator->() const { return this->template value<T>(); }
V8_INLINE T* operator*() const { return this->operator->(); }
/** * Checks whether two handles are equal or different. * They are equal iff they are both empty or they are both non-empty and the * objects to which they refer are physically equal. * * If both handles refer to JS objects, this is the same as strict * non-equality. For primitives, such as numbers or strings, a `true` return * value does not indicate that the values aren't equal in the JavaScript * sense. Use `Value::StrictEquals()` to check primitives for equality. */
template <class S> V8_INLINE bool operator==(const Local<S>& that) const { return internal::HandleHelper::EqualHandles(*this, that); }
template <class S> V8_INLINE bool operator==(const PersistentBase<S>& that) const { return internal::HandleHelper::EqualHandles(*this, that); }
template <class S> V8_INLINE bool operator!=(const Local<S>& that) const { return !operator==(that); }
template <class S> V8_INLINE bool operator!=(const Persistent<S>& that) const { return !operator==(that); }
/** * Cast a handle to a subclass, e.g. Local<Value> to Local<Object>. * This is only valid if the handle actually refers to a value of the * target type or if the handle is empty. */ template <class S> V8_INLINE static Local<T> Cast(Local<S> that) { #ifdef V8_ENABLE_CHECKS // If we're going to perform the type check then we have to check // that the handle isn't empty before doing the checked cast. if (that.IsEmpty()) return Local<T>(); T::Cast(that.template value<S>()); #endif return Local<T>(LocalBase<T>(that)); }
/** * Calling this is equivalent to Local<S>::Cast(). * In particular, this is only valid if the handle actually refers to a value * of the target type or if the handle is empty. */ template <class S> V8_INLINE Local<S> As() const { return Local<S>::Cast(*this); }
/** * Create a local handle for the content of another handle. * The referee is kept alive by the local handle even when * the original handle is destroyed/disposed. */ V8_INLINE static Local<T> New(Isolate* isolate, Local<T> that) { return New(isolate, that.template value<T, true>()); }
V8_INLINE static Local<T> New(Isolate* isolate, const PersistentBase<T>& that) { return New(isolate, that.template value<T, true>()); }
V8_INLINE static Local<T> New(Isolate* isolate, const BasicTracedReference<T>& that) { return New(isolate, that.template value<T, true>()); }
private: friend class TracedReferenceBase; friend class Utils; template <class F> friend class Eternal; template <class F> friend class Global; template <class F> friend class Local; template <class F> friend class MaybeLocal; template <class F, class M> friend class Persistent; template <class F> friend class FunctionCallbackInfo; template <class F> friend class PropertyCallbackInfo; friend class String; friend class Object; friend class Context; friend class Isolate; friend class Private; template <class F> friend class internal::CustomArguments; friend Local<Primitive> Undefined(Isolate* isolate); friend Local<Primitive> Null(Isolate* isolate); friend Local<Boolean> True(Isolate* isolate); friend Local<Boolean> False(Isolate* isolate); friend class HandleScope; friend class EscapableHandleScope; friend class InternalEscapableScope; template <class F1, class F2, class F3> friend class PersistentValueMapBase; template <class F> friend class ReturnValue; template <class F> friend class Traced; friend class internal::SamplingHeapProfiler; friend class internal::HandleHelper; friend class debug::ConsoleCallArguments; friend class internal::LocalUnchecked<T>;
explicit Local(no_checking_tag do_not_check) : LocalBase<T>(), StackAllocated(do_not_check) {} explicit Local(const Local<T>& other, no_checking_tag do_not_check) : LocalBase<T>(other), StackAllocated(do_not_check) {}
V8_INLINE explicit Local(const LocalBase<T>& other) : LocalBase<T>(other) {}
V8_INLINE static Local<T> FromRepr( internal::ValueHelper::InternalRepresentationType repr) { return Local<T>(LocalBase<T>::FromRepr(repr)); }
V8_INLINE static Local<T> FromSlot(internal::Address* slot) { return Local<T>(LocalBase<T>::FromSlot(slot)); }
#ifdef V8_ENABLE_DIRECT_HANDLE friend class TypecheckWitness;
V8_INLINE static Local<T> FromAddress(internal::Address ptr) { return Local<T>(LocalBase<T>(ptr)); } #endif // V8_ENABLE_DIRECT_HANDLE
V8_INLINE static Local<T> New(Isolate* isolate, internal::Address value) { return Local<T>(LocalBase<T>::New(isolate, value)); }
V8_INLINE static Local<T> New(Isolate* isolate, T* that) { return Local<T>(LocalBase<T>::New(isolate, that)); }
// Unsafe cast, should be avoided. template <class S> V8_INLINE Local<S> UnsafeAs() const { return Local<S>(LocalBase<S>(*this)); } };
namespace internal { // A local variant that is suitable for off-stack allocation. // Used internally by LocalVector<T>. Not to be used directly! template <typename T> class V8_TRIVIAL_ABI LocalUnchecked : public Local<T> { public: LocalUnchecked() : Local<T>(Local<T>::do_not_check) {}
#if defined(V8_ENABLE_LOCAL_OFF_STACK_CHECK) && V8_HAS_ATTRIBUTE_TRIVIAL_ABI // In this case, the check is also enforced in the copy constructor and we // need to suppress it. LocalUnchecked( const LocalUnchecked& other) noexcept // NOLINT(runtime/explicit) : Local<T>(other, Local<T>::do_not_check) {} LocalUnchecked& operator=(const LocalUnchecked&) noexcept = default; #endif
// Implicit conversion from Local. LocalUnchecked(const Local<T>& other) noexcept // NOLINT(runtime/explicit) : Local<T>(other, Local<T>::do_not_check) {} };
#ifdef V8_ENABLE_DIRECT_HANDLE // Off-stack allocated direct locals must be registered as strong roots. // For off-stack indirect locals, this is not necessary.
template <typename T> class StrongRootAllocator<LocalUnchecked<T>> : public StrongRootAllocatorBase { public: using value_type = LocalUnchecked<T>; static_assert(std::is_standard_layout_v<value_type>); static_assert(sizeof(value_type) == sizeof(Address));
template <typename HeapOrIsolateT> explicit StrongRootAllocator(HeapOrIsolateT* heap_or_isolate) : StrongRootAllocatorBase(heap_or_isolate) {} template <typename U> StrongRootAllocator(const StrongRootAllocator<U>& other) noexcept : StrongRootAllocatorBase(other) {}
value_type* allocate(size_t n) { return reinterpret_cast<value_type*>(allocate_impl(n)); } void deallocate(value_type* p, size_t n) noexcept { return deallocate_impl(reinterpret_cast<Address*>(p), n); } }; #endif // V8_ENABLE_DIRECT_HANDLE } // namespace internal
template <typename T> class LocalVector { private: using element_type = internal::LocalUnchecked<T>;
#ifdef V8_ENABLE_DIRECT_HANDLE using allocator_type = internal::StrongRootAllocator<element_type>;
static allocator_type make_allocator(Isolate* isolate) noexcept { return allocator_type(isolate); } #else using allocator_type = std::allocator<element_type>;
static allocator_type make_allocator(Isolate* isolate) noexcept { return allocator_type(); } #endif // V8_ENABLE_DIRECT_HANDLE
using vector_type = std::vector<element_type, allocator_type>;
public: using value_type = Local<T>; using reference = value_type&; using const_reference = const value_type&; using size_type = size_t; using difference_type = ptrdiff_t; using iterator = internal::WrappedIterator<typename vector_type::iterator, Local<T>>; using const_iterator = internal::WrappedIterator<typename vector_type::const_iterator, const Local<T>>;
explicit LocalVector(Isolate* isolate) : backing_(make_allocator(isolate)) {} LocalVector(Isolate* isolate, size_t n) : backing_(n, make_allocator(isolate)) {} explicit LocalVector(Isolate* isolate, std::initializer_list<Local<T>> init) : backing_(make_allocator(isolate)) { if (init.size() == 0) return; backing_.reserve(init.size()); backing_.insert(backing_.end(), init.begin(), init.end()); }
iterator begin() noexcept { return iterator(backing_.begin()); } const_iterator begin() const noexcept { return const_iterator(backing_.begin()); } iterator end() noexcept { return iterator(backing_.end()); } const_iterator end() const noexcept { return const_iterator(backing_.end()); }
size_t size() const noexcept { return backing_.size(); } bool empty() const noexcept { return backing_.empty(); } void reserve(size_t n) { backing_.reserve(n); } void shrink_to_fit() { backing_.shrink_to_fit(); }
Local<T>& operator[](size_t n) { return backing_[n]; } const Local<T>& operator[](size_t n) const { return backing_[n]; }
Local<T>& at(size_t n) { return backing_.at(n); } const Local<T>& at(size_t n) const { return backing_.at(n); }
Local<T>& front() { return backing_.front(); } const Local<T>& front() const { return backing_.front(); } Local<T>& back() { return backing_.back(); } const Local<T>& back() const { return backing_.back(); }
Local<T>* data() noexcept { return backing_.data(); } const Local<T>* data() const noexcept { return backing_.data(); }
iterator insert(const_iterator pos, const Local<T>& value) { return iterator(backing_.insert(pos.base(), value)); }
template <typename InputIt> iterator insert(const_iterator pos, InputIt first, InputIt last) { return iterator(backing_.insert(pos.base(), first, last)); }
iterator insert(const_iterator pos, std::initializer_list<Local<T>> init) { return iterator(backing_.insert(pos.base(), init.begin(), init.end())); }
LocalVector<T>& operator=(std::initializer_list<Local<T>> init) { backing_.clear(); backing_.reserve(init.size()); backing_.insert(backing_.end(), init.begin(), init.end()); return *this; }
void push_back(const Local<T>& x) { backing_.push_back(x); } void pop_back() { backing_.pop_back(); }
template <typename... Args> void emplace_back(Args&&... args) { backing_.push_back(value_type{std::forward<Args>(args)...}); }
void clear() noexcept { backing_.clear(); } void resize(size_t n) { backing_.resize(n); } void swap(LocalVector<T>& other) { backing_.swap(other.backing_); }
friend bool operator==(const LocalVector<T>& x, const LocalVector<T>& y) { return x.backing_ == y.backing_; } friend bool operator!=(const LocalVector<T>& x, const LocalVector<T>& y) { return x.backing_ != y.backing_; } friend bool operator<(const LocalVector<T>& x, const LocalVector<T>& y) { return x.backing_ < y.backing_; } friend bool operator>(const LocalVector<T>& x, const LocalVector<T>& y) { return x.backing_ > y.backing_; } friend bool operator<=(const LocalVector<T>& x, const LocalVector<T>& y) { return x.backing_ <= y.backing_; } friend bool operator>=(const LocalVector<T>& x, const LocalVector<T>& y) { return x.backing_ >= y.backing_; }
private: vector_type backing_; };
#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) // Handle is an alias for Local for historical reasons. template <class T> using Handle = Local<T>; #endif
/** * A MaybeLocal<> is a wrapper around Local<> that enforces a check whether * the Local<> is empty before it can be used. * * If an API method returns a MaybeLocal<>, the API method can potentially fail * either because an exception is thrown, or because an exception is pending, * e.g. because a previous API call threw an exception that hasn't been caught * yet, or because a TerminateExecution exception was thrown. In that case, an * empty MaybeLocal is returned. */ template <class T> class MaybeLocal { public: /** * Default constructor: Returns an empty handle. */ V8_INLINE MaybeLocal() = default; /** * Implicitly construct MaybeLocal from Local. */ template <class S> requires std::is_base_of_v<T, S> V8_INLINE MaybeLocal(Local<S> that) : local_(that) {} /** * Implicitly up-cast MaybeLocal<S> to MaybeLocal<T> if T is a base of S. */ template <class S> requires std::is_base_of_v<T, S> V8_INLINE MaybeLocal(MaybeLocal<S> that) : local_(that.local_) {}
V8_INLINE bool IsEmpty() const { return local_.IsEmpty(); }
/** * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, * |false| is returned and |out| is assigned with nullptr. */ template <class S> V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local<S>* out) const { *out = local_; return !IsEmpty(); }
/** * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, * V8 will crash the process. */ V8_INLINE Local<T> ToLocalChecked() { if (V8_UNLIKELY(IsEmpty())) api_internal::ToLocalEmpty(); return local_; }
/** * Converts this MaybeLocal<> to a Local<>, using a default value if this * MaybeLocal<> is empty. */ template <class S> V8_INLINE Local<S> FromMaybe(Local<S> default_value) const { return IsEmpty() ? default_value : Local<S>(local_); }
/** * Cast a handle to a subclass, e.g. MaybeLocal<Value> to MaybeLocal<Object>. * This is only valid if the handle actually refers to a value of the target * type or if the handle is empty. */ template <class S> V8_INLINE static MaybeLocal<T> Cast(MaybeLocal<S> that) { return MaybeLocal<T>{Local<T>::Cast(that.local_)}; }
/** * Calling this is equivalent to MaybeLocal<S>::Cast(). * In particular, this is only valid if the handle actually refers to a value * of the target type or if the handle is empty. */ template <class S> V8_INLINE MaybeLocal<S> As() const { return MaybeLocal<S>::Cast(*this); }
private: Local<T> local_;
template <typename S> friend class MaybeLocal; };
/** * A HandleScope which first allocates a handle in the current scope * which will be later filled with the escape value. */ class V8_EXPORT V8_NODISCARD EscapableHandleScopeBase : public HandleScope { public: explicit EscapableHandleScopeBase(Isolate* isolate); V8_INLINE ~EscapableHandleScopeBase() = default;
EscapableHandleScopeBase(const EscapableHandleScopeBase&) = delete; void operator=(const EscapableHandleScopeBase&) = delete; void* operator new(size_t size) = delete; void* operator new[](size_t size) = delete; void operator delete(void*, size_t) = delete; void operator delete[](void*, size_t) = delete;
protected: /** * Pushes the value into the previous scope and returns a handle to it. * Cannot be called twice. */ internal::Address* EscapeSlot(internal::Address* escape_value);
private: internal::Address* escape_slot_; };
class V8_EXPORT V8_NODISCARD EscapableHandleScope : public EscapableHandleScopeBase { public: explicit EscapableHandleScope(Isolate* isolate) : EscapableHandleScopeBase(isolate) {} V8_INLINE ~EscapableHandleScope() = default; template <class T> V8_INLINE Local<T> Escape(Local<T> value) { #ifdef V8_ENABLE_DIRECT_HANDLE return value; #else if (value.IsEmpty()) return value; return Local<T>::FromSlot(EscapeSlot(value.slot())); #endif }
template <class T> V8_INLINE MaybeLocal<T> EscapeMaybe(MaybeLocal<T> value) { return Escape(value.FromMaybe(Local<T>())); } };
/** * A SealHandleScope acts like a handle scope in which no handle allocations * are allowed. It can be useful for debugging handle leaks. * Handles can be allocated within inner normal HandleScopes. */ class V8_EXPORT V8_NODISCARD SealHandleScope { public: explicit SealHandleScope(Isolate* isolate); ~SealHandleScope();
SealHandleScope(const SealHandleScope&) = delete; void operator=(const SealHandleScope&) = delete; void* operator new(size_t size) = delete; void* operator new[](size_t size) = delete; void operator delete(void*, size_t) = delete; void operator delete[](void*, size_t) = delete;
private: internal::Isolate* const i_isolate_; internal::Address* prev_limit_; int prev_sealed_level_; };
} // namespace v8
#endif // INCLUDE_V8_LOCAL_HANDLE_H_
|