// 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 "src/heap/cppgc/marking-visitor.h" #include "include/cppgc/allocation.h" #include "include/cppgc/member.h" #include "include/cppgc/persistent.h" #include "include/cppgc/source-location.h" #include "src/heap/cppgc/globals.h" #include "src/heap/cppgc/heap-object-header.h" #include "src/heap/cppgc/marker.h" #include "src/heap/cppgc/marking-state.h" #include "test/unittests/heap/cppgc/tests.h" #include "testing/gtest/include/gtest/gtest.h" namespace cppgc { namespace internal { namespace { class MarkingVisitorTest : public testing::TestWithHeap { public: MarkingVisitorTest() : marker_(MarkerFactory::CreateAndStartMarking( *Heap::From(GetHeap()), GetPlatformHandle().get())) {} ~MarkingVisitorTest() override { marker_->ClearAllWorklistsForTesting(); } Marker* GetMarker() { return marker_.get(); } private: std::unique_ptr marker_; }; class GCed : public GarbageCollected { public: void Trace(cppgc::Visitor*) const {} }; class Mixin : public GarbageCollectedMixin {}; class GCedWithMixin : public GarbageCollected, public Mixin { public: void Trace(cppgc::Visitor*) const override {} }; class TestMarkingVisitor : public MarkingVisitor { public: explicit TestMarkingVisitor(Marker* marker) : MarkingVisitor(marker->heap(), marker->MarkingStateForTesting()) {} ~TestMarkingVisitor() { marking_state_.Publish(); } }; } // namespace TEST_F(MarkingVisitorTest, MarkedBytesAreInitiallyZero) { EXPECT_EQ(0u, GetMarker()->MarkingStateForTesting().marked_bytes()); } // Strong references are marked. TEST_F(MarkingVisitorTest, MarkMember) { Member object(MakeGarbageCollected(GetAllocationHandle())); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.Trace(object); EXPECT_TRUE(header.IsMarked()); } TEST_F(MarkingVisitorTest, MarkMemberMixin) { GCedWithMixin* object( MakeGarbageCollected(GetAllocationHandle())); Member mixin(object); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.Trace(mixin); EXPECT_TRUE(header.IsMarked()); } TEST_F(MarkingVisitorTest, MarkPersistent) { Persistent object(MakeGarbageCollected(GetAllocationHandle())); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.TraceRootForTesting(object, SourceLocation::Current()); EXPECT_TRUE(header.IsMarked()); } TEST_F(MarkingVisitorTest, MarkPersistentMixin) { GCedWithMixin* object( MakeGarbageCollected(GetAllocationHandle())); Persistent mixin(object); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.TraceRootForTesting(mixin, SourceLocation::Current()); EXPECT_TRUE(header.IsMarked()); } // Weak references are not marked. TEST_F(MarkingVisitorTest, DontMarkWeakMember) { WeakMember object(MakeGarbageCollected(GetAllocationHandle())); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.Trace(object); EXPECT_FALSE(header.IsMarked()); } TEST_F(MarkingVisitorTest, DontMarkWeakMemberMixin) { GCedWithMixin* object( MakeGarbageCollected(GetAllocationHandle())); WeakMember mixin(object); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.Trace(mixin); EXPECT_FALSE(header.IsMarked()); } TEST_F(MarkingVisitorTest, DontMarkWeakPersistent) { WeakPersistent object( MakeGarbageCollected(GetAllocationHandle())); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.TraceRootForTesting(object, SourceLocation::Current()); EXPECT_FALSE(header.IsMarked()); } TEST_F(MarkingVisitorTest, DontMarkWeakPersistentMixin) { GCedWithMixin* object( MakeGarbageCollected(GetAllocationHandle())); WeakPersistent mixin(object); HeapObjectHeader& header = HeapObjectHeader::FromPayload(object); TestMarkingVisitor visitor(GetMarker()); EXPECT_FALSE(header.IsMarked()); visitor.TraceRootForTesting(mixin, SourceLocation::Current()); EXPECT_FALSE(header.IsMarked()); } // In construction objects are not marked. namespace { class GCedWithInConstructionCallback : public GarbageCollected { public: template explicit GCedWithInConstructionCallback(Callback callback) { callback(this); } void Trace(cppgc::Visitor*) const {} }; class MixinWithInConstructionCallback : public GarbageCollectedMixin { public: template explicit MixinWithInConstructionCallback(Callback callback) { callback(this); } }; class GCedWithMixinWithInConstructionCallback : public GarbageCollected, public MixinWithInConstructionCallback { public: template explicit GCedWithMixinWithInConstructionCallback(Callback callback) : MixinWithInConstructionCallback(callback) {} void Trace(cppgc::Visitor*) const override {} }; } // namespace TEST_F(MarkingVisitorTest, MarkMemberInConstruction) { TestMarkingVisitor visitor(GetMarker()); GCedWithInConstructionCallback* gced = MakeGarbageCollected( GetAllocationHandle(), [&visitor](GCedWithInConstructionCallback* obj) { Member object(obj); visitor.Trace(object); }); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked()); } TEST_F(MarkingVisitorTest, MarkMemberMixinInConstruction) { TestMarkingVisitor visitor(GetMarker()); GCedWithMixinWithInConstructionCallback* gced = MakeGarbageCollected( GetAllocationHandle(), [&visitor](MixinWithInConstructionCallback* obj) { Member mixin(obj); visitor.Trace(mixin); }); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked()); } TEST_F(MarkingVisitorTest, DontMarkWeakMemberInConstruction) { TestMarkingVisitor visitor(GetMarker()); GCedWithInConstructionCallback* gced = MakeGarbageCollected( GetAllocationHandle(), [&visitor](GCedWithInConstructionCallback* obj) { WeakMember object(obj); visitor.Trace(object); }); EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked()); } TEST_F(MarkingVisitorTest, DontMarkWeakMemberMixinInConstruction) { TestMarkingVisitor visitor(GetMarker()); GCedWithMixinWithInConstructionCallback* gced = MakeGarbageCollected( GetAllocationHandle(), [&visitor](MixinWithInConstructionCallback* obj) { WeakMember mixin(obj); visitor.Trace(mixin); }); EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked()); } TEST_F(MarkingVisitorTest, MarkPersistentInConstruction) { TestMarkingVisitor visitor(GetMarker()); GCedWithInConstructionCallback* gced = MakeGarbageCollected( GetAllocationHandle(), [&visitor](GCedWithInConstructionCallback* obj) { Persistent object(obj); visitor.TraceRootForTesting(object, SourceLocation::Current()); }); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked()); } TEST_F(MarkingVisitorTest, MarkPersistentMixinInConstruction) { TestMarkingVisitor visitor(GetMarker()); GCedWithMixinWithInConstructionCallback* gced = MakeGarbageCollected( GetAllocationHandle(), [&visitor](MixinWithInConstructionCallback* obj) { Persistent mixin(obj); visitor.TraceRootForTesting(mixin, SourceLocation::Current()); }); EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked()); } } // namespace internal } // namespace cppgc