f2ff0d5065
This CL changes MaybeLocal::ToLocal documentation to assign |out| with nullptr when the MaybeLocal is empty to be consistent with the behavior. Bug: v8:12845 Change-Id: I8145d6604c51b79f137b686b3e9b4f328534e0a8 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3616432 Commit-Queue: 王澳 <wangao.james@bytedance.com> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Cr-Commit-Position: refs/heads/main@{#80329}
456 lines
14 KiB
C++
456 lines
14 KiB
C++
// 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_
|