// Copyright 2020 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "fxjs/gc/heap.h" #include #include #include "testing/fxgc_unittest.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/v8_test_environment.h" #include "third_party/base/containers/contains.h" #include "v8/include/cppgc/allocation.h" #include "v8/include/cppgc/persistent.h" namespace { class PseudoCollectible : public cppgc::GarbageCollected { public: static void ClearCounts() { s_live_.clear(); s_dead_.clear(); } static size_t LiveCount() { return s_live_.size(); } static size_t DeadCount() { return s_dead_.size(); } PseudoCollectible() { s_live_.insert(this); } virtual ~PseudoCollectible() { s_live_.erase(this); s_dead_.insert(this); } bool IsLive() const { return pdfium::Contains(s_live_, this); } virtual void Trace(cppgc::Visitor* visitor) const {} private: static std::set s_live_; static std::set s_dead_; }; std::set PseudoCollectible::s_live_; std::set PseudoCollectible::s_dead_; class CollectibleHolder { public: explicit CollectibleHolder(PseudoCollectible* holdee) : holdee_(holdee) {} ~CollectibleHolder() = default; PseudoCollectible* holdee() const { return holdee_; } private: cppgc::Persistent holdee_; }; class Bloater : public cppgc::GarbageCollected { public: void Trace(cppgc::Visitor* visitor) const {} uint8_t bloat_[65536]; }; } // namespace class HeapUnitTest : public FXGCUnitTest { public: HeapUnitTest() = default; ~HeapUnitTest() override = default; // FXGCUnitTest: void TearDown() override { PseudoCollectible::ClearCounts(); FXGCUnitTest::TearDown(); } }; TEST_F(HeapUnitTest, SeveralHeaps) { FXGCScopedHeap heap1 = FXGC_CreateHeap(); EXPECT_TRUE(heap1); FXGCScopedHeap heap2 = FXGC_CreateHeap(); EXPECT_TRUE(heap2); FXGCScopedHeap heap3 = FXGC_CreateHeap(); EXPECT_TRUE(heap3); // Test manually destroying the heap. heap3.reset(); EXPECT_FALSE(heap3); heap3.reset(); EXPECT_FALSE(heap3); } TEST_F(HeapUnitTest, NoReferences) { FXGCScopedHeap heap1 = FXGC_CreateHeap(); ASSERT_TRUE(heap1); { auto holder = std::make_unique( cppgc::MakeGarbageCollected( heap1->GetAllocationHandle())); EXPECT_TRUE(holder->holdee()->IsLive()); EXPECT_EQ(1u, PseudoCollectible::LiveCount()); EXPECT_EQ(0u, PseudoCollectible::DeadCount()); } FXGC_ForceGarbageCollection(heap1.get()); EXPECT_EQ(0u, PseudoCollectible::LiveCount()); EXPECT_EQ(1u, PseudoCollectible::DeadCount()); } TEST_F(HeapUnitTest, HasReferences) { FXGCScopedHeap heap1 = FXGC_CreateHeap(); ASSERT_TRUE(heap1); { auto holder = std::make_unique( cppgc::MakeGarbageCollected( heap1->GetAllocationHandle())); EXPECT_TRUE(holder->holdee()->IsLive()); EXPECT_EQ(1u, PseudoCollectible::LiveCount()); EXPECT_EQ(0u, PseudoCollectible::DeadCount()); FXGC_ForceGarbageCollection(heap1.get()); EXPECT_TRUE(holder->holdee()->IsLive()); EXPECT_EQ(1u, PseudoCollectible::LiveCount()); EXPECT_EQ(0u, PseudoCollectible::DeadCount()); } } // TODO(tsepez): enable when CPPGC fixes this segv. TEST_F(HeapUnitTest, DISABLED_DeleteHeapHasReferences) { FXGCScopedHeap heap1 = FXGC_CreateHeap(); ASSERT_TRUE(heap1); { auto holder = std::make_unique( cppgc::MakeGarbageCollected( heap1->GetAllocationHandle())); EXPECT_TRUE(holder->holdee()->IsLive()); EXPECT_EQ(1u, PseudoCollectible::LiveCount()); EXPECT_EQ(0u, PseudoCollectible::DeadCount()); heap1.reset(); // Maybe someday magically nulled by heap destruction. EXPECT_FALSE(holder->holdee()); EXPECT_EQ(1u, PseudoCollectible::LiveCount()); EXPECT_EQ(0u, PseudoCollectible::DeadCount()); } } TEST_F(HeapUnitTest, DeleteHeapNoReferences) { FXGCScopedHeap heap1 = FXGC_CreateHeap(); ASSERT_TRUE(heap1); { auto holder = std::make_unique( cppgc::MakeGarbageCollected( heap1->GetAllocationHandle())); EXPECT_TRUE(holder->holdee()->IsLive()); EXPECT_EQ(1u, PseudoCollectible::LiveCount()); EXPECT_EQ(0u, PseudoCollectible::DeadCount()); } heap1.reset(); EXPECT_EQ(0u, PseudoCollectible::LiveCount()); EXPECT_EQ(1u, PseudoCollectible::DeadCount()); } TEST_F(HeapUnitTest, Bloat) { ASSERT_TRUE(heap()); for (int i = 0; i < 100000; ++i) { cppgc::MakeGarbageCollected(heap()->GetAllocationHandle()); Pump(); // Do not force GC, must happen implicitly when space required. } }