v8/test/unittests/heap/cppgc/visitor-unittest.cc
Omer Katz 288b545ad0 cppgc: Add TraceCallback to GCInfo
This is needed to trace objects found durinbg stack scanning.

Bug: chromium:1056170
Change-Id: I1280d98f2fe69281c514b3a7d4a57f909a2eed96
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2190425
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67788}
2020-05-13 23:53:43 +00:00

233 lines
7.2 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 "src/heap/cppgc/visitor.h"
#include "include/cppgc/allocation.h"
#include "include/cppgc/garbage-collected.h"
#include "include/cppgc/trace-trait.h"
#include "src/base/macros.h"
#include "src/heap/cppgc/heap.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
namespace {
class TraceTraitTest : public testing::TestSupportingAllocationOnly {};
class VisitorTest : public testing::TestSupportingAllocationOnly {};
class GCed : public GarbageCollected<GCed> {
public:
static size_t trace_callcount;
GCed() { trace_callcount = 0; }
virtual void Trace(cppgc::Visitor* visitor) const { trace_callcount++; }
};
size_t GCed::trace_callcount;
class GCedMixin : public GarbageCollectedMixin {};
class OtherPayload {
public:
virtual void* GetDummy() const { return nullptr; }
};
class GCedMixinApplication : public GCed,
public OtherPayload,
public GCedMixin {
USING_GARBAGE_COLLECTED_MIXIN();
public:
void Trace(cppgc::Visitor* visitor) const override {
GCed::Trace(visitor);
GCedMixin::Trace(visitor);
}
};
} // namespace
TEST_F(TraceTraitTest, GetObjectStartGCed) {
auto* gced = MakeGarbageCollected<GCed>(GetHeap());
EXPECT_EQ(gced,
TraceTrait<GCed>::GetTraceDescriptor(gced).base_object_payload);
}
TEST_F(TraceTraitTest, GetObjectStartGCedMixin) {
auto* gced_mixin_app = MakeGarbageCollected<GCedMixinApplication>(GetHeap());
auto* gced_mixin = static_cast<GCedMixin*>(gced_mixin_app);
EXPECT_EQ(gced_mixin_app,
TraceTrait<GCedMixin>::GetTraceDescriptor(gced_mixin)
.base_object_payload);
}
TEST_F(TraceTraitTest, TraceGCed) {
auto* gced = MakeGarbageCollected<GCed>(GetHeap());
EXPECT_EQ(0u, GCed::trace_callcount);
TraceTrait<GCed>::Trace(nullptr, gced);
EXPECT_EQ(1u, GCed::trace_callcount);
}
TEST_F(TraceTraitTest, TraceGCedMixin) {
auto* gced_mixin_app = MakeGarbageCollected<GCedMixinApplication>(GetHeap());
auto* gced_mixin = static_cast<GCedMixin*>(gced_mixin_app);
EXPECT_EQ(0u, GCed::trace_callcount);
TraceTrait<GCedMixin>::Trace(nullptr, gced_mixin);
EXPECT_EQ(1u, GCed::trace_callcount);
}
TEST_F(TraceTraitTest, TraceGCedThroughTraceDescriptor) {
auto* gced = MakeGarbageCollected<GCed>(GetHeap());
EXPECT_EQ(0u, GCed::trace_callcount);
TraceDescriptor desc = TraceTrait<GCed>::GetTraceDescriptor(gced);
desc.callback(nullptr, desc.base_object_payload);
EXPECT_EQ(1u, GCed::trace_callcount);
}
TEST_F(TraceTraitTest, TraceGCedMixinThroughTraceDescriptor) {
auto* gced_mixin_app = MakeGarbageCollected<GCedMixinApplication>(GetHeap());
auto* gced_mixin = static_cast<GCedMixin*>(gced_mixin_app);
EXPECT_EQ(0u, GCed::trace_callcount);
TraceDescriptor desc = TraceTrait<GCedMixin>::GetTraceDescriptor(gced_mixin);
desc.callback(nullptr, desc.base_object_payload);
EXPECT_EQ(1u, GCed::trace_callcount);
}
namespace {
class DispatchingVisitor final : public VisitorBase {
public:
DispatchingVisitor(const void* object, const void* payload)
: object_(object), payload_(payload) {}
protected:
void Visit(const void* t, TraceDescriptor desc) final {
EXPECT_EQ(object_, t);
EXPECT_EQ(payload_, desc.base_object_payload);
desc.callback(this, desc.base_object_payload);
}
void VisitWeak(const void* t, TraceDescriptor desc, WeakCallback callback,
const void* weak_member) final {
EXPECT_EQ(object_, t);
EXPECT_EQ(payload_, desc.base_object_payload);
LivenessBroker broker = LivenessBrokerFactory::Create();
callback(broker, weak_member);
}
private:
const void* object_;
const void* payload_;
};
} // namespace
TEST_F(VisitorTest, DispatchTraceGCed) {
Member<GCed> ref = MakeGarbageCollected<GCed>(GetHeap());
DispatchingVisitor visitor(ref, ref);
EXPECT_EQ(0u, GCed::trace_callcount);
visitor.Trace(ref);
EXPECT_EQ(1u, GCed::trace_callcount);
}
TEST_F(VisitorTest, DispatchTraceGCedMixin) {
auto* gced_mixin_app = MakeGarbageCollected<GCedMixinApplication>(GetHeap());
auto* gced_mixin = static_cast<GCedMixin*>(gced_mixin_app);
// Ensure that we indeed test dispatching an inner object.
EXPECT_NE(static_cast<void*>(gced_mixin_app), static_cast<void*>(gced_mixin));
Member<GCedMixin> ref = gced_mixin;
DispatchingVisitor visitor(gced_mixin, gced_mixin_app);
EXPECT_EQ(0u, GCed::trace_callcount);
visitor.Trace(ref);
EXPECT_EQ(1u, GCed::trace_callcount);
}
TEST_F(VisitorTest, DispatchTraceWeakGCed) {
WeakMember<GCed> ref = MakeGarbageCollected<GCed>(GetHeap());
DispatchingVisitor visitor(ref, ref);
visitor.Trace(ref);
// No marking, so reference should be cleared.
EXPECT_EQ(nullptr, ref.Get());
}
TEST_F(VisitorTest, DispatchTraceWeakGCedMixin) {
auto* gced_mixin_app = MakeGarbageCollected<GCedMixinApplication>(GetHeap());
auto* gced_mixin = static_cast<GCedMixin*>(gced_mixin_app);
// Ensure that we indeed test dispatching an inner object.
EXPECT_NE(static_cast<void*>(gced_mixin_app), static_cast<void*>(gced_mixin));
WeakMember<GCedMixin> ref = gced_mixin;
DispatchingVisitor visitor(gced_mixin, gced_mixin_app);
visitor.Trace(ref);
// No marking, so reference should be cleared.
EXPECT_EQ(nullptr, ref.Get());
}
namespace {
class WeakCallbackVisitor final : public VisitorBase {
public:
void RegisterWeakCallback(WeakCallback callback, const void* param) final {
LivenessBroker broker = LivenessBrokerFactory::Create();
callback(broker, param);
}
};
struct WeakCallbackDispatcher {
static size_t callback_callcount;
static const void* callback_param;
static void Setup(const void* expected_param) {
callback_callcount = 0;
callback_param = expected_param;
}
static void Call(const LivenessBroker& broker, const void* param) {
EXPECT_EQ(callback_param, param);
callback_callcount++;
}
};
size_t WeakCallbackDispatcher::callback_callcount;
const void* WeakCallbackDispatcher::callback_param;
class GCedWithCustomWeakCallback final
: public GarbageCollected<GCedWithCustomWeakCallback> {
public:
void CustomWeakCallbackMethod(const LivenessBroker& broker) {
WeakCallbackDispatcher::Call(broker, this);
}
void Trace(cppgc::Visitor* visitor) const {
visitor->RegisterWeakCallbackMethod<
GCedWithCustomWeakCallback,
&GCedWithCustomWeakCallback::CustomWeakCallbackMethod>(this);
}
};
} // namespace
TEST_F(VisitorTest, DispatchRegisterWeakCallback) {
WeakCallbackVisitor visitor;
WeakCallbackDispatcher::Setup(&visitor);
EXPECT_EQ(0u, WeakCallbackDispatcher::callback_callcount);
visitor.RegisterWeakCallback(WeakCallbackDispatcher::Call, &visitor);
EXPECT_EQ(1u, WeakCallbackDispatcher::callback_callcount);
}
TEST_F(VisitorTest, DispatchRegisterWeakCallbackMethod) {
WeakCallbackVisitor visitor;
auto* gced = MakeGarbageCollected<GCedWithCustomWeakCallback>(GetHeap());
WeakCallbackDispatcher::Setup(gced);
EXPECT_EQ(0u, WeakCallbackDispatcher::callback_callcount);
gced->Trace(&visitor);
EXPECT_EQ(1u, WeakCallbackDispatcher::callback_callcount);
}
} // namespace internal
} // namespace cppgc