[isolate] Move HandleScopeImplementer to IsolateData

To allow faster access from hot generated code, this commit moves
the HandleScopeImplementer directly into IsolateData, enabling
access via the root register.

To support this migration, the following changes were made:
* Extracted HandleScopeImplementer from api.cc and api.h into its
  own dedicated files in src/handles/.
* Replaced std::optional for last_handle_before_persistent_block_
  with a raw pointer and a sentinel value to optimize the structure.
* Updated builtins and assembler code to access the implementer
  via IsolateFieldId rather than relying on an external reference.

Change-Id: I3a5d8997363e02d94f3b4628423624951b93bd8e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7881490
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#107761}
This commit is contained in:
Andreas Haas authored and v8-scoped@luci-project-accounts.iam.gserviceaccount.com committed 2026-06-03 08:29:15 -07:00
1 parent be01443eed
commit 5127e5f8d5
53 files changed
+561 -418

No files matched your search

+3
View File
@@ -1754,6 +1754,9 @@ filegroup(
"src/handles/global-handles.cc",
"src/handles/global-handles.h",
"src/handles/global-handles-inl.h",
"src/handles/handle-scope-implementer.cc",
"src/handles/handle-scope-implementer.h",
"src/handles/handle-scope-implementer-inl.h",
"src/handles/handles.cc",
"src/handles/handles.h",
"src/handles/handles-inl.h",
+3
View File
@@ -4075,6 +4075,8 @@ v8_header_set("v8_internal_headers") {
"src/flags/save-flags.h",
"src/handles/global-handles-inl.h",
"src/handles/global-handles.h",
"src/handles/handle-scope-implementer-inl.h",
"src/handles/handle-scope-implementer.h",
"src/handles/handles-inl.h",
"src/handles/handles.h",
"src/handles/local-handles-inl.h",
@@ -5997,6 +5999,7 @@ v8_cluster_source_set("v8_base_without_compiler") {
"src/flags/flags.cc",
"src/flags/save-flags.cc",
"src/handles/global-handles.cc",
"src/handles/handle-scope-implementer.cc",
"src/handles/handles.cc",
"src/handles/local-handles.cc",
"src/handles/persistent-handles.cc",
+5 -1
View File
@@ -1045,6 +1045,8 @@ class Internals {
static const int kThreadLocalTopSize = 30 * kApiSystemPointerSize;
static const int kHandleScopeDataSize =
2 * kApiSystemPointerSize + 2 * kApiInt32Size;
static const int kHandleScopeImplementerSize =
11 * kApiSystemPointerSize + kHandleScopeDataSize;
// ExternalPointerTable and TrustedPointerTable layout guarantees.
static const int kExternalEntityTableBasePointerOffset = 0;
@@ -1089,8 +1091,10 @@ class Internals {
kIsolateLongTaskStatsCounterOffset + kApiSizetSize;
static const int kIsolateHandleScopeDataOffset =
kIsolateThreadLocalTopOffset + kThreadLocalTopSize;
static const int kIsolateEmbedderDataOffset =
static const int kIsolateHandleScopeImplementerOffset =
kIsolateHandleScopeDataOffset + kHandleScopeDataSize;
static const int kIsolateEmbedderDataOffset =
kIsolateHandleScopeImplementerOffset + kHandleScopeImplementerSize;
#ifdef V8_COMPRESS_POINTERS
static const int kIsolateExternalPointerTableOffset =
kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize;
-14
View File
@@ -501,20 +501,6 @@ inline bool V8_EXPORT TryToCopyAndConvertArrayToCppBuffer(Local<Array> src,
uint32_t max_length) {
return CopyAndConvertArrayToCppBuffer<type_info_id, T>(src, dst, max_length);
}
namespace internal {
void HandleScopeImplementer::EnterContext(Tagged<NativeContext> context) {
entered_contexts_.push_back(context);
isolate_->set_last_entered_context(context);
}
DirectHandle<NativeContext> HandleScopeImplementer::LastEnteredContext() {
if (entered_contexts_.empty()) return {};
return direct_handle(entered_contexts_.back(), isolate_);
}
} // namespace internal
} // namespace v8
#endif // V8_API_API_INL_H_
+1 -132
View File
@@ -68,6 +68,7 @@
#include "src/execution/v8threads.h"
#include "src/execution/vm-state-inl.h"
#include "src/handles/global-handles.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/handles/persistent-handles.h"
#include "src/handles/shared-object-conveyor-handles.h"
#include "src/handles/traced-handles-inl.h"
@@ -12165,138 +12166,6 @@ std::shared_ptr<WasmStreaming> WasmStreaming::Unpack(Isolate* v8_isolate,
namespace internal {
const size_t HandleScopeImplementer::kEnteredContextsOffset =
offsetof(HandleScopeImplementer, entered_contexts_);
void HandleScopeImplementer::FreeThreadResources() { Free(); }
char* HandleScopeImplementer::ArchiveThread(char* storage) {
HandleScopeData* current = isolate_->handle_scope_data();
handle_scope_data_ = *current;
MemCopy(storage, this, sizeof(*this));
ResetAfterArchive();
current->Initialize();
return storage + ArchiveSpacePerThread();
}
int HandleScopeImplementer::ArchiveSpacePerThread() {
return sizeof(HandleScopeImplementer);
}
char* HandleScopeImplementer::RestoreThread(char* storage) {
MemCopy(this, storage, sizeof(*this));
*isolate_->handle_scope_data() = handle_scope_data_;
isolate_->set_last_entered_context(entered_contexts_.empty()
? Tagged<NativeContext>()
: entered_contexts_.back());
return storage + ArchiveSpacePerThread();
}
void HandleScopeImplementer::IterateThis(RootVisitor* v) {
#ifdef DEBUG
bool found_block_before_persistent = false;
#endif
// Iterate over all handles in the blocks except for the last.
for (int i = static_cast<int>(blocks()->size()) - 2; i >= 0; --i) {
Address* block = blocks()->at(i);
// Cast possibly-unrelated pointers to plain Address before comparing them
// to avoid undefined behavior.
if (HasPersistentScope() &&
(reinterpret_cast<Address>(
last_handle_before_persistent_block_.value()) <=
reinterpret_cast<Address>(&block[kHandleBlockSize])) &&
(reinterpret_cast<Address>(
last_handle_before_persistent_block_.value()) >=
reinterpret_cast<Address>(block))) {
v->VisitRootPointers(
Root::kHandleScope, nullptr, FullObjectSlot(block),
FullObjectSlot(last_handle_before_persistent_block_.value()));
DCHECK(!found_block_before_persistent);
#ifdef DEBUG
found_block_before_persistent = true;
#endif
} else {
v->VisitRootPointers(Root::kHandleScope, nullptr, FullObjectSlot(block),
FullObjectSlot(&block[kHandleBlockSize]));
}
}
DCHECK_EQ(HasPersistentScope() &&
last_handle_before_persistent_block_.value() != nullptr,
found_block_before_persistent);
// Iterate over live handles in the last block (if any).
if (!blocks()->empty()) {
v->VisitRootPointers(Root::kHandleScope, nullptr,
FullObjectSlot(blocks()->back()),
FullObjectSlot(handle_scope_data_.next));
}
v->VisitRootPointer(Root::kHandleScope, nullptr,
FullObjectSlot(isolate_->last_entered_context_address()));
saved_contexts_.shrink_to_fit();
if (!saved_contexts_.empty()) {
FullObjectSlot start(&saved_contexts_.front());
v->VisitRootPointers(Root::kHandleScope, nullptr, start,
start + static_cast<int>(saved_contexts_.size()));
}
entered_contexts_.shrink_to_fit();
if (!entered_contexts_.empty()) {
FullObjectSlot start(&entered_contexts_.front());
v->VisitRootPointers(Root::kHandleScope, nullptr, start,
start + static_cast<int>(entered_contexts_.size()));
}
}
void HandleScopeImplementer::Iterate(RootVisitor* v) {
HandleScopeData* current = isolate_->handle_scope_data();
handle_scope_data_ = *current;
IterateThis(v);
}
char* HandleScopeImplementer::Iterate(RootVisitor* v, char* storage) {
HandleScopeImplementer* scope_implementer =
reinterpret_cast<HandleScopeImplementer*>(storage);
scope_implementer->IterateThis(v);
return storage + ArchiveSpacePerThread();
}
std::unique_ptr<PersistentHandles> HandleScopeImplementer::DetachPersistent(
Address* first_block) {
std::unique_ptr<PersistentHandles> ph(new PersistentHandles(isolate()));
DCHECK(HasPersistentScope());
DCHECK_NOT_NULL(first_block);
Address* block_start;
do {
block_start = blocks_.back();
ph->blocks_.push_back(blocks_.back());
#if DEBUG
ph->ordered_blocks_.insert(blocks_.back());
#endif
blocks_.pop_back();
} while (block_start != first_block);
// ph->blocks_ now contains the blocks installed on the HandleScope stack
// since BeginPersistentScope was called, but in reverse order.
// Switch first and last blocks, such that the last block is the one
// that is potentially half full.
DCHECK(!ph->blocks_.empty());
std::swap(ph->blocks_.front(), ph->blocks_.back());
ph->block_next_ = isolate()->handle_scope_data()->next;
block_start = ph->blocks_.back();
ph->block_limit_ = block_start + kHandleBlockSize;
DCHECK_EQ(blocks_.empty(),
last_handle_before_persistent_block_.value() == nullptr);
last_handle_before_persistent_block_.reset();
return ph;
}
void InvokeAccessorGetterCallback(
v8::Local<v8::Name> property,
+1 -197
View File
@@ -15,7 +15,6 @@
#include "include/v8-typed-array.h"
#include "include/v8-wasm.h"
#include "src/base/contextual.h"
#include "src/execution/isolate.h"
#include "src/objects/bigint.h"
#include "src/objects/contexts.h"
#include "src/objects/js-array-buffer.h"
@@ -38,6 +37,7 @@ class Template;
enum class Intercepted : uint32_t;
namespace internal {
class Isolate;
class JSArrayBufferView;
class JSFinalizationRegistry;
} // namespace internal
@@ -318,202 +318,6 @@ namespace internal {
class PersistentHandles;
// This class is here in order to be able to declare it a friend of
// HandleScope. Moving these methods to be members of HandleScope would be
// neat in some ways, but it would expose internal implementation details in
// our public header file, which is undesirable.
//
// An isolate has a single instance of this class to hold the current thread's
// data. In multithreaded V8 programs this data is copied in and out of storage
// so that the currently executing thread always has its own copy of this
// data.
class HandleScopeImplementer {
public:
class V8_NODISCARD EnteredContextRewindScope {
public:
explicit EnteredContextRewindScope(HandleScopeImplementer* hsi)
: hsi_(hsi), saved_entered_context_count_(hsi->EnteredContextCount()) {}
~EnteredContextRewindScope() {
DCHECK_LE(saved_entered_context_count_, hsi_->EnteredContextCount());
while (saved_entered_context_count_ < hsi_->EnteredContextCount()) {
hsi_->LeaveContext();
}
}
private:
HandleScopeImplementer* hsi_;
size_t saved_entered_context_count_;
};
explicit HandleScopeImplementer(Isolate* isolate)
: isolate_(isolate), spare_(nullptr) {}
~HandleScopeImplementer() { DeleteArray(spare_); }
HandleScopeImplementer(const HandleScopeImplementer&) = delete;
HandleScopeImplementer& operator=(const HandleScopeImplementer&) = delete;
// Threading support for handle data.
static int ArchiveSpacePerThread();
char* RestoreThread(char* from);
char* ArchiveThread(char* to);
void FreeThreadResources();
// Garbage collection support.
V8_EXPORT_PRIVATE void Iterate(i::RootVisitor* v);
V8_EXPORT_PRIVATE static char* Iterate(i::RootVisitor* v, char* data);
inline internal::Address* GetSpareOrNewBlock();
inline void DeleteExtensions(internal::Address* prev_limit);
inline void EnterContext(Tagged<NativeContext> context);
inline void LeaveContext();
inline bool LastEnteredContextWas(Tagged<NativeContext> context);
inline size_t EnteredContextCount() const { return entered_contexts_.size(); }
// Returns the last entered context or an empty handle if no
// contexts have been entered.
inline DirectHandle<NativeContext> LastEnteredContext();
inline void SaveContext(Tagged<Context> context);
inline Tagged<Context> RestoreContext();
inline bool HasSavedContexts();
inline DetachableVector<Address*>* blocks() { return &blocks_; }
Isolate* isolate() const { return isolate_; }
void ReturnBlock(Address* block) {
DCHECK_NOT_NULL(block);
if (spare_ != nullptr) DeleteArray(spare_);
spare_ = block;
}
static const size_t kEnteredContextsOffset;
private:
void ResetAfterArchive() {
blocks_.detach();
entered_contexts_.detach();
saved_contexts_.detach();
spare_ = nullptr;
last_handle_before_persistent_block_.reset();
isolate_->set_last_entered_context(Tagged<NativeContext>());
}
void Free() {
DCHECK(blocks_.empty());
DCHECK(entered_contexts_.empty());
DCHECK(saved_contexts_.empty());
blocks_.free();
entered_contexts_.free();
saved_contexts_.free();
if (spare_ != nullptr) {
DeleteArray(spare_);
spare_ = nullptr;
}
DCHECK(isolate_->thread_local_top()->CallDepthIsZero());
}
void BeginPersistentScope() {
DCHECK(!last_handle_before_persistent_block_.has_value());
last_handle_before_persistent_block_ = isolate()->handle_scope_data()->next;
}
bool HasPersistentScope() const {
return last_handle_before_persistent_block_.has_value();
}
std::unique_ptr<PersistentHandles> DetachPersistent(Address* first_block);
Isolate* isolate_;
DetachableVector<Address*> blocks_;
// Used as a stack to keep track of entered contexts.
DetachableVector<Tagged<NativeContext>> entered_contexts_;
// Used as a stack to keep track of saved contexts.
DetachableVector<Tagged<Context>> saved_contexts_;
Address* spare_;
std::optional<Address*> last_handle_before_persistent_block_;
// This is only used for threading support.
HandleScopeData handle_scope_data_;
void IterateThis(RootVisitor* v);
char* RestoreThreadHelper(char* from);
char* ArchiveThreadHelper(char* to);
friend class HandleScopeImplementerOffsets;
friend class PersistentHandlesScope;
};
const int kHandleBlockSize = i::KB - 2; // fit in one page
void HandleScopeImplementer::SaveContext(Tagged<Context> context) {
saved_contexts_.push_back(context);
}
Tagged<Context> HandleScopeImplementer::RestoreContext() {
Tagged<Context> last_context = saved_contexts_.back();
saved_contexts_.pop_back();
return last_context;
}
bool HandleScopeImplementer::HasSavedContexts() {
return !saved_contexts_.empty();
}
void HandleScopeImplementer::LeaveContext() {
DCHECK(!entered_contexts_.empty());
entered_contexts_.pop_back();
isolate_->set_last_entered_context(entered_contexts_.empty()
? Tagged<NativeContext>()
: entered_contexts_.back());
}
bool HandleScopeImplementer::LastEnteredContextWas(
Tagged<NativeContext> context) {
return !entered_contexts_.empty() && entered_contexts_.back() == context;
}
// If there's a spare block, use it for growing the current scope.
internal::Address* HandleScopeImplementer::GetSpareOrNewBlock() {
internal::Address* block =
(spare_ != nullptr) ? spare_
: NewArray<internal::Address>(kHandleBlockSize);
spare_ = nullptr;
return block;
}
void HandleScopeImplementer::DeleteExtensions(internal::Address* prev_limit) {
while (!blocks_.empty()) {
internal::Address* block_start = blocks_.back();
internal::Address* block_limit = block_start + kHandleBlockSize;
// SealHandleScope may make the prev_limit to point inside the block.
// Cast possibly-unrelated pointers to plain Address before comparing them
// to avoid undefined behavior.
if (reinterpret_cast<Address>(block_start) <
reinterpret_cast<Address>(prev_limit) &&
reinterpret_cast<Address>(prev_limit) <=
reinterpret_cast<Address>(block_limit)) {
#ifdef ENABLE_LOCAL_HANDLE_ZAPPING
internal::HandleScope::ZapRange(prev_limit, block_limit);
#endif
break;
}
blocks_.pop_back();
#ifdef ENABLE_LOCAL_HANDLE_ZAPPING
internal::HandleScope::ZapRange(block_start, block_limit);
#endif
if (spare_ != nullptr) {
DeleteArray(spare_);
}
spare_ = block_start;
}
DCHECK((blocks_.empty() && prev_limit == nullptr) ||
(!blocks_.empty() && prev_limit != nullptr));
}
// This is a wrapper function called from CallApiGetter builtin when profiling
// or side-effect checking is enabled. It's supposed to set up the runtime
+1 -1
View File
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/api/api-inl.h"
#include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/compiler.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/logging/counters.h"
#include "src/numbers/conversions.h"
#include "src/objects/api-callbacks.h"
+20 -38
View File
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/api/api.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/codegen/code-stub-assembler-inl.h"
#include "src/execution/microtask-queue.h"
#include "src/handles/handle-scope-implementer.h"
#include "src/objects/js-generator-inl.h"
#include "src/objects/js-weak-refs.h"
#include "src/objects/microtask-inl.h"
@@ -150,18 +150,11 @@ void MicrotaskQueueBuiltinsAssembler::PrepareForContext(
if (DEBUG_BOOL) {
// Postcondition: entered_contexts_.back() == native_context, i.e. the
// context the microtask is about to run in is visible to API callers.
using ContextStack = DetachableVector<Context>;
TNode<IntPtrT> size_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kSizeOffset);
TNode<IntPtrT> data_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kDataOffset);
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref));
TNode<IntPtrT> size = Load<IntPtrT>(hsi, size_offset);
TNode<IntPtrT> size =
Load<IntPtrT>(IsolateField(IsolateFieldId::kEnteredContextCount));
CSA_CHECK(this, IntPtrGreaterThan(size, IntPtrConstant(0)));
TNode<RawPtrT> data = Load<RawPtrT>(hsi, data_offset);
TNode<RawPtrT> data =
Load<RawPtrT>(IsolateField(IsolateFieldId::kEnteredContextData));
TNode<Object> back = LoadFullTagged(
data, TimesSystemPointerSize(IntPtrSub(size, IntPtrConstant(1))));
CSA_CHECK(this, TaggedEqual(back, native_context));
@@ -581,42 +574,32 @@ void MicrotaskQueueBuiltinsAssembler::SetCurrentContext(
}
TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetEnteredContextCount() {
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref));
using ContextStack = DetachableVector<Context>;
TNode<IntPtrT> size_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kSizeOffset);
return Load<IntPtrT>(hsi, size_offset);
return Load<IntPtrT>(IsolateField(IsolateFieldId::kEnteredContextCount));
}
void MicrotaskQueueBuiltinsAssembler::EnterContext(
TNode<Context> native_context) {
CSA_DCHECK(this, IsNativeContext(native_context));
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref));
TNode<RawPtrT> hsi = ReinterpretCast<RawPtrT>(
IsolateField(IsolateFieldId::kHandleScopeImplementer));
using ContextStack = DetachableVector<Context>;
TNode<IntPtrT> capacity_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kCapacityOffset);
TNode<IntPtrT> size_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kSizeOffset);
TNode<IntPtrT> capacity = Load<IntPtrT>(hsi, capacity_offset);
TNode<IntPtrT> size = Load<IntPtrT>(hsi, size_offset);
TNode<IntPtrT> capacity =
Load<IntPtrT>(IsolateField(IsolateFieldId::kEnteredContextCapacity));
TNode<IntPtrT> size =
Load<IntPtrT>(IsolateField(IsolateFieldId::kEnteredContextCount));
Label if_append(this), if_grow(this, Label::kDeferred), done(this);
Branch(WordEqual(size, capacity), &if_grow, &if_append);
BIND(&if_append);
{
TNode<IntPtrT> data_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kDataOffset);
TNode<RawPtrT> data = Load<RawPtrT>(hsi, data_offset);
TNode<RawPtrT> data =
Load<RawPtrT>(IsolateField(IsolateFieldId::kEnteredContextData));
StoreFullTaggedNoWriteBarrier(data, TimesSystemPointerSize(size),
native_context);
@@ -645,8 +628,8 @@ void MicrotaskQueueBuiltinsAssembler::EnterContext(
void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
TNode<IntPtrT> saved_entered_context_count) {
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref));
TNode<RawPtrT> hsi = ReinterpretCast<RawPtrT>(
IsolateField(IsolateFieldId::kHandleScopeImplementer));
using ContextStack = DetachableVector<Context>;
TNode<IntPtrT> size_offset =
@@ -654,7 +637,8 @@ void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
ContextStack::kSizeOffset);
if (DEBUG_BOOL) {
TNode<IntPtrT> size = Load<IntPtrT>(hsi, size_offset);
TNode<IntPtrT> size =
Load<IntPtrT>(IsolateField(IsolateFieldId::kEnteredContextCount));
CSA_CHECK(this, IntPtrLessThanOrEqual(saved_entered_context_count, size));
}
@@ -672,10 +656,8 @@ void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
}
BIND(&if_not_empty);
{
TNode<IntPtrT> data_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kDataOffset);
TNode<RawPtrT> data = Load<RawPtrT>(hsi, data_offset);
TNode<RawPtrT> data =
Load<RawPtrT>(IsolateField(IsolateFieldId::kEnteredContextData));
TNode<IntPtrT> index =
IntPtrSub(saved_entered_context_count, IntPtrConstant(1));
TNode<Object> last_context =
+1 -1
View File
@@ -4,7 +4,6 @@
#include "src/builtins/builtins.h"
#include "src/api/api-inl.h"
#include "src/base/logging.h"
#include "src/builtins/builtins-descriptors.h"
#include "src/builtins/builtins-inl.h"
@@ -15,6 +14,7 @@
#include "src/codegen/macro-assembler.h"
#include "src/diagnostics/code-tracer.h"
#include "src/execution/isolate.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/heap/combined-heap.h"
#include "src/interpreter/bytecodes.h"
#include "src/logging/code-events.h" // For CodeCreateEvent.
+1
View File
@@ -29,6 +29,7 @@
#include "src/objects/heap-number.h"
#include "src/objects/heap-object.h"
#include "src/objects/hole.h"
#include "src/objects/js-collection.h"
#include "src/objects/js-function.h"
#include "src/objects/js-objects.h"
#include "src/objects/js-promise.h"
+2 -6
View File
@@ -8,7 +8,7 @@
#include "include/cppgc/allocation.h"
#include "include/v8-fast-api-calls.h"
#include "src/api/api-inl.h"
#include "src/api/api.h"
#include "src/base/bits.h"
#include "src/base/ieee754.h"
#include "src/base/logging.h"
@@ -23,6 +23,7 @@
#include "src/execution/isolate.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/simulator.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/heap.h"
#include "src/ic/stub-cache.h"
@@ -281,11 +282,6 @@ ExternalReference ExternalReference::isolate_address() {
return ExternalReference(IsolateFieldId::kIsolateAddress);
}
ExternalReference ExternalReference::handle_scope_implementer_address(
Isolate* isolate) {
return ExternalReference(isolate->handle_scope_implementer_address());
}
#ifdef V8_ENABLE_SANDBOX
ExternalReference ExternalReference::sandbox_base_address() {
return ExternalReference(Sandbox::current()->base_address());
-2
View File
@@ -28,8 +28,6 @@ enum class IsolateFieldId : uint8_t;
// TODO(ishell): Remove entries accessible via IsolateFieldId.
#define EXTERNAL_REFERENCE_LIST_WITH_ISOLATE(V) \
V(isolate_address, "isolate") \
V(handle_scope_implementer_address, \
"Isolate::handle_scope_implementer_address") \
V(address_of_interpreter_entry_trampoline_instruction_start, \
"Address of the InterpreterEntryTrampoline instruction start") \
V(interpreter_dispatch_table_address, "Interpreter::dispatch_table_address") \
+1
View File
@@ -9,6 +9,7 @@
#include "src/compiler/js-operator.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/write-barrier-kind.h"
#include "src/objects/bigint.h"
#include "src/objects/elements-kind.h"
#include "src/objects/js-objects.h"
#include "src/objects/property-details.h"
+1
View File
@@ -15,6 +15,7 @@
#include "src/objects/instance-type.h"
#include "src/objects/object-list-macros.h"
#include "src/objects/property-details.h"
#include "src/objects/shared-function-info.h"
#include "src/utils/boxed-float.h"
#include "src/zone/zone-compact-set.h"
+1
View File
@@ -7,6 +7,7 @@
#include <functional>
#include <optional>
#include "src/api/api.h"
#include "src/base/container-utils.h"
#include "src/base/small-vector.h"
#include "src/builtins/builtins-promise.h"
@@ -6,6 +6,7 @@
#include <optional>
#include "src/api/api.h"
#include "src/base/iterator.h"
#include "src/base/logging.h"
#include "src/builtins/accessors.h"
+11
View File
@@ -15,11 +15,22 @@
#include "src/codegen/tnode.h"
#include "src/compiler/turboshaft/fast-hash.h"
#include "src/compiler/turboshaft/representations.h"
#include "src/objects/bigint.h"
#include "src/objects/contexts.h"
#include "src/objects/heap-number.h"
#include "src/objects/js-array-buffer.h"
#include "src/objects/js-collection.h"
#include "src/objects/js-function.h"
#include "src/objects/js-generator.h"
#include "src/objects/js-promise.h"
#include "src/objects/js-proxy.h"
#include "src/objects/objects.h"
#include "src/objects/oddball.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/source-text-module.h"
#include "src/objects/string.h"
#include "src/objects/tagged.h"
#include "src/objects/templates.h"
#define TURBOSHAFT_ALLOW_IMPLICIT_OPINDEX_INITIALIZATION_FOR_V 1
@@ -9,6 +9,7 @@
#include <optional>
#include <type_traits>
#include "src/api/api.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/small-vector.h"
+1
View File
@@ -23,6 +23,7 @@
#include "include/v8-container.h"
#include "include/v8-template.h"
#include "src/api/api.h"
namespace v8 {
+1
View File
@@ -11,6 +11,7 @@
#include <vector>
#include "src/api/api.h"
#include "src/base/platform/platform-win32.h"
#include "src/base/platform/platform.h"
#include "src/d8/d8.h"
+14
View File
@@ -63,6 +63,8 @@ static constexpr int kFastCCallAlignmentPaddingCount = 1;
V(LongTaskStatsCounter, kSizetSize, long_task_stats_counter) \
V(ThreadLocalTop, ThreadLocalTop::kSizeInBytes, thread_local_top) \
V(HandleScopeData, HandleScopeData::kSizeInBytes, handle_scope_data) \
V(HandleScopeImplementer, HandleScopeImplementer::kSizeInBytes, \
handle_scope_implementer) \
V(EmbedderData, Internals::kNumIsolateDataSlots* kSystemPointerSize, \
embedder_data) \
ISOLATE_DATA_FIELDS_POINTER_COMPRESSION(V) \
@@ -172,6 +174,18 @@ static constexpr int kFastCCallAlignmentPaddingCount = 1;
V(HandleScopeLimit, handle_scope_limit, handle_scope_data, \
offsetof(HandleScopeData, limit)) \
\
/* HandleScopeImplementer fields. */ \
V(EnteredContextCount, entered_context_count, handle_scope_implementer, \
static_cast<int>(HandleScopeImplementer::kEnteredContextsOffset + \
DetachableVectorBase::kSizeOffset)) \
V(EnteredContextCapacity, entered_context_capacity, \
handle_scope_implementer, \
static_cast<int>(HandleScopeImplementer::kEnteredContextsOffset + \
DetachableVectorBase::kCapacityOffset)) \
V(EnteredContextData, entered_context_data, handle_scope_implementer, \
static_cast<int>(HandleScopeImplementer::kEnteredContextsOffset + \
DetachableVectorBase::kDataOffset)) \
\
/* NewAllocationInfo fields. */ \
V(NewAllocationInfoStart, new_allocation_info_start, new_allocation_info, \
LinearAllocationArea::StartOffset()) \
+11 -1
View File
@@ -11,6 +11,7 @@
#include "src/execution/isolate-data-fields.h"
#include "src/execution/stack-guard.h"
#include "src/execution/thread-local-top.h"
#include "src/handles/handle-scope-implementer.h"
#include "src/heap/linear-allocation-area.h"
#include "src/init/isolate-group.h"
#include "src/roots/roots.h"
@@ -93,7 +94,8 @@ class IsolateData final {
#ifdef V8_COMPRESS_POINTERS
cage_base_(group->GetPtrComprCageBase()),
#endif
stack_guard_(isolate)
stack_guard_(isolate),
handle_scope_implementer_()
#ifdef V8_ENABLE_SANDBOX
,
trusted_cage_base_(group->GetTrustedPtrComprCageBase())
@@ -200,6 +202,12 @@ class IsolateData final {
}
ThreadLocalTop& thread_local_top() { return thread_local_top_; }
ThreadLocalTop const& thread_local_top() const { return thread_local_top_; }
HandleScopeImplementer* handle_scope_implementer() {
return &handle_scope_implementer_;
}
const HandleScopeImplementer* handle_scope_implementer() const {
return &handle_scope_implementer_;
}
Address* builtin_entry_table() { return builtin_entry_table_; }
Address* builtin_table() { return builtin_table_; }
#if V8_ENABLE_WEBASSEMBLY
@@ -379,6 +387,7 @@ class IsolateData final {
ThreadLocalTop thread_local_top_;
HandleScopeData handle_scope_data_;
HandleScopeImplementer handle_scope_implementer_;
// These fields are accessed through the API, offsets must be kept in sync
// with v8::internal::Internals (in include/v8-internal.h) constants. The
@@ -471,6 +480,7 @@ class IsolateData final {
V8_INLINE static void AssertPredictableLayout();
friend class HandleScopeImplementer;
friend class Isolate;
friend class Heap;
FRIEND_TEST(HeapTest, ExternalLimitDefault);
+5 -4
View File
@@ -682,6 +682,8 @@ void Isolate::Iterate(RootVisitor* v, ThreadLocalTop* thread) {
FullObjectSlot(&thread->pending_message_));
v->VisitRootPointer(Root::kStackRoots, nullptr,
FullObjectSlot(&thread->context_));
v->VisitRootPointer(Root::kStackRoots, nullptr,
FullObjectSlot(&thread->last_entered_context_));
for (v8::TryCatch* block = thread->try_catch_handler_; block != nullptr;
block = block->next_) {
@@ -4857,6 +4859,9 @@ void Isolate::CheckIsolateLayout() {
static_assert(
static_cast<int>(OFFSET_OF(Isolate, isolate_data_.handle_scope_data_)) ==
Internals::kIsolateHandleScopeDataOffset);
static_assert(static_cast<int>(OFFSET_OF(
Isolate, isolate_data_.handle_scope_implementer_)) ==
Internals::kIsolateHandleScopeImplementerOffset);
static_assert(
static_cast<int>(OFFSET_OF(Isolate, isolate_data_.embedder_data_)) ==
Internals::kIsolateEmbedderDataOffset);
@@ -5317,9 +5322,6 @@ Isolate::~Isolate() {
delete v8_file_logger_;
v8_file_logger_ = nullptr;
delete handle_scope_implementer_;
handle_scope_implementer_ = nullptr;
delete code_tracer();
set_code_tracer(nullptr);
@@ -6118,7 +6120,6 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
global_handles_ = new GlobalHandles(this);
eternal_handles_ = new EternalHandles();
bootstrapper_ = new Bootstrapper(this);
handle_scope_implementer_ = new HandleScopeImplementer(this);
load_stub_cache_ = new StubCache(this);
store_stub_cache_ = new StubCache(this);
define_own_stub_cache_ = new StubCache(this);
+13 -8
View File
@@ -1216,7 +1216,17 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
OFFSET_OF(Isolate, heap_));
}
static Isolate* FromHandleScopeImplementer(
const HandleScopeImplementer* handle_scope_implementer) {
Address hsi_addr = reinterpret_cast<Address>(handle_scope_implementer);
Address isolate_addr = hsi_addr -
IsolateData::kHandleScopeImplementerOffset -
OFFSET_OF(Isolate, isolate_data_);
return reinterpret_cast<Isolate*>(isolate_addr);
}
const IsolateData* isolate_data() const { return &isolate_data_; }
IsolateData* isolate_data() { return &isolate_data_; }
// When pointer compression is on, this is the base address of the pointer
@@ -1399,9 +1409,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
return &isolate_data_.handle_scope_data_;
}
HandleScopeImplementer* handle_scope_implementer() const {
DCHECK(handle_scope_implementer_);
return handle_scope_implementer_;
HandleScopeImplementer* handle_scope_implementer() {
return isolate_data_.handle_scope_implementer();
}
UnicodeCache* unicode_cache() const { return unicode_cache_; }
@@ -1901,10 +1910,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
javascript_execution_counter_++;
}
Address handle_scope_implementer_address() {
return reinterpret_cast<Address>(&handle_scope_implementer_);
}
void SetReleaseCppHeapCallback(v8::Isolate::ReleaseCppHeapCallback callback);
void RunReleaseCppHeapCallback(std::unique_ptr<v8::CppHeap> cpp_heap);
@@ -2652,7 +2657,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
StackTrace::StackTraceOptions stack_trace_for_uncaught_exceptions_options_ =
StackTrace::kOverview;
DescriptorLookupCache* descriptor_lookup_cache_ = nullptr;
HandleScopeImplementer* handle_scope_implementer_ = nullptr;
UnicodeCache* unicode_cache_ = nullptr;
AccountingAllocator* allocator_ = nullptr;
InnerPointerToCodeCache* inner_pointer_to_code_cache_ = nullptr;
@@ -3016,6 +3020,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
bool is_frozen_ = false;
friend class HandleScopeImplementer;
friend class GlobalSafepoint;
friend class heap::HeapTester;
friend class IsolateForPointerCompression;
+1
View File
@@ -11,6 +11,7 @@
#include "src/api/api-inl.h"
#include "src/base/logging.h"
#include "src/execution/isolate.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/handles/handles-inl.h"
#include "src/objects/microtask-inl.h"
#include "src/objects/visitors.h"
+1
View File
@@ -21,6 +21,7 @@ void ThreadLocalTop::Clear() {
c_entry_fp_ = kNullAddress;
c_function_ = kNullAddress;
context_ = {};
last_entered_context_ = {};
topmost_script_having_context_ = {};
thread_id_ = ThreadId();
pending_handler_entrypoint_ = kNullAddress;
+1 -1
View File
@@ -5,11 +5,11 @@
#include "src/execution/v8threads.h"
#include "include/v8-locker.h"
#include "src/api/api.h"
#include "src/debug/debug.h"
#include "src/execution/execution.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/stack-guard.h"
#include "src/handles/handle-scope-implementer.h"
#include "src/init/bootstrapper.h"
#include "src/objects/visitors.h"
#include "src/regexp/regexp-stack.h"
+1
View File
@@ -5,6 +5,7 @@
#include "src/extensions/statistics-extension.h"
#include "include/v8-template.h"
#include "src/api/api.h"
#include "src/common/assert-scope.h"
#include "src/execution/isolate.h"
#include "src/heap/heap-inl.h" // crbug.com/v8/8499
+108
View File
@@ -0,0 +1,108 @@
// Copyright 2026 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 V8_HANDLES_HANDLE_SCOPE_IMPLEMENTER_INL_H_
#define V8_HANDLES_HANDLE_SCOPE_IMPLEMENTER_INL_H_
#include "src/handles/handle-scope-implementer.h"
#include "src/handles/handles-inl.h"
#include "src/heap/heap-inl.h"
namespace v8 {
namespace internal {
void HandleScopeImplementer::SaveContext(Tagged<Context> context) {
saved_contexts_.push_back(context);
}
Tagged<Context> HandleScopeImplementer::RestoreContext() {
Tagged<Context> last_context = saved_contexts_.back();
saved_contexts_.pop_back();
return last_context;
}
bool HandleScopeImplementer::HasSavedContexts() {
return !saved_contexts_.empty();
}
void HandleScopeImplementer::LeaveContext() {
DCHECK(!entered_contexts_.empty());
entered_contexts_.pop_back();
Isolate::FromHandleScopeImplementer(this)->set_last_entered_context(
entered_contexts_.empty() ? Tagged<NativeContext>()
: entered_contexts_.back());
}
HandleScopeImplementer::EnteredContextRewindScope::EnteredContextRewindScope(
HandleScopeImplementer* hsi)
: hsi_(hsi), saved_entered_context_count_(hsi->EnteredContextCount()) {}
HandleScopeImplementer::EnteredContextRewindScope::
~EnteredContextRewindScope() {
DCHECK_LE(saved_entered_context_count_, hsi_->EnteredContextCount());
while (saved_entered_context_count_ < hsi_->EnteredContextCount()) {
hsi_->LeaveContext();
}
}
bool HandleScopeImplementer::LastEnteredContextWas(
Tagged<NativeContext> context) {
return !entered_contexts_.empty() && entered_contexts_.back() == context;
}
// If there's a spare block, use it for growing the current scope.
internal::Address* HandleScopeImplementer::GetSpareOrNewBlock() {
internal::Address* block =
(spare_ != nullptr) ? spare_
: NewArray<internal::Address>(kHandleBlockSize);
spare_ = nullptr;
return block;
}
void HandleScopeImplementer::DeleteExtensions(internal::Address* prev_limit) {
while (!blocks_.empty()) {
internal::Address* block_start = blocks_.back();
internal::Address* block_limit = block_start + kHandleBlockSize;
// SealHandleScope may make the prev_limit to point inside the block.
// Cast possibly-unrelated pointers to plain Address before comparing them
// to avoid undefined behavior.
if (reinterpret_cast<Address>(block_start) <
reinterpret_cast<Address>(prev_limit) &&
reinterpret_cast<Address>(prev_limit) <=
reinterpret_cast<Address>(block_limit)) {
#ifdef ENABLE_LOCAL_HANDLE_ZAPPING
internal::HandleScope::ZapRange(prev_limit, block_limit);
#endif
break;
}
blocks_.pop_back();
#ifdef ENABLE_LOCAL_HANDLE_ZAPPING
internal::HandleScope::ZapRange(block_start, block_limit);
#endif
if (spare_ != nullptr) {
DeleteArray(spare_);
}
spare_ = block_start;
}
DCHECK((blocks_.empty() && prev_limit == nullptr) ||
(!blocks_.empty() && prev_limit != nullptr));
}
void HandleScopeImplementer::EnterContext(Tagged<NativeContext> context) {
entered_contexts_.push_back(context);
Isolate::FromHandleScopeImplementer(this)->set_last_entered_context(context);
}
DirectHandle<NativeContext> HandleScopeImplementer::LastEnteredContext() {
if (entered_contexts_.empty()) return {};
return direct_handle(entered_contexts_.back(),
Isolate::FromHandleScopeImplementer(this));
}
} // namespace internal
} // namespace v8
#endif // V8_HANDLES_HANDLE_SCOPE_IMPLEMENTER_INL_H_
+178
View File
@@ -0,0 +1,178 @@
// Copyright 2026 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.
#include "src/handles/handle-scope-implementer.h"
#include "src/execution/isolate-data.h"
#include "src/execution/isolate.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/handles/persistent-handles.h"
namespace v8 {
namespace internal {
void HandleScopeImplementer::ResetAfterArchive() {
blocks_.detach();
entered_contexts_.detach();
saved_contexts_.detach();
spare_ = nullptr;
last_handle_before_persistent_block_ = kInvalidLastHandle;
Isolate::FromHandleScopeImplementer(this)->set_last_entered_context(
Tagged<NativeContext>());
}
void HandleScopeImplementer::Free() {
DCHECK(blocks_.empty());
DCHECK(entered_contexts_.empty());
DCHECK(saved_contexts_.empty());
blocks_.free();
entered_contexts_.free();
saved_contexts_.free();
if (spare_ != nullptr) {
DeleteArray(spare_);
spare_ = nullptr;
}
DCHECK(Isolate::FromHandleScopeImplementer(this)
->thread_local_top()
->CallDepthIsZero());
}
void HandleScopeImplementer::BeginPersistentScope() {
DCHECK_EQ(last_handle_before_persistent_block_, kInvalidLastHandle);
last_handle_before_persistent_block_ =
Isolate::FromHandleScopeImplementer(this)->handle_scope_data()->next;
}
void HandleScopeImplementer::FreeThreadResources() { Free(); }
char* HandleScopeImplementer::ArchiveThread(char* storage) {
HandleScopeData* current =
Isolate::FromHandleScopeImplementer(this)->handle_scope_data();
handle_scope_data_ = *current;
MemCopy(storage, this, sizeof(*this));
ResetAfterArchive();
current->Initialize();
return storage + ArchiveSpacePerThread();
}
int HandleScopeImplementer::ArchiveSpacePerThread() {
return sizeof(HandleScopeImplementer);
}
char* HandleScopeImplementer::RestoreThread(char* storage) {
MemCopy(this, storage, sizeof(*this));
*Isolate::FromHandleScopeImplementer(this)->handle_scope_data() =
handle_scope_data_;
Isolate::FromHandleScopeImplementer(this)->set_last_entered_context(
entered_contexts_.empty() ? Tagged<NativeContext>()
: entered_contexts_.back());
return storage + ArchiveSpacePerThread();
}
void HandleScopeImplementer::IterateThis(RootVisitor* v) {
#ifdef DEBUG
bool found_block_before_persistent = false;
#endif
// Iterate over all handles in the blocks except for the last.
for (int i = static_cast<int>(blocks()->size()) - 2; i >= 0; --i) {
Address* block = blocks()->at(i);
// Cast possibly-unrelated pointers to plain Address before comparing them
// to avoid undefined behavior.
if (HasPersistentScope() &&
(reinterpret_cast<Address>(last_handle_before_persistent_block_) <=
reinterpret_cast<Address>(&block[kHandleBlockSize])) &&
(reinterpret_cast<Address>(last_handle_before_persistent_block_) >=
reinterpret_cast<Address>(block))) {
v->VisitRootPointers(
Root::kHandleScope, nullptr, FullObjectSlot(block),
FullObjectSlot(last_handle_before_persistent_block_));
DCHECK(!found_block_before_persistent);
#ifdef DEBUG
found_block_before_persistent = true;
#endif
} else {
v->VisitRootPointers(Root::kHandleScope, nullptr, FullObjectSlot(block),
FullObjectSlot(&block[kHandleBlockSize]));
}
}
DCHECK_EQ(
HasPersistentScope() && last_handle_before_persistent_block_ != nullptr,
found_block_before_persistent);
// Iterate over live handles in the last block (if any).
if (!blocks()->empty()) {
v->VisitRootPointers(Root::kHandleScope, nullptr,
FullObjectSlot(blocks()->back()),
FullObjectSlot(handle_scope_data_.next));
}
saved_contexts_.shrink_to_fit();
if (!saved_contexts_.empty()) {
FullObjectSlot start(&saved_contexts_.front());
v->VisitRootPointers(Root::kHandleScope, nullptr, start,
start + static_cast<int>(saved_contexts_.size()));
}
entered_contexts_.shrink_to_fit();
if (!entered_contexts_.empty()) {
FullObjectSlot start(&entered_contexts_.front());
v->VisitRootPointers(Root::kHandleScope, nullptr, start,
start + static_cast<int>(entered_contexts_.size()));
}
}
void HandleScopeImplementer::Iterate(RootVisitor* v) {
HandleScopeData* current =
Isolate::FromHandleScopeImplementer(this)->handle_scope_data();
handle_scope_data_ = *current;
IterateThis(v);
}
char* HandleScopeImplementer::Iterate(RootVisitor* v, char* storage) {
HandleScopeImplementer* scope_implementer =
reinterpret_cast<HandleScopeImplementer*>(storage);
scope_implementer->IterateThis(v);
return storage + ArchiveSpacePerThread();
}
std::unique_ptr<PersistentHandles> HandleScopeImplementer::DetachPersistent(
Address* first_block) {
std::unique_ptr<PersistentHandles> ph(
new PersistentHandles(Isolate::FromHandleScopeImplementer(this)));
DCHECK(HasPersistentScope());
DCHECK_NOT_NULL(first_block);
Address* block_start;
do {
block_start = blocks_.back();
ph->blocks_.push_back(blocks_.back());
#if DEBUG
ph->ordered_blocks_.insert(blocks_.back());
#endif
blocks_.pop_back();
} while (block_start != first_block);
// ph->blocks_ now contains the blocks installed on the HandleScope stack
// since BeginPersistentScope was called, but in reverse order.
// Switch first and last blocks, such that the last block is the one
// that is potentially half full.
DCHECK(!ph->blocks_.empty());
std::swap(ph->blocks_.front(), ph->blocks_.back());
ph->block_next_ =
Isolate::FromHandleScopeImplementer(this)->handle_scope_data()->next;
block_start = ph->blocks_.back();
ph->block_limit_ = block_start + kHandleBlockSize;
DCHECK_EQ(blocks_.empty(), last_handle_before_persistent_block_ == nullptr);
last_handle_before_persistent_block_ = kInvalidLastHandle;
return ph;
}
} // namespace internal
} // namespace v8
+133
View File
@@ -0,0 +1,133 @@
// Copyright 2026 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 V8_HANDLES_HANDLE_SCOPE_IMPLEMENTER_H_
#define V8_HANDLES_HANDLE_SCOPE_IMPLEMENTER_H_
#include <memory>
#include "include/v8-internal.h"
#include "src/base/macros.h"
#include "src/objects/contexts.h"
#include "src/utils/detachable-vector.h"
namespace v8 {
namespace internal {
class Isolate;
class PersistentHandles;
// This class is here in order to be able to declare it a friend of
// HandleScope. Moving these methods to be members of HandleScope would be
// neat in some ways, but it would expose internal implementation details in
// our public header file, which is undesirable.
//
// An isolate has a single instance of this class to hold the current thread's
// data. In multithreaded V8 programs this data is copied in and out of storage
// so that the currently executing thread always has its own copy of this
// data.
class HandleScopeImplementer {
public:
static constexpr int kSizeInBytes =
11 * kSystemPointerSize + HandleScopeData::kSizeInBytes;
class V8_NODISCARD EnteredContextRewindScope {
public:
explicit inline EnteredContextRewindScope(HandleScopeImplementer* hsi);
inline ~EnteredContextRewindScope();
private:
HandleScopeImplementer* hsi_;
size_t saved_entered_context_count_;
};
HandleScopeImplementer() : spare_(nullptr) {}
~HandleScopeImplementer() { DeleteArray(spare_); }
HandleScopeImplementer(const HandleScopeImplementer&) = delete;
HandleScopeImplementer& operator=(const HandleScopeImplementer&) = delete;
// Threading support for handle data.
static int ArchiveSpacePerThread();
char* RestoreThread(char* from);
char* ArchiveThread(char* to);
void FreeThreadResources();
// Garbage collection support.
V8_EXPORT_PRIVATE void Iterate(RootVisitor* v);
V8_EXPORT_PRIVATE static char* Iterate(RootVisitor* v, char* data);
inline internal::Address* GetSpareOrNewBlock();
inline void DeleteExtensions(internal::Address* prev_limit);
inline void EnterContext(Tagged<NativeContext> context);
inline void LeaveContext();
inline bool LastEnteredContextWas(Tagged<NativeContext> context);
inline size_t EnteredContextCount() const { return entered_contexts_.size(); }
// Returns the last entered context or an empty handle if no
// contexts have been entered.
inline DirectHandle<NativeContext> LastEnteredContext();
inline void SaveContext(Tagged<Context> context);
inline Tagged<Context> RestoreContext();
inline bool HasSavedContexts();
inline DetachableVector<Address*>* blocks() { return &blocks_; }
void ReturnBlock(Address* block) {
DCHECK_NOT_NULL(block);
if (spare_ != nullptr) DeleteArray(spare_);
spare_ = block;
}
static const size_t kEnteredContextsOffset;
private:
static constexpr Address kSentinel = kNullAddress;
static constexpr Address* kInvalidLastHandle =
const_cast<Address*>(&kSentinel);
void ResetAfterArchive();
void Free();
void BeginPersistentScope();
bool HasPersistentScope() const {
return last_handle_before_persistent_block_ != kInvalidLastHandle;
}
std::unique_ptr<PersistentHandles> DetachPersistent(Address* first_block);
DetachableVector<Address*> blocks_;
// Used as a stack to keep track of entered contexts.
DetachableVector<Tagged<NativeContext>> entered_contexts_;
// Used as a stack to keep track of saved contexts.
DetachableVector<Tagged<Context>> saved_contexts_;
Address* spare_;
Address* last_handle_before_persistent_block_ = kInvalidLastHandle;
// This is only used for threading support.
HandleScopeData handle_scope_data_;
void IterateThis(RootVisitor* v);
char* RestoreThreadHelper(char* from);
char* ArchiveThreadHelper(char* to);
friend class HandleScopeImplementerOffsets;
friend class PersistentHandlesScope;
};
inline constexpr size_t HandleScopeImplementer::kEnteredContextsOffset =
offsetof(HandleScopeImplementer, entered_contexts_);
static_assert(HandleScopeImplementer::kSizeInBytes ==
sizeof(HandleScopeImplementer));
const int kHandleBlockSize = KB - 2; // fit in one page
} // namespace internal
} // namespace v8
#endif // V8_HANDLES_HANDLE_SCOPE_IMPLEMENTER_H_
+1
View File
@@ -9,6 +9,7 @@
#include "src/codegen/optimized-compilation-info.h"
#include "src/execution/isolate.h"
#include "src/execution/thread-id.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/handles/maybe-handles.h"
#include "src/heap/base/stack.h"
#include "src/heap/heap-layout-inl.h"
+1 -1
View File
@@ -4,8 +4,8 @@
#include "src/handles/local-handles.h"
#include "src/api/api.h"
#include "src/execution/isolate.h"
#include "src/handles/handle-scope-implementer.h"
#include "src/handles/handles-inl.h"
#include "src/handles/handles.h"
#include "src/heap/heap-inl.h"
+9 -5
View File
@@ -4,7 +4,7 @@
#include "src/handles/persistent-handles.h"
#include "src/api/api.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/safepoint.h"
#include "src/utils/allocation.h"
@@ -139,7 +139,8 @@ void PersistentHandlesList::Iterate(RootVisitor* visitor, Isolate* isolate) {
PersistentHandlesScope::PersistentHandlesScope(Isolate* isolate)
: impl_(isolate->handle_scope_implementer()) {
impl_->BeginPersistentScope();
HandleScopeData* data = impl_->isolate()->handle_scope_data();
HandleScopeData* data =
Isolate::FromHandleScopeImplementer(impl_)->handle_scope_data();
Address* new_next = impl_->GetSpareOrNewBlock();
Address* new_limit = &new_next[kHandleBlockSize];
impl_->blocks()->push_back(new_next);
@@ -157,13 +158,16 @@ PersistentHandlesScope::PersistentHandlesScope(Isolate* isolate)
PersistentHandlesScope::~PersistentHandlesScope() {
DCHECK(handles_detached_);
impl_->isolate()->handle_scope_data()->level--;
DCHECK_EQ(impl_->isolate()->handle_scope_data()->level, prev_level_);
Isolate::FromHandleScopeImplementer(impl_)->handle_scope_data()->level--;
DCHECK_EQ(
Isolate::FromHandleScopeImplementer(impl_)->handle_scope_data()->level,
prev_level_);
}
std::unique_ptr<PersistentHandles> PersistentHandlesScope::Detach() {
std::unique_ptr<PersistentHandles> ph = impl_->DetachPersistent(first_block_);
HandleScopeData* data = impl_->isolate()->handle_scope_data();
HandleScopeData* data =
Isolate::FromHandleScopeImplementer(impl_)->handle_scope_data();
data->next = prev_next_;
data->limit = prev_limit_;
#ifdef DEBUG
+1 -1
View File
@@ -8,9 +8,9 @@
#include <vector>
#include "include/v8-internal.h"
#include "src/api/api.h"
#include "src/base/macros.h"
#include "src/execution/isolate.h"
#include "src/handles/handle-scope-implementer.h"
#include "src/objects/visitors.h"
#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
+1
View File
@@ -38,6 +38,7 @@
#include "src/objects/map-word-inl.h"
#include "src/objects/object-predicates-inl.h"
#include "src/objects/slots-inl.h"
#include "src/objects/templates.h"
#include "src/objects/visitors-inl.h"
#include "src/roots/static-roots.h"
#include "src/utils/allocation.h"
+1
View File
@@ -8,6 +8,7 @@
#include <limits>
#include <optional>
#include "src/api/api.h"
#include "src/base/bounds.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
+5
View File
@@ -45,6 +45,11 @@
#include "src/maglev/maglev-regalloc-node-info.h"
#include "src/objects/arguments.h"
#include "src/objects/heap-number.h"
#include "src/objects/js-array-buffer.h"
#include "src/objects/js-collection.h"
#include "src/objects/js-generator.h"
#include "src/objects/js-promise.h"
#include "src/objects/js-proxy.h"
#include "src/objects/property-details.h"
#include "src/objects/smi.h"
#include "src/objects/tagged-index.h"
+2
View File
@@ -9,6 +9,8 @@
// Include the non-inl header before the rest of the headers.
#include "src/heap/heap-write-barrier-inl.h"
// The include of `biging.h` is needed because BigInt is part of JSAny.
#include "src/objects/bigint.h"
#include "src/objects/smi-inl.h"
#include "src/objects/tagged-field-inl.h"
+1
View File
@@ -8,6 +8,7 @@
#include "src/objects/module.h"
// Include the non-inl header before the rest of the headers.
#include "src/objects/js-promise-inl.h"
#include "src/objects/scope-info.h"
#include "src/objects/source-text-module.h"
#include "src/objects/string-inl.h"
+3
View File
@@ -8,8 +8,11 @@
#include "src/objects/source-text-module.h"
// Include the non-inl header before the rest of the headers.
#include "src/objects/js-function-inl.h"
#include "src/objects/js-generator-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/shared-function-info-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
+2
View File
@@ -8,6 +8,8 @@
#include "src/execution/isolate.h"
#include "src/execution/local-isolate.h"
#include "src/heap/factory.h"
#include "src/objects/objects.h"
#include "src/objects/string.h"
#include "src/utils/memcopy.h"
namespace v8 {
+1 -2
View File
@@ -4,8 +4,6 @@
#include <memory>
#include "src/api/api-inl.h"
#include "src/api/api.h"
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/builtins/superspread.h"
@@ -15,6 +13,7 @@
#include "src/execution/messages.h"
#include "src/execution/protectors-inl.h"
#include "src/execution/tiering-manager.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/handles/maybe-handles.h"
#include "src/logging/counters.h"
#include "src/numbers/conversions.h"
+1
View File
@@ -6,6 +6,7 @@
#include <cstring>
#include "include/v8-wasm.h"
#include "src/api/api.h"
#include "src/base/memory.h"
#include "src/base/platform/mutex.h"
#include "src/builtins/builtins-inl.h"
+1 -1
View File
@@ -9,11 +9,11 @@
#include <optional>
#include "src/api/api-inl.h"
#include "src/asmjs/asm-js.h"
#include "src/codegen/compiler.h"
#include "src/compiler/fast-api-calls.h"
#include "src/compiler/wasm-compiler.h"
#include "src/handles/handle-scope-implementer-inl.h"
#include "src/logging/counters-scopes.h"
#include "src/logging/metrics.h"
#include "src/numbers/conversions-inl.h"
@@ -2,6 +2,7 @@
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.
#include "src/api/api.h"
#include "src/base/memory.h"
#include "src/codegen/external-reference.h"
#include "test/cctest/cctest.h"
+1
View File
@@ -6,6 +6,7 @@
#include <functional>
#include <limits>
#include "src/api/api.h"
#include "src/base/bits.h"
#include "src/base/ieee754.h"
#include "src/base/numerics/safe_conversions.h"
+1
View File
@@ -30,6 +30,7 @@
#include "test/cctest/profiler-extension.h"
#include "include/v8-template.h"
#include "src/api/api.h"
#include "test/cctest/cctest.h"
namespace v8 {
+1
View File
@@ -6,6 +6,7 @@
#include "include/v8-function-callback.h"
#include "include/v8-function.h"
#include "src/api/api.h"
#include "src/base/strings.h"
#include "test/cctest/cctest.h"
+1
View File
@@ -32,6 +32,7 @@
#include "include/v8-extension.h"
#include "include/v8-function.h"
#include "include/v8-locker.h"
#include "src/api/api.h"
#include "src/base/platform/platform.h"
#include "src/objects/objects-inl.h"
#include "src/sandbox/sandboxable-thread.h"
+1
View File
@@ -11,6 +11,7 @@
#include "include/v8-local-handle.h"
#include "include/v8-template.h"
#include "include/v8-unwinder.h"
#include "src/api/api.h"
#include "src/flags/flags.h"
#include "test/cctest/cctest.h"
+1
View File
@@ -29,6 +29,7 @@
#include "include/v8-profiler.h"
#include "include/v8-template.h"
#include "src/api/api.h"
#include "src/execution/vm-state-inl.h"
#include "src/objects/smi.h"
#include "src/profiler/tick-sample.h"
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Copyright 2019 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.
@@ -687,7 +687,7 @@ function print_handles_data(print_handles = false) {
let iso = cast(isolate_address, "v8::internal::Isolate");
let hsd = iso.handle_scope_data_;
let hsimpl = iso.handle_scope_implementer_;
let hsimpl = iso.isolate_data_.handle_scope_implementer_;
// depth level
print(`Nested depth level: ${hsd.level}`);