86af7c6a76
Replaces Payload* terminiology with Object* terminology. HoH::ObjectSize = just the object, without the header. HoH::AllocatedSize = both the object and the header. Payload terminology is retained only for pages. Bug: chromium:1056170 Change-Id: I568a324ae8728f098be642b024493c375ec873cb Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2892079 Commit-Queue: Omer Katz <omerkatz@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#74538}
253 lines
8.3 KiB
C++
253 lines
8.3 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 "include/cppgc/garbage-collected.h"
|
|
|
|
#include "include/cppgc/allocation.h"
|
|
#include "include/cppgc/type-traits.h"
|
|
#include "src/base/platform/mutex.h"
|
|
#include "src/heap/cppgc/heap-object-header.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 GCed : public GarbageCollected<GCed> {
|
|
public:
|
|
void Trace(Visitor*) const {}
|
|
};
|
|
class NotGCed {};
|
|
class Mixin : public GarbageCollectedMixin {};
|
|
class GCedWithMixin : public GarbageCollected<GCedWithMixin>, public Mixin {};
|
|
class OtherMixin : public GarbageCollectedMixin {};
|
|
class MergedMixins : public Mixin, public OtherMixin {
|
|
public:
|
|
void Trace(cppgc::Visitor* visitor) const override {
|
|
Mixin::Trace(visitor);
|
|
OtherMixin::Trace(visitor);
|
|
}
|
|
};
|
|
class GCWithMergedMixins : public GCed, public MergedMixins {
|
|
public:
|
|
void Trace(cppgc::Visitor* visitor) const override {
|
|
MergedMixins::Trace(visitor);
|
|
}
|
|
};
|
|
|
|
class GarbageCollectedTestWithHeap
|
|
: public testing::TestSupportingAllocationOnly {};
|
|
|
|
} // namespace
|
|
|
|
TEST(GarbageCollectedTest, GarbageCollectedTrait) {
|
|
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>);
|
|
}
|
|
|
|
TEST(GarbageCollectedTest, GarbageCollectedMixinTrait) {
|
|
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>);
|
|
}
|
|
|
|
TEST(GarbageCollectedTest, GarbageCollectedOrMixinTrait) {
|
|
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>);
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ForwardDeclaredType;
|
|
|
|
} // namespace
|
|
|
|
TEST(GarbageCollectedTest, CompleteTypeTrait) {
|
|
STATIC_ASSERT(IsCompleteV<GCed>);
|
|
STATIC_ASSERT(!IsCompleteV<ForwardDeclaredType>);
|
|
}
|
|
|
|
TEST_F(GarbageCollectedTestWithHeap, GetObjectStartReturnsCurrentAddress) {
|
|
GCed* gced = MakeGarbageCollected<GCed>(GetAllocationHandle());
|
|
GCedWithMixin* gced_with_mixin =
|
|
MakeGarbageCollected<GCedWithMixin>(GetAllocationHandle());
|
|
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);
|
|
}
|
|
|
|
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(
|
|
internal::HeapObjectHeader::FromObject(object).IsInConstruction());
|
|
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(
|
|
internal::HeapObjectHeader::FromObject(object).IsInConstruction());
|
|
internal::GCedWithMixinWithPostConstructionCallback::cb_callcount++;
|
|
}
|
|
};
|
|
|
|
namespace internal {
|
|
|
|
TEST_F(GarbageCollectedTestWithHeap, PostConstructionCallback) {
|
|
EXPECT_EQ(0u, GCedWithPostConstructionCallback::cb_callcount);
|
|
MakeGarbageCollected<GCedWithPostConstructionCallback>(GetAllocationHandle());
|
|
EXPECT_EQ(1u, GCedWithPostConstructionCallback::cb_callcount);
|
|
}
|
|
|
|
TEST_F(GarbageCollectedTestWithHeap, PostConstructionCallbackForMixin) {
|
|
EXPECT_EQ(0u, MixinWithPostConstructionCallback::cb_callcount);
|
|
MakeGarbageCollected<GCedWithMixinWithPostConstructionCallback>(
|
|
GetAllocationHandle());
|
|
EXPECT_EQ(1u, MixinWithPostConstructionCallback::cb_callcount);
|
|
}
|
|
|
|
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_(
|
|
HeapObjectHeader::FromObject(this).IsInConstruction()),
|
|
unused_int_(GetDummyValue()) {
|
|
EXPECT_TRUE(in_construction_before_initializer_list_);
|
|
EXPECT_TRUE(HeapObjectHeader::FromObject(this).IsInConstruction());
|
|
}
|
|
|
|
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_(
|
|
HeapObjectHeader::FromObject(payload_start).IsInConstruction()),
|
|
unused_int_(GetDummyValue()) {
|
|
EXPECT_TRUE(in_construction_before_initializer_list_);
|
|
EXPECT_TRUE(HeapObjectHeader::FromObject(payload_start).IsInConstruction());
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace cppgc
|