441cb86805
Bug: chromium:1056170 Change-Id: I24442979954f63dc8a2f8fd0494cc5d537b733a6 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2273131 Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Commit-Queue: Anton Bikineev <bikineev@chromium.org> Auto-Submit: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Anton Bikineev <bikineev@chromium.org> Cr-Commit-Position: refs/heads/master@{#68587}
701 lines
24 KiB
C++
701 lines
24 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/persistent.h"
|
|
|
|
#include <vector>
|
|
|
|
#include "include/cppgc/allocation.h"
|
|
#include "include/cppgc/garbage-collected.h"
|
|
#include "include/cppgc/internal/pointer-policies.h"
|
|
#include "include/cppgc/member.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>
|
|
PersistentRegion& GetRegion(cppgc::Heap* heap) {
|
|
auto* heap_impl = internal::Heap::From(heap);
|
|
return IsWeak<PersistentType<GCed>>::value
|
|
? heap_impl->GetWeakPersistentRegion()
|
|
: heap_impl->GetStrongPersistentRegion();
|
|
}
|
|
|
|
template <typename T>
|
|
using LocalizedPersistent =
|
|
internal::BasicPersistent<T, internal::StrongPersistentPolicy,
|
|
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) final {
|
|
desc.callback(this, desc.base_object_payload);
|
|
}
|
|
void VisitWeakRoot(const void*, TraceDescriptor, WeakCallback callback,
|
|
const void* object) 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
template <template <typename> class PersistentType>
|
|
void NullStateAssignemnt(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();
|
|
NullStateAssignemnt<Persistent>(heap);
|
|
NullStateAssignemnt<WeakPersistent>(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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
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());
|
|
}
|
|
{
|
|
// 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 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());
|
|
}
|
|
{
|
|
// 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());
|
|
}
|
|
{
|
|
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());
|
|
}
|
|
{
|
|
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());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
} // namespace internal
|
|
} // namespace cppgc
|