2020-03-27 10:02:58 +00:00
|
|
|
// 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/garbage-collected.h"
|
|
|
|
|
2020-04-01 15:15:38 +00:00
|
|
|
#include "include/cppgc/allocation.h"
|
2020-05-05 07:12:03 +00:00
|
|
|
#include "include/cppgc/type-traits.h"
|
2021-04-09 11:27:13 +00:00
|
|
|
#include "src/base/platform/mutex.h"
|
2020-06-29 17:17:20 +00:00
|
|
|
#include "src/heap/cppgc/heap-object-header.h"
|
2020-04-01 15:15:38 +00:00
|
|
|
#include "src/heap/cppgc/heap.h"
|
|
|
|
#include "test/unittests/heap/cppgc/tests.h"
|
2020-03-27 10:02:58 +00:00
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace cppgc {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2020-05-13 23:20:23 +00:00
|
|
|
class GCed : public GarbageCollected<GCed> {
|
|
|
|
public:
|
|
|
|
void Trace(Visitor*) const {}
|
|
|
|
};
|
2020-03-27 10:02:58 +00:00
|
|
|
class NotGCed {};
|
2020-04-07 12:35:52 +00:00
|
|
|
class Mixin : public GarbageCollectedMixin {};
|
2020-07-10 10:09:55 +00:00
|
|
|
class GCedWithMixin : public GarbageCollected<GCedWithMixin>, public Mixin {};
|
2020-04-01 15:15:38 +00:00
|
|
|
class OtherMixin : public GarbageCollectedMixin {};
|
|
|
|
class MergedMixins : public Mixin, public OtherMixin {
|
2020-04-07 12:35:52 +00:00
|
|
|
public:
|
2020-04-20 11:50:46 +00:00
|
|
|
void Trace(cppgc::Visitor* visitor) const override {
|
2020-04-07 12:35:52 +00:00
|
|
|
Mixin::Trace(visitor);
|
|
|
|
OtherMixin::Trace(visitor);
|
|
|
|
}
|
2020-04-01 15:15:38 +00:00
|
|
|
};
|
|
|
|
class GCWithMergedMixins : public GCed, public MergedMixins {
|
2020-04-07 12:35:52 +00:00
|
|
|
public:
|
2020-04-20 11:50:46 +00:00
|
|
|
void Trace(cppgc::Visitor* visitor) const override {
|
|
|
|
MergedMixins::Trace(visitor);
|
|
|
|
}
|
2020-04-01 15:15:38 +00:00
|
|
|
};
|
|
|
|
|
2020-04-07 21:35:29 +00:00
|
|
|
class GarbageCollectedTestWithHeap
|
|
|
|
: public testing::TestSupportingAllocationOnly {};
|
2020-03-27 10:02:58 +00:00
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST(GarbageCollectedTest, GarbageCollectedTrait) {
|
2021-01-22 19:23:19 +00:00
|
|
|
STATIC_ASSERT(!IsGarbageCollectedTypeV<int>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedTypeV<NotGCed>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedTypeV<GCed>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedTypeV<Mixin>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedTypeV<GCedWithMixin>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedTypeV<MergedMixins>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedTypeV<GCWithMergedMixins>);
|
2020-04-01 15:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GarbageCollectedTest, GarbageCollectedMixinTrait) {
|
2021-01-22 19:23:19 +00:00
|
|
|
STATIC_ASSERT(!IsGarbageCollectedMixinTypeV<int>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedMixinTypeV<GCed>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedMixinTypeV<NotGCed>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedMixinTypeV<Mixin>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedMixinTypeV<GCedWithMixin>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedMixinTypeV<MergedMixins>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedMixinTypeV<GCWithMergedMixins>);
|
2021-01-22 15:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GarbageCollectedTest, GarbageCollectedOrMixinTrait) {
|
2021-01-22 19:23:19 +00:00
|
|
|
STATIC_ASSERT(!IsGarbageCollectedOrMixinTypeV<int>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedOrMixinTypeV<GCed>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedOrMixinTypeV<NotGCed>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedOrMixinTypeV<Mixin>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedOrMixinTypeV<GCedWithMixin>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedOrMixinTypeV<MergedMixins>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedOrMixinTypeV<GCWithMergedMixins>);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GarbageCollectedTest, GarbageCollectedWithMixinTrait) {
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedWithMixinTypeV<int>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedWithMixinTypeV<GCed>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedWithMixinTypeV<NotGCed>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedWithMixinTypeV<Mixin>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedWithMixinTypeV<GCedWithMixin>);
|
|
|
|
STATIC_ASSERT(!IsGarbageCollectedWithMixinTypeV<MergedMixins>);
|
|
|
|
STATIC_ASSERT(IsGarbageCollectedWithMixinTypeV<GCWithMergedMixins>);
|
2020-03-27 10:02:58 +00:00
|
|
|
}
|
|
|
|
|
2021-05-10 11:15:34 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class ForwardDeclaredType;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST(GarbageCollectedTest, CompleteTypeTrait) {
|
|
|
|
STATIC_ASSERT(IsCompleteV<GCed>);
|
|
|
|
STATIC_ASSERT(!IsCompleteV<ForwardDeclaredType>);
|
|
|
|
}
|
|
|
|
|
2020-05-07 20:36:03 +00:00
|
|
|
TEST_F(GarbageCollectedTestWithHeap, GetObjectStartReturnsCurrentAddress) {
|
2020-06-10 22:28:41 +00:00
|
|
|
GCed* gced = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
2020-04-01 15:15:38 +00:00
|
|
|
GCedWithMixin* gced_with_mixin =
|
2020-06-10 22:28:41 +00:00
|
|
|
MakeGarbageCollected<GCedWithMixin>(GetAllocationHandle());
|
2020-07-10 10:09:55 +00:00
|
|
|
const void* base_object_payload = TraceTrait<Mixin>::GetTraceDescriptor(
|
|
|
|
static_cast<Mixin*>(gced_with_mixin))
|
|
|
|
.base_object_payload;
|
|
|
|
EXPECT_EQ(gced_with_mixin, base_object_payload);
|
|
|
|
EXPECT_NE(gced, base_object_payload);
|
2020-04-01 15:15:38 +00:00
|
|
|
}
|
2020-04-06 13:29:41 +00:00
|
|
|
|
2020-05-05 07:12:03 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class GCedWithPostConstructionCallback final : public GCed {
|
|
|
|
public:
|
|
|
|
static size_t cb_callcount;
|
|
|
|
GCedWithPostConstructionCallback() { cb_callcount = 0; }
|
|
|
|
};
|
|
|
|
size_t GCedWithPostConstructionCallback::cb_callcount;
|
|
|
|
|
|
|
|
class MixinWithPostConstructionCallback {
|
|
|
|
public:
|
|
|
|
static size_t cb_callcount;
|
|
|
|
MixinWithPostConstructionCallback() { cb_callcount = 0; }
|
|
|
|
using MarkerForMixinWithPostConstructionCallback = int;
|
|
|
|
};
|
|
|
|
size_t MixinWithPostConstructionCallback::cb_callcount;
|
|
|
|
|
|
|
|
class GCedWithMixinWithPostConstructionCallback final
|
|
|
|
: public GCed,
|
|
|
|
public MixinWithPostConstructionCallback {};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
template <>
|
|
|
|
struct PostConstructionCallbackTrait<
|
|
|
|
internal::GCedWithPostConstructionCallback> {
|
|
|
|
static void Call(internal::GCedWithPostConstructionCallback* object) {
|
|
|
|
EXPECT_FALSE(
|
2021-05-12 18:08:11 +00:00
|
|
|
internal::HeapObjectHeader::FromObject(object).IsInConstruction());
|
2020-05-05 07:12:03 +00:00
|
|
|
internal::GCedWithPostConstructionCallback::cb_callcount++;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
struct PostConstructionCallbackTrait<
|
|
|
|
T,
|
|
|
|
internal::void_t<typename T::MarkerForMixinWithPostConstructionCallback>> {
|
|
|
|
// The parameter could just be T*.
|
|
|
|
static void Call(
|
|
|
|
internal::GCedWithMixinWithPostConstructionCallback* object) {
|
|
|
|
EXPECT_FALSE(
|
2021-05-12 18:08:11 +00:00
|
|
|
internal::HeapObjectHeader::FromObject(object).IsInConstruction());
|
2020-05-05 07:12:03 +00:00
|
|
|
internal::GCedWithMixinWithPostConstructionCallback::cb_callcount++;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
TEST_F(GarbageCollectedTestWithHeap, PostConstructionCallback) {
|
|
|
|
EXPECT_EQ(0u, GCedWithPostConstructionCallback::cb_callcount);
|
2020-06-10 22:28:41 +00:00
|
|
|
MakeGarbageCollected<GCedWithPostConstructionCallback>(GetAllocationHandle());
|
2020-05-05 07:12:03 +00:00
|
|
|
EXPECT_EQ(1u, GCedWithPostConstructionCallback::cb_callcount);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(GarbageCollectedTestWithHeap, PostConstructionCallbackForMixin) {
|
|
|
|
EXPECT_EQ(0u, MixinWithPostConstructionCallback::cb_callcount);
|
2020-06-10 22:28:41 +00:00
|
|
|
MakeGarbageCollected<GCedWithMixinWithPostConstructionCallback>(
|
|
|
|
GetAllocationHandle());
|
2020-05-05 07:12:03 +00:00
|
|
|
EXPECT_EQ(1u, MixinWithPostConstructionCallback::cb_callcount);
|
|
|
|
}
|
|
|
|
|
2021-04-09 11:27:13 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
int GetDummyValue() {
|
|
|
|
static v8::base::Mutex mutex;
|
|
|
|
static int ret = 43;
|
|
|
|
// Global lock access to avoid reordering.
|
|
|
|
v8::base::MutexGuard guard(&mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
class CheckObjectInConstructionBeforeInitializerList final
|
|
|
|
: public GarbageCollected<CheckObjectInConstructionBeforeInitializerList> {
|
|
|
|
public:
|
|
|
|
CheckObjectInConstructionBeforeInitializerList()
|
|
|
|
: in_construction_before_initializer_list_(
|
2021-05-12 18:08:11 +00:00
|
|
|
HeapObjectHeader::FromObject(this).IsInConstruction()),
|
2021-04-09 11:27:13 +00:00
|
|
|
unused_int_(GetDummyValue()) {
|
|
|
|
EXPECT_TRUE(in_construction_before_initializer_list_);
|
2021-05-12 18:08:11 +00:00
|
|
|
EXPECT_TRUE(HeapObjectHeader::FromObject(this).IsInConstruction());
|
2021-04-09 11:27:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Trace(Visitor*) const {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool in_construction_before_initializer_list_;
|
|
|
|
int unused_int_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CheckMixinInConstructionBeforeInitializerList
|
|
|
|
: public GarbageCollectedMixin {
|
|
|
|
public:
|
|
|
|
explicit CheckMixinInConstructionBeforeInitializerList(void* payload_start)
|
|
|
|
: in_construction_before_initializer_list_(
|
2021-05-12 18:08:11 +00:00
|
|
|
HeapObjectHeader::FromObject(payload_start).IsInConstruction()),
|
2021-04-09 11:27:13 +00:00
|
|
|
unused_int_(GetDummyValue()) {
|
|
|
|
EXPECT_TRUE(in_construction_before_initializer_list_);
|
2021-05-12 18:08:11 +00:00
|
|
|
EXPECT_TRUE(HeapObjectHeader::FromObject(payload_start).IsInConstruction());
|
2021-04-09 11:27:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Trace(Visitor*) const override {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool in_construction_before_initializer_list_;
|
|
|
|
int unused_int_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class UnmanagedMixinForcingVTable {
|
|
|
|
protected:
|
|
|
|
virtual void ForceVTable() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CheckGCedWithMixinInConstructionBeforeInitializerList
|
|
|
|
: public GarbageCollected<
|
|
|
|
CheckGCedWithMixinInConstructionBeforeInitializerList>,
|
|
|
|
public UnmanagedMixinForcingVTable,
|
|
|
|
public CheckMixinInConstructionBeforeInitializerList {
|
|
|
|
public:
|
|
|
|
CheckGCedWithMixinInConstructionBeforeInitializerList()
|
|
|
|
: CheckMixinInConstructionBeforeInitializerList(this) {
|
|
|
|
// Ensure that compiler indeed generated an inner object.
|
|
|
|
CHECK_NE(
|
|
|
|
this,
|
|
|
|
static_cast<void*>(
|
|
|
|
static_cast<CheckMixinInConstructionBeforeInitializerList*>(this)));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST_F(GarbageCollectedTestWithHeap, GarbageCollectedInConstructionDuringCtor) {
|
|
|
|
MakeGarbageCollected<CheckObjectInConstructionBeforeInitializerList>(
|
|
|
|
GetAllocationHandle());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(GarbageCollectedTestWithHeap,
|
|
|
|
GarbageCollectedMixinInConstructionDuringCtor) {
|
|
|
|
MakeGarbageCollected<CheckGCedWithMixinInConstructionBeforeInitializerList>(
|
|
|
|
GetAllocationHandle());
|
|
|
|
}
|
|
|
|
|
2020-03-27 10:02:58 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace cppgc
|