cppgc: Add support for mixins
This CL introduces - GarbageCollectedMixin - IsGarbageCollectedMixinType - HeapObjectHeaderFor (utility method to get correct header for mixins) Bug: chromium:1056170 Change-Id: I48c2c02fa57487824130ea3620c975a0785075e4 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2130275 Commit-Queue: Omer Katz <omerkatz@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Anton Bikineev <bikineev@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#66953}
This commit is contained in:
parent
5762c61874
commit
03c38afa41
@ -72,6 +72,10 @@ class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<T> {
|
||||
static T* Call(Heap* heap, Args&&... args) {
|
||||
static_assert(internal::IsGarbageCollectedType<T>::value,
|
||||
"T needs to be a garbage collected object");
|
||||
static_assert(
|
||||
!internal::IsGarbageCollectedMixinType<T>::value ||
|
||||
sizeof(T) <= internal::api_constants::kLargeObjectSizeThreshold,
|
||||
"GarbageCollectedMixin may not be a large object");
|
||||
void* memory = MakeGarbageCollectedTraitBase<T>::Allocate(heap, sizeof(T));
|
||||
T* object = ::new (memory) T(std::forward<Args>(args)...);
|
||||
MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object);
|
||||
|
@ -9,12 +9,26 @@
|
||||
|
||||
#include "include/cppgc/internals.h"
|
||||
#include "include/cppgc/platform.h"
|
||||
#include "include/cppgc/type_traits.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct IsGarbageCollectedType : std::false_type {
|
||||
struct IsGarbageCollectedMixinType : std::false_type {
|
||||
static_assert(sizeof(T), "T must be fully defined");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsGarbageCollectedMixinType<
|
||||
T,
|
||||
void_t<typename std::remove_const_t<T>::IsGarbageCollectedMixinTypeMarker>>
|
||||
: std::true_type {
|
||||
static_assert(sizeof(T), "T must be fully defined");
|
||||
};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct IsGarbageCollectedType : IsGarbageCollectedMixinType<T> {
|
||||
static_assert(sizeof(T), "T must be fully defined");
|
||||
};
|
||||
|
||||
@ -25,13 +39,8 @@ struct IsGarbageCollectedType<
|
||||
static_assert(sizeof(T), "T must be fully defined");
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename>
|
||||
class GarbageCollected {
|
||||
class GarbageCollectedBase {
|
||||
public:
|
||||
using IsGarbageCollectedTypeMarker = void;
|
||||
|
||||
// Must use MakeGarbageCollected.
|
||||
void* operator new(size_t) = delete;
|
||||
void* operator new[](size_t) = delete;
|
||||
@ -44,10 +53,90 @@ class GarbageCollected {
|
||||
}
|
||||
void operator delete[](void*) = delete;
|
||||
|
||||
protected:
|
||||
GarbageCollectedBase() = default;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename>
|
||||
class GarbageCollected : public internal::GarbageCollectedBase {
|
||||
public:
|
||||
using IsGarbageCollectedTypeMarker = void;
|
||||
|
||||
protected:
|
||||
GarbageCollected() = default;
|
||||
};
|
||||
|
||||
class GarbageCollectedMixin : public internal::GarbageCollectedBase {
|
||||
public:
|
||||
using IsGarbageCollectedMixinTypeMarker = void;
|
||||
|
||||
// Sentinel used to mark not-fully-constructed mixins.
|
||||
static constexpr void* kNotFullyConstructedObject = nullptr;
|
||||
|
||||
// TODO(chromium:1056170): Add virtual Trace method.
|
||||
|
||||
protected:
|
||||
// Provide default implementation that indicate that the vtable is not yet
|
||||
// set up properly. This is used to to get GCInfo objects for mixins so that
|
||||
// these objects can be processed later on.
|
||||
virtual const void* GetObjectStart() const {
|
||||
return kNotFullyConstructedObject;
|
||||
}
|
||||
|
||||
GarbageCollectedMixin() = default;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
class __thisIsHereToForceASemicolonAfterThisMacro {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// The USING_GARBAGE_COLLECTED_MIXIN macro defines all methods and markers
|
||||
// needed for handling mixins. HasUsingGarbageCollectedMixinMacro is used
|
||||
// by the clang GC plugin to check for proper usages of the
|
||||
// USING_GARBAGE_COLLECTED_MIXIN macro.
|
||||
#define USING_GARBAGE_COLLECTED_MIXIN() \
|
||||
public: \
|
||||
typedef int HasUsingGarbageCollectedMixinMacro; \
|
||||
const void* GetObjectStart() const override { \
|
||||
static_assert( \
|
||||
internal::IsSubclassOfTemplate< \
|
||||
std::remove_const_t<std::remove_pointer_t<decltype(this)>>, \
|
||||
cppgc::GarbageCollected>::value, \
|
||||
"Only garbage collected objects can have garbage collected mixins"); \
|
||||
return this; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
friend class internal::__thisIsHereToForceASemicolonAfterThisMacro
|
||||
|
||||
// Merge two or more Mixins into one:
|
||||
//
|
||||
// class A : public GarbageCollectedMixin {};
|
||||
// class B : public GarbageCollectedMixin {};
|
||||
// class C : public A, public B {
|
||||
// // C::GetObjectStart is now ambiguous because there are two
|
||||
// // candidates: A::GetObjectStart and B::GetObjectStart. Ditto for
|
||||
// // other functions.
|
||||
//
|
||||
// MERGE_GARBAGE_COLLECTED_MIXINS();
|
||||
// // The macro defines C::GetObjectStart, similar to GarbageCollectedMixin,ß
|
||||
// // so that they are no longer ambiguous.
|
||||
// // USING_GARBAGE_COLLECTED_MIXIN() overrides them later and provides the
|
||||
// // implementations.
|
||||
// };
|
||||
#define MERGE_GARBAGE_COLLECTED_MIXINS() \
|
||||
public: \
|
||||
const void* GetObjectStart() const override { \
|
||||
return kNotFullyConstructedObject; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
friend class internal::__thisIsHereToForceASemicolonAfterThisMacro
|
||||
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_
|
||||
|
@ -33,6 +33,8 @@ static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload =
|
||||
// Mask for in-construction bit.
|
||||
static constexpr size_t kFullyConstructedBitMask = size_t{1};
|
||||
|
||||
static constexpr size_t kLargeObjectSizeThreshold = 65536;
|
||||
|
||||
} // namespace api_constants
|
||||
|
||||
} // namespace internal
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
|
||||
#include "include/cppgc/garbage-collected.h"
|
||||
#include "include/v8config.h"
|
||||
|
||||
namespace cppgc {
|
||||
|
@ -21,6 +21,18 @@ template <typename T, typename WriteBarrierPolicy>
|
||||
struct IsWeak<internal::BasicWeakMember<T, WriteBarrierPolicy>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T, template <typename... V> class U>
|
||||
struct IsSubclassOfTemplate {
|
||||
private:
|
||||
template <typename... W>
|
||||
static std::true_type SubclassCheck(U<W...>*);
|
||||
static std::false_type SubclassCheck(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value =
|
||||
decltype(SubclassCheck(std::declval<T*>()))::value;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
|
@ -11,6 +11,9 @@
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
STATIC_ASSERT(api_constants::kLargeObjectSizeThreshold ==
|
||||
kLargeObjectSizeThreshold);
|
||||
|
||||
// static
|
||||
void* MakeGarbageCollectedTraitInternal::Allocate(cppgc::Heap* heap,
|
||||
size_t size,
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
#include "include/cppgc/garbage-collected.h"
|
||||
|
||||
#include "include/cppgc/allocation.h"
|
||||
#include "src/heap/cppgc/heap.h"
|
||||
#include "test/unittests/heap/cppgc/tests.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace cppgc {
|
||||
@ -13,14 +16,57 @@ namespace {
|
||||
|
||||
class GCed : public GarbageCollected<GCed> {};
|
||||
class NotGCed {};
|
||||
class Mixin : public GarbageCollectedMixin {
|
||||
public:
|
||||
using GarbageCollectedMixin::GetObjectStart;
|
||||
};
|
||||
class GCedWithMixin : public GarbageCollected<GCedWithMixin>, public Mixin {
|
||||
USING_GARBAGE_COLLECTED_MIXIN();
|
||||
};
|
||||
class OtherMixin : public GarbageCollectedMixin {};
|
||||
class MergedMixins : public Mixin, public OtherMixin {
|
||||
MERGE_GARBAGE_COLLECTED_MIXINS();
|
||||
};
|
||||
class GCWithMergedMixins : public GCed, public MergedMixins {
|
||||
USING_GARBAGE_COLLECTED_MIXIN();
|
||||
};
|
||||
|
||||
class GarbageCollectedTestWithHeap : public testing::TestWithHeap {
|
||||
void TearDown() override {
|
||||
internal::Heap::From(GetHeap())->CollectGarbage();
|
||||
TestWithHeap::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(GarbageCollectedTest, GarbageCollectedTrait) {
|
||||
EXPECT_FALSE(IsGarbageCollectedType<int>::value);
|
||||
EXPECT_FALSE(IsGarbageCollectedType<NotGCed>::value);
|
||||
EXPECT_TRUE(IsGarbageCollectedType<GCed>::value);
|
||||
STATIC_ASSERT(!IsGarbageCollectedType<int>::value);
|
||||
STATIC_ASSERT(!IsGarbageCollectedType<NotGCed>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedType<GCed>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedType<Mixin>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedType<GCedWithMixin>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedType<MergedMixins>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedType<GCWithMergedMixins>::value);
|
||||
}
|
||||
|
||||
TEST(GarbageCollectedTest, GarbageCollectedMixinTrait) {
|
||||
STATIC_ASSERT(!IsGarbageCollectedMixinType<int>::value);
|
||||
STATIC_ASSERT(!IsGarbageCollectedMixinType<GCed>::value);
|
||||
STATIC_ASSERT(!IsGarbageCollectedMixinType<NotGCed>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedMixinType<Mixin>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedMixinType<GCedWithMixin>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedMixinType<MergedMixins>::value);
|
||||
STATIC_ASSERT(IsGarbageCollectedMixinType<GCWithMergedMixins>::value);
|
||||
}
|
||||
|
||||
TEST_F(GarbageCollectedTestWithHeap, GetObjectStartReturnsCorrentAddress) {
|
||||
GCed* gced = MakeGarbageCollected<GCed>(GetHeap());
|
||||
GCedWithMixin* gced_with_mixin =
|
||||
MakeGarbageCollected<GCedWithMixin>(GetHeap());
|
||||
EXPECT_EQ(gced_with_mixin,
|
||||
static_cast<Mixin*>(gced_with_mixin)->GetObjectStart());
|
||||
EXPECT_NE(gced, static_cast<Mixin*>(gced_with_mixin)->GetObjectStart());
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "include/cppgc/garbage-collected.h"
|
||||
#include "include/cppgc/member.h"
|
||||
#include "include/cppgc/type_traits.h"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user