Add test support for types and objects creation

* test_support::TypeInfoHolder to create TypeInfo given payload description
* test_support::Object<Payload> to create objects with Payload and to get them from ObjHeader* after checking that their type_info are layout compatible.
* test_support::*Array<Count> to create various arrays with given length and similarly get them from ArrayHeader*.
This commit is contained in:
Alexander Shabalin
2021-03-15 11:25:04 +00:00
committed by Space
parent aff49c76a9
commit f51c85a63f
17 changed files with 1041 additions and 241 deletions
@@ -3327,6 +3327,10 @@ void RestoreMemory(MemoryState* memoryState) {
::memoryState = memoryState;
}
void ClearMemoryForTests(MemoryState*) {
// Nothing to do, DeinitMemory will do the job.
}
OBJ_GETTER(AllocInstanceStrict, const TypeInfo* type_info) {
RETURN_RESULT_OF(allocInstance<true>, type_info);
}
@@ -10,6 +10,7 @@
#include "FinalizerHooksTestSupport.hpp"
#include "Memory.h"
#include "ObjectTestSupport.hpp"
using namespace kotlin;
@@ -17,6 +18,11 @@ using ::testing::_;
namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
class FinalizerHooksTest : public testing::Test {
public:
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
@@ -28,55 +34,51 @@ private:
} // namespace
TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithoutExtra) {
TypeInfo type;
type.typeInfo_ = &type;
type.flags_ |= TF_HAS_FINALIZER;
ObjHeader obj = {&type};
ASSERT_FALSE(obj.has_meta_object());
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>().addFlag(TF_HAS_FINALIZER)};
test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader* obj = object.header();
ASSERT_FALSE(obj->has_meta_object());
EXPECT_TRUE(HasFinalizers(&obj));
EXPECT_CALL(finalizerHook(), Call(&obj));
RunFinalizers(&obj);
EXPECT_FALSE(obj.has_meta_object());
EXPECT_TRUE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(obj));
RunFinalizers(obj);
EXPECT_FALSE(obj->has_meta_object());
}
TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithExtra) {
TypeInfo type;
type.typeInfo_ = &type;
type.flags_ |= TF_HAS_FINALIZER;
ObjHeader obj = {&type};
ObjHeader::createMetaObject(&obj);
ASSERT_TRUE(obj.has_meta_object());
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>().addFlag(TF_HAS_FINALIZER)};
test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader* obj = object.header();
ObjHeader::createMetaObject(obj);
ASSERT_TRUE(obj->has_meta_object());
EXPECT_TRUE(HasFinalizers(&obj));
EXPECT_CALL(finalizerHook(), Call(&obj));
RunFinalizers(&obj);
EXPECT_FALSE(obj.has_meta_object());
EXPECT_TRUE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(obj));
RunFinalizers(obj);
EXPECT_FALSE(obj->has_meta_object());
}
TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithoutExtra) {
TypeInfo type;
type.typeInfo_ = &type;
type.flags_ &= ~TF_HAS_FINALIZER;
ObjHeader obj = {&type};
ASSERT_FALSE(obj.has_meta_object());
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader* obj = object.header();
ASSERT_FALSE(obj->has_meta_object());
EXPECT_FALSE(HasFinalizers(&obj));
EXPECT_FALSE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(_)).Times(0);
RunFinalizers(&obj);
EXPECT_FALSE(obj.has_meta_object());
RunFinalizers(obj);
EXPECT_FALSE(obj->has_meta_object());
}
TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithExtra) {
TypeInfo type;
type.typeInfo_ = &type;
type.flags_ &= ~TF_HAS_FINALIZER;
ObjHeader obj = {&type};
ObjHeader::createMetaObject(&obj);
ASSERT_TRUE(obj.has_meta_object());
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader* obj = object.header();
ObjHeader::createMetaObject(obj);
ASSERT_TRUE(obj->has_meta_object());
EXPECT_TRUE(HasFinalizers(&obj));
EXPECT_TRUE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(_)).Times(0);
RunFinalizers(&obj);
EXPECT_FALSE(obj.has_meta_object());
RunFinalizers(obj);
EXPECT_FALSE(obj->has_meta_object());
}
@@ -10,6 +10,7 @@
#include "FreezeHooksTestSupport.hpp"
#include "Memory.h"
#include "ObjectTestSupport.hpp"
using namespace kotlin;
@@ -17,6 +18,11 @@ using ::testing::_;
namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
class FreezeHooksTest : public testing::Test {
public:
testing::MockFunction<void(ObjHeader*)>& freezeHook() { return freezeHooks_.freezeHook(); }
@@ -28,19 +34,17 @@ private:
} // namespace
TEST_F(FreezeHooksTest, TypeWithFreezeHook) {
TypeInfo type;
type.typeInfo_ = &type;
type.flags_ |= TF_HAS_FREEZE_HOOK;
ObjHeader obj = {&type};
EXPECT_CALL(freezeHook(), Call(&obj));
RunFreezeHooks(&obj);
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>().addFlag(TF_HAS_FREEZE_HOOK)};
test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader* obj = object.header();
EXPECT_CALL(freezeHook(), Call(obj));
RunFreezeHooks(obj);
}
TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) {
TypeInfo type;
type.typeInfo_ = &type;
type.flags_ &= ~TF_HAS_FREEZE_HOOK;
ObjHeader obj = {&type};
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader* obj = object.header();
EXPECT_CALL(freezeHook(), Call(_)).Times(0);
RunFreezeHooks(&obj);
RunFreezeHooks(obj);
}
@@ -138,6 +138,7 @@ struct MemoryState;
MemoryState* InitMemory(bool firstRuntime);
void DeinitMemory(MemoryState*, bool destroyRuntime);
void RestoreMemory(MemoryState*);
void ClearMemoryForTests(MemoryState*);
//
// Object allocation.
@@ -0,0 +1,276 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include <array>
#include <type_traits>
#include "KAssert.h"
#include "Memory.h"
#include "TypeInfo.h"
#include "Types.h"
#include "Utils.hpp"
namespace kotlin {
namespace test_support {
// TODO: Some concepts from here can be used in production code.
class TypeInfoHolder : private Pinned {
private:
class Builder {
protected:
friend class TypeInfoHolder;
virtual ~Builder() = default;
int32_t instanceSize_ = 0;
KStdVector<int32_t> objOffsets_;
int32_t flags_ = 0;
};
public:
template <typename Payload>
class ObjectBuilder : public Builder {
public:
ObjectBuilder() noexcept;
ObjectBuilder&& addFlag(Konan_TypeFlags flag) noexcept {
flags_ |= flag;
return std::move(*this);
}
ObjectBuilder&& removeFlag(Konan_TypeFlags flag) noexcept {
flags_ &= ~flag;
return std::move(*this);
}
};
template <typename Payload>
class ArrayBuilder : public Builder {
public:
ArrayBuilder() noexcept { instanceSize_ = -static_cast<int32_t>(sizeof(Payload)); }
ArrayBuilder&& addFlag(Konan_TypeFlags flag) noexcept {
flags_ |= flag;
return std::move(*this);
}
ArrayBuilder&& removeFlag(Konan_TypeFlags flag) noexcept {
flags_ &= ~flag;
return std::move(*this);
}
};
explicit TypeInfoHolder(Builder&& builder) noexcept {
typeInfo_.typeInfo_ = &typeInfo_;
typeInfo_.instanceSize_ = builder.instanceSize_;
objOffsets_ = std::move(builder.objOffsets_);
typeInfo_.objOffsets_ = objOffsets_.data();
if (&typeInfo_ == theArrayTypeInfo) {
// Following RTTIGenerator.kt
typeInfo_.objOffsetsCount_ = 1;
} else {
typeInfo_.objOffsetsCount_ = objOffsets_.size();
}
typeInfo_.flags_ = builder.flags_;
}
TypeInfo* typeInfo() noexcept { return &typeInfo_; }
private:
TypeInfo typeInfo_{};
KStdVector<int32_t> objOffsets_;
};
template <typename Payload>
class Object : private Pinned {
public:
class FieldIterator {
public:
FieldIterator(Object& owner, size_t index) noexcept : owner_(owner), index_(index) {}
ObjHeader*& operator*() noexcept {
auto* header = &owner_.header_;
return *reinterpret_cast<ObjHeader**>(reinterpret_cast<uintptr_t>(header) + header->type_info()->objOffsets_[index_]);
}
FieldIterator& operator++() noexcept {
++index_;
return *this;
}
bool operator==(const FieldIterator& rhs) const noexcept { return &owner_ == &rhs.owner_ && index_ == rhs.index_; }
bool operator!=(const FieldIterator& rhs) const noexcept { return !(*this == rhs); }
private:
Object& owner_;
size_t index_;
};
class FieldIterable {
public:
explicit FieldIterable(Object& owner) noexcept : owner_(owner) {}
size_t size() const noexcept { return owner_.header_.type_info()->objOffsetsCount_; }
ObjHeader*& operator[](size_t index) noexcept { return *FieldIterator(owner_, index); }
FieldIterator begin() noexcept { return FieldIterator(owner_, 0); }
FieldIterator end() noexcept { return FieldIterator(owner_, size()); }
private:
Object& owner_;
};
static Object<Payload>& FromObjHeader(ObjHeader* obj) noexcept {
static_assert(std::is_trivially_destructible_v<Object>, "Object destructor is not guaranteed to be called.");
RuntimeAssert(
TypeInfoHolder{TypeInfoHolder::ObjectBuilder<Payload>()}.typeInfo()->IsLayoutCompatible(obj->type_info()),
"getting object from incompatible ObjHeader");
auto& object = *reinterpret_cast<Object<Payload>*>(obj);
RuntimeAssert(object.header() == obj, "Object layout is broken");
return object;
}
explicit Object(const TypeInfo* typeInfo) noexcept {
static_assert(std::is_trivially_destructible_v<Object>, "Object destructor is not guaranteed to be called.");
RuntimeAssert(
TypeInfoHolder{TypeInfoHolder::ObjectBuilder<Payload>()}.typeInfo()->IsLayoutCompatible(typeInfo),
"constructing object from incompatible type info");
header_.typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
}
ObjHeader* header() noexcept { return &header_; }
Payload& operator*() noexcept { return payload_; }
Payload* operator->() noexcept { return &payload_; }
FieldIterable fields() noexcept { return FieldIterable(*this); }
private:
ObjHeader header_;
Payload payload_{};
};
template <typename Payload>
TypeInfoHolder::ObjectBuilder<Payload>::ObjectBuilder() noexcept {
instanceSize_ = sizeof(Object<Payload>);
char c;
Object<Payload>& object = *reinterpret_cast<Object<Payload>*>(&c);
auto& payload = *object;
using Field = ObjHeader* Payload::*;
for (Field field : Payload::kFields) {
auto& actualField = payload.*field;
objOffsets_.push_back(reinterpret_cast<uintptr_t>(&actualField) - reinterpret_cast<uintptr_t>(object.header()));
}
}
namespace internal {
// Array types are predetermined, use one of the subclasses below.
template <typename Payload, size_t ElementCount>
class Array : private Pinned {
public:
static Array<Payload, ElementCount>& FromArrayHeader(ArrayHeader* arr) noexcept {
static_assert(std::is_trivially_destructible_v<Array>, "Array destructor is not guaranteed to be called.");
RuntimeAssert(
TypeInfoHolder{TypeInfoHolder::ArrayBuilder<Payload>()}.typeInfo()->IsLayoutCompatible(arr->type_info()),
"getting array from incompatible ArrayHeader");
RuntimeAssert(arr->count_ == ElementCount, "getting array from ArrayHeader with different element count");
auto& array = *reinterpret_cast<Array<Payload, ElementCount>*>(arr);
RuntimeAssert(array.arrayHeader() == arr, "Array layout is broken");
return array;
}
explicit Array(const TypeInfo* typeInfo) noexcept {
static_assert(std::is_trivially_destructible_v<Array>, "Array destructor is not guaranteed to be called.");
RuntimeAssert(
TypeInfoHolder{TypeInfoHolder::ArrayBuilder<Payload>()}.typeInfo()->IsLayoutCompatible(typeInfo),
"constructing array from incompatible type info");
header_.typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
header_.count_ = ElementCount;
}
ObjHeader* header() noexcept { return header_.obj(); }
ArrayHeader* arrayHeader() noexcept { return &header_; }
std::array<Payload, ElementCount>& elements() noexcept { return elements_; }
private:
ArrayHeader header_;
std::array<Payload, ElementCount> elements_{};
};
} // namespace internal
template <size_t ElementCount>
class ObjectArray : public internal::Array<ObjHeader*, ElementCount> {
public:
ObjectArray() noexcept : internal::Array<ObjHeader*, ElementCount>(theArrayTypeInfo) {}
};
template <size_t ElementCount>
class BooleanArray : public internal::Array<KBoolean, ElementCount> {
public:
BooleanArray() noexcept : internal::Array<KBoolean, ElementCount>(theBooleanArrayTypeInfo) {}
};
template <size_t ElementCount>
class ByteArray : public internal::Array<KByte, ElementCount> {
public:
ByteArray() noexcept : internal::Array<KByte, ElementCount>(theByteArrayTypeInfo) {}
};
template <size_t ElementCount>
class CharArray : public internal::Array<KChar, ElementCount> {
public:
CharArray() noexcept : internal::Array<KChar, ElementCount>(theCharArrayTypeInfo) {}
};
template <size_t ElementCount>
class DoubleArray : public internal::Array<KDouble, ElementCount> {
public:
DoubleArray() noexcept : internal::Array<KDouble, ElementCount>(theDoubleArrayTypeInfo) {}
};
template <size_t ElementCount>
class FloatArray : public internal::Array<KFloat, ElementCount> {
public:
FloatArray() noexcept : internal::Array<KFloat, ElementCount>(theFloatArrayTypeInfo) {}
};
template <size_t ElementCount>
class IntArray : public internal::Array<KInt, ElementCount> {
public:
IntArray() noexcept : internal::Array<KInt, ElementCount>(theIntArrayTypeInfo) {}
};
template <size_t ElementCount>
class LongArray : public internal::Array<KLong, ElementCount> {
public:
LongArray() noexcept : internal::Array<KLong, ElementCount>(theLongArrayTypeInfo) {}
};
template <size_t ElementCount>
class NativePtrArray : public internal::Array<KNativePtr, ElementCount> {
public:
NativePtrArray() noexcept : internal::Array<KNativePtr, ElementCount>(theNativePtrArrayTypeInfo) {}
};
template <size_t ElementCount>
class ShortArray : public internal::Array<KShort, ElementCount> {
public:
ShortArray() noexcept : internal::Array<KShort, ElementCount>(theShortArrayTypeInfo) {}
};
template <size_t ElementCount>
class String : public internal::Array<KChar, ElementCount> {
public:
String() noexcept : internal::Array<KChar, ElementCount>(theStringTypeInfo) {}
};
} // namespace test_support
} // namespace kotlin
@@ -0,0 +1,385 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "ObjectTestSupport.hpp"
#include <thread>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Natives.h"
using namespace kotlin;
namespace {
struct RegularPayload {
ObjHeader* field1;
ObjHeader* field2;
ObjHeader* field3;
static constexpr std::array kFields{
&RegularPayload::field1,
&RegularPayload::field2,
&RegularPayload::field3,
};
};
struct IrregularPayload {
int skipBefore;
ObjHeader* field1;
int skip;
ObjHeader* field2;
std::array<int, 10> skipALot;
ObjHeader* field3;
static constexpr std::array kFields{
&IrregularPayload::field1,
&IrregularPayload::field2,
&IrregularPayload::field3,
};
};
struct RegularObjectTestCase {
using Payload = RegularPayload;
static constexpr const char* name = "RegularPayload";
};
struct IrregularObjectTestCase {
using Payload = IrregularPayload;
static constexpr const char* name = "IrregularPayload";
};
class ObjectTestCaseNames {
public:
template <typename T>
static std::string GetName(int i) {
return T::name;
}
};
template <typename TestCase>
class ObjectTestSupportObjectTest : public testing::Test {};
using ObjectTestCases = testing::Types<RegularObjectTestCase, IrregularObjectTestCase>;
TYPED_TEST_SUITE(ObjectTestSupportObjectTest, ObjectTestCases, ObjectTestCaseNames);
// TODO: Replace with common implementation.
template <typename F>
void RunInNewThread(F f) {
std::thread([&f]() {
auto* memory = InitMemory(false);
f();
ClearMemoryForTests(memory);
DeinitMemory(memory, false);
}).join();
}
template <typename Payload>
KStdVector<ObjHeader**> Collect(test_support::Object<Payload>& object) {
KStdVector<ObjHeader**> result;
for (auto& field : object.fields()) {
result.push_back(&field);
}
return result;
}
} // namespace
TYPED_TEST(ObjectTestSupportObjectTest, Local) {
using Payload = typename TypeParam::Payload;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
test_support::Object<Payload> object(type.typeInfo());
EXPECT_THAT(object.header()->type_info(), type.typeInfo());
EXPECT_THAT(object.header()->type_info()->objOffsetsCount_, 3);
EXPECT_THAT(
reinterpret_cast<uintptr_t>(&object->field1),
reinterpret_cast<uintptr_t>(object.header()) + object.header()->type_info()->objOffsets_[0]);
EXPECT_THAT(
reinterpret_cast<uintptr_t>(&object->field2),
reinterpret_cast<uintptr_t>(object.header()) + object.header()->type_info()->objOffsets_[1]);
EXPECT_THAT(
reinterpret_cast<uintptr_t>(&object->field3),
reinterpret_cast<uintptr_t>(object.header()) + object.header()->type_info()->objOffsets_[2]);
EXPECT_THAT(object.fields().size(), 3);
EXPECT_THAT(&object.fields()[0], &object->field1);
EXPECT_THAT(&object.fields()[1], &object->field2);
EXPECT_THAT(&object.fields()[2], &object->field3);
EXPECT_THAT(Collect(object), testing::ElementsAre(&object->field1, &object->field2, &object->field3));
EXPECT_THAT(object.fields()[0], nullptr);
EXPECT_THAT(object.fields()[1], nullptr);
EXPECT_THAT(object.fields()[2], nullptr);
auto& recoveredObject = test_support::Object<Payload>::FromObjHeader(object.header());
EXPECT_THAT(&recoveredObject, &object);
}
TYPED_TEST(ObjectTestSupportObjectTest, Heap) {
using Payload = typename TypeParam::Payload;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
RunInNewThread([&type]() {
ObjHolder resultHolder;
ObjHeader* result = AllocInstance(type.typeInfo(), resultHolder.slot());
ASSERT_THAT(result, testing::Ne(nullptr));
auto& object = test_support::Object<Payload>::FromObjHeader(result);
EXPECT_THAT(object.header(), result);
EXPECT_THAT(object.header()->type_info(), type.typeInfo());
EXPECT_THAT(object.header()->type_info()->objOffsetsCount_, 3);
EXPECT_THAT(
reinterpret_cast<uintptr_t>(&object->field1),
reinterpret_cast<uintptr_t>(object.header()) + object.header()->type_info()->objOffsets_[0]);
EXPECT_THAT(
reinterpret_cast<uintptr_t>(&object->field2),
reinterpret_cast<uintptr_t>(object.header()) + object.header()->type_info()->objOffsets_[1]);
EXPECT_THAT(
reinterpret_cast<uintptr_t>(&object->field3),
reinterpret_cast<uintptr_t>(object.header()) + object.header()->type_info()->objOffsets_[2]);
EXPECT_THAT(object.fields().size(), 3);
EXPECT_THAT(&object.fields()[0], &object->field1);
EXPECT_THAT(&object.fields()[1], &object->field2);
EXPECT_THAT(&object.fields()[2], &object->field3);
EXPECT_THAT(Collect(object), testing::ElementsAre(&object->field1, &object->field2, &object->field3));
EXPECT_THAT(object.fields()[0], nullptr);
EXPECT_THAT(object.fields()[1], nullptr);
EXPECT_THAT(object.fields()[2], nullptr);
});
}
namespace {
template <typename Payload>
struct PayloadTraits;
template <>
struct PayloadTraits<ObjHeader*> {
template <size_t Size>
using Array = test_support::ObjectArray<Size>;
static const TypeInfo* GetTypeInfo() { return theArrayTypeInfo; }
static constexpr const char* name = "Array";
};
template <>
struct PayloadTraits<KBoolean> {
template <size_t Size>
using Array = test_support::BooleanArray<Size>;
static const TypeInfo* GetTypeInfo() { return theBooleanArrayTypeInfo; }
static constexpr const char* name = "BooleanArray";
};
template <>
struct PayloadTraits<KByte> {
template <size_t Size>
using Array = test_support::ByteArray<Size>;
static const TypeInfo* GetTypeInfo() { return theByteArrayTypeInfo; }
static constexpr const char* name = "ByteArray";
};
template <>
struct PayloadTraits<KChar> {
template <size_t Size>
using Array = test_support::CharArray<Size>;
static const TypeInfo* GetTypeInfo() { return theCharArrayTypeInfo; }
static constexpr const char* name = "CharArray";
};
template <>
struct PayloadTraits<KDouble> {
template <size_t Size>
using Array = test_support::DoubleArray<Size>;
static const TypeInfo* GetTypeInfo() { return theDoubleArrayTypeInfo; }
static constexpr const char* name = "DoubleArray";
};
template <>
struct PayloadTraits<KFloat> {
template <size_t Size>
using Array = test_support::FloatArray<Size>;
static const TypeInfo* GetTypeInfo() { return theFloatArrayTypeInfo; }
static constexpr const char* name = "FloatArray";
};
template <>
struct PayloadTraits<KInt> {
template <size_t Size>
using Array = test_support::IntArray<Size>;
static const TypeInfo* GetTypeInfo() { return theIntArrayTypeInfo; }
static constexpr const char* name = "IntArray";
};
template <>
struct PayloadTraits<KLong> {
template <size_t Size>
using Array = test_support::LongArray<Size>;
static const TypeInfo* GetTypeInfo() { return theLongArrayTypeInfo; }
static constexpr const char* name = "LongArray";
};
template <>
struct PayloadTraits<KNativePtr> {
template <size_t Size>
using Array = test_support::NativePtrArray<Size>;
static const TypeInfo* GetTypeInfo() { return theNativePtrArrayTypeInfo; }
static constexpr const char* name = "NativePtrArray";
};
template <>
struct PayloadTraits<KShort> {
template <size_t Size>
using Array = test_support::ShortArray<Size>;
static const TypeInfo* GetTypeInfo() { return theShortArrayTypeInfo; }
static constexpr const char* name = "ShortArray";
};
template <size_t Size>
struct SizeTraits;
template <>
struct SizeTraits<0> {
static constexpr const char* name = "Empty";
};
template <>
struct SizeTraits<3> {
static constexpr const char* name = "";
};
template <typename T, size_t Size>
struct ArrayTestCase {
using Payload = T;
using Array = typename PayloadTraits<Payload>::template Array<Size>;
static constexpr size_t size = Size;
static const TypeInfo* GetTypeInfo() { return PayloadTraits<Payload>::GetTypeInfo(); }
static std::string GetName() { return std::string(SizeTraits<Size>::name) + std::string(PayloadTraits<Payload>::name); }
};
template <size_t Size>
struct StringTestCase {
using Payload = KChar;
using Array = test_support::String<Size>;
static constexpr size_t size = Size;
static const TypeInfo* GetTypeInfo() { return theStringTypeInfo; }
static std::string GetName() { return std::string(SizeTraits<Size>::name) + std::string("String"); }
};
class ArrayTestCaseNames {
public:
template <typename T>
static std::string GetName(int i) {
return T::GetName();
}
};
template <typename TestCase>
class ObjectTestSupportArrayTest : public testing::Test {};
using ArrayTestCases = testing::Types<
ArrayTestCase<ObjHeader*, 0>,
ArrayTestCase<ObjHeader*, 3>,
ArrayTestCase<KBoolean, 0>,
ArrayTestCase<KBoolean, 3>,
ArrayTestCase<KByte, 0>,
ArrayTestCase<KByte, 3>,
ArrayTestCase<KChar, 0>,
ArrayTestCase<KChar, 3>,
ArrayTestCase<KDouble, 0>,
ArrayTestCase<KDouble, 3>,
ArrayTestCase<KFloat, 0>,
ArrayTestCase<KFloat, 3>,
ArrayTestCase<KInt, 0>,
ArrayTestCase<KInt, 3>,
ArrayTestCase<KLong, 0>,
ArrayTestCase<KLong, 3>,
ArrayTestCase<KNativePtr, 0>,
ArrayTestCase<KNativePtr, 3>,
ArrayTestCase<KShort, 0>,
ArrayTestCase<KShort, 3>,
StringTestCase<0>,
StringTestCase<3>>;
TYPED_TEST_SUITE(ObjectTestSupportArrayTest, ArrayTestCases, ArrayTestCaseNames);
template <typename Payload, size_t ElementCount>
KStdVector<Payload*> Collect(test_support::internal::Array<Payload, ElementCount>& array) {
KStdVector<Payload*> result;
for (auto& element : array.elements()) {
result.push_back(&element);
}
return result;
}
} // namespace
TYPED_TEST(ObjectTestSupportArrayTest, Local) {
using Payload = typename TypeParam::Payload;
using Array = typename TypeParam::Array;
const auto typeInfo = TypeParam::GetTypeInfo();
constexpr auto size = TypeParam::size;
Array array;
EXPECT_THAT(array.header()->type_info(), typeInfo);
EXPECT_THAT(array.arrayHeader()->count_, size);
EXPECT_THAT(array.elements().size(), size);
KStdVector<Payload*> expected;
for (size_t i = 0; i < size; ++i) {
auto* element = AddressOfElementAt<Payload>(array.arrayHeader(), i);
EXPECT_THAT(&array.elements()[i], element);
EXPECT_THAT(array.elements()[i], Payload{});
expected.push_back(element);
}
EXPECT_THAT(Collect(array), testing::ElementsAreArray(expected));
auto& recoveredArray = Array::FromArrayHeader(array.arrayHeader());
EXPECT_THAT(&recoveredArray, &array);
}
TYPED_TEST(ObjectTestSupportArrayTest, Heap) {
using Payload = typename TypeParam::Payload;
using Array = typename TypeParam::Array;
const auto typeInfo = TypeParam::GetTypeInfo();
constexpr auto size = TypeParam::size;
RunInNewThread([typeInfo]() {
ObjHolder resultHolder;
ObjHeader* result = AllocArrayInstance(typeInfo, size, resultHolder.slot());
ASSERT_THAT(result, testing::Ne(nullptr));
auto& array = Array::FromArrayHeader(result->array());
EXPECT_THAT(array.header(), result);
EXPECT_THAT(array.header()->type_info(), typeInfo);
EXPECT_THAT(array.arrayHeader()->count_, size);
EXPECT_THAT(array.elements().size(), size);
KStdVector<Payload*> expected;
for (size_t i = 0; i < size; ++i) {
auto* element = AddressOfElementAt<Payload>(array.arrayHeader(), i);
EXPECT_THAT(&array.elements()[i], element);
EXPECT_THAT(array.elements()[i], Payload{});
expected.push_back(element);
}
EXPECT_THAT(Collect(array), testing::ElementsAreArray(expected));
});
}
@@ -8,6 +8,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "Types.h"
#include "Utils.hpp"
@@ -17,47 +18,6 @@ using ::testing::_;
namespace {
template <size_t Count>
class Object : private Pinned {
public:
Object() {
header_.typeInfoOrMeta_ = &type_;
type_.typeInfo_ = &type_;
type_.objOffsetsCount_ = Count;
type_.objOffsets_ = fieldOffsets_.data();
for (size_t i = 0; i < Count; ++i) {
fieldOffsets_[i] = reinterpret_cast<uintptr_t>(&fields_[i]) - reinterpret_cast<uintptr_t>(&header_);
}
}
ObjHeader* header() { return &header_; }
ObjHeader*& operator[](size_t index) { return fields_[index]; }
private:
ObjHeader header_;
TypeInfo type_;
std::array<int32_t, Count> fieldOffsets_;
std::array<ObjHeader*, Count> fields_{};
};
template <size_t Count>
class Array : private Pinned {
public:
Array() {
header_.typeInfoOrMeta_ = const_cast<TypeInfo*>(theArrayTypeInfo);
header_.count_ = Count;
}
ObjHeader* header() { return header_.obj(); }
ObjHeader*& operator[](size_t index) { return fields_[index]; }
private:
ArrayHeader header_;
std::array<ObjHeader*, Count> fields_{};
};
struct CallableWithExceptions {
void operator()(ObjHeader*) noexcept(false) {}
void operator()(ObjHeader**) noexcept(false) {}
@@ -68,6 +28,23 @@ struct CallableWithoutExceptions {
void operator()(ObjHeader**) noexcept {}
};
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
struct Payload {
ObjHeader* field1;
ObjHeader* field2;
ObjHeader* field3;
static constexpr std::array kFields{
&Payload::field1,
&Payload::field2,
&Payload::field3,
};
};
} // namespace
TEST(ObjectTraversalTest, TraverseFieldsExceptions) {
@@ -80,7 +57,8 @@ TEST(ObjectTraversalTest, TraverseFieldsExceptions) {
}
TEST(ObjectTraversalTest, TraverseEmptyObjectFields) {
Object<0> object;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(_)).Times(0);
@@ -88,34 +66,36 @@ TEST(ObjectTraversalTest, TraverseEmptyObjectFields) {
}
TEST(ObjectTraversalTest, TraverseObjectFields) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1;
ObjHeader field3;
Object<3> object;
object[0] = &field1;
object[2] = &field3;
test_support::Object<Payload> object(type.typeInfo());
object->field1 = &field1;
object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&object[0]));
EXPECT_CALL(process, Call(&object[1]));
EXPECT_CALL(process, Call(&object[2]));
EXPECT_CALL(process, Call(&object->field1));
EXPECT_CALL(process, Call(&object->field2));
EXPECT_CALL(process, Call(&object->field3));
traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); });
}
TEST(ObjectTraversalTest, TraverseObjectFieldsWithException) {
constexpr int kException = 1;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1;
ObjHeader field2;
ObjHeader field3;
Object<3> object;
object[0] = &field1;
object[1] = &field2;
object[2] = &field3;
test_support::Object<Payload> object(type.typeInfo());
object->field1 = &field1;
object->field2 = &field2;
object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&object[0]));
EXPECT_CALL(process, Call(&object[1])).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&object[2])).Times(0);
EXPECT_CALL(process, Call(&object->field1));
EXPECT_CALL(process, Call(&object->field2)).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&object->field3)).Times(0);
try {
traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); });
} catch (int exception) {
@@ -126,7 +106,7 @@ TEST(ObjectTraversalTest, TraverseObjectFieldsWithException) {
}
TEST(ObjectTraversalTest, TraverseEmptyArrayFields) {
Array<0> array;
test_support::ObjectArray<0> array;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(_)).Times(0);
@@ -136,14 +116,14 @@ TEST(ObjectTraversalTest, TraverseEmptyArrayFields) {
TEST(ObjectTraversalTest, TraverseArrayFields) {
ObjHeader element1;
ObjHeader element3;
Array<3> array;
array[0] = &element1;
array[2] = &element3;
test_support::ObjectArray<3> array;
array.elements()[0] = &element1;
array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&array[0]));
EXPECT_CALL(process, Call(&array[1]));
EXPECT_CALL(process, Call(&array[2]));
EXPECT_CALL(process, Call(&array.elements()[0]));
EXPECT_CALL(process, Call(&array.elements()[1]));
EXPECT_CALL(process, Call(&array.elements()[2]));
traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); });
}
@@ -153,15 +133,15 @@ TEST(ObjectTraversalTest, TraverseArrayFieldsWithException) {
ObjHeader element1;
ObjHeader element2;
ObjHeader element3;
Array<3> array;
array[0] = &element1;
array[1] = &element2;
array[2] = &element3;
test_support::ObjectArray<3> array;
array.elements()[0] = &element1;
array.elements()[1] = &element2;
array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&array[0]));
EXPECT_CALL(process, Call(&array[1])).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&array[2])).Times(0);
EXPECT_CALL(process, Call(&array.elements()[0]));
EXPECT_CALL(process, Call(&array.elements()[1])).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&array.elements()[2])).Times(0);
try {
traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); });
} catch (int exception) {
@@ -181,7 +161,8 @@ TEST(ObjectTraversalTest, TraverseRefsExceptions) {
}
TEST(ObjectTraversalTest, TraverseEmptyObjectRefs) {
Object<0> object;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(_)).Times(0);
@@ -189,11 +170,12 @@ TEST(ObjectTraversalTest, TraverseEmptyObjectRefs) {
}
TEST(ObjectTraversalTest, TraverseObjectRefs) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1;
ObjHeader field3;
Object<3> object;
object[0] = &field1;
object[2] = &field3;
test_support::Object<Payload> object(type.typeInfo());
object->field1 = &field1;
object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&field1));
@@ -204,13 +186,14 @@ TEST(ObjectTraversalTest, TraverseObjectRefs) {
TEST(ObjectTraversalTest, TraverseObjectRefsWithException) {
constexpr int kException = 1;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1;
ObjHeader field2;
ObjHeader field3;
Object<3> object;
object[0] = &field1;
object[1] = &field2;
object[2] = &field3;
test_support::Object<Payload> object(type.typeInfo());
object->field1 = &field1;
object->field2 = &field2;
object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&field1));
@@ -226,7 +209,7 @@ TEST(ObjectTraversalTest, TraverseObjectRefsWithException) {
}
TEST(ObjectTraversalTest, TraverseEmptyArrayRefs) {
Array<0> array;
test_support::ObjectArray<0> array;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(_)).Times(0);
@@ -236,9 +219,9 @@ TEST(ObjectTraversalTest, TraverseEmptyArrayRefs) {
TEST(ObjectTraversalTest, TraverseArrayRefs) {
ObjHeader element1;
ObjHeader element3;
Array<3> array;
array[0] = &element1;
array[2] = &element3;
test_support::ObjectArray<3> array;
array.elements()[0] = &element1;
array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&element1));
@@ -252,10 +235,10 @@ TEST(ObjectTraversalTest, TraverseArrayRefsWithException) {
ObjHeader element1;
ObjHeader element2;
ObjHeader element3;
Array<3> array;
array[0] = &element1;
array[1] = &element2;
array[2] = &element3;
test_support::ObjectArray<3> array;
array.elements()[0] = &element1;
array.elements()[1] = &element2;
array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&element1));
@@ -17,6 +17,7 @@
#ifndef RUNTIME_TYPEINFO_H
#define RUNTIME_TYPEINFO_H
#include <algorithm>
#include <cstdint>
#include "Common.h"
@@ -160,6 +161,18 @@ struct TypeInfo {
}
inline bool IsArray() const { return instanceSize_ < 0; }
bool IsLayoutCompatible(const TypeInfo* rhs) const noexcept {
// TODO: Use debug info if it's present?
// This automatically checks array vs object discrepancy.
if (instanceSize_ != rhs->instanceSize_) return false;
if (!IsArray()) {
if (!std::equal(objOffsets_, objOffsets_ + objOffsetsCount_, rhs->objOffsets_, rhs->objOffsets_ + rhs->objOffsetsCount_)) {
return false;
}
}
return true;
}
#endif
};
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "TypeInfo.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "Types.h"
using namespace kotlin;
namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
struct Payload1 {
ObjHeader* field1;
ObjHeader* field2;
static constexpr std::array kFields{
&Payload1::field1,
&Payload1::field2,
};
};
struct Payload2 {
ObjHeader* field1;
ObjHeader* field2;
static constexpr std::array kFields{
&Payload2::field1,
&Payload2::field2,
};
};
test_support::TypeInfoHolder emptyObjectTypeHolder{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::TypeInfoHolder object1TypeHolder{test_support::TypeInfoHolder::ObjectBuilder<Payload1>()};
test_support::TypeInfoHolder object2TypeHolder{test_support::TypeInfoHolder::ObjectBuilder<Payload2>()};
const TypeInfo* emptyObjectType = emptyObjectTypeHolder.typeInfo();
const TypeInfo* object1Type = object1TypeHolder.typeInfo();
const TypeInfo* object2Type = object2TypeHolder.typeInfo();
using LayoutCompatibleTestParam = std::tuple<const TypeInfo*, const TypeInfo*, bool, const char*>;
class LayoutCompatibleTest : public testing::TestWithParam<LayoutCompatibleTestParam> {
public:
static std::string Print(const testing::TestParamInfo<LayoutCompatibleTestParam>& param) { return std::get<3>(param.param); }
const TypeInfo* lhsType() { return std::get<0>(GetParam()); }
const TypeInfo* rhsType() { return std::get<1>(GetParam()); }
bool expectCompatible() { return std::get<2>(GetParam()); }
};
INSTANTIATE_TEST_SUITE_P(
,
LayoutCompatibleTest,
testing::Values(
std::make_tuple(emptyObjectType, emptyObjectType, true, "empty_empty"),
std::make_tuple(emptyObjectType, object1Type, false, "empty_obj1"),
std::make_tuple(emptyObjectType, object2Type, false, "empty_obj2"),
std::make_tuple(emptyObjectType, theArrayTypeInfo, false, "empty_arr"),
std::make_tuple(emptyObjectType, theCharArrayTypeInfo, false, "empty_charArr"),
std::make_tuple(object1Type, emptyObjectType, false, "obj1_empty"),
std::make_tuple(object1Type, object1Type, true, "obj1_obj1"),
std::make_tuple(object1Type, object2Type, true, "obj1_obj2"),
std::make_tuple(object1Type, theArrayTypeInfo, false, "obj1_arr"),
std::make_tuple(object1Type, theCharArrayTypeInfo, false, "obj1_charArr"),
std::make_tuple(object2Type, emptyObjectType, false, "obj2_empty"),
std::make_tuple(object2Type, object1Type, true, "obj2_obj1"),
std::make_tuple(object2Type, object2Type, true, "obj2_obj2"),
std::make_tuple(object2Type, theArrayTypeInfo, false, "obj2_arr"),
std::make_tuple(object2Type, theCharArrayTypeInfo, false, "obj2_charArr"),
std::make_tuple(theArrayTypeInfo, emptyObjectType, false, "arr_empty"),
std::make_tuple(theArrayTypeInfo, object1Type, false, "arr_obj1"),
std::make_tuple(theArrayTypeInfo, object2Type, false, "arr_obj2"),
std::make_tuple(theArrayTypeInfo, theArrayTypeInfo, true, "arr_arr"),
std::make_tuple(theArrayTypeInfo, theCharArrayTypeInfo, false, "arr_charArr"),
std::make_tuple(theCharArrayTypeInfo, emptyObjectType, false, "charArr_empty"),
std::make_tuple(theCharArrayTypeInfo, object1Type, false, "charArr_obj1"),
std::make_tuple(theCharArrayTypeInfo, object2Type, false, "charArr_obj2"),
std::make_tuple(theCharArrayTypeInfo, theArrayTypeInfo, false, "charArr_arr"),
std::make_tuple(theCharArrayTypeInfo, theCharArrayTypeInfo, true, "charArr_charArr")),
&LayoutCompatibleTest::Print);
} // namespace
TEST_P(LayoutCompatibleTest, IsLayoutCompatible) {
EXPECT_THAT(lhsType()->IsLayoutCompatible(rhsType()), expectCompatible());
}
@@ -19,11 +19,6 @@ namespace {
class ExceptionObjHolderTest : public ::testing::Test {
public:
~ExceptionObjHolderTest() {
auto& stableRefs = mm::StableRefRegistry::Instance();
stableRefs.ClearForTests();
}
static KStdVector<ObjHeader*> Collect(mm::ThreadData& threadData) {
auto& stableRefs = mm::StableRefRegistry::Instance();
stableRefs.ProcessThread(&threadData);
@@ -11,35 +11,42 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "TestSupport.hpp"
using namespace kotlin;
namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
} // namespace
TEST(ExtraObjectDataTest, Install) {
TypeInfo typeInfo;
typeInfo.typeInfo_ = &typeInfo;
ObjHeader object;
object.typeInfoOrMeta_ = &typeInfo;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
auto* typeInfo = object.header()->type_info();
ASSERT_FALSE(object.has_meta_object());
ASSERT_FALSE(object.header()->has_meta_object());
auto& extraData = mm::ExtraObjectData::Install(&object);
auto& extraData = mm::ExtraObjectData::Install(object.header());
EXPECT_TRUE(object.has_meta_object());
EXPECT_THAT(object.meta_object(), extraData.AsMetaObjHeader());
EXPECT_THAT(object.type_info(), &typeInfo);
EXPECT_TRUE(object.header()->has_meta_object());
EXPECT_THAT(object.header()->meta_object(), extraData.AsMetaObjHeader());
EXPECT_THAT(object.header()->type_info(), typeInfo);
mm::ExtraObjectData::Uninstall(&object);
mm::ExtraObjectData::Uninstall(object.header());
EXPECT_FALSE(object.has_meta_object());
EXPECT_THAT(object.type_info(), &typeInfo);
EXPECT_FALSE(object.header()->has_meta_object());
EXPECT_THAT(object.header()->type_info(), typeInfo);
}
TEST(ExtraObjectDataTest, ConcurrentInstall) {
TypeInfo typeInfo;
typeInfo.typeInfo_ = &typeInfo;
ObjHeader object;
object.typeInfoOrMeta_ = &typeInfo;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
test_support::Object<EmptyPayload> object(type.typeInfo());
constexpr int kThreadCount = kDefaultThreadCount;
@@ -53,7 +60,7 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
++readyCount;
while (!canStart) {
}
auto& extraData = mm::ExtraObjectData::Install(&object);
auto& extraData = mm::ExtraObjectData::Install(object.header());
actual[i] = &extraData;
});
}
@@ -70,5 +77,5 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
EXPECT_THAT(actual, testing::ElementsAreArray(expected));
mm::ExtraObjectData::Uninstall(&object);
mm::ExtraObjectData::Uninstall(object.header());
}
@@ -11,6 +11,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "TestSupport.hpp"
#include "ThreadData.hpp"
#include "Types.h"
@@ -21,12 +22,14 @@ using testing::_;
namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
class InitSingletonTest : public testing::Test {
public:
InitSingletonTest() {
typeInfo_.typeInfo_ = &typeInfo_;
typeInfo_.instanceSize_ = sizeof(ObjHeader);
globalConstructor_ = &constructor_;
for (auto& threadData : threadDatas_) {
@@ -38,8 +41,7 @@ public:
globalConstructor_ = nullptr;
// Make sure to clean everything allocated by the tests.
for (auto& threadData : threadDatas_) {
threadData->objectFactoryThreadQueue().ClearForTests();
threadData->globalsThreadQueue().ClearForTests();
threadData->ClearForTests();
}
}
@@ -48,18 +50,18 @@ public:
testing::MockFunction<void(ObjHeader*)>& constructor() { return constructor_; }
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, size_t threadIndex) {
RETURN_RESULT_OF(mm::InitThreadLocalSingleton, threadDatas_[threadIndex].get(), location, &typeInfo_, constructorImpl);
RETURN_RESULT_OF(mm::InitThreadLocalSingleton, threadDatas_[threadIndex].get(), location, type_.typeInfo(), constructorImpl);
}
OBJ_GETTER(InitSingleton, ObjHeader** location, size_t threadIndex) {
RETURN_RESULT_OF(mm::InitSingleton, threadDatas_[threadIndex].get(), location, &typeInfo_, constructorImpl);
RETURN_RESULT_OF(mm::InitSingleton, threadDatas_[threadIndex].get(), location, type_.typeInfo(), constructorImpl);
}
private:
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> constructor_;
// TODO: It makes sense to somehow abstract `ThreadData` stuff away. Allocation in this case.
std::array<KStdUniquePtr<mm::ThreadData>, kDefaultThreadCount> threadDatas_;
TypeInfo typeInfo_; // Only used for allocator calls, uninteresting for these tests.
test_support::TypeInfoHolder type_{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
static testing::MockFunction<void(ObjHeader*)>* globalConstructor_;
@@ -116,6 +116,11 @@ extern "C" void RestoreMemory(MemoryState*) {
// TODO: Remove when legacy MM is gone.
}
extern "C" void ClearMemoryForTests(MemoryState* state) {
auto* threadData = FromMemoryState(state)->Get();
threadData->ClearForTests();
}
extern "C" RUNTIME_NOTHROW OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
RETURN_RESULT_OF(mm::AllocateObject, threadData, typeInfo);
@@ -13,6 +13,7 @@
#include "gtest/gtest.h"
#include "GC.hpp"
#include "ObjectTestSupport.hpp"
#include "TestSupport.hpp"
#include "Types.h"
@@ -745,29 +746,25 @@ public:
using ObjectFactory = mm::ObjectFactory<GC>;
KStdUniquePtr<TypeInfo> MakeObjectTypeInfo(int32_t size) {
auto typeInfo = make_unique<TypeInfo>();
typeInfo->typeInfo_ = typeInfo.get();
typeInfo->instanceSize_ = size;
return typeInfo;
}
struct Payload {
ObjHeader* field1;
ObjHeader* field2;
KStdUniquePtr<TypeInfo> MakeArrayTypeInfo(int32_t elementSize) {
auto typeInfo = make_unique<TypeInfo>();
typeInfo->typeInfo_ = typeInfo.get();
typeInfo->instanceSize_ = -elementSize;
return typeInfo;
}
static constexpr std::array kFields{
&Payload::field1,
&Payload::field2,
};
};
} // namespace
TEST(ObjectFactoryTest, CreateObject) {
auto typeInfo = MakeObjectTypeInfo(24);
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* object = threadQueue.CreateObject(typeInfo.get());
auto* object = threadQueue.CreateObject(type.typeInfo());
threadQueue.Publish();
auto node = ObjectFactory::NodeRef::From(object);
@@ -782,13 +779,32 @@ TEST(ObjectFactoryTest, CreateObject) {
EXPECT_THAT(it, iter.end());
}
TEST(ObjectFactoryTest, CreateArray) {
auto typeInfo = MakeArrayTypeInfo(24);
TEST(ObjectFactoryTest, CreateObjectArray) {
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* array = threadQueue.CreateArray(typeInfo.get(), 3);
auto* array = threadQueue.CreateArray(theArrayTypeInfo, 3);
threadQueue.Publish();
auto node = ObjectFactory::NodeRef::From(array);
EXPECT_TRUE(node.IsArray());
EXPECT_THAT(node.GetArrayHeader(), array);
EXPECT_THAT(node.GCObjectData().flags, 42);
auto iter = objectFactory.Iter();
auto it = iter.begin();
EXPECT_THAT(*it, node);
++it;
EXPECT_THAT(it, iter.end());
}
TEST(ObjectFactoryTest, CreateCharArray) {
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* array = threadQueue.CreateArray(theCharArrayTypeInfo, 3);
threadQueue.Publish();
auto node = ObjectFactory::NodeRef::From(array);
@@ -804,15 +820,14 @@ TEST(ObjectFactoryTest, CreateArray) {
}
TEST(ObjectFactoryTest, Erase) {
auto objectTypeInfo = MakeObjectTypeInfo(24);
auto arrayTypeInfo = MakeArrayTypeInfo(24);
test_support::TypeInfoHolder objectType{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
for (int i = 0; i < 10; ++i) {
threadQueue.CreateObject(objectTypeInfo.get());
threadQueue.CreateArray(arrayTypeInfo.get(), 3);
threadQueue.CreateObject(objectType.typeInfo());
threadQueue.CreateArray(theArrayTypeInfo, 3);
}
threadQueue.Publish();
@@ -839,16 +854,15 @@ TEST(ObjectFactoryTest, Erase) {
}
TEST(ObjectFactoryTest, Move) {
auto objectTypeInfo = MakeObjectTypeInfo(24);
auto arrayTypeInfo = MakeArrayTypeInfo(24);
test_support::TypeInfoHolder objectType{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
ObjectFactory::FinalizerQueue finalizerQueue;
for (int i = 0; i < 10; ++i) {
threadQueue.CreateObject(objectTypeInfo.get());
threadQueue.CreateArray(arrayTypeInfo.get(), 3);
threadQueue.CreateObject(objectType.typeInfo());
threadQueue.CreateArray(theArrayTypeInfo, 3);
}
threadQueue.Publish();
@@ -883,7 +897,7 @@ TEST(ObjectFactoryTest, Move) {
}
TEST(ObjectFactoryTest, ConcurrentPublish) {
auto typeInfo = MakeObjectTypeInfo(24);
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjectFactory objectFactory;
constexpr int kThreadCount = kDefaultThreadCount;
std::atomic<bool> canStart(false);
@@ -893,10 +907,10 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
KStdVector<ObjHeader*> expected;
for (int i = 0; i < kThreadCount; ++i) {
threads.emplace_back([&typeInfo, &objectFactory, &canStart, &readyCount, &expected, &expectedMutex]() {
threads.emplace_back([&type, &objectFactory, &canStart, &readyCount, &expected, &expectedMutex]() {
GC::ThreadData gc;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* object = threadQueue.CreateObject(typeInfo.get());
auto* object = threadQueue.CreateObject(type.typeInfo());
{
std::lock_guard<std::mutex> guard(expectedMutex);
expected.push_back(object);
@@ -29,6 +29,8 @@ void RunInNewThread(F f) {
} registration;
f(registration.threadData());
registration.threadData().ClearForTests();
}).join();
}
@@ -66,6 +66,12 @@ public:
objectFactoryThreadQueue_.Publish();
}
void ClearForTests() noexcept {
globalsThreadQueue_.ClearForTests();
stableRefThreadQueue_.ClearForTests();
objectFactoryThreadQueue_.ClearForTests();
}
private:
const pthread_t threadId_;
GlobalsRegistry::ThreadQueue globalsThreadQueue_;
@@ -5,42 +5,42 @@
#include "TestSupportCompilerGenerated.hpp"
#include "ObjectTestSupport.hpp"
#include "Types.h"
namespace {
class TypeInfoImpl {
public:
TypeInfoImpl() { type_.typeInfo_ = &type_; }
TypeInfo* type() { return &type_; }
private:
TypeInfo type_;
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
TypeInfoImpl theAnyTypeInfoImpl;
TypeInfoImpl theArrayTypeInfoImpl;
TypeInfoImpl theBooleanArrayTypeInfoImpl;
TypeInfoImpl theByteArrayTypeInfoImpl;
TypeInfoImpl theCharArrayTypeInfoImpl;
TypeInfoImpl theDoubleArrayTypeInfoImpl;
TypeInfoImpl theFloatArrayTypeInfoImpl;
TypeInfoImpl theForeignObjCObjectTypeInfoImpl;
TypeInfoImpl theFreezableAtomicReferenceTypeInfoImpl;
TypeInfoImpl theIntArrayTypeInfoImpl;
TypeInfoImpl theLongArrayTypeInfoImpl;
TypeInfoImpl theNativePtrArrayTypeInfoImpl;
TypeInfoImpl theObjCObjectWrapperTypeInfoImpl;
TypeInfoImpl theOpaqueFunctionTypeInfoImpl;
TypeInfoImpl theShortArrayTypeInfoImpl;
TypeInfoImpl theStringTypeInfoImpl;
TypeInfoImpl theThrowableTypeInfoImpl;
TypeInfoImpl theUnitTypeInfoImpl;
TypeInfoImpl theWorkerBoundReferenceTypeInfoImpl;
TypeInfoImpl theCleanerImplTypeInfoImpl;
kotlin::test_support::TypeInfoHolder theAnyTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<ObjHeader*>()};
kotlin::test_support::TypeInfoHolder theBooleanArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KBoolean>()};
kotlin::test_support::TypeInfoHolder theByteArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KByte>()};
kotlin::test_support::TypeInfoHolder theCharArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KChar>()};
kotlin::test_support::TypeInfoHolder theDoubleArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KDouble>()};
kotlin::test_support::TypeInfoHolder theFloatArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KFloat>()};
kotlin::test_support::TypeInfoHolder theForeignObjCObjectTypeInfoHolder{
kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theFreezableAtomicReferenceTypeInfoHolder{
kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theIntArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KInt>()};
kotlin::test_support::TypeInfoHolder theLongArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KLong>()};
kotlin::test_support::TypeInfoHolder theNativePtrArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KNativePtr>()};
kotlin::test_support::TypeInfoHolder theObjCObjectWrapperTypeInfoHolder{
kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theOpaqueFunctionTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theShortArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KShort>()};
kotlin::test_support::TypeInfoHolder theStringTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KChar>()};
kotlin::test_support::TypeInfoHolder theThrowableTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theUnitTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theWorkerBoundReferenceTypeInfoHolder{
kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
kotlin::test_support::TypeInfoHolder theCleanerImplTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
ArrayHeader theEmptyStringImpl = {theStringTypeInfoImpl.type(), /* element count */ 0};
ArrayHeader theEmptyStringImpl = {theStringTypeInfoHolder.typeInfo(), /* element count */ 0};
template <class T>
struct KBox {
@@ -58,28 +58,28 @@ extern "C" {
// Set to 1 to enable runtime assertions.
extern const int KonanNeedDebugInfo = 1;
extern const TypeInfo* theAnyTypeInfo = theAnyTypeInfoImpl.type();
extern const TypeInfo* theArrayTypeInfo = theArrayTypeInfoImpl.type();
extern const TypeInfo* theBooleanArrayTypeInfo = theBooleanArrayTypeInfoImpl.type();
extern const TypeInfo* theByteArrayTypeInfo = theByteArrayTypeInfoImpl.type();
extern const TypeInfo* theCharArrayTypeInfo = theCharArrayTypeInfoImpl.type();
extern const TypeInfo* theDoubleArrayTypeInfo = theDoubleArrayTypeInfoImpl.type();
extern const TypeInfo* theFloatArrayTypeInfo = theFloatArrayTypeInfoImpl.type();
extern const TypeInfo* theForeignObjCObjectTypeInfo = theForeignObjCObjectTypeInfoImpl.type();
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo = theFreezableAtomicReferenceTypeInfoImpl.type();
extern const TypeInfo* theIntArrayTypeInfo = theIntArrayTypeInfoImpl.type();
extern const TypeInfo* theLongArrayTypeInfo = theLongArrayTypeInfoImpl.type();
extern const TypeInfo* theNativePtrArrayTypeInfo = theNativePtrArrayTypeInfoImpl.type();
extern const TypeInfo* theObjCObjectWrapperTypeInfo = theObjCObjectWrapperTypeInfoImpl.type();
extern const TypeInfo* theOpaqueFunctionTypeInfo = theOpaqueFunctionTypeInfoImpl.type();
extern const TypeInfo* theShortArrayTypeInfo = theShortArrayTypeInfoImpl.type();
extern const TypeInfo* theStringTypeInfo = theStringTypeInfoImpl.type();
extern const TypeInfo* theThrowableTypeInfo = theThrowableTypeInfoImpl.type();
extern const TypeInfo* theUnitTypeInfo = theUnitTypeInfoImpl.type();
extern const TypeInfo* theWorkerBoundReferenceTypeInfo = theWorkerBoundReferenceTypeInfoImpl.type();
extern const TypeInfo* theCleanerImplTypeInfo = theCleanerImplTypeInfoImpl.type();
extern const TypeInfo* theAnyTypeInfo = theAnyTypeInfoHolder.typeInfo();
extern const TypeInfo* theArrayTypeInfo = theArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theBooleanArrayTypeInfo = theBooleanArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theByteArrayTypeInfo = theByteArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theCharArrayTypeInfo = theCharArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theDoubleArrayTypeInfo = theDoubleArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theFloatArrayTypeInfo = theFloatArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theForeignObjCObjectTypeInfo = theForeignObjCObjectTypeInfoHolder.typeInfo();
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo = theFreezableAtomicReferenceTypeInfoHolder.typeInfo();
extern const TypeInfo* theIntArrayTypeInfo = theIntArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theLongArrayTypeInfo = theLongArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theNativePtrArrayTypeInfo = theNativePtrArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theObjCObjectWrapperTypeInfo = theObjCObjectWrapperTypeInfoHolder.typeInfo();
extern const TypeInfo* theOpaqueFunctionTypeInfo = theOpaqueFunctionTypeInfoHolder.typeInfo();
extern const TypeInfo* theShortArrayTypeInfo = theShortArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theStringTypeInfo = theStringTypeInfoHolder.typeInfo();
extern const TypeInfo* theThrowableTypeInfo = theThrowableTypeInfoHolder.typeInfo();
extern const TypeInfo* theUnitTypeInfo = theUnitTypeInfoHolder.typeInfo();
extern const TypeInfo* theWorkerBoundReferenceTypeInfo = theWorkerBoundReferenceTypeInfoHolder.typeInfo();
extern const TypeInfo* theCleanerImplTypeInfo = theCleanerImplTypeInfoHolder.typeInfo();
extern const ArrayHeader theEmptyArray = {theArrayTypeInfoImpl.type(), /* element count */ 0};
extern const ArrayHeader theEmptyArray = {theArrayTypeInfoHolder.typeInfo(), /* element count */ 0};
OBJ_GETTER0(TheEmptyString) {
RETURN_OBJ(theEmptyStringImpl.obj());