blob: b57eb8f050683dca044257ab7530fb4a6cc73c77 [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 "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 "src/heap/cppgc/liveness-broker.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 {
public:
void Trace(cppgc::Visitor* visitor) const override {
GCed::Trace(visitor);
GCedMixin::Trace(visitor);
}
};
} // namespace
TEST_F(TraceTraitTest, GetObjectStartGCed) {
auto* gced = MakeGarbageCollected<GCed>(GetAllocationHandle());
EXPECT_EQ(gced,
TraceTrait<GCed>::GetTraceDescriptor(gced).base_object_payload);
}
TEST_F(TraceTraitTest, GetObjectStartGCedMixin) {
auto* gced_mixin_app =
MakeGarbageCollected<GCedMixinApplication>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
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>(GetAllocationHandle());
WeakCallbackDispatcher::Setup(gced);
EXPECT_EQ(0u, WeakCallbackDispatcher::callback_callcount);
gced->Trace(&visitor);
EXPECT_EQ(1u, WeakCallbackDispatcher::callback_callcount);
}
namespace {
class Composite final {
public:
static size_t callback_callcount;
Composite() { callback_callcount = 0; }
void Trace(Visitor* visitor) const { callback_callcount++; }
};
size_t Composite::callback_callcount;
class GCedWithComposite final : public GarbageCollected<GCedWithComposite> {
public:
void Trace(Visitor* visitor) const { visitor->Trace(composite); }
Composite composite;
};
} // namespace
TEST_F(VisitorTest, DispatchToCompositeObject) {
Member<GCedWithComposite> ref =
MakeGarbageCollected<GCedWithComposite>(GetAllocationHandle());
DispatchingVisitor visitor(ref, ref);
EXPECT_EQ(0u, Composite::callback_callcount);
visitor.Trace(ref);
EXPECT_EQ(1u, Composite::callback_callcount);
}
} // namespace internal
} // namespace cppgc