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:
committed by
Space
parent
aff49c76a9
commit
f51c85a63f
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user