blob: ae6ee236250fb53dbc496c31e9ca886da489172e [file] [log] [blame]
// 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 <vector>
#include "include/cppgc/allocation.h"
#include "include/cppgc/cross-thread-persistent.h"
#include "include/cppgc/garbage-collected.h"
#include "include/cppgc/internal/pointer-policies.h"
#include "include/cppgc/member.h"
#include "include/cppgc/persistent.h"
#include "include/cppgc/source-location.h"
#include "include/cppgc/type-traits.h"
#include "src/heap/cppgc/heap.h"
#include "src/heap/cppgc/liveness-broker.h"
#include "src/heap/cppgc/visitor.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
namespace {
struct GCed : GarbageCollected<GCed> {
static size_t trace_call_count;
virtual void Trace(cppgc::Visitor*) const { ++trace_call_count; }
};
size_t GCed::trace_call_count = 0;
struct DerivedGCed : GCed {
void Trace(cppgc::Visitor* v) const override { GCed::Trace(v); }
};
template <template <typename> class PersistentType>
struct PersistentRegionTrait;
template <>
struct PersistentRegionTrait<Persistent> {
static PersistentRegion& Get(cppgc::Heap* heap) {
return internal::Heap::From(heap)->GetStrongPersistentRegion();
}
};
template <>
struct PersistentRegionTrait<WeakPersistent> {
static PersistentRegion& Get(cppgc::Heap* heap) {
return internal::Heap::From(heap)->GetWeakPersistentRegion();
}
};
template <>
struct PersistentRegionTrait<subtle::CrossThreadPersistent> {
static PersistentRegion& Get(cppgc::Heap* heap) {
return internal::Heap::From(heap)->GetStrongCrossThreadPersistentRegion();
}
};
template <>
struct PersistentRegionTrait<subtle::WeakCrossThreadPersistent> {
static PersistentRegion& Get(cppgc::Heap* heap) {
return internal::Heap::From(heap)->GetWeakCrossThreadPersistentRegion();
}
};
template <template <typename> class PersistentType>
PersistentRegion& GetRegion(cppgc::Heap* heap) {
return PersistentRegionTrait<PersistentType>::Get(heap);
}
template <typename T>
using LocalizedPersistent =
internal::BasicPersistent<T, internal::StrongPersistentPolicy,
internal::KeepLocationPolicy,
internal::DefaultCheckingPolicy>;
template <typename T>
using LocalizedCrossThreadPersistent = internal::BasicCrossThreadPersistent<
T, internal::StrongCrossThreadPersistentPolicy,
internal::KeepLocationPolicy, internal::DefaultCheckingPolicy>;
class RootVisitor final : public VisitorBase {
public:
RootVisitor() = default;
const auto& WeakCallbacks() const { return weak_callbacks_; }
void ProcessWeakCallbacks() {
const auto info = LivenessBrokerFactory::Create();
for (const auto& cb : weak_callbacks_) {
cb.first(info, cb.second);
}
weak_callbacks_.clear();
}
protected:
void VisitRoot(const void* t, TraceDescriptor desc,
const SourceLocation&) final {
desc.callback(this, desc.base_object_payload);
}
void VisitWeakRoot(const void*, TraceDescriptor, WeakCallback callback,
const void* object, const SourceLocation&) final {
weak_callbacks_.emplace_back(callback, object);
}
private:
std::vector<std::pair<WeakCallback, const void*>> weak_callbacks_;
};
class PersistentTest : public testing::TestSupportingAllocationOnly {};
} // namespace
template <template <typename> class PersistentType>
void NullStateCtor(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse());
{
PersistentType<GCed> empty;
EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse());
}
{
PersistentType<GCed> empty = nullptr;
EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse());
}
{
PersistentType<GCed> empty = kSentinelPointer;
EXPECT_EQ(kSentinelPointer, empty);
EXPECT_EQ(kSentinelPointer, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse());
}
{
// Runtime null must not allocated associated node.
PersistentType<GCed> empty = static_cast<GCed*>(0);
EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, NullStateCtor) {
auto* heap = GetHeap();
NullStateCtor<Persistent>(heap);
NullStateCtor<WeakPersistent>(heap);
NullStateCtor<subtle::CrossThreadPersistent>(heap);
NullStateCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
void RawCtor(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
{
PersistentType<GCed> p = gced;
EXPECT_EQ(gced, p.Get());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p = *gced;
EXPECT_EQ(gced, p.Get());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, RawCtor) {
auto* heap = GetHeap();
RawCtor<Persistent>(heap);
RawCtor<WeakPersistent>(heap);
RawCtor<subtle::CrossThreadPersistent>(heap);
RawCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
void CopyCtor(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p1 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
PersistentType<GCed> p2 = p1;
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(p1.Get(), p2.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p1;
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
PersistentType<GCed> p2 = p1;
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(nullptr, p1.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<DerivedGCed> p1 =
MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
PersistentType<GCed> p2 = p1;
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(p1.Get(), p2.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
static constexpr size_t kSlots = 512u;
const PersistentType<GCed> prototype =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
std::vector<PersistentType<GCed>> vector;
vector.reserve(kSlots);
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
for (size_t i = 0; i < kSlots; ++i) {
vector.emplace_back(prototype);
EXPECT_EQ(i + 2, GetRegion<PersistentType>(heap).NodesInUse());
}
vector.clear();
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, CopyCtor) {
auto* heap = GetHeap();
CopyCtor<Persistent>(heap);
CopyCtor<WeakPersistent>(heap);
CopyCtor<subtle::CrossThreadPersistent>(heap);
CopyCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
void MoveCtor(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p1 = gced;
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
PersistentType<GCed> p2 = std::move(p1);
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(gced, p2.Get());
// Moved-from-object is in the valid specified (nullptr) state.
EXPECT_EQ(nullptr, p1.Get());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<DerivedGCed> p1 =
MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
// Move ctor is not heterogeneous - fall back to copy ctor.
PersistentType<GCed> p2 = std::move(p1);
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(p1.Get(), p2.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p1;
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
PersistentType<GCed> p2 = std::move(p1);
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(nullptr, p1.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, MoveCtor) {
auto* heap = GetHeap();
MoveCtor<Persistent>(heap);
MoveCtor<WeakPersistent>(heap);
MoveCtor<subtle::CrossThreadPersistent>(heap);
MoveCtor<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType,
template <typename> class MemberType>
void MemberCtor(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
MemberType<GCed> m =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p = m;
EXPECT_EQ(m.Get(), p.Get());
EXPECT_EQ(m, p);
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, MemberCtor) {
auto* heap = GetHeap();
MemberCtor<Persistent, Member>(heap);
MemberCtor<Persistent, WeakMember>(heap);
MemberCtor<Persistent, UntracedMember>(heap);
MemberCtor<WeakPersistent, Member>(heap);
MemberCtor<WeakPersistent, WeakMember>(heap);
MemberCtor<WeakPersistent, UntracedMember>(heap);
MemberCtor<subtle::CrossThreadPersistent, Member>(heap);
MemberCtor<subtle::CrossThreadPersistent, WeakMember>(heap);
MemberCtor<subtle::CrossThreadPersistent, UntracedMember>(heap);
MemberCtor<subtle::WeakCrossThreadPersistent, Member>(heap);
MemberCtor<subtle::WeakCrossThreadPersistent, WeakMember>(heap);
MemberCtor<subtle::WeakCrossThreadPersistent, UntracedMember>(heap);
}
template <template <typename> class PersistentType>
void NullStateAssignment(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
p = nullptr;
EXPECT_EQ(nullptr, p.Get());
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
{
PersistentType<GCed> p =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
p = kSentinelPointer;
EXPECT_EQ(kSentinelPointer, p);
EXPECT_EQ(kSentinelPointer, p.Get());
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
{
PersistentType<GCed> p =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
p = static_cast<GCed*>(0);
EXPECT_EQ(nullptr, p.Get());
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
}
TEST_F(PersistentTest, NullStateAssignemnt) {
auto* heap = GetHeap();
NullStateAssignment<Persistent>(heap);
NullStateAssignment<WeakPersistent>(heap);
NullStateAssignment<subtle::CrossThreadPersistent>(heap);
NullStateAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
void RawAssignment(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
{
PersistentType<GCed> p;
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
p = gced;
EXPECT_EQ(gced, p.Get());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p;
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
p = *gced;
EXPECT_EQ(gced, p.Get());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, RawAssignment) {
auto* heap = GetHeap();
RawAssignment<Persistent>(heap);
RawAssignment<WeakPersistent>(heap);
RawAssignment<subtle::CrossThreadPersistent>(heap);
RawAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
void CopyAssignment(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p1 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p2;
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
p2 = p1;
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(p1.Get(), p2.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p1 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p2 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
p2 = p1;
// The old node from p2 must be dropped.
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(p1.Get(), p2.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<DerivedGCed> p1 =
MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
PersistentType<GCed> p2;
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
p2 = p1;
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(p1.Get(), p2.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
static constexpr size_t kSlots = 512u;
const PersistentType<GCed> prototype =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
std::vector<PersistentType<GCed>> vector(kSlots);
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
size_t i = 0;
for (auto& p : vector) {
p = prototype;
EXPECT_EQ(i + 2, GetRegion<PersistentType>(heap).NodesInUse());
++i;
}
vector.clear();
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, CopyAssignment) {
auto* heap = GetHeap();
CopyAssignment<Persistent>(heap);
CopyAssignment<WeakPersistent>(heap);
CopyAssignment<subtle::CrossThreadPersistent>(heap);
CopyAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
void MoveAssignment(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p1 = gced;
PersistentType<GCed> p2;
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
p2 = std::move(p1);
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(gced, p2.Get());
// Moved-from-object is in the valid specified (nullptr) state.
EXPECT_EQ(nullptr, p1.Get());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<GCed> p1;
PersistentType<GCed> p2 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
p2 = std::move(p1);
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
// Moved-from-object is in the valid specified (nullptr) state.
EXPECT_EQ(nullptr, p2.Get());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p1 = gced;
PersistentType<GCed> p2 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
p2 = std::move(p1);
// The old node from p2 must be dropped.
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(gced, p2.Get());
// Moved-from-object is in the valid specified (nullptr) state.
EXPECT_EQ(nullptr, p1.Get());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
PersistentType<DerivedGCed> p1 =
MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
PersistentType<GCed> p2;
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
// Move ctor is not heterogeneous - fall back to copy assignment.
p2 = std::move(p1);
EXPECT_EQ(2u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_EQ(p1.Get(), p2.Get());
EXPECT_EQ(p1, p2);
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, MoveAssignment) {
auto* heap = GetHeap();
MoveAssignment<Persistent>(heap);
MoveAssignment<WeakPersistent>(heap);
MoveAssignment<subtle::CrossThreadPersistent>(heap);
MoveAssignment<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType,
template <typename> class MemberType>
void MemberAssignment(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{
MemberType<GCed> m =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p;
p = m;
EXPECT_EQ(m.Get(), p.Get());
EXPECT_EQ(m, p);
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, MemberAssignment) {
auto* heap = GetHeap();
MemberAssignment<Persistent, Member>(heap);
MemberAssignment<Persistent, WeakMember>(heap);
MemberAssignment<Persistent, UntracedMember>(heap);
MemberAssignment<WeakPersistent, Member>(heap);
MemberAssignment<WeakPersistent, WeakMember>(heap);
MemberAssignment<WeakPersistent, UntracedMember>(heap);
MemberAssignment<subtle::CrossThreadPersistent, Member>(heap);
MemberAssignment<subtle::CrossThreadPersistent, WeakMember>(heap);
MemberAssignment<subtle::CrossThreadPersistent, UntracedMember>(heap);
MemberAssignment<subtle::WeakCrossThreadPersistent, Member>(heap);
MemberAssignment<subtle::WeakCrossThreadPersistent, WeakMember>(heap);
MemberAssignment<subtle::WeakCrossThreadPersistent, UntracedMember>(heap);
}
template <template <typename> class PersistentType>
void ClearTest(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
PersistentType<GCed> p =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_NE(nullptr, p.Get());
p.Clear();
EXPECT_EQ(nullptr, p.Get());
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, Clear) {
auto* heap = GetHeap();
ClearTest<Persistent>(heap);
ClearTest<WeakPersistent>(heap);
ClearTest<subtle::CrossThreadPersistent>(heap);
ClearTest<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType>
void ReleaseTest(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
GCed* gced = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType<GCed> p = gced;
EXPECT_EQ(1u, GetRegion<PersistentType>(heap).NodesInUse());
EXPECT_NE(nullptr, p.Get());
GCed* raw = p.Release();
EXPECT_EQ(gced, raw);
EXPECT_EQ(nullptr, p.Get());
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
}
TEST_F(PersistentTest, Release) {
auto* heap = GetHeap();
ReleaseTest<Persistent>(heap);
ReleaseTest<WeakPersistent>(heap);
ReleaseTest<subtle::CrossThreadPersistent>(heap);
ReleaseTest<subtle::WeakCrossThreadPersistent>(heap);
}
template <template <typename> class PersistentType1,
template <typename> class PersistentType2>
void HeterogeneousConversion(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(0u, GetRegion<PersistentType2>(heap).NodesInUse());
{
PersistentType1<GCed> persistent1 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType2<GCed> persistent2 = persistent1;
EXPECT_EQ(persistent1.Get(), persistent2.Get());
EXPECT_EQ(1u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(1u, GetRegion<PersistentType2>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(0u, GetRegion<PersistentType2>(heap).NodesInUse());
{
PersistentType1<DerivedGCed> persistent1 =
MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
PersistentType2<GCed> persistent2 = persistent1;
EXPECT_EQ(persistent1.Get(), persistent2.Get());
EXPECT_EQ(1u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(1u, GetRegion<PersistentType2>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(0u, GetRegion<PersistentType2>(heap).NodesInUse());
{
PersistentType1<GCed> persistent1 =
MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
PersistentType2<GCed> persistent2;
persistent2 = persistent1;
EXPECT_EQ(persistent1.Get(), persistent2.Get());
EXPECT_EQ(1u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(1u, GetRegion<PersistentType2>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(0u, GetRegion<PersistentType2>(heap).NodesInUse());
{
PersistentType1<DerivedGCed> persistent1 =
MakeGarbageCollected<DerivedGCed>(heap->GetAllocationHandle());
PersistentType2<GCed> persistent2;
persistent2 = persistent1;
EXPECT_EQ(persistent1.Get(), persistent2.Get());
EXPECT_EQ(1u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(1u, GetRegion<PersistentType2>(heap).NodesInUse());
}
EXPECT_EQ(0u, GetRegion<PersistentType1>(heap).NodesInUse());
EXPECT_EQ(0u, GetRegion<PersistentType2>(heap).NodesInUse());
}
TEST_F(PersistentTest, HeterogeneousConversion) {
auto* heap = GetHeap();
HeterogeneousConversion<Persistent, WeakPersistent>(heap);
HeterogeneousConversion<WeakPersistent, Persistent>(heap);
}
TEST_F(PersistentTest, TraceStrong) {
auto* heap = GetHeap();
static constexpr size_t kItems = 512;
std::vector<Persistent<GCed>> vec(kItems);
for (auto& p : vec) {
p = MakeGarbageCollected<GCed>(GetAllocationHandle());
}
{
GCed::trace_call_count = 0;
RootVisitor v;
GetRegion<Persistent>(heap).Trace(&v);
EXPECT_EQ(kItems, GCed::trace_call_count);
EXPECT_EQ(kItems, GetRegion<Persistent>(heap).NodesInUse());
}
{
GCed::trace_call_count = 0;
vec[0].Clear();
vec[kItems / 2].Clear();
vec[kItems / 4].Clear();
vec[kItems - 1].Clear();
RootVisitor v;
GetRegion<Persistent>(heap).Trace(&v);
EXPECT_EQ(kItems - 4, GCed::trace_call_count);
EXPECT_EQ(kItems - 4, GetRegion<Persistent>(heap).NodesInUse());
}
{
GCed::trace_call_count = 0;
vec.clear();
RootVisitor v;
GetRegion<Persistent>(heap).Trace(&v);
EXPECT_EQ(0u, GCed::trace_call_count);
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse());
}
}
TEST_F(PersistentTest, TraceWeak) {
auto* heap = GetHeap();
static constexpr size_t kItems = 512;
std::vector<WeakPersistent<GCed>> vec(kItems);
for (auto& p : vec) {
p = MakeGarbageCollected<GCed>(GetAllocationHandle());
}
GCed::trace_call_count = 0;
RootVisitor v;
GetRegion<WeakPersistent>(heap).Trace(&v);
const auto& callbacks = v.WeakCallbacks();
EXPECT_EQ(kItems, callbacks.size());
EXPECT_EQ(kItems, GetRegion<WeakPersistent>(heap).NodesInUse());
v.ProcessWeakCallbacks();
for (const auto& p : vec) {
EXPECT_EQ(nullptr, p.Get());
}
EXPECT_EQ(0u, GetRegion<WeakPersistent>(heap).NodesInUse());
}
TEST_F(PersistentTest, ClearOnHeapDestruction) {
Persistent<GCed> persistent;
WeakPersistent<GCed> weak_persistent;
// Create another heap that can be destroyed during the test.
auto heap = Heap::Create(GetPlatformHandle());
persistent = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
weak_persistent = MakeGarbageCollected<GCed>(heap->GetAllocationHandle());
const Persistent<GCed> persistent_sentinel(kSentinelPointer);
const WeakPersistent<GCed> weak_persistent_sentinel(kSentinelPointer);
const subtle::CrossThreadPersistent<GCed> cross_thread_persistent_sentinel(
kSentinelPointer);
const subtle::WeakCrossThreadPersistent<GCed>
cross_thread_weak_persistent_sentinel(kSentinelPointer);
heap.reset();
EXPECT_EQ(nullptr, persistent);
EXPECT_EQ(nullptr, weak_persistent);
// Sentinel values survive as they do not represent actual heap objects.
EXPECT_EQ(kSentinelPointer, persistent_sentinel);
EXPECT_EQ(kSentinelPointer, weak_persistent_sentinel);
}
#if CPPGC_SUPPORTS_SOURCE_LOCATION
TEST_F(PersistentTest, LocalizedPersistent) {
GCed* gced = MakeGarbageCollected<GCed>(GetAllocationHandle());
{
const auto expected_loc = SourceLocation::Current();
LocalizedPersistent<GCed> p = gced;
const auto actual_loc = p.Location();
EXPECT_STREQ(expected_loc.Function(), actual_loc.Function());
EXPECT_STREQ(expected_loc.FileName(), actual_loc.FileName());
EXPECT_EQ(expected_loc.Line() + 1, actual_loc.Line());
}
{
const auto expected_loc = SourceLocation::Current();
LocalizedCrossThreadPersistent<GCed> p = gced;
const auto actual_loc = p.Location();
EXPECT_STREQ(expected_loc.Function(), actual_loc.Function());
EXPECT_STREQ(expected_loc.FileName(), actual_loc.FileName());
EXPECT_EQ(expected_loc.Line() + 1, actual_loc.Line());
}
{
// Copy ctor doesn't copy source location.
LocalizedPersistent<GCed> p1 = gced;
LocalizedPersistent<GCed> p2 = p1;
EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
// Copy ctor doesn't copy source location.
LocalizedCrossThreadPersistent<GCed> p1 = gced;
LocalizedCrossThreadPersistent<GCed> p2 = p1;
EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
// Copy assignment doesn't copy source location.
LocalizedPersistent<GCed> p1 = gced;
LocalizedPersistent<GCed> p2;
p2 = p1;
EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
// Copy assignment doesn't copy source location.
LocalizedCrossThreadPersistent<GCed> p1 = gced;
LocalizedCrossThreadPersistent<GCed> p2;
p2 = p1;
EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
// Clearing doesn't clear source location.
LocalizedPersistent<GCed> p1 = gced;
LocalizedPersistent<GCed> p2 = gced;
p2.Clear();
EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
// Clearing doesn't clear source location.
LocalizedCrossThreadPersistent<GCed> p1 = gced;
LocalizedCrossThreadPersistent<GCed> p2 = gced;
p2.Clear();
EXPECT_STREQ(p1.Location().Function(), p2.Location().Function());
EXPECT_STREQ(p1.Location().FileName(), p2.Location().FileName());
EXPECT_EQ(p1.Location().Line() + 1, p2.Location().Line());
}
{
LocalizedPersistent<GCed> p1 = gced;
const auto expected_loc = p1.Location();
LocalizedPersistent<GCed> p2 = std::move(p1);
EXPECT_STREQ(expected_loc.Function(), p2.Location().Function());
EXPECT_STREQ(expected_loc.FileName(), p2.Location().FileName());
EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
}
{
LocalizedCrossThreadPersistent<GCed> p1 = gced;
const auto expected_loc = p1.Location();
LocalizedCrossThreadPersistent<GCed> p2 = std::move(p1);
EXPECT_STREQ(expected_loc.Function(), p2.Location().Function());
EXPECT_STREQ(expected_loc.FileName(), p2.Location().FileName());
EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
}
{
LocalizedPersistent<GCed> p1 = gced;
const auto expected_loc = p1.Location();
LocalizedPersistent<GCed> p2;
p2 = std::move(p1);
EXPECT_STREQ(expected_loc.Function(), p2.Location().Function());
EXPECT_STREQ(expected_loc.FileName(), p2.Location().FileName());
EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
}
{
LocalizedCrossThreadPersistent<GCed> p1 = gced;
const auto expected_loc = p1.Location();
LocalizedCrossThreadPersistent<GCed> p2;
p2 = std::move(p1);
EXPECT_STREQ(expected_loc.Function(), p2.Location().Function());
EXPECT_STREQ(expected_loc.FileName(), p2.Location().FileName());
EXPECT_EQ(expected_loc.Line(), p2.Location().Line());
}
}
#endif
namespace {
class ExpectingLocationVisitor final : public VisitorBase {
public:
explicit ExpectingLocationVisitor(const SourceLocation& expected_location)
: expected_loc_(expected_location) {}
protected:
void VisitRoot(const void* t, TraceDescriptor desc,
const SourceLocation& loc) final {
EXPECT_STREQ(expected_loc_.Function(), loc.Function());
EXPECT_STREQ(expected_loc_.FileName(), loc.FileName());
EXPECT_EQ(expected_loc_.Line(), loc.Line());
}
private:
const SourceLocation& expected_loc_;
};
} // namespace
TEST_F(PersistentTest, PersistentTraceLocation) {
GCed* gced = MakeGarbageCollected<GCed>(GetAllocationHandle());
{
#if CPPGC_SUPPORTS_SOURCE_LOCATION
// Baseline for creating expected location which has a different line
// number.
const auto loc = SourceLocation::Current();
const auto expected_loc =
SourceLocation::Current(loc.Function(), loc.FileName(), loc.Line() + 6);
#else // !CCPPGC_SUPPORTS_SOURCE_LOCATION
const SourceLocation expected_loc;
#endif // !CCPPGC_SUPPORTS_SOURCE_LOCATION
LocalizedPersistent<GCed> p = gced;
ExpectingLocationVisitor visitor(expected_loc);
visitor.TraceRootForTesting(p, p.Location());
}
}
} // namespace internal
} // namespace cppgc