// 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 #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(this) ->~TypeWithCustomFinalizationMethodAtBaseChild(); } template void ExpectFinalizerIsInvoked(Type* object) { InvokeCounter::Reset(); EXPECT_NE(nullptr, FinalizerTrait::kCallback); FinalizerTrait::kCallback(object); EXPECT_EQ(1u, InvokeCounter::kCallcount); operator delete(object); } } // namespace TEST(FinalizerTrait, TypeWithoutDestructorHasNoFinalizer) { static_assert(std::is_trivially_destructible::value, "trivially destructible"); EXPECT_EQ(nullptr, FinalizerTrait::kCallback); } TEST(FinalizerTrait, TypeWithPrimitiveHasNoFinalizer) { static_assert(std::is_trivially_destructible::value, "trivially destructible"); EXPECT_EQ(nullptr, FinalizerTrait::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