v8/test/unittests/heap/unified-heap-unittest.cc
Michael Lippautz eb4536797e cppgc: Rework testing GC infrastructure
Instead of moving the stand-alone logic to the base heap, allows
specific heaps to override their stand-alone GC behavior. This allows
CppHeap to reuse the unified heap bottlenecks and visitors for
testing. This works as long as any v8 references are empty as there is
no Isolate attached to the heap in this case.

- Reverts parts of https://crrev.com/c/2716291
- Relands parts of https://crrev.com/c/2718146

In addition, add tests covering v8::CppHeap and cppgc::Heap.

Bug: chromium:1056170
Change-Id: I47dc88c7f0e4961a1aadd60da9b05bff4dcfb27a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2718612
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73077}
2021-02-26 18:27:42 +00:00

180 lines
6.8 KiB
C++

// Copyright 2020 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 "include/cppgc/allocation.h"
#include "include/cppgc/garbage-collected.h"
#include "include/cppgc/persistent.h"
#include "include/cppgc/platform.h"
#include "include/cppgc/testing.h"
#include "include/v8-cppgc.h"
#include "include/v8.h"
#include "src/api/api-inl.h"
#include "src/heap/cppgc-js/cpp-heap.h"
#include "src/heap/cppgc/sweeper.h"
#include "src/objects/objects-inl.h"
#include "test/unittests/heap/heap-utils.h"
#include "test/unittests/heap/unified-heap-utils.h"
namespace v8 {
namespace internal {
namespace {
class Wrappable final : public cppgc::GarbageCollected<Wrappable> {
public:
static size_t destructor_callcount;
~Wrappable() { destructor_callcount++; }
void Trace(cppgc::Visitor* visitor) const { visitor->Trace(wrapper_); }
void SetWrapper(v8::Isolate* isolate, v8::Local<v8::Object> wrapper) {
wrapper_.Reset(isolate, wrapper);
}
TracedReference<v8::Object>& wrapper() { return wrapper_; }
private:
TracedReference<v8::Object> wrapper_;
};
size_t Wrappable::destructor_callcount = 0;
using UnifiedHeapDetachedTest = TestWithHeapInternals;
} // namespace
TEST_F(UnifiedHeapTest, OnlyGC) { CollectGarbageWithEmbedderStack(); }
TEST_F(UnifiedHeapTest, FindingV8ToBlinkReference) {
v8::HandleScope scope(v8_isolate());
v8::Local<v8::Context> context = v8::Context::New(v8_isolate());
v8::Context::Scope context_scope(context);
uint16_t wrappable_type = WrapperHelper::kTracedEmbedderId;
v8::Local<v8::Object> api_object = WrapperHelper::CreateWrapper(
context, &wrappable_type,
cppgc::MakeGarbageCollected<Wrappable>(allocation_handle()));
Wrappable::destructor_callcount = 0;
EXPECT_FALSE(api_object.IsEmpty());
EXPECT_EQ(0u, Wrappable::destructor_callcount);
CollectGarbageWithoutEmbedderStack(cppgc::Heap::SweepingType::kAtomic);
EXPECT_EQ(0u, Wrappable::destructor_callcount);
WrapperHelper::ResetWrappableConnection(api_object);
CollectGarbageWithoutEmbedderStack(cppgc::Heap::SweepingType::kAtomic);
EXPECT_EQ(1u, Wrappable::destructor_callcount);
}
TEST_F(UnifiedHeapTest, WriteBarrierV8ToCppReference) {
v8::HandleScope scope(v8_isolate());
v8::Local<v8::Context> context = v8::Context::New(v8_isolate());
v8::Context::Scope context_scope(context);
void* wrappable = cppgc::MakeGarbageCollected<Wrappable>(allocation_handle());
v8::Local<v8::Object> api_object =
WrapperHelper::CreateWrapper(context, nullptr, nullptr);
Wrappable::destructor_callcount = 0;
WrapperHelper::ResetWrappableConnection(api_object);
SimulateIncrementalMarking();
{
// The following snippet shows the embedder code for implementing a GC-safe
// setter for JS to C++ references.
WrapperHelper::SetWrappableConnection(api_object, wrappable, wrappable);
JSHeapConsistency::WriteBarrierParams params;
auto barrier_type = JSHeapConsistency::GetWriteBarrierType(
api_object, 1, wrappable, params,
[this]() -> cppgc::HeapHandle& { return cpp_heap().GetHeapHandle(); });
EXPECT_EQ(JSHeapConsistency::WriteBarrierType::kMarking, barrier_type);
JSHeapConsistency::DijkstraMarkingBarrier(
params, cpp_heap().GetHeapHandle(), wrappable);
}
CollectGarbageWithoutEmbedderStack(cppgc::Heap::SweepingType::kAtomic);
EXPECT_EQ(0u, Wrappable::destructor_callcount);
}
TEST_F(UnifiedHeapTest, WriteBarrierCppToV8Reference) {
v8::HandleScope scope(v8_isolate());
v8::Local<v8::Context> context = v8::Context::New(v8_isolate());
v8::Context::Scope context_scope(context);
cppgc::Persistent<Wrappable> wrappable =
cppgc::MakeGarbageCollected<Wrappable>(allocation_handle());
Wrappable::destructor_callcount = 0;
SimulateIncrementalMarking();
// Pick a sentinel to compare against.
void* kMagicAddress = &Wrappable::destructor_callcount;
{
// The following snippet shows the embedder code for implementing a GC-safe
// setter for C++ to JS references.
v8::HandleScope nested_scope(v8_isolate());
v8::Local<v8::Object> api_object =
WrapperHelper::CreateWrapper(context, nullptr, nullptr);
// Setting only one field to avoid treating this as wrappable backref, see
// `LocalEmbedderHeapTracer::ExtractWrapperInfo`.
api_object->SetAlignedPointerInInternalField(1, kMagicAddress);
wrappable->SetWrapper(v8_isolate(), api_object);
JSHeapConsistency::WriteBarrierParams params;
auto barrier_type = JSHeapConsistency::GetWriteBarrierType(
wrappable->wrapper(), params,
[this]() -> cppgc::HeapHandle& { return cpp_heap().GetHeapHandle(); });
EXPECT_EQ(JSHeapConsistency::WriteBarrierType::kMarking, barrier_type);
JSHeapConsistency::DijkstraMarkingBarrier(
params, cpp_heap().GetHeapHandle(), wrappable->wrapper());
}
CollectGarbageWithoutEmbedderStack(cppgc::Heap::SweepingType::kAtomic);
EXPECT_EQ(0u, Wrappable::destructor_callcount);
EXPECT_EQ(kMagicAddress,
wrappable->wrapper()->GetAlignedPointerFromInternalField(1));
}
TEST_F(UnifiedHeapDetachedTest, AllocationBeforeConfigureHeap) {
auto heap = v8::CppHeap::Create(
V8::GetCurrentPlatform(),
CppHeapCreateParams{{}, WrapperHelper::DefaultWrapperDescriptor()});
auto* object =
cppgc::MakeGarbageCollected<Wrappable>(heap->GetAllocationHandle());
cppgc::WeakPersistent<Wrappable> weak_holder{object};
auto& js_heap = *isolate()->heap();
js_heap.AttachCppHeap(heap.get());
auto& cpp_heap = *CppHeap::From(isolate()->heap()->cpp_heap());
{
CollectGarbage(OLD_SPACE);
cpp_heap.AsBase().sweeper().FinishIfRunning();
EXPECT_TRUE(weak_holder);
}
USE(object);
{
js_heap.SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
CollectGarbage(OLD_SPACE);
cpp_heap.AsBase().sweeper().FinishIfRunning();
EXPECT_FALSE(weak_holder);
}
}
TEST_F(UnifiedHeapDetachedTest, StandAloneCppGC) {
// Test ensures that stand-alone C++ GC are possible when using CppHeap. This
// works even in the presence of wrappables using TracedReference as long
// as the reference is empty.
auto heap = v8::CppHeap::Create(
V8::GetCurrentPlatform(),
CppHeapCreateParams{{}, WrapperHelper::DefaultWrapperDescriptor()});
auto* object =
cppgc::MakeGarbageCollected<Wrappable>(heap->GetAllocationHandle());
cppgc::WeakPersistent<Wrappable> weak_holder{object};
heap->EnableDetachedGarbageCollectionsForTesting();
{
heap->CollectGarbageForTesting(
cppgc::EmbedderStackState::kMayContainHeapPointers);
EXPECT_TRUE(weak_holder);
}
USE(object);
{
heap->CollectGarbageForTesting(cppgc::EmbedderStackState::kNoHeapPointers);
EXPECT_FALSE(weak_holder);
}
}
} // namespace internal
} // namespace v8