blob: 23da432730fb5283a71a68ae60f1fb04c6bb2c88 [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 "include/cppgc/internal/finalizer-trait.h"
#include <type_traits>
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
namespace {
// Trivially destructible types.
class TypeWithoutDestructor final {};
class TypeWithPrimitive final {
public:
int foo = 0;
};
class InvokeCounter {
public:
static size_t kCallcount;
static void Reset() { kCallcount = 0; }
static void Invoke() { kCallcount++; }
};
size_t InvokeCounter::kCallcount = 0;
// Regular C++ use cases.
class TypeWithDestructor final : public InvokeCounter {
public:
~TypeWithDestructor() { Invoke(); }
};
class TypeWithVirtualDestructorBase {
public:
virtual ~TypeWithVirtualDestructorBase() = default;
};
class TypeWithVirtualDestructorChild final
: public TypeWithVirtualDestructorBase,
public InvokeCounter {
public:
~TypeWithVirtualDestructorChild() final { Invoke(); }
};
// Manual dispatch to avoid vtables.
class TypeWithCustomFinalizationMethod final : public InvokeCounter {
public:
void FinalizeGarbageCollectedObject() { Invoke(); }
};
class TypeWithCustomFinalizationMethodAtBase {
public:
void FinalizeGarbageCollectedObject();
};
class TypeWithCustomFinalizationMethodAtBaseChild
: public TypeWithCustomFinalizationMethodAtBase,
public InvokeCounter {
public:
~TypeWithCustomFinalizationMethodAtBaseChild() { Invoke(); }
};
void TypeWithCustomFinalizationMethodAtBase::FinalizeGarbageCollectedObject() {
// The test knows that base is only inherited by a single child. In practice
// users can maintain a map of valid types in already existing storage.
static_cast<TypeWithCustomFinalizationMethodAtBaseChild*>(this)
->~TypeWithCustomFinalizationMethodAtBaseChild();
}
template <typename Type>
void ExpectFinalizerIsInvoked(Type* object) {
InvokeCounter::Reset();
EXPECT_NE(nullptr, FinalizerTrait<Type>::kCallback);
FinalizerTrait<Type>::kCallback(object);
EXPECT_EQ(1u, InvokeCounter::kCallcount);
operator delete(object);
}
} // namespace
TEST(FinalizerTrait, TypeWithoutDestructorHasNoFinalizer) {
static_assert(std::is_trivially_destructible<TypeWithoutDestructor>::value,
"trivially destructible");
EXPECT_EQ(nullptr, FinalizerTrait<TypeWithoutDestructor>::kCallback);
}
TEST(FinalizerTrait, TypeWithPrimitiveHasNoFinalizer) {
static_assert(std::is_trivially_destructible<TypeWithPrimitive>::value,
"trivially destructible");
EXPECT_EQ(nullptr, FinalizerTrait<TypeWithPrimitive>::kCallback);
}
TEST(FinalizerTrait, FinalizerForTypeWithDestructor) {
ExpectFinalizerIsInvoked(new TypeWithDestructor());
}
TEST(FinalizerTrait, FinalizerForTypeWithVirtualBaseDtor) {
TypeWithVirtualDestructorBase* base = new TypeWithVirtualDestructorChild();
ExpectFinalizerIsInvoked(base);
}
TEST(FinalizerTrait, FinalizerForCustomFinalizationMethod) {
ExpectFinalizerIsInvoked(new TypeWithCustomFinalizationMethod());
}
TEST(FinalizerTrait, FinalizerForCustomFinalizationMethodInBase) {
TypeWithCustomFinalizationMethodAtBase* base =
new TypeWithCustomFinalizationMethodAtBaseChild();
ExpectFinalizerIsInvoked(base);
}
} // namespace internal
} // namespace cppgc