diff options
Diffstat (limited to 'deps/v8/test/unittests/heap/embedder-tracing-unittest.cc')
-rw-r--r-- | deps/v8/test/unittests/heap/embedder-tracing-unittest.cc | 1212 |
1 files changed, 0 insertions, 1212 deletions
diff --git a/deps/v8/test/unittests/heap/embedder-tracing-unittest.cc b/deps/v8/test/unittests/heap/embedder-tracing-unittest.cc deleted file mode 100644 index 39b9712bc6..0000000000 --- a/deps/v8/test/unittests/heap/embedder-tracing-unittest.cc +++ /dev/null @@ -1,1212 +0,0 @@ -// Copyright 2016 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/heap/embedder-tracing.h" - -#include "include/v8-function.h" -#include "include/v8-template.h" -#include "src/handles/global-handles.h" -#include "src/heap/gc-tracer.h" -#include "src/heap/heap.h" -#include "test/unittests/heap/heap-utils.h" -#include "test/unittests/test-utils.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace v8 { -namespace internal { - -using LocalEmbedderHeapTracerWithIsolate = TestWithHeapInternals; - -namespace heap { - -using testing::StrictMock; -using testing::_; -using testing::Return; -using v8::EmbedderHeapTracer; -using v8::internal::LocalEmbedderHeapTracer; - -namespace { - -LocalEmbedderHeapTracer::WrapperInfo CreateWrapperInfo() { - return LocalEmbedderHeapTracer::WrapperInfo(nullptr, nullptr); -} - -} // namespace - -START_ALLOW_USE_DEPRECATED() -class MockEmbedderHeapTracer : public EmbedderHeapTracer { - public: - MOCK_METHOD(void, TracePrologue, (EmbedderHeapTracer::TraceFlags), - (override)); - MOCK_METHOD(void, TraceEpilogue, (EmbedderHeapTracer::TraceSummary*), - (override)); - MOCK_METHOD(void, EnterFinalPause, (EmbedderHeapTracer::EmbedderStackState), - (override)); - MOCK_METHOD(bool, IsTracingDone, (), (override)); - MOCK_METHOD(void, RegisterV8References, - ((const std::vector<std::pair<void*, void*> >&)), (override)); - MOCK_METHOD(bool, AdvanceTracing, (double deadline_in_ms), (override)); -}; - -END_ALLOW_USE_DEPRECATED() - -TEST(LocalEmbedderHeapTracer, InUse) { - MockEmbedderHeapTracer mock_remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&mock_remote_tracer); - EXPECT_TRUE(local_tracer.InUse()); -} - -TEST(LocalEmbedderHeapTracer, NoRemoteTracer) { - LocalEmbedderHeapTracer local_tracer(nullptr); - // We should be able to call all functions without a remote tracer being - // attached. - EXPECT_FALSE(local_tracer.InUse()); - local_tracer.TracePrologue(EmbedderHeapTracer::TraceFlags::kNoFlags); - local_tracer.EnterFinalPause(); - bool done = local_tracer.Trace(std::numeric_limits<double>::infinity()); - EXPECT_TRUE(done); - local_tracer.TraceEpilogue(); -} - -TEST(LocalEmbedderHeapTracer, TracePrologueForwards) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_CALL(remote_tracer, TracePrologue(_)); - local_tracer.TracePrologue(EmbedderHeapTracer::TraceFlags::kNoFlags); -} - -TEST(LocalEmbedderHeapTracer, TracePrologueForwardsMemoryReducingFlag) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_CALL(remote_tracer, - TracePrologue(EmbedderHeapTracer::TraceFlags::kReduceMemory)); - local_tracer.TracePrologue(EmbedderHeapTracer::TraceFlags::kReduceMemory); -} - -TEST(LocalEmbedderHeapTracer, TraceEpilogueForwards) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_CALL(remote_tracer, TraceEpilogue(_)); - local_tracer.TraceEpilogue(); -} - -TEST(LocalEmbedderHeapTracer, EnterFinalPauseForwards) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_CALL(remote_tracer, EnterFinalPause(_)); - local_tracer.EnterFinalPause(); -} - -TEST(LocalEmbedderHeapTracer, IsRemoteTracingDoneForwards) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_CALL(remote_tracer, IsTracingDone()); - local_tracer.IsRemoteTracingDone(); -} - -TEST(LocalEmbedderHeapTracer, EnterFinalPauseDefaultStackStateUnkown) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - // The default stack state is expected to be unkown. - EXPECT_CALL( - remote_tracer, - EnterFinalPause( - EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers)); - local_tracer.EnterFinalPause(); -} - -TEST_F(LocalEmbedderHeapTracerWithIsolate, - EnterFinalPauseStackStateIsForwarded) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(isolate()); - local_tracer.SetRemoteTracer(&remote_tracer); - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - &local_tracer, - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - EXPECT_CALL( - remote_tracer, - EnterFinalPause(EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers)); - local_tracer.EnterFinalPause(); -} - -TEST_F(LocalEmbedderHeapTracerWithIsolate, TemporaryEmbedderStackState) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(isolate()); - local_tracer.SetRemoteTracer(&remote_tracer); - // Default is unknown, see above. - { - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - &local_tracer, - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - EXPECT_CALL(remote_tracer, - EnterFinalPause( - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers)); - local_tracer.EnterFinalPause(); - } -} - -TEST_F(LocalEmbedderHeapTracerWithIsolate, - TemporaryEmbedderStackStateRestores) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(isolate()); - local_tracer.SetRemoteTracer(&remote_tracer); - // Default is unknown, see above. - { - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - &local_tracer, - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - { - EmbedderStackStateScope nested_scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - &local_tracer, - EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers); - EXPECT_CALL( - remote_tracer, - EnterFinalPause( - EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers)); - local_tracer.EnterFinalPause(); - } - EXPECT_CALL(remote_tracer, - EnterFinalPause( - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers)); - local_tracer.EnterFinalPause(); - } -} - -TEST_F(LocalEmbedderHeapTracerWithIsolate, TraceEpilogueStackStateResets) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(isolate()); - local_tracer.SetRemoteTracer(&remote_tracer); - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - &local_tracer, - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - EXPECT_CALL( - remote_tracer, - EnterFinalPause(EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers)); - local_tracer.EnterFinalPause(); - EXPECT_CALL(remote_tracer, TraceEpilogue(_)); - local_tracer.TraceEpilogue(); - EXPECT_CALL( - remote_tracer, - EnterFinalPause( - EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers)); - local_tracer.EnterFinalPause(); -} - -TEST(LocalEmbedderHeapTracer, IsRemoteTracingDoneIncludesRemote) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_CALL(remote_tracer, IsTracingDone()); - local_tracer.IsRemoteTracingDone(); -} - -TEST(LocalEmbedderHeapTracer, RegisterV8ReferencesWithRemoteTracer) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(nullptr); - local_tracer.SetRemoteTracer(&remote_tracer); - { - LocalEmbedderHeapTracer::ProcessingScope scope(&local_tracer); - scope.AddWrapperInfoForTesting(CreateWrapperInfo()); - EXPECT_CALL(remote_tracer, RegisterV8References(_)); - } - EXPECT_CALL(remote_tracer, IsTracingDone()).WillOnce(Return(false)); - EXPECT_FALSE(local_tracer.IsRemoteTracingDone()); -} - -TEST_F(LocalEmbedderHeapTracerWithIsolate, SetRemoteTracerSetsIsolate) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - LocalEmbedderHeapTracer local_tracer(isolate()); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_EQ(isolate(), reinterpret_cast<Isolate*>(remote_tracer.isolate())); -} - -TEST_F(LocalEmbedderHeapTracerWithIsolate, DestructorClearsIsolate) { - StrictMock<MockEmbedderHeapTracer> remote_tracer; - { - LocalEmbedderHeapTracer local_tracer(isolate()); - local_tracer.SetRemoteTracer(&remote_tracer); - EXPECT_EQ(isolate(), reinterpret_cast<Isolate*>(remote_tracer.isolate())); - } - EXPECT_EQ(nullptr, remote_tracer.isolate()); -} - -namespace { - -v8::Local<v8::Object> ConstructTraceableJSApiObject( - v8::Local<v8::Context> context, void* first_field, void* second_field) { - v8::EscapableHandleScope scope(context->GetIsolate()); - v8::Local<v8::FunctionTemplate> function_t = - v8::FunctionTemplate::New(context->GetIsolate()); - v8::Local<v8::ObjectTemplate> instance_t = function_t->InstanceTemplate(); - instance_t->SetInternalFieldCount(2); - v8::Local<v8::Function> function = - function_t->GetFunction(context).ToLocalChecked(); - v8::Local<v8::Object> instance = - function->NewInstance(context).ToLocalChecked(); - instance->SetAlignedPointerInInternalField(0, first_field); - instance->SetAlignedPointerInInternalField(1, second_field); - EXPECT_FALSE(instance.IsEmpty()); - i::Handle<i::JSReceiver> js_obj = v8::Utils::OpenHandle(*instance); - EXPECT_EQ(i::JS_API_OBJECT_TYPE, js_obj->map().instance_type()); - return scope.Escape(instance); -} - -enum class TracePrologueBehavior { kNoop, kCallV8WriteBarrier }; - -START_ALLOW_USE_DEPRECATED() - -class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer { - public: - TestEmbedderHeapTracer() = default; - TestEmbedderHeapTracer(TracePrologueBehavior prologue_behavior, - v8::Global<v8::Array> array) - : prologue_behavior_(prologue_behavior), array_(std::move(array)) {} - - void RegisterV8References( - const std::vector<std::pair<void*, void*>>& embedder_fields) final { - registered_from_v8_.insert(registered_from_v8_.end(), - embedder_fields.begin(), embedder_fields.end()); - } - - void AddReferenceForTracing(v8::TracedReference<v8::Value>* ref) { - to_register_with_v8_references_.push_back(ref); - } - - bool AdvanceTracing(double deadline_in_ms) final { - for (auto ref : to_register_with_v8_references_) { - RegisterEmbedderReference(ref->As<v8::Data>()); - } - to_register_with_v8_references_.clear(); - return true; - } - - bool IsTracingDone() final { return to_register_with_v8_references_.empty(); } - - void TracePrologue(EmbedderHeapTracer::TraceFlags) final { - if (prologue_behavior_ == TracePrologueBehavior::kCallV8WriteBarrier) { - auto local = array_.Get(isolate()); - local - ->Set(local->GetCreationContext().ToLocalChecked(), 0, - v8::Object::New(isolate())) - .Check(); - } - } - - void TraceEpilogue(TraceSummary*) final {} - void EnterFinalPause(EmbedderStackState) final {} - - bool IsRegisteredFromV8(void* first_field) const { - for (auto pair : registered_from_v8_) { - if (pair.first == first_field) return true; - } - return false; - } - - void DoNotConsiderAsRootForScavenge(v8::TracedReference<v8::Value>* handle) { - handle->SetWrapperClassId(17); - non_root_handles_.push_back(handle); - } - - bool IsRootForNonTracingGC( - const v8::TracedReference<v8::Value>& handle) final { - return handle.WrapperClassId() != 17; - } - - void ResetHandleInNonTracingGC( - const v8::TracedReference<v8::Value>& handle) final { - for (auto* non_root_handle : non_root_handles_) { - if (*non_root_handle == handle) { - non_root_handle->Reset(); - } - } - } - - private: - std::vector<std::pair<void*, void*>> registered_from_v8_; - std::vector<v8::TracedReference<v8::Value>*> to_register_with_v8_references_; - TracePrologueBehavior prologue_behavior_ = TracePrologueBehavior::kNoop; - v8::Global<v8::Array> array_; - std::vector<v8::TracedReference<v8::Value>*> non_root_handles_; -}; - -class V8_NODISCARD TemporaryEmbedderHeapTracerScope final { - public: - TemporaryEmbedderHeapTracerScope(v8::Isolate* isolate, - v8::EmbedderHeapTracer* tracer) - : isolate_(isolate) { - isolate_->SetEmbedderHeapTracer(tracer); - } - - ~TemporaryEmbedderHeapTracerScope() { - isolate_->SetEmbedderHeapTracer(nullptr); - } - - private: - v8::Isolate* const isolate_; -}; - -END_ALLOW_USE_DEPRECATED() - -} // namespace - -using EmbedderTracingTest = TestWithHeapInternalsAndContext; - -TEST_F(EmbedderTracingTest, V8RegisterEmbedderReference) { - // Tests that wrappers are properly registered with the embedder heap - // tracer. - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - v8::HandleScope scope(v8_isolate()); - v8::Local<v8::Context> context = v8::Context::New(v8_isolate()); - v8::Context::Scope context_scope(context); - - void* first_and_second_field = reinterpret_cast<void*>(0x2); - v8::Local<v8::Object> api_object = ConstructTraceableJSApiObject( - context, first_and_second_field, first_and_second_field); - ASSERT_FALSE(api_object.IsEmpty()); - CollectGarbage(i::OLD_SPACE); - EXPECT_TRUE(tracer.IsRegisteredFromV8(first_and_second_field)); -} - -TEST_F(EmbedderTracingTest, EmbedderRegisteringV8Reference) { - // Tests that references that are registered by the embedder heap tracer are - // considered live by V8. - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - v8::HandleScope scope(v8_isolate()); - v8::Local<v8::Context> context = v8::Context::New(v8_isolate()); - v8::Context::Scope context_scope(context); - - auto handle = std::make_unique<v8::TracedReference<v8::Value>>(); - { - v8::HandleScope inner_scope(v8_isolate()); - v8::Local<v8::Value> o = - v8::Local<v8::Object>::New(v8_isolate(), v8::Object::New(v8_isolate())); - handle->Reset(v8_isolate(), o); - } - tracer.AddReferenceForTracing(handle.get()); - CollectGarbage(i::OLD_SPACE); - EXPECT_FALSE(handle->IsEmpty()); -} - -TEST_F(EmbedderTracingTest, TracingInEphemerons) { - // Tests that wrappers that are part of ephemerons are traced. - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - v8::HandleScope scope(v8_isolate()); - v8::Local<v8::Context> context = v8::Context::New(v8_isolate()); - v8::Context::Scope context_scope(context); - - v8::Local<v8::Object> key = - v8::Local<v8::Object>::New(v8_isolate(), v8::Object::New(v8_isolate())); - void* first_and_second_field = reinterpret_cast<void*>(0x8); - Handle<JSWeakMap> weak_map = i_isolate()->factory()->NewJSWeakMap(); - { - v8::HandleScope inner_scope(v8_isolate()); - v8::Local<v8::Object> api_object = ConstructTraceableJSApiObject( - context, first_and_second_field, first_and_second_field); - EXPECT_FALSE(api_object.IsEmpty()); - Handle<JSObject> js_key = - handle(JSObject::cast(*v8::Utils::OpenHandle(*key)), i_isolate()); - Handle<JSReceiver> js_api_object = v8::Utils::OpenHandle(*api_object); - int32_t hash = js_key->GetOrCreateHash(i_isolate()).value(); - JSWeakCollection::Set(weak_map, js_key, js_api_object, hash); - } - CollectGarbage(i::OLD_SPACE); - EXPECT_TRUE(tracer.IsRegisteredFromV8(first_and_second_field)); -} - -TEST_F(EmbedderTracingTest, FinalizeTracingIsNoopWhenNotMarking) { - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - - // Finalize a potentially running garbage collection. - CollectGarbage(OLD_SPACE); - EXPECT_TRUE(i_isolate()->heap()->incremental_marking()->IsStopped()); - - int gc_counter = i_isolate()->heap()->gc_count(); - tracer.FinalizeTracing(); - EXPECT_TRUE(i_isolate()->heap()->incremental_marking()->IsStopped()); - EXPECT_EQ(gc_counter, i_isolate()->heap()->gc_count()); -} - -TEST_F(EmbedderTracingTest, FinalizeTracingWhenMarking) { - if (!v8_flags.incremental_marking) return; - ManualGCScope manual_gc(i_isolate()); - Heap* heap = i_isolate()->heap(); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - - // Finalize a potentially running garbage collection. - CollectGarbage(OLD_SPACE); - if (heap->sweeping_in_progress()) { - heap->EnsureSweepingCompleted( - Heap::SweepingForcedFinalizationMode::kV8Only); - } - heap->tracer()->StopFullCycleIfNeeded(); - EXPECT_TRUE(heap->incremental_marking()->IsStopped()); - - i::IncrementalMarking* marking = heap->incremental_marking(); - { - IsolateSafepointScope scope(heap); - heap->tracer()->StartCycle( - GarbageCollector::MARK_COMPACTOR, GarbageCollectionReason::kTesting, - "collector cctest", GCTracer::MarkingType::kIncremental); - marking->Start(GarbageCollector::MARK_COMPACTOR, - GarbageCollectionReason::kTesting); - } - - // Sweeping is not runing so we should immediately start marking. - EXPECT_TRUE(marking->IsMarking()); - tracer.FinalizeTracing(); - EXPECT_TRUE(marking->IsStopped()); -} - -namespace { - -void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context, - v8::TracedReference<v8::Object>* handle) { - v8::HandleScope scope(isolate); - v8::Local<v8::Object> object(v8::Object::New(isolate)); - EXPECT_FALSE(object.IsEmpty()); - *handle = v8::TracedReference<v8::Object>(isolate, object); - EXPECT_FALSE(handle->IsEmpty()); -} - -template <typename T> -void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context, - T* global) { - v8::HandleScope scope(isolate); - v8::Local<v8::Object> object( - ConstructTraceableJSApiObject(context, nullptr, nullptr)); - EXPECT_FALSE(object.IsEmpty()); - *global = T(isolate, object); - EXPECT_FALSE(global->IsEmpty()); -} - -enum class SurvivalMode { kSurvives, kDies }; - -template <typename ModifierFunction, typename ConstructTracedReferenceFunction, - typename GCFunction> -void TracedReferenceTest(v8::Isolate* isolate, - ConstructTracedReferenceFunction construct_function, - ModifierFunction modifier_function, - GCFunction gc_function, SurvivalMode survives) { - v8::HandleScope scope(isolate); - v8::Local<v8::Context> context = v8::Context::New(isolate); - v8::Context::Scope context_scope(context); - auto* global_handles = - reinterpret_cast<i::Isolate*>(isolate)->global_handles(); - - const size_t initial_count = global_handles->handles_count(); - auto handle = std::make_unique<v8::TracedReference<v8::Object>>(); - construct_function(isolate, context, handle.get()); - ASSERT_TRUE(IsNewObjectInCorrectGeneration(isolate, *handle)); - modifier_function(*handle); - const size_t after_modification_count = global_handles->handles_count(); - gc_function(); - // Cannot check the handle as it is not explicitly cleared by the GC. Instead - // check the handles count. - CHECK_IMPLIES(survives == SurvivalMode::kSurvives, - after_modification_count == global_handles->handles_count()); - CHECK_IMPLIES(survives == SurvivalMode::kDies, - initial_count == global_handles->handles_count()); -} - -} // namespace - -TEST_F(EmbedderTracingTest, TracedReferenceReset) { - v8::HandleScope scope(v8_isolate()); - v8::TracedReference<v8::Object> handle; - ConstructJSObject(v8_isolate(), v8_isolate()->GetCurrentContext(), &handle); - EXPECT_FALSE(handle.IsEmpty()); - handle.Reset(); - EXPECT_TRUE(handle.IsEmpty()); -} - -TEST_F(EmbedderTracingTest, TracedReferenceCopyReferences) { - ManualGCScope manual_gc(i_isolate()); - v8::HandleScope outer_scope(v8_isolate()); - auto* traced_handles = i_isolate()->traced_handles(); - - const size_t initial_count = traced_handles->used_node_count(); - auto handle1 = std::make_unique<v8::TracedReference<v8::Value>>(); - { - v8::HandleScope scope(v8_isolate()); - handle1->Reset(v8_isolate(), v8::Object::New(v8_isolate())); - } - auto handle2 = std::make_unique<v8::TracedReference<v8::Value>>(*handle1); - auto handle3 = std::make_unique<v8::TracedReference<v8::Value>>(); - *handle3 = *handle2; - EXPECT_EQ(initial_count + 3, traced_handles->used_node_count()); - EXPECT_FALSE(handle1->IsEmpty()); - EXPECT_EQ(*handle1, *handle2); - EXPECT_EQ(*handle2, *handle3); - { - v8::HandleScope scope(v8_isolate()); - auto tmp = v8::Local<v8::Value>::New(v8_isolate(), *handle3); - EXPECT_FALSE(tmp.IsEmpty()); - // Conservative scanning may find stale pointers to on-stack handles. - // Disable scanning, assuming the slots are overwritten. - EmbedderStackStateScope stack_scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - reinterpret_cast<i::Isolate*>(v8_isolate()) - ->heap() - ->local_embedder_heap_tracer(), - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - FullGC(); - } - EXPECT_EQ(initial_count, traced_handles->used_node_count()); -} - -TEST_F(EmbedderTracingTest, TracedReferenceToUnmodifiedJSObjectDiesOnFullGC) { - // When stressing incremental marking, a write barrier may keep the object - // alive. - if (v8_flags.stress_incremental_marking) return; - - TracedReferenceTest( - v8_isolate(), ConstructJSObject, - [](const TracedReference<v8::Object>&) {}, [this]() { FullGC(); }, - SurvivalMode::kDies); -} - -TEST_F( - EmbedderTracingTest, - TracedReferenceToUnmodifiedJSObjectDiesOnFullGCEvenWhenPointeeIsHeldAlive) { - ManualGCScope manual_gcs(i_isolate()); - // The TracedReference itself will die as it's not found by the full GC. The - // pointee will be kept alive through other means. - v8::Global<v8::Object> strong_global; - TracedReferenceTest( - v8_isolate(), ConstructJSObject, - [this, &strong_global](const TracedReference<v8::Object>& handle) { - v8::HandleScope scope(v8_isolate()); - strong_global = - v8::Global<v8::Object>(v8_isolate(), handle.Get(v8_isolate())); - }, - [this, &strong_global]() { - FullGC(); - strong_global.Reset(); - }, - SurvivalMode::kDies); -} - -TEST_F(EmbedderTracingTest, - TracedReferenceToUnmodifiedJSObjectSurvivesYoungGC) { - if (v8_flags.single_generation) return; - ManualGCScope manual_gc(i_isolate()); - TracedReferenceTest( - v8_isolate(), ConstructJSObject, - [](const TracedReference<v8::Object>&) {}, [this]() { YoungGC(); }, - SurvivalMode::kSurvives); -} - -TEST_F( - EmbedderTracingTest, - TracedReferenceToUnmodifiedJSObjectSurvivesYoungGCWhenExcludedFromRoots) { - if (v8_flags.single_generation) return; - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - TracedReferenceTest( - v8_isolate(), ConstructJSObject, - [&tracer](const TracedReference<v8::Object>& handle) { - tracer.DoNotConsiderAsRootForScavenge(&handle.As<v8::Value>()); - }, - [this]() { YoungGC(); }, SurvivalMode::kSurvives); -} - -TEST_F(EmbedderTracingTest, - TracedReferenceToUnmodifiedJSApiObjectSurvivesScavengePerDefault) { - if (v8_flags.single_generation) return; - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - TracedReferenceTest( - v8_isolate(), ConstructJSApiObject<TracedReference<v8::Object>>, - [](const TracedReference<v8::Object>&) {}, [this]() { YoungGC(); }, - SurvivalMode::kSurvives); -} - -TEST_F( - EmbedderTracingTest, - TracedReferenceToUnmodifiedJSApiObjectDiesOnScavengeWhenExcludedFromRoots) { - if (v8_flags.single_generation) return; - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - TracedReferenceTest( - v8_isolate(), ConstructJSApiObject<TracedReference<v8::Object>>, - [&tracer](const TracedReference<v8::Object>& handle) { - tracer.DoNotConsiderAsRootForScavenge(&handle.As<v8::Value>()); - }, - [this]() { YoungGC(); }, SurvivalMode::kDies); -} - -TEST_F(EmbedderTracingTest, TracedReferenceWrapperClassId) { - ManualGCScope manual_gc(i_isolate()); - v8::HandleScope scope(v8_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - - v8::TracedReference<v8::Object> traced; - ConstructJSObject(v8_isolate(), v8_isolate()->GetCurrentContext(), &traced); - EXPECT_EQ(0, traced.WrapperClassId()); - traced.SetWrapperClassId(17); - EXPECT_EQ(17, traced.WrapperClassId()); -} - -TEST_F(EmbedderTracingTest, TracedReferenceHandlesMarking) { - ManualGCScope manual_gc(i_isolate()); - v8::HandleScope scope(v8_isolate()); - auto live = std::make_unique<v8::TracedReference<v8::Value>>(); - auto dead = std::make_unique<v8::TracedReference<v8::Value>>(); - live->Reset(v8_isolate(), v8::Undefined(v8_isolate())); - dead->Reset(v8_isolate(), v8::Undefined(v8_isolate())); - auto* traced_handles = i_isolate()->traced_handles(); - { - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - tracer.AddReferenceForTracing(live.get()); - const size_t initial_count = traced_handles->used_node_count(); - { - // Conservative scanning may find stale pointers to on-stack handles. - // Disable scanning, assuming the slots are overwritten. - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - reinterpret_cast<i::Isolate*>(v8_isolate()) - ->heap() - ->local_embedder_heap_tracer(), - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - FullGC(); - } - const size_t final_count = traced_handles->used_node_count(); - // Handles are not black allocated, so `dead` is immediately reclaimed. - EXPECT_EQ(initial_count, final_count + 1); - } -} - -TEST_F(EmbedderTracingTest, TracedReferenceHandlesDoNotLeak) { - // TracedReference handles are not cleared by the destructor of the embedder - // object. To avoid leaks we need to mark these handles during GC. - // This test checks that unmarked handles do not leak. - ManualGCScope manual_gc(i_isolate()); - v8::HandleScope scope(v8_isolate()); - auto ref = std::make_unique<v8::TracedReference<v8::Value>>(); - ref->Reset(v8_isolate(), v8::Undefined(v8_isolate())); - auto* traced_handles = i_isolate()->traced_handles(); - const size_t initial_count = traced_handles->used_node_count(); - // We need two GCs because handles are black allocated. - FullGC(); - FullGC(); - const size_t final_count = traced_handles->used_node_count(); - EXPECT_EQ(initial_count, final_count + 1); -} - -namespace { - -START_ALLOW_USE_DEPRECATED() - -class TracedReferenceVisitor final - : public v8::EmbedderHeapTracer::TracedGlobalHandleVisitor { - public: - ~TracedReferenceVisitor() override = default; - - void VisitTracedReference(const TracedReference<Value>& value) final { - if (value.WrapperClassId() == 57) { - count_++; - } - } - - size_t count() const { return count_; } - - private: - size_t count_ = 0; -}; - -END_ALLOW_USE_DEPRECATED() - -} // namespace - -TEST_F(EmbedderTracingTest, TracedReferenceIteration) { - ManualGCScope manual_gc(i_isolate()); - v8::HandleScope scope(v8_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - - auto handle = std::make_unique<v8::TracedReference<v8::Object>>(); - ConstructJSObject(v8_isolate(), v8_isolate()->GetCurrentContext(), - handle.get()); - EXPECT_FALSE(handle->IsEmpty()); - handle->SetWrapperClassId(57); - TracedReferenceVisitor visitor; - { - v8::HandleScope new_scope(v8_isolate()); - tracer.IterateTracedGlobalHandles(&visitor); - } - EXPECT_EQ(1u, visitor.count()); -} - -TEST_F(EmbedderTracingTest, TracePrologueCallingIntoV8WriteBarrier) { - // Regression test: https://crbug.com/940003 - if (!v8_flags.incremental_marking) return; - ManualGCScope manual_gc(isolate()); - v8::HandleScope scope(v8_isolate()); - v8::Global<v8::Array> global; - { - v8::HandleScope new_scope(v8_isolate()); - auto local = v8::Array::New(v8_isolate(), 10); - global.Reset(v8_isolate(), local); - } - TestEmbedderHeapTracer tracer(TracePrologueBehavior::kCallV8WriteBarrier, - std::move(global)); - TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - SimulateIncrementalMarking(); - // Finish GC to avoid removing the tracer while GC is running which may end up - // in an infinite loop because of unprocessed objects. - FullGC(); -} - -TEST_F(EmbedderTracingTest, BasicTracedReference) { - ManualGCScope manual_gc(i_isolate()); - v8::HandleScope scope(v8_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - tracer.SetStackStart( - static_cast<void*>(base::Stack::GetCurrentFrameAddress())); - auto* traced_handles = i_isolate()->traced_handles(); - - const size_t initial_count = traced_handles->used_node_count(); - char* memory = new char[sizeof(v8::TracedReference<v8::Value>)]; - auto* traced = new (memory) v8::TracedReference<v8::Value>(); - { - v8::HandleScope new_scope(v8_isolate()); - v8::Local<v8::Value> object(ConstructTraceableJSApiObject( - v8_isolate()->GetCurrentContext(), nullptr, nullptr)); - EXPECT_TRUE(traced->IsEmpty()); - *traced = v8::TracedReference<v8::Value>(v8_isolate(), object); - EXPECT_FALSE(traced->IsEmpty()); - EXPECT_EQ(initial_count + 1, traced_handles->used_node_count()); - } - traced->~TracedReference<v8::Value>(); - EXPECT_EQ(initial_count + 1, traced_handles->used_node_count()); - { - // Conservative scanning may find stale pointers to on-stack handles. - // Disable scanning, assuming the slots are overwritten. - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - reinterpret_cast<i::Isolate*>(v8_isolate()) - ->heap() - ->local_embedder_heap_tracer(), - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - FullGC(); - } - EXPECT_EQ(initial_count, traced_handles->used_node_count()); - delete[] memory; -} - -namespace { - -START_ALLOW_USE_DEPRECATED() - -class EmptyEmbedderHeapTracer : public v8::EmbedderHeapTracer { - public: - void RegisterV8References( - const std::vector<std::pair<void*, void*>>& embedder_fields) final {} - - bool AdvanceTracing(double deadline_in_ms) final { return true; } - bool IsTracingDone() final { return true; } - void TracePrologue(EmbedderHeapTracer::TraceFlags) final {} - void TraceEpilogue(TraceSummary*) final {} - void EnterFinalPause(EmbedderStackState) final {} -}; - -END_ALLOW_USE_DEPRECATED() - -// EmbedderHeapTracer that can optimize Scavenger handling when used with -// TracedReference. -class EmbedderHeapTracerNoDestructorNonTracingClearing final - : public EmptyEmbedderHeapTracer { - public: - explicit EmbedderHeapTracerNoDestructorNonTracingClearing( - uint16_t class_id_to_optimize) - : class_id_to_optimize_(class_id_to_optimize) {} - - bool IsRootForNonTracingGC( - const v8::TracedReference<v8::Value>& handle) final { - return handle.WrapperClassId() != class_id_to_optimize_; - } - - void ResetHandleInNonTracingGC( - const v8::TracedReference<v8::Value>& handle) final { - if (handle.WrapperClassId() != class_id_to_optimize_) return; - - // Convention (for test): Objects that are optimized have their first field - // set as a back pointer. - BasicTracedReference<v8::Value>* original_handle = - reinterpret_cast<BasicTracedReference<v8::Value>*>( - v8::Object::GetAlignedPointerFromInternalField( - handle.As<v8::Object>(), 0)); - original_handle->Reset(); - } - - private: - uint16_t class_id_to_optimize_; -}; - -template <typename T> -void SetupOptimizedAndNonOptimizedHandle(v8::Isolate* isolate, - uint16_t optimized_class_id, - T* optimized_handle, - T* non_optimized_handle) { - v8::HandleScope scope(isolate); - - v8::Local<v8::Object> optimized_object(ConstructTraceableJSApiObject( - isolate->GetCurrentContext(), optimized_handle, nullptr)); - EXPECT_TRUE(optimized_handle->IsEmpty()); - *optimized_handle = T(isolate, optimized_object); - EXPECT_FALSE(optimized_handle->IsEmpty()); - optimized_handle->SetWrapperClassId(optimized_class_id); - - v8::Local<v8::Object> non_optimized_object(ConstructTraceableJSApiObject( - isolate->GetCurrentContext(), nullptr, nullptr)); - EXPECT_TRUE(non_optimized_handle->IsEmpty()); - *non_optimized_handle = T(isolate, non_optimized_object); - EXPECT_FALSE(non_optimized_handle->IsEmpty()); -} - -} // namespace - -TEST_F(EmbedderTracingTest, TracedReferenceNoDestructorReclaimedOnScavenge) { - if (v8_flags.single_generation) return; - ManualGCScope manual_gc(i_isolate()); - v8::HandleScope scope(v8_isolate()); - constexpr uint16_t kClassIdToOptimize = 23; - EmbedderHeapTracerNoDestructorNonTracingClearing tracer(kClassIdToOptimize); - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - auto* traced_handles = i_isolate()->traced_handles(); - - const size_t initial_count = traced_handles->used_node_count(); - auto* optimized_handle = new v8::TracedReference<v8::Value>(); - auto* non_optimized_handle = new v8::TracedReference<v8::Value>(); - SetupOptimizedAndNonOptimizedHandle(v8_isolate(), kClassIdToOptimize, - optimized_handle, non_optimized_handle); - EXPECT_EQ(initial_count + 2, traced_handles->used_node_count()); - YoungGC(); - EXPECT_EQ(initial_count + 1, traced_handles->used_node_count()); - EXPECT_TRUE(optimized_handle->IsEmpty()); - delete optimized_handle; - EXPECT_FALSE(non_optimized_handle->IsEmpty()); - non_optimized_handle->Reset(); - delete non_optimized_handle; - EXPECT_EQ(initial_count, traced_handles->used_node_count()); -} - -namespace { - -template <typename T> -V8_NOINLINE void OnStackTest(v8::Isolate* v8_isolate, - TestEmbedderHeapTracer* tracer) { - v8::Global<v8::Object> observer; - T stack_ref; - { - v8::HandleScope scope(v8_isolate); - v8::Local<v8::Object> object(ConstructTraceableJSApiObject( - v8_isolate->GetCurrentContext(), nullptr, nullptr)); - stack_ref.Reset(v8_isolate, object); - observer.Reset(v8_isolate, object); - observer.SetWeak(); - } - EXPECT_FALSE(observer.IsEmpty()); - FullGC(v8_isolate); - EXPECT_FALSE(observer.IsEmpty()); -} - -} // namespace - -TEST_F(EmbedderTracingTest, TracedReferenceOnStack) { - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - tracer.SetStackStart( - static_cast<void*>(base::Stack::GetCurrentFrameAddress())); - OnStackTest<v8::TracedReference<v8::Value>>(v8_isolate(), &tracer); -} - -namespace { - -enum class Operation { - kCopy, - kMove, -}; - -template <typename T> -V8_NOINLINE void PerformOperation(Operation op, T* target, T* source) { - switch (op) { - case Operation::kMove: - *target = std::move(*source); - break; - case Operation::kCopy: - *target = *source; - source->Reset(); - break; - } -} - -enum class TargetHandling { - kNonInitialized, - kInitializedYoungGen, - kInitializedOldGen -}; - -V8_NOINLINE void StackToHeapTest(v8::Isolate* v8_isolate, - TestEmbedderHeapTracer* tracer, Operation op, - TargetHandling target_handling) { - v8::Global<v8::Object> observer; - v8::TracedReference<v8::Value> stack_handle; - v8::TracedReference<v8::Value>* heap_handle = - new v8::TracedReference<v8::Value>(); - if (target_handling != TargetHandling::kNonInitialized) { - v8::HandleScope scope(v8_isolate); - v8::Local<v8::Object> to_object(ConstructTraceableJSApiObject( - v8_isolate->GetCurrentContext(), nullptr, nullptr)); - EXPECT_TRUE( - IsNewObjectInCorrectGeneration(*v8::Utils::OpenHandle(*to_object))); - if (!v8_flags.single_generation && - target_handling == TargetHandling::kInitializedOldGen) { - FullGC(v8_isolate); - EXPECT_FALSE( - i::Heap::InYoungGeneration(*v8::Utils::OpenHandle(*to_object))); - } - heap_handle->Reset(v8_isolate, to_object); - } - { - v8::HandleScope scope(v8_isolate); - v8::Local<v8::Object> object(ConstructTraceableJSApiObject( - v8_isolate->GetCurrentContext(), nullptr, nullptr)); - stack_handle.Reset(v8_isolate, object); - observer.Reset(v8_isolate, object); - observer.SetWeak(); - } - EXPECT_FALSE(observer.IsEmpty()); - tracer->AddReferenceForTracing(heap_handle); - FullGC(v8_isolate); - EXPECT_FALSE(observer.IsEmpty()); - PerformOperation(op, heap_handle, &stack_handle); - tracer->AddReferenceForTracing(heap_handle); - FullGC(v8_isolate); - EXPECT_FALSE(observer.IsEmpty()); - { - // Conservative scanning may find stale pointers to on-stack handles. - // Disable scanning, assuming the slots are overwritten. - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - reinterpret_cast<i::Isolate*>(v8_isolate) - ->heap() - ->local_embedder_heap_tracer(), - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - FullGC(v8_isolate); - } - ASSERT_TRUE(observer.IsEmpty()); - delete heap_handle; -} - -V8_NOINLINE void HeapToStackTest(v8::Isolate* v8_isolate, - TestEmbedderHeapTracer* tracer, Operation op, - TargetHandling target_handling) { - v8::Global<v8::Object> observer; - v8::TracedReference<v8::Value> stack_handle; - v8::TracedReference<v8::Value>* heap_handle = - new v8::TracedReference<v8::Value>(); - if (target_handling != TargetHandling::kNonInitialized) { - v8::HandleScope scope(v8_isolate); - v8::Local<v8::Object> to_object(ConstructTraceableJSApiObject( - v8_isolate->GetCurrentContext(), nullptr, nullptr)); - EXPECT_TRUE( - IsNewObjectInCorrectGeneration(*v8::Utils::OpenHandle(*to_object))); - if (!v8_flags.single_generation && - target_handling == TargetHandling::kInitializedOldGen) { - FullGC(v8_isolate); - EXPECT_FALSE( - i::Heap::InYoungGeneration(*v8::Utils::OpenHandle(*to_object))); - } - stack_handle.Reset(v8_isolate, to_object); - } - { - v8::HandleScope scope(v8_isolate); - v8::Local<v8::Object> object(ConstructTraceableJSApiObject( - v8_isolate->GetCurrentContext(), nullptr, nullptr)); - heap_handle->Reset(v8_isolate, object); - observer.Reset(v8_isolate, object); - observer.SetWeak(); - } - EXPECT_FALSE(observer.IsEmpty()); - tracer->AddReferenceForTracing(heap_handle); - FullGC(v8_isolate); - EXPECT_FALSE(observer.IsEmpty()); - PerformOperation(op, &stack_handle, heap_handle); - FullGC(v8_isolate); - EXPECT_FALSE(observer.IsEmpty()); - stack_handle.Reset(); - FullGC(v8_isolate); - EXPECT_TRUE(observer.IsEmpty()); - delete heap_handle; -} - -V8_NOINLINE void StackToStackTest(v8::Isolate* v8_isolate, - TestEmbedderHeapTracer* tracer, Operation op, - TargetHandling target_handling) { - v8::Global<v8::Object> observer; - v8::TracedReference<v8::Value> stack_handle1; - v8::TracedReference<v8::Value> stack_handle2; - if (target_handling != TargetHandling::kNonInitialized) { - v8::HandleScope scope(v8_isolate); - v8::Local<v8::Object> to_object(ConstructTraceableJSApiObject( - v8_isolate->GetCurrentContext(), nullptr, nullptr)); - EXPECT_TRUE( - IsNewObjectInCorrectGeneration(*v8::Utils::OpenHandle(*to_object))); - if (!v8_flags.single_generation && - target_handling == TargetHandling::kInitializedOldGen) { - FullGC(v8_isolate); - EXPECT_FALSE( - i::Heap::InYoungGeneration(*v8::Utils::OpenHandle(*to_object))); - } - stack_handle2.Reset(v8_isolate, to_object); - } - { - v8::HandleScope scope(v8_isolate); - v8::Local<v8::Object> object(ConstructTraceableJSApiObject( - v8_isolate->GetCurrentContext(), nullptr, nullptr)); - stack_handle1.Reset(v8_isolate, object); - observer.Reset(v8_isolate, object); - observer.SetWeak(); - } - EXPECT_FALSE(observer.IsEmpty()); - FullGC(v8_isolate); - EXPECT_FALSE(observer.IsEmpty()); - PerformOperation(op, &stack_handle2, &stack_handle1); - FullGC(v8_isolate); - EXPECT_FALSE(observer.IsEmpty()); - stack_handle2.Reset(); - FullGC(v8_isolate); - EXPECT_TRUE(observer.IsEmpty()); -} - -} // namespace - -TEST_F(EmbedderTracingTest, TracedReferenceMove) { - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - tracer.SetStackStart( - static_cast<void*>(base::Stack::GetCurrentFrameAddress())); - StackToHeapTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kNonInitialized); - StackToHeapTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kInitializedYoungGen); - StackToHeapTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kInitializedOldGen); - HeapToStackTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kNonInitialized); - HeapToStackTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kInitializedYoungGen); - HeapToStackTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kInitializedOldGen); - StackToStackTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kNonInitialized); - StackToStackTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kInitializedYoungGen); - StackToStackTest(v8_isolate(), &tracer, Operation::kMove, - TargetHandling::kInitializedOldGen); -} - -TEST_F(EmbedderTracingTest, TracedReferenceCopy) { - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - tracer.SetStackStart( - static_cast<void*>(base::Stack::GetCurrentFrameAddress())); - StackToHeapTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kNonInitialized); - StackToHeapTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kInitializedYoungGen); - StackToHeapTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kInitializedOldGen); - HeapToStackTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kNonInitialized); - HeapToStackTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kInitializedYoungGen); - HeapToStackTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kInitializedOldGen); - StackToStackTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kNonInitialized); - StackToStackTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kInitializedYoungGen); - StackToStackTest(v8_isolate(), &tracer, Operation::kCopy, - TargetHandling::kInitializedOldGen); -} - -namespace { - -V8_NOINLINE void CreateTracedReferenceInDeepStack( - v8::Isolate* isolate, v8::Global<v8::Object>* observer) { - v8::TracedReference<v8::Value> stack_ref; - v8::HandleScope scope(isolate); - v8::Local<v8::Object> object(ConstructTraceableJSApiObject( - isolate->GetCurrentContext(), nullptr, nullptr)); - stack_ref.Reset(isolate, object); - observer->Reset(isolate, object); - observer->SetWeak(); -} - -V8_NOINLINE void TracedReferenceOnStackReferencesAreTemporaryTest( - v8::Isolate* v8_isolate, TestEmbedderHeapTracer* tracer) { - v8::Global<v8::Object> observer; - CreateTracedReferenceInDeepStack(v8_isolate, &observer); - EXPECT_FALSE(observer.IsEmpty()); - { - // Conservative scanning may find stale pointers to on-stack handles. - // Disable scanning, assuming the slots are overwritten. - EmbedderStackStateScope scope = - EmbedderStackStateScope::ExplicitScopeForTesting( - reinterpret_cast<i::Isolate*>(v8_isolate) - ->heap() - ->local_embedder_heap_tracer(), - EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers); - FullGC(v8_isolate); - } - EXPECT_TRUE(observer.IsEmpty()); -} - -} // namespace - -TEST_F(EmbedderTracingTest, OnStackReferencesAreTemporary) { - ManualGCScope manual_gc(i_isolate()); - TestEmbedderHeapTracer tracer; - heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer); - tracer.SetStackStart( - static_cast<void*>(base::Stack::GetCurrentFrameAddress())); - TracedReferenceOnStackReferencesAreTemporaryTest(v8_isolate(), &tracer); -} - -} // namespace heap -} // namespace internal -} // namespace v8 |