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; ::memoryState = memoryState;
} }
void ClearMemoryForTests(MemoryState*) {
// Nothing to do, DeinitMemory will do the job.
}
OBJ_GETTER(AllocInstanceStrict, const TypeInfo* type_info) { OBJ_GETTER(AllocInstanceStrict, const TypeInfo* type_info) {
RETURN_RESULT_OF(allocInstance<true>, type_info); RETURN_RESULT_OF(allocInstance<true>, type_info);
} }
@@ -10,6 +10,7 @@
#include "FinalizerHooksTestSupport.hpp" #include "FinalizerHooksTestSupport.hpp"
#include "Memory.h" #include "Memory.h"
#include "ObjectTestSupport.hpp"
using namespace kotlin; using namespace kotlin;
@@ -17,6 +18,11 @@ using ::testing::_;
namespace { namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
class FinalizerHooksTest : public testing::Test { class FinalizerHooksTest : public testing::Test {
public: public:
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); } testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
@@ -28,55 +34,51 @@ private:
} // namespace } // namespace
TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithoutExtra) { TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithoutExtra) {
TypeInfo type; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>().addFlag(TF_HAS_FINALIZER)};
type.typeInfo_ = &type; test_support::Object<EmptyPayload> object(type.typeInfo());
type.flags_ |= TF_HAS_FINALIZER; ObjHeader* obj = object.header();
ObjHeader obj = {&type}; ASSERT_FALSE(obj->has_meta_object());
ASSERT_FALSE(obj.has_meta_object());
EXPECT_TRUE(HasFinalizers(&obj)); EXPECT_TRUE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(&obj)); EXPECT_CALL(finalizerHook(), Call(obj));
RunFinalizers(&obj); RunFinalizers(obj);
EXPECT_FALSE(obj.has_meta_object()); EXPECT_FALSE(obj->has_meta_object());
} }
TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithExtra) { TEST_F(FinalizerHooksTest, TypeWithFinalizerHookWithExtra) {
TypeInfo type; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>().addFlag(TF_HAS_FINALIZER)};
type.typeInfo_ = &type; test_support::Object<EmptyPayload> object(type.typeInfo());
type.flags_ |= TF_HAS_FINALIZER; ObjHeader* obj = object.header();
ObjHeader obj = {&type}; ObjHeader::createMetaObject(obj);
ObjHeader::createMetaObject(&obj); ASSERT_TRUE(obj->has_meta_object());
ASSERT_TRUE(obj.has_meta_object());
EXPECT_TRUE(HasFinalizers(&obj)); EXPECT_TRUE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(&obj)); EXPECT_CALL(finalizerHook(), Call(obj));
RunFinalizers(&obj); RunFinalizers(obj);
EXPECT_FALSE(obj.has_meta_object()); EXPECT_FALSE(obj->has_meta_object());
} }
TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithoutExtra) { TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithoutExtra) {
TypeInfo type; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
type.typeInfo_ = &type; test_support::Object<EmptyPayload> object(type.typeInfo());
type.flags_ &= ~TF_HAS_FINALIZER; ObjHeader* obj = object.header();
ObjHeader obj = {&type}; ASSERT_FALSE(obj->has_meta_object());
ASSERT_FALSE(obj.has_meta_object());
EXPECT_FALSE(HasFinalizers(&obj)); EXPECT_FALSE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(_)).Times(0); EXPECT_CALL(finalizerHook(), Call(_)).Times(0);
RunFinalizers(&obj); RunFinalizers(obj);
EXPECT_FALSE(obj.has_meta_object()); EXPECT_FALSE(obj->has_meta_object());
} }
TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithExtra) { TEST_F(FinalizerHooksTest, TypeWithoutFinalizerHookWithExtra) {
TypeInfo type; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
type.typeInfo_ = &type; test_support::Object<EmptyPayload> object(type.typeInfo());
type.flags_ &= ~TF_HAS_FINALIZER; ObjHeader* obj = object.header();
ObjHeader obj = {&type}; ObjHeader::createMetaObject(obj);
ObjHeader::createMetaObject(&obj); ASSERT_TRUE(obj->has_meta_object());
ASSERT_TRUE(obj.has_meta_object());
EXPECT_TRUE(HasFinalizers(&obj)); EXPECT_TRUE(HasFinalizers(obj));
EXPECT_CALL(finalizerHook(), Call(_)).Times(0); EXPECT_CALL(finalizerHook(), Call(_)).Times(0);
RunFinalizers(&obj); RunFinalizers(obj);
EXPECT_FALSE(obj.has_meta_object()); EXPECT_FALSE(obj->has_meta_object());
} }
@@ -10,6 +10,7 @@
#include "FreezeHooksTestSupport.hpp" #include "FreezeHooksTestSupport.hpp"
#include "Memory.h" #include "Memory.h"
#include "ObjectTestSupport.hpp"
using namespace kotlin; using namespace kotlin;
@@ -17,6 +18,11 @@ using ::testing::_;
namespace { namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
class FreezeHooksTest : public testing::Test { class FreezeHooksTest : public testing::Test {
public: public:
testing::MockFunction<void(ObjHeader*)>& freezeHook() { return freezeHooks_.freezeHook(); } testing::MockFunction<void(ObjHeader*)>& freezeHook() { return freezeHooks_.freezeHook(); }
@@ -28,19 +34,17 @@ private:
} // namespace } // namespace
TEST_F(FreezeHooksTest, TypeWithFreezeHook) { TEST_F(FreezeHooksTest, TypeWithFreezeHook) {
TypeInfo type; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>().addFlag(TF_HAS_FREEZE_HOOK)};
type.typeInfo_ = &type; test_support::Object<EmptyPayload> object(type.typeInfo());
type.flags_ |= TF_HAS_FREEZE_HOOK; ObjHeader* obj = object.header();
ObjHeader obj = {&type}; EXPECT_CALL(freezeHook(), Call(obj));
EXPECT_CALL(freezeHook(), Call(&obj)); RunFreezeHooks(obj);
RunFreezeHooks(&obj);
} }
TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) { TEST_F(FreezeHooksTest, TypeWithoutFreezeHook) {
TypeInfo type; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
type.typeInfo_ = &type; test_support::Object<EmptyPayload> object(type.typeInfo());
type.flags_ &= ~TF_HAS_FREEZE_HOOK; ObjHeader* obj = object.header();
ObjHeader obj = {&type};
EXPECT_CALL(freezeHook(), Call(_)).Times(0); EXPECT_CALL(freezeHook(), Call(_)).Times(0);
RunFreezeHooks(&obj); RunFreezeHooks(obj);
} }
@@ -138,6 +138,7 @@ struct MemoryState;
MemoryState* InitMemory(bool firstRuntime); MemoryState* InitMemory(bool firstRuntime);
void DeinitMemory(MemoryState*, bool destroyRuntime); void DeinitMemory(MemoryState*, bool destroyRuntime);
void RestoreMemory(MemoryState*); void RestoreMemory(MemoryState*);
void ClearMemoryForTests(MemoryState*);
// //
// Object allocation. // 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 "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "Types.h" #include "Types.h"
#include "Utils.hpp" #include "Utils.hpp"
@@ -17,47 +18,6 @@ using ::testing::_;
namespace { 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 { struct CallableWithExceptions {
void operator()(ObjHeader*) noexcept(false) {} void operator()(ObjHeader*) noexcept(false) {}
void operator()(ObjHeader**) noexcept(false) {} void operator()(ObjHeader**) noexcept(false) {}
@@ -68,6 +28,23 @@ struct CallableWithoutExceptions {
void operator()(ObjHeader**) noexcept {} 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 } // namespace
TEST(ObjectTraversalTest, TraverseFieldsExceptions) { TEST(ObjectTraversalTest, TraverseFieldsExceptions) {
@@ -80,7 +57,8 @@ TEST(ObjectTraversalTest, TraverseFieldsExceptions) {
} }
TEST(ObjectTraversalTest, TraverseEmptyObjectFields) { 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; testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(_)).Times(0); EXPECT_CALL(process, Call(_)).Times(0);
@@ -88,34 +66,36 @@ TEST(ObjectTraversalTest, TraverseEmptyObjectFields) {
} }
TEST(ObjectTraversalTest, TraverseObjectFields) { TEST(ObjectTraversalTest, TraverseObjectFields) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1; ObjHeader field1;
ObjHeader field3; ObjHeader field3;
Object<3> object; test_support::Object<Payload> object(type.typeInfo());
object[0] = &field1; object->field1 = &field1;
object[2] = &field3; object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&object[0])); EXPECT_CALL(process, Call(&object->field1));
EXPECT_CALL(process, Call(&object[1])); EXPECT_CALL(process, Call(&object->field2));
EXPECT_CALL(process, Call(&object[2])); EXPECT_CALL(process, Call(&object->field3));
traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); }); traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); });
} }
TEST(ObjectTraversalTest, TraverseObjectFieldsWithException) { TEST(ObjectTraversalTest, TraverseObjectFieldsWithException) {
constexpr int kException = 1; constexpr int kException = 1;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1; ObjHeader field1;
ObjHeader field2; ObjHeader field2;
ObjHeader field3; ObjHeader field3;
Object<3> object; test_support::Object<Payload> object(type.typeInfo());
object[0] = &field1; object->field1 = &field1;
object[1] = &field2; object->field2 = &field2;
object[2] = &field3; object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&object[0])); EXPECT_CALL(process, Call(&object->field1));
EXPECT_CALL(process, Call(&object[1])).WillOnce([]() { throw kException; }); EXPECT_CALL(process, Call(&object->field2)).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&object[2])).Times(0); EXPECT_CALL(process, Call(&object->field3)).Times(0);
try { try {
traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); }); traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); });
} catch (int exception) { } catch (int exception) {
@@ -126,7 +106,7 @@ TEST(ObjectTraversalTest, TraverseObjectFieldsWithException) {
} }
TEST(ObjectTraversalTest, TraverseEmptyArrayFields) { TEST(ObjectTraversalTest, TraverseEmptyArrayFields) {
Array<0> array; test_support::ObjectArray<0> array;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(_)).Times(0); EXPECT_CALL(process, Call(_)).Times(0);
@@ -136,14 +116,14 @@ TEST(ObjectTraversalTest, TraverseEmptyArrayFields) {
TEST(ObjectTraversalTest, TraverseArrayFields) { TEST(ObjectTraversalTest, TraverseArrayFields) {
ObjHeader element1; ObjHeader element1;
ObjHeader element3; ObjHeader element3;
Array<3> array; test_support::ObjectArray<3> array;
array[0] = &element1; array.elements()[0] = &element1;
array[2] = &element3; array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&array[0])); EXPECT_CALL(process, Call(&array.elements()[0]));
EXPECT_CALL(process, Call(&array[1])); EXPECT_CALL(process, Call(&array.elements()[1]));
EXPECT_CALL(process, Call(&array[2])); EXPECT_CALL(process, Call(&array.elements()[2]));
traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); }); traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); });
} }
@@ -153,15 +133,15 @@ TEST(ObjectTraversalTest, TraverseArrayFieldsWithException) {
ObjHeader element1; ObjHeader element1;
ObjHeader element2; ObjHeader element2;
ObjHeader element3; ObjHeader element3;
Array<3> array; test_support::ObjectArray<3> array;
array[0] = &element1; array.elements()[0] = &element1;
array[1] = &element2; array.elements()[1] = &element2;
array[2] = &element3; array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
EXPECT_CALL(process, Call(&array[0])); EXPECT_CALL(process, Call(&array.elements()[0]));
EXPECT_CALL(process, Call(&array[1])).WillOnce([]() { throw kException; }); EXPECT_CALL(process, Call(&array.elements()[1])).WillOnce([]() { throw kException; });
EXPECT_CALL(process, Call(&array[2])).Times(0); EXPECT_CALL(process, Call(&array.elements()[2])).Times(0);
try { try {
traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); }); traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); });
} catch (int exception) { } catch (int exception) {
@@ -181,7 +161,8 @@ TEST(ObjectTraversalTest, TraverseRefsExceptions) {
} }
TEST(ObjectTraversalTest, TraverseEmptyObjectRefs) { 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; testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(_)).Times(0); EXPECT_CALL(process, Call(_)).Times(0);
@@ -189,11 +170,12 @@ TEST(ObjectTraversalTest, TraverseEmptyObjectRefs) {
} }
TEST(ObjectTraversalTest, TraverseObjectRefs) { TEST(ObjectTraversalTest, TraverseObjectRefs) {
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1; ObjHeader field1;
ObjHeader field3; ObjHeader field3;
Object<3> object; test_support::Object<Payload> object(type.typeInfo());
object[0] = &field1; object->field1 = &field1;
object[2] = &field3; object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&field1)); EXPECT_CALL(process, Call(&field1));
@@ -204,13 +186,14 @@ TEST(ObjectTraversalTest, TraverseObjectRefs) {
TEST(ObjectTraversalTest, TraverseObjectRefsWithException) { TEST(ObjectTraversalTest, TraverseObjectRefsWithException) {
constexpr int kException = 1; constexpr int kException = 1;
test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjHeader field1; ObjHeader field1;
ObjHeader field2; ObjHeader field2;
ObjHeader field3; ObjHeader field3;
Object<3> object; test_support::Object<Payload> object(type.typeInfo());
object[0] = &field1; object->field1 = &field1;
object[1] = &field2; object->field2 = &field2;
object[2] = &field3; object->field3 = &field3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&field1)); EXPECT_CALL(process, Call(&field1));
@@ -226,7 +209,7 @@ TEST(ObjectTraversalTest, TraverseObjectRefsWithException) {
} }
TEST(ObjectTraversalTest, TraverseEmptyArrayRefs) { TEST(ObjectTraversalTest, TraverseEmptyArrayRefs) {
Array<0> array; test_support::ObjectArray<0> array;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(_)).Times(0); EXPECT_CALL(process, Call(_)).Times(0);
@@ -236,9 +219,9 @@ TEST(ObjectTraversalTest, TraverseEmptyArrayRefs) {
TEST(ObjectTraversalTest, TraverseArrayRefs) { TEST(ObjectTraversalTest, TraverseArrayRefs) {
ObjHeader element1; ObjHeader element1;
ObjHeader element3; ObjHeader element3;
Array<3> array; test_support::ObjectArray<3> array;
array[0] = &element1; array.elements()[0] = &element1;
array[2] = &element3; array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&element1)); EXPECT_CALL(process, Call(&element1));
@@ -252,10 +235,10 @@ TEST(ObjectTraversalTest, TraverseArrayRefsWithException) {
ObjHeader element1; ObjHeader element1;
ObjHeader element2; ObjHeader element2;
ObjHeader element3; ObjHeader element3;
Array<3> array; test_support::ObjectArray<3> array;
array[0] = &element1; array.elements()[0] = &element1;
array[1] = &element2; array.elements()[1] = &element2;
array[2] = &element3; array.elements()[2] = &element3;
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process; testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
EXPECT_CALL(process, Call(&element1)); EXPECT_CALL(process, Call(&element1));
@@ -17,6 +17,7 @@
#ifndef RUNTIME_TYPEINFO_H #ifndef RUNTIME_TYPEINFO_H
#define RUNTIME_TYPEINFO_H #define RUNTIME_TYPEINFO_H
#include <algorithm>
#include <cstdint> #include <cstdint>
#include "Common.h" #include "Common.h"
@@ -160,6 +161,18 @@ struct TypeInfo {
} }
inline bool IsArray() const { return instanceSize_ < 0; } 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 #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 { class ExceptionObjHolderTest : public ::testing::Test {
public: public:
~ExceptionObjHolderTest() {
auto& stableRefs = mm::StableRefRegistry::Instance();
stableRefs.ClearForTests();
}
static KStdVector<ObjHeader*> Collect(mm::ThreadData& threadData) { static KStdVector<ObjHeader*> Collect(mm::ThreadData& threadData) {
auto& stableRefs = mm::StableRefRegistry::Instance(); auto& stableRefs = mm::StableRefRegistry::Instance();
stableRefs.ProcessThread(&threadData); stableRefs.ProcessThread(&threadData);
@@ -11,35 +11,42 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "TestSupport.hpp" #include "TestSupport.hpp"
using namespace kotlin; using namespace kotlin;
namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
} // namespace
TEST(ExtraObjectDataTest, Install) { TEST(ExtraObjectDataTest, Install) {
TypeInfo typeInfo; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
typeInfo.typeInfo_ = &typeInfo; test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader object; auto* typeInfo = object.header()->type_info();
object.typeInfoOrMeta_ = &typeInfo;
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_TRUE(object.header()->has_meta_object());
EXPECT_THAT(object.meta_object(), extraData.AsMetaObjHeader()); EXPECT_THAT(object.header()->meta_object(), extraData.AsMetaObjHeader());
EXPECT_THAT(object.type_info(), &typeInfo); EXPECT_THAT(object.header()->type_info(), typeInfo);
mm::ExtraObjectData::Uninstall(&object); mm::ExtraObjectData::Uninstall(object.header());
EXPECT_FALSE(object.has_meta_object()); EXPECT_FALSE(object.header()->has_meta_object());
EXPECT_THAT(object.type_info(), &typeInfo); EXPECT_THAT(object.header()->type_info(), typeInfo);
} }
TEST(ExtraObjectDataTest, ConcurrentInstall) { TEST(ExtraObjectDataTest, ConcurrentInstall) {
TypeInfo typeInfo; test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
typeInfo.typeInfo_ = &typeInfo; test_support::Object<EmptyPayload> object(type.typeInfo());
ObjHeader object;
object.typeInfoOrMeta_ = &typeInfo;
constexpr int kThreadCount = kDefaultThreadCount; constexpr int kThreadCount = kDefaultThreadCount;
@@ -53,7 +60,7 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
++readyCount; ++readyCount;
while (!canStart) { while (!canStart) {
} }
auto& extraData = mm::ExtraObjectData::Install(&object); auto& extraData = mm::ExtraObjectData::Install(object.header());
actual[i] = &extraData; actual[i] = &extraData;
}); });
} }
@@ -70,5 +77,5 @@ TEST(ExtraObjectDataTest, ConcurrentInstall) {
EXPECT_THAT(actual, testing::ElementsAreArray(expected)); EXPECT_THAT(actual, testing::ElementsAreArray(expected));
mm::ExtraObjectData::Uninstall(&object); mm::ExtraObjectData::Uninstall(object.header());
} }
@@ -11,6 +11,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "ObjectTestSupport.hpp"
#include "TestSupport.hpp" #include "TestSupport.hpp"
#include "ThreadData.hpp" #include "ThreadData.hpp"
#include "Types.h" #include "Types.h"
@@ -21,12 +22,14 @@ using testing::_;
namespace { namespace {
struct EmptyPayload {
using Field = ObjHeader* EmptyPayload::*;
static constexpr std::array<Field, 0> kFields{};
};
class InitSingletonTest : public testing::Test { class InitSingletonTest : public testing::Test {
public: public:
InitSingletonTest() { InitSingletonTest() {
typeInfo_.typeInfo_ = &typeInfo_;
typeInfo_.instanceSize_ = sizeof(ObjHeader);
globalConstructor_ = &constructor_; globalConstructor_ = &constructor_;
for (auto& threadData : threadDatas_) { for (auto& threadData : threadDatas_) {
@@ -38,8 +41,7 @@ public:
globalConstructor_ = nullptr; globalConstructor_ = nullptr;
// Make sure to clean everything allocated by the tests. // Make sure to clean everything allocated by the tests.
for (auto& threadData : threadDatas_) { for (auto& threadData : threadDatas_) {
threadData->objectFactoryThreadQueue().ClearForTests(); threadData->ClearForTests();
threadData->globalsThreadQueue().ClearForTests();
} }
} }
@@ -48,18 +50,18 @@ public:
testing::MockFunction<void(ObjHeader*)>& constructor() { return constructor_; } testing::MockFunction<void(ObjHeader*)>& constructor() { return constructor_; }
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, size_t threadIndex) { 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) { 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: private:
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> constructor_; testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> constructor_;
// TODO: It makes sense to somehow abstract `ThreadData` stuff away. Allocation in this case. // TODO: It makes sense to somehow abstract `ThreadData` stuff away. Allocation in this case.
std::array<KStdUniquePtr<mm::ThreadData>, kDefaultThreadCount> threadDatas_; 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_; static testing::MockFunction<void(ObjHeader*)>* globalConstructor_;
@@ -116,6 +116,11 @@ extern "C" void RestoreMemory(MemoryState*) {
// TODO: Remove when legacy MM is gone. // 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) { extern "C" RUNTIME_NOTHROW OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
RETURN_RESULT_OF(mm::AllocateObject, threadData, typeInfo); RETURN_RESULT_OF(mm::AllocateObject, threadData, typeInfo);
@@ -13,6 +13,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "GC.hpp" #include "GC.hpp"
#include "ObjectTestSupport.hpp"
#include "TestSupport.hpp" #include "TestSupport.hpp"
#include "Types.h" #include "Types.h"
@@ -745,29 +746,25 @@ public:
using ObjectFactory = mm::ObjectFactory<GC>; using ObjectFactory = mm::ObjectFactory<GC>;
KStdUniquePtr<TypeInfo> MakeObjectTypeInfo(int32_t size) { struct Payload {
auto typeInfo = make_unique<TypeInfo>(); ObjHeader* field1;
typeInfo->typeInfo_ = typeInfo.get(); ObjHeader* field2;
typeInfo->instanceSize_ = size;
return typeInfo;
}
KStdUniquePtr<TypeInfo> MakeArrayTypeInfo(int32_t elementSize) { static constexpr std::array kFields{
auto typeInfo = make_unique<TypeInfo>(); &Payload::field1,
typeInfo->typeInfo_ = typeInfo.get(); &Payload::field2,
typeInfo->instanceSize_ = -elementSize; };
return typeInfo; };
}
} // namespace } // namespace
TEST(ObjectFactoryTest, CreateObject) { TEST(ObjectFactoryTest, CreateObject) {
auto typeInfo = MakeObjectTypeInfo(24); test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
GC::ThreadData gc; GC::ThreadData gc;
ObjectFactory objectFactory; ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc); ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* object = threadQueue.CreateObject(typeInfo.get()); auto* object = threadQueue.CreateObject(type.typeInfo());
threadQueue.Publish(); threadQueue.Publish();
auto node = ObjectFactory::NodeRef::From(object); auto node = ObjectFactory::NodeRef::From(object);
@@ -782,13 +779,32 @@ TEST(ObjectFactoryTest, CreateObject) {
EXPECT_THAT(it, iter.end()); EXPECT_THAT(it, iter.end());
} }
TEST(ObjectFactoryTest, CreateArray) { TEST(ObjectFactoryTest, CreateObjectArray) {
auto typeInfo = MakeArrayTypeInfo(24);
GC::ThreadData gc; GC::ThreadData gc;
ObjectFactory objectFactory; ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc); 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(); threadQueue.Publish();
auto node = ObjectFactory::NodeRef::From(array); auto node = ObjectFactory::NodeRef::From(array);
@@ -804,15 +820,14 @@ TEST(ObjectFactoryTest, CreateArray) {
} }
TEST(ObjectFactoryTest, Erase) { TEST(ObjectFactoryTest, Erase) {
auto objectTypeInfo = MakeObjectTypeInfo(24); test_support::TypeInfoHolder objectType{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
auto arrayTypeInfo = MakeArrayTypeInfo(24);
GC::ThreadData gc; GC::ThreadData gc;
ObjectFactory objectFactory; ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc); ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
threadQueue.CreateObject(objectTypeInfo.get()); threadQueue.CreateObject(objectType.typeInfo());
threadQueue.CreateArray(arrayTypeInfo.get(), 3); threadQueue.CreateArray(theArrayTypeInfo, 3);
} }
threadQueue.Publish(); threadQueue.Publish();
@@ -839,16 +854,15 @@ TEST(ObjectFactoryTest, Erase) {
} }
TEST(ObjectFactoryTest, Move) { TEST(ObjectFactoryTest, Move) {
auto objectTypeInfo = MakeObjectTypeInfo(24); test_support::TypeInfoHolder objectType{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
auto arrayTypeInfo = MakeArrayTypeInfo(24);
GC::ThreadData gc; GC::ThreadData gc;
ObjectFactory objectFactory; ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc); ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
ObjectFactory::FinalizerQueue finalizerQueue; ObjectFactory::FinalizerQueue finalizerQueue;
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
threadQueue.CreateObject(objectTypeInfo.get()); threadQueue.CreateObject(objectType.typeInfo());
threadQueue.CreateArray(arrayTypeInfo.get(), 3); threadQueue.CreateArray(theArrayTypeInfo, 3);
} }
threadQueue.Publish(); threadQueue.Publish();
@@ -883,7 +897,7 @@ TEST(ObjectFactoryTest, Move) {
} }
TEST(ObjectFactoryTest, ConcurrentPublish) { TEST(ObjectFactoryTest, ConcurrentPublish) {
auto typeInfo = MakeObjectTypeInfo(24); test_support::TypeInfoHolder type{test_support::TypeInfoHolder::ObjectBuilder<Payload>()};
ObjectFactory objectFactory; ObjectFactory objectFactory;
constexpr int kThreadCount = kDefaultThreadCount; constexpr int kThreadCount = kDefaultThreadCount;
std::atomic<bool> canStart(false); std::atomic<bool> canStart(false);
@@ -893,10 +907,10 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
KStdVector<ObjHeader*> expected; KStdVector<ObjHeader*> expected;
for (int i = 0; i < kThreadCount; ++i) { 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; GC::ThreadData gc;
ObjectFactory::ThreadQueue threadQueue(objectFactory, 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); std::lock_guard<std::mutex> guard(expectedMutex);
expected.push_back(object); expected.push_back(object);
@@ -29,6 +29,8 @@ void RunInNewThread(F f) {
} registration; } registration;
f(registration.threadData()); f(registration.threadData());
registration.threadData().ClearForTests();
}).join(); }).join();
} }
@@ -66,6 +66,12 @@ public:
objectFactoryThreadQueue_.Publish(); objectFactoryThreadQueue_.Publish();
} }
void ClearForTests() noexcept {
globalsThreadQueue_.ClearForTests();
stableRefThreadQueue_.ClearForTests();
objectFactoryThreadQueue_.ClearForTests();
}
private: private:
const pthread_t threadId_; const pthread_t threadId_;
GlobalsRegistry::ThreadQueue globalsThreadQueue_; GlobalsRegistry::ThreadQueue globalsThreadQueue_;
@@ -5,42 +5,42 @@
#include "TestSupportCompilerGenerated.hpp" #include "TestSupportCompilerGenerated.hpp"
#include "ObjectTestSupport.hpp"
#include "Types.h" #include "Types.h"
namespace { namespace {
class TypeInfoImpl { struct EmptyPayload {
public: using Field = ObjHeader* EmptyPayload::*;
TypeInfoImpl() { type_.typeInfo_ = &type_; } static constexpr std::array<Field, 0> kFields{};
TypeInfo* type() { return &type_; }
private:
TypeInfo type_;
}; };
TypeInfoImpl theAnyTypeInfoImpl; kotlin::test_support::TypeInfoHolder theAnyTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
TypeInfoImpl theArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<ObjHeader*>()};
TypeInfoImpl theBooleanArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theBooleanArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KBoolean>()};
TypeInfoImpl theByteArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theByteArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KByte>()};
TypeInfoImpl theCharArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theCharArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KChar>()};
TypeInfoImpl theDoubleArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theDoubleArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KDouble>()};
TypeInfoImpl theFloatArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theFloatArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KFloat>()};
TypeInfoImpl theForeignObjCObjectTypeInfoImpl; kotlin::test_support::TypeInfoHolder theForeignObjCObjectTypeInfoHolder{
TypeInfoImpl theFreezableAtomicReferenceTypeInfoImpl; kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
TypeInfoImpl theIntArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theFreezableAtomicReferenceTypeInfoHolder{
TypeInfoImpl theLongArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
TypeInfoImpl theNativePtrArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theIntArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KInt>()};
TypeInfoImpl theObjCObjectWrapperTypeInfoImpl; kotlin::test_support::TypeInfoHolder theLongArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KLong>()};
TypeInfoImpl theOpaqueFunctionTypeInfoImpl; kotlin::test_support::TypeInfoHolder theNativePtrArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KNativePtr>()};
TypeInfoImpl theShortArrayTypeInfoImpl; kotlin::test_support::TypeInfoHolder theObjCObjectWrapperTypeInfoHolder{
TypeInfoImpl theStringTypeInfoImpl; kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
TypeInfoImpl theThrowableTypeInfoImpl; kotlin::test_support::TypeInfoHolder theOpaqueFunctionTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
TypeInfoImpl theUnitTypeInfoImpl; kotlin::test_support::TypeInfoHolder theShortArrayTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KShort>()};
TypeInfoImpl theWorkerBoundReferenceTypeInfoImpl; kotlin::test_support::TypeInfoHolder theStringTypeInfoHolder{kotlin::test_support::TypeInfoHolder::ArrayBuilder<KChar>()};
TypeInfoImpl theCleanerImplTypeInfoImpl; 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> template <class T>
struct KBox { struct KBox {
@@ -58,28 +58,28 @@ extern "C" {
// Set to 1 to enable runtime assertions. // Set to 1 to enable runtime assertions.
extern const int KonanNeedDebugInfo = 1; extern const int KonanNeedDebugInfo = 1;
extern const TypeInfo* theAnyTypeInfo = theAnyTypeInfoImpl.type(); extern const TypeInfo* theAnyTypeInfo = theAnyTypeInfoHolder.typeInfo();
extern const TypeInfo* theArrayTypeInfo = theArrayTypeInfoImpl.type(); extern const TypeInfo* theArrayTypeInfo = theArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theBooleanArrayTypeInfo = theBooleanArrayTypeInfoImpl.type(); extern const TypeInfo* theBooleanArrayTypeInfo = theBooleanArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theByteArrayTypeInfo = theByteArrayTypeInfoImpl.type(); extern const TypeInfo* theByteArrayTypeInfo = theByteArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theCharArrayTypeInfo = theCharArrayTypeInfoImpl.type(); extern const TypeInfo* theCharArrayTypeInfo = theCharArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theDoubleArrayTypeInfo = theDoubleArrayTypeInfoImpl.type(); extern const TypeInfo* theDoubleArrayTypeInfo = theDoubleArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theFloatArrayTypeInfo = theFloatArrayTypeInfoImpl.type(); extern const TypeInfo* theFloatArrayTypeInfo = theFloatArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theForeignObjCObjectTypeInfo = theForeignObjCObjectTypeInfoImpl.type(); extern const TypeInfo* theForeignObjCObjectTypeInfo = theForeignObjCObjectTypeInfoHolder.typeInfo();
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo = theFreezableAtomicReferenceTypeInfoImpl.type(); extern const TypeInfo* theFreezableAtomicReferenceTypeInfo = theFreezableAtomicReferenceTypeInfoHolder.typeInfo();
extern const TypeInfo* theIntArrayTypeInfo = theIntArrayTypeInfoImpl.type(); extern const TypeInfo* theIntArrayTypeInfo = theIntArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theLongArrayTypeInfo = theLongArrayTypeInfoImpl.type(); extern const TypeInfo* theLongArrayTypeInfo = theLongArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theNativePtrArrayTypeInfo = theNativePtrArrayTypeInfoImpl.type(); extern const TypeInfo* theNativePtrArrayTypeInfo = theNativePtrArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theObjCObjectWrapperTypeInfo = theObjCObjectWrapperTypeInfoImpl.type(); extern const TypeInfo* theObjCObjectWrapperTypeInfo = theObjCObjectWrapperTypeInfoHolder.typeInfo();
extern const TypeInfo* theOpaqueFunctionTypeInfo = theOpaqueFunctionTypeInfoImpl.type(); extern const TypeInfo* theOpaqueFunctionTypeInfo = theOpaqueFunctionTypeInfoHolder.typeInfo();
extern const TypeInfo* theShortArrayTypeInfo = theShortArrayTypeInfoImpl.type(); extern const TypeInfo* theShortArrayTypeInfo = theShortArrayTypeInfoHolder.typeInfo();
extern const TypeInfo* theStringTypeInfo = theStringTypeInfoImpl.type(); extern const TypeInfo* theStringTypeInfo = theStringTypeInfoHolder.typeInfo();
extern const TypeInfo* theThrowableTypeInfo = theThrowableTypeInfoImpl.type(); extern const TypeInfo* theThrowableTypeInfo = theThrowableTypeInfoHolder.typeInfo();
extern const TypeInfo* theUnitTypeInfo = theUnitTypeInfoImpl.type(); extern const TypeInfo* theUnitTypeInfo = theUnitTypeInfoHolder.typeInfo();
extern const TypeInfo* theWorkerBoundReferenceTypeInfo = theWorkerBoundReferenceTypeInfoImpl.type(); extern const TypeInfo* theWorkerBoundReferenceTypeInfo = theWorkerBoundReferenceTypeInfoHolder.typeInfo();
extern const TypeInfo* theCleanerImplTypeInfo = theCleanerImplTypeInfoImpl.type(); 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) { OBJ_GETTER0(TheEmptyString) {
RETURN_OBJ(theEmptyStringImpl.obj()); RETURN_OBJ(theEmptyStringImpl.obj());