blob: cbf87f949d09fa7194663ef486d4b0dddfd92d59 [file] [log] [blame]
// 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 "v8-internal.h" // NOLINT(build/include_directory)
namespace v8 {
class Boolean;
template <class T>
class BasicTracedReference;
class Context;
class EscapableHandleScope;
template <class F>
class Eternal;
template <class F>
class FunctionCallbackInfo;
class Isolate;
template <class F>
class MaybeLocal;
template <class T>
class NonCopyablePersistentTraits;
class Object;
template <class T, class M = NonCopyablePersistentTraits<T>>
class Persistent;
template <class T>
class PersistentBase;
template <class F1, class F2, class F3>
class PersistentValueMapBase;
template <class F1, class F2>
class PersistentValueVector;
class Primitive;
class Private;
template <class F>
class PropertyCallbackInfo;
template <class F>
class ReturnValue;
class String;
template <class F>
class Traced;
template <class F>
class TracedReference;
class TracedReferenceBase;
class Utils;
namespace internal {
template <typename T>
class CustomArguments;
} // 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;
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_;
// Local::New uses CreateHandle with an Isolate* parameter.
template <class F>
friend class Local;
// Object::GetInternalField and Context::GetEmbedderData use CreateHandle with
// a HeapObject in their shortcuts.
friend class Object;
friend class Context;
};
/**
* 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 Local {
public:
V8_INLINE Local() : val_(nullptr) {}
template <class S>
V8_INLINE Local(Local<S> that) : val_(reinterpret_cast<T*>(*that)) {
/**
* This check fails when trying to convert between incompatible
* handles. For example, converting from a Local<String> to a
* Local<Number>.
*/
static_assert(std::is_base_of<T, S>::value, "type check");
}
/**
* Returns true if the handle is empty.
*/
V8_INLINE bool IsEmpty() const { return val_ == nullptr; }
/**
* Sets the handle to be empty. IsEmpty() will then return true.
*/
V8_INLINE void Clear() { val_ = nullptr; }
V8_INLINE T* operator->() const { return val_; }
V8_INLINE T* operator*() const { return val_; }
/**
* Checks whether two handles are the same.
* Returns true if both are empty, or if the objects to which they refer
* are identical.
*
* If both handles refer to JS objects, this is the same as strict equality.
* For primitives, such as numbers or strings, a `false` 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 {
internal::Address* a = reinterpret_cast<internal::Address*>(this->val_);
internal::Address* b = reinterpret_cast<internal::Address*>(that.val_);
if (a == nullptr) return b == nullptr;
if (b == nullptr) return false;
return *a == *b;
}
template <class S>
V8_INLINE bool operator==(const PersistentBase<S>& that) const {
internal::Address* a = reinterpret_cast<internal::Address*>(this->val_);
internal::Address* b = reinterpret_cast<internal::Address*>(that.val_);
if (a == nullptr) return b == nullptr;
if (b == nullptr) return false;
return *a == *b;
}
/**
* Checks whether two handles are different.
* Returns true if only one of the handles is empty, or if
* the objects to which they refer are different.
*
* 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 !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.
*/
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>();
#endif
return Local<T>(T::Cast(*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.
*/
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.val_);
}
V8_INLINE static Local<T> New(Isolate* isolate,
const PersistentBase<T>& that) {
return New(isolate, that.val_);
}
V8_INLINE static Local<T> New(Isolate* isolate,
const BasicTracedReference<T>& that) {
return New(isolate, *that);
}
private:
friend class TracedReferenceBase;
friend class Utils;
template <class F>
friend class Eternal;
template <class F>
friend class PersistentBase;
template <class F, class M>
friend class Persistent;
template <class F>
friend class Local;
template <class F>
friend class MaybeLocal;
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;
template <class F1, class F2, class F3>
friend class PersistentValueMapBase;
template <class F1, class F2>
friend class PersistentValueVector;
template <class F>
friend class ReturnValue;
template <class F>
friend class Traced;
template <class F>
friend class BasicTracedReference;
template <class F>
friend class TracedReference;
explicit V8_INLINE Local(T* that) : val_(that) {}
V8_INLINE static Local<T> New(Isolate* isolate, T* that) {
if (that == nullptr) return Local<T>();
T* that_ptr = that;
internal::Address* p = reinterpret_cast<internal::Address*>(that_ptr);
return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(
reinterpret_cast<internal::Isolate*>(isolate), *p)));
}
T* val_;
};
#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:
V8_INLINE MaybeLocal() : val_(nullptr) {}
template <class S>
V8_INLINE MaybeLocal(Local<S> that) : val_(reinterpret_cast<T*>(*that)) {
static_assert(std::is_base_of<T, S>::value, "type check");
}
V8_INLINE bool IsEmpty() const { return val_ == nullptr; }
/**
* 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->val_ = IsEmpty() ? nullptr : this->val_;
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(val_ == nullptr)) api_internal::ToLocalEmpty();
return Local<T>(val_);
}
/**
* 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>(val_);
}
private:
T* val_;
};
/**
* 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 EscapableHandleScope : public HandleScope {
public:
explicit EscapableHandleScope(Isolate* isolate);
V8_INLINE ~EscapableHandleScope() = default;
/**
* Pushes the value into the previous scope and returns a handle to it.
* Cannot be called twice.
*/
template <class T>
V8_INLINE Local<T> Escape(Local<T> value) {
internal::Address* slot =
Escape(reinterpret_cast<internal::Address*>(*value));
return Local<T>(reinterpret_cast<T*>(slot));
}
template <class T>
V8_INLINE MaybeLocal<T> EscapeMaybe(MaybeLocal<T> value) {
return Escape(value.FromMaybe(Local<T>()));
}
EscapableHandleScope(const EscapableHandleScope&) = delete;
void operator=(const EscapableHandleScope&) = delete;
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::Address* Escape(internal::Address* escape_value);
internal::Address* escape_slot_;
};
/**
* 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;
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* const i_isolate_;
internal::Address* prev_limit_;
int prev_sealed_level_;
};
} // namespace v8
#endif // INCLUDE_V8_LOCAL_HANDLE_H_