Commit 03c38afa authored by Omer Katz's avatar Omer Katz Committed by Commit Bot

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: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarAnton Bikineev <bikineev@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66953}
parent 5762c618
...@@ -72,6 +72,10 @@ class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<T> { ...@@ -72,6 +72,10 @@ class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<T> {
static T* Call(Heap* heap, Args&&... args) { static T* Call(Heap* heap, Args&&... args) {
static_assert(internal::IsGarbageCollectedType<T>::value, static_assert(internal::IsGarbageCollectedType<T>::value,
"T needs to be a garbage collected object"); "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)); void* memory = MakeGarbageCollectedTraitBase<T>::Allocate(heap, sizeof(T));
T* object = ::new (memory) T(std::forward<Args>(args)...); T* object = ::new (memory) T(std::forward<Args>(args)...);
MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object); MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object);
......
...@@ -9,12 +9,26 @@ ...@@ -9,12 +9,26 @@
#include "include/cppgc/internals.h" #include "include/cppgc/internals.h"
#include "include/cppgc/platform.h" #include "include/cppgc/platform.h"
#include "include/cppgc/type_traits.h"
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
template <typename T, typename = void> 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"); static_assert(sizeof(T), "T must be fully defined");
}; };
...@@ -25,13 +39,8 @@ struct IsGarbageCollectedType< ...@@ -25,13 +39,8 @@ struct IsGarbageCollectedType<
static_assert(sizeof(T), "T must be fully defined"); static_assert(sizeof(T), "T must be fully defined");
}; };
} // namespace internal class GarbageCollectedBase {
template <typename>
class GarbageCollected {
public: public:
using IsGarbageCollectedTypeMarker = void;
// Must use MakeGarbageCollected. // Must use MakeGarbageCollected.
void* operator new(size_t) = delete; void* operator new(size_t) = delete;
void* operator new[](size_t) = delete; void* operator new[](size_t) = delete;
...@@ -44,10 +53,90 @@ class GarbageCollected { ...@@ -44,10 +53,90 @@ class GarbageCollected {
} }
void operator delete[](void*) = delete; void operator delete[](void*) = delete;
protected:
GarbageCollectedBase() = default;
};
} // namespace internal
template <typename>
class GarbageCollected : public internal::GarbageCollectedBase {
public:
using IsGarbageCollectedTypeMarker = void;
protected: protected:
GarbageCollected() = default; 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 } // namespace cppgc
#endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ #endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_
...@@ -33,6 +33,8 @@ static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload = ...@@ -33,6 +33,8 @@ static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload =
// Mask for in-construction bit. // Mask for in-construction bit.
static constexpr size_t kFullyConstructedBitMask = size_t{1}; static constexpr size_t kFullyConstructedBitMask = size_t{1};
static constexpr size_t kLargeObjectSizeThreshold = 65536;
} // namespace api_constants } // namespace api_constants
} // namespace internal } // namespace internal
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include <atomic> #include <atomic>
#include <cstddef> #include <cstddef>
#include "include/cppgc/garbage-collected.h"
#include "include/v8config.h" #include "include/v8config.h"
namespace cppgc { namespace cppgc {
......
...@@ -21,6 +21,18 @@ template <typename T, typename WriteBarrierPolicy> ...@@ -21,6 +21,18 @@ template <typename T, typename WriteBarrierPolicy>
struct IsWeak<internal::BasicWeakMember<T, WriteBarrierPolicy>> struct IsWeak<internal::BasicWeakMember<T, WriteBarrierPolicy>>
: std::true_type {}; : 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 } // namespace internal
template <typename T> template <typename T>
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
STATIC_ASSERT(api_constants::kLargeObjectSizeThreshold ==
kLargeObjectSizeThreshold);
// static // static
void* MakeGarbageCollectedTraitInternal::Allocate(cppgc::Heap* heap, void* MakeGarbageCollectedTraitInternal::Allocate(cppgc::Heap* heap,
size_t size, size_t size,
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#include "include/cppgc/garbage-collected.h" #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" #include "testing/gtest/include/gtest/gtest.h"
namespace cppgc { namespace cppgc {
...@@ -13,14 +16,57 @@ namespace { ...@@ -13,14 +16,57 @@ namespace {
class GCed : public GarbageCollected<GCed> {}; class GCed : public GarbageCollected<GCed> {};
class NotGCed {}; 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 } // namespace
TEST(GarbageCollectedTest, GarbageCollectedTrait) { TEST(GarbageCollectedTest, GarbageCollectedTrait) {
EXPECT_FALSE(IsGarbageCollectedType<int>::value); STATIC_ASSERT(!IsGarbageCollectedType<int>::value);
EXPECT_FALSE(IsGarbageCollectedType<NotGCed>::value); STATIC_ASSERT(!IsGarbageCollectedType<NotGCed>::value);
EXPECT_TRUE(IsGarbageCollectedType<GCed>::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 internal
} // namespace cppgc } // namespace cppgc
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <array> #include <array>
#include "include/cppgc/garbage-collected.h"
#include "include/cppgc/member.h" #include "include/cppgc/member.h"
#include "include/cppgc/type_traits.h" #include "include/cppgc/type_traits.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment