diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index c5c1b258761..90f00a4e30b 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -1,5 +1,6 @@ #include +#include "Assert.h" #include "Memory.h" void FreeObject(ContainerHeader* header) { @@ -16,16 +17,28 @@ ArenaContainer::ArenaContainer(uint32_t size) { header->end_ = header->current_ + size; } -void ObjectContainer::Init(const TypeInfo* type_info, uint32_t elements) { +void ObjectContainer::Init(const TypeInfo* type_info) { + RuntimeAssert(type_info->instanceSize_ >= 0, "Must be an object"); header_ = reinterpret_cast( calloc(sizeof(ContainerHeader) + sizeof(ObjHeader) + - type_info->size_ * elements, 1)); + type_info->instanceSize_, 1)); header_->ref_count_ = CONTAINER_TAG_INCREMENT; SetMeta(GetPlace(), type_info); } +void ArrayContainer::Init(const TypeInfo* type_info, uint32_t elements) { + RuntimeAssert(type_info->instanceSize_ < 0, "Must be an array"); + header_ = reinterpret_cast( + calloc(sizeof(ContainerHeader) + sizeof(ArrayHeader) - + type_info->instanceSize_ * elements, 1)); + header_->ref_count_ = CONTAINER_TAG_INCREMENT; + GetPlace()->count_ = elements; + SetMeta(GetPlace(), type_info); +} + ObjHeader* ArenaContainer::PlaceObject(const TypeInfo* type_info) { - int size = type_info->size_ + sizeof(ObjHeader); + RuntimeAssert(type_info->instanceSize_ >= 0, "must be an object"); + uint32_t size = type_info->instanceSize_ + sizeof(ObjHeader); ObjHeader* result = reinterpret_cast(Place(size)); if (!result) { return nullptr; @@ -35,7 +48,8 @@ ObjHeader* ArenaContainer::PlaceObject(const TypeInfo* type_info) { } ArrayHeader* ArenaContainer::PlaceArray(const TypeInfo* type_info, int count) { - int size = sizeof(ArrayHeader) + type_info->size_ * count; + RuntimeAssert(type_info->instanceSize_ < 0, "must be an array"); + uint32_t size = sizeof(ArrayHeader) - type_info->instanceSize_ * count; ArrayHeader* result = reinterpret_cast(Place(size)); if (!result) { return nullptr; @@ -55,11 +69,13 @@ void InitMemory() { // Now we ignore all placement hints and always allocate heap space for new object. void* AllocInstance(const TypeInfo* type_info, PlacementHint hint) { + RuntimeAssert(type_info->instanceSize_ >= 0, "must be an object"); return ObjectContainer(type_info).GetPlace(); } void* AllocArrayInstance(const TypeInfo* type_info, PlacementHint hint, uint32_t elements) { - return ObjectContainer(type_info, elements).GetPlace(); + RuntimeAssert(type_info->instanceSize_ < 0, "must be an array"); + return ArrayContainer(type_info, elements).GetPlace(); } #ifdef __cplusplus diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index 52151bb1311..60f6a638a61 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -5,8 +5,11 @@ #include "TypeInfo.h" typedef enum { + // Allocation guaranteed to be frame local. SCOPE_FRAME = 0, + // Allocation is generic global allocation. SCOPE_GLOBAL = 1, + // Allocation shall take place in current arena. SCOPE_ARENA = 2 } PlacementHint; @@ -22,7 +25,7 @@ typedef enum { CONTAINER_TAG_INVALID = 3, // Actual value to increment/decrement conatiner by. Tag is in lower bits. CONTAINER_TAG_INCREMENT = 1 << 2, - // + // Mask for container type. CONTAINER_TAG_MASK = (CONTAINER_TAG_INCREMENT - 1) } ContainerTag; @@ -37,12 +40,13 @@ struct ObjHeader { // Header of value type array objects. struct ArrayHeader : public ObjHeader { + // Elements count. Element size is stored in instanceSize_ field of TypeInfo, negated. uint32_t count_; }; // Header of all container objects. Contains reference counter. struct ContainerHeader { - // Reference counter of container. Maybe use some upper bit of counter for + // Reference counter of container. Uses two lower bits of counter for // container type (for polymorphism in ::Release()). volatile uint32_t ref_count_; }; @@ -55,7 +59,27 @@ struct ArenaContainerHeader : public ContainerHeader { uint8_t* end_; }; -// Thos two operations are implemented by translator when storing references +inline void* AddressOfElementAt(ArrayHeader* obj, int32_t index) { + // Instance size is negative. + return reinterpret_cast(obj + 1) - obj->type_info_->instanceSize_ * index; +} + +// Optimized version not accessing type info. +inline uint8_t* ByteArrayAddressOfElementAt(ArrayHeader* obj, int32_t index) { + return reinterpret_cast(obj + 1) + index; +} + +inline const uint8_t* ByteArrayAddressOfElementAt(const ArrayHeader* obj, int32_t index) { + return reinterpret_cast(obj + 1) + index; +} + +inline uint32_t ArraySizeBytes(const ArrayHeader* obj) { + // Instance size is negative. + return -obj->type_info_->instanceSize_ * obj->count_; +} + + +// Those two operations are implemented by translator when storing references // to objects. inline void AddRef(ContainerHeader* header) { // Looking at container type we may want to skip AddRef() totally @@ -141,12 +165,9 @@ class Container { // Container for a single object. class ObjectContainer : public Container { public: + // Single instance. explicit ObjectContainer(const TypeInfo* type_info) { - Init(type_info, 1); - } - - ObjectContainer(const TypeInfo* type_info, uint32_t elements) { - Init(type_info, elements); + Init(type_info); } // Object container shalln't have any dtor, as it's being freed by ::Release(). @@ -155,10 +176,28 @@ class ObjectContainer : public Container { reinterpret_cast(header_) + sizeof(ContainerHeader)); } + private: + void Init(const TypeInfo* type_info); +}; + + +class ArrayContainer : public Container { + public: + ArrayContainer(const TypeInfo* type_info, uint32_t elements) { + Init(type_info, elements); + } + + // Array container shalln't have any dtor, as it's being freed by ::Release(). + ArrayHeader* GetPlace() const { + return reinterpret_cast( + reinterpret_cast(header_) + sizeof(ContainerHeader)); + } + private: void Init(const TypeInfo* type_info, uint32_t elements); }; + // Class representing arena-style placement container. // Container is used for reference counting, // and it is assumed that objects with related placement will share container. Only diff --git a/runtime/src/main/cpp/Natives.cpp b/runtime/src/main/cpp/Natives.cpp new file mode 100644 index 00000000000..be9f220610e --- /dev/null +++ b/runtime/src/main/cpp/Natives.cpp @@ -0,0 +1,46 @@ +#include + +#include "Assert.h" +#include "Exceptions.h" +#include "Memory.h" +#include "Natives.h" +#include "Types.h" + +extern "C" { + +// TODO: those must be compiler intrinsics afterwards. +KByte Kotlin_ByteArray_get(const ArrayHeader* obj, int32_t index) { + if (static_cast(index) >= obj->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + return *ByteArrayAddressOfElementAt(obj, index); +} + +void Kotlin_ByteArray_set(ArrayHeader* obj, int32_t index, KByte value) { + if (static_cast(index) >= obj->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + *ByteArrayAddressOfElementAt(obj, index) = value; +} + +KChar Kotlin_String_get(const ArrayHeader* obj, int32_t index) { + // TODO: support full UTF-8. + if (static_cast(index) >= obj->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + return *ByteArrayAddressOfElementAt(obj, index); +} + +ArrayHeader* Kotlin_String_fromUtf8Array(const ArrayHeader* array) { + RuntimeAssert(array->type_info_ == theByteArrayTypeInfo, "Must get a byte array"); + uint32_t length = ArraySizeBytes(array); + // TODO: support full UTF-8. + ArrayHeader* result = ArrayContainer(theStringTypeInfo, length).GetPlace(); + memcpy( + ByteArrayAddressOfElementAt(result, 0), + ByteArrayAddressOfElementAt(array, 0), + length); + return result; +} + +} diff --git a/runtime/src/main/cpp/Natives.h b/runtime/src/main/cpp/Natives.h new file mode 100644 index 00000000000..4c405a827a2 --- /dev/null +++ b/runtime/src/main/cpp/Natives.h @@ -0,0 +1,24 @@ +#ifndef RUNTIME_NATIVES_H +#define RUNTIME_NATIVES_H + +#include "Memory.h" +#include "Types.h" + +typedef uint16_t KChar; +typedef uint8_t KByte; + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: those must be compiler intrinsics afterwards. +KByte Kotlin_ByteArray_get(const ArrayHeader* obj, int32_t index); +void Kotlin_ByteArray_set(ArrayHeader* obj, int32_t index, KByte value); +KChar Kotlin_String_get(const ArrayHeader* obj, int32_t index); +ArrayHeader* Kotlin_String_fromUtf8Array(const ArrayHeader* array); + +#ifdef __cplusplus +} +#endif + +#endif // RUNTIME_NATIVES_H diff --git a/runtime/src/main/cpp/TypeInfo.h b/runtime/src/main/cpp/TypeInfo.h index 8c433221d95..52f1ad9076d 100644 --- a/runtime/src/main/cpp/TypeInfo.h +++ b/runtime/src/main/cpp/TypeInfo.h @@ -6,6 +6,8 @@ #include "Names.h" // An element of sorted by hash in-place array representing methods. +// For systems where introspection is not needed - only open methods are in +// this table. struct MethodTableRecord { MethodNameHash nameSignature_; void* methodEntryPoint_; @@ -17,11 +19,13 @@ struct FieldTableRecord { int fieldOffset_; }; -// This struct represents runtime type information and by itself is compile time +// This struct represents runtime type information and by itself is the compile time // constant. struct TypeInfo { ClassNameHash name_; - int size_; + // Negative value marks array class/string, and it is negated element size. + int32_t instanceSize_; + // Must be pointer to Any for array classes, and null for Any. const TypeInfo* superType_; const int* objOffsets_; int objOffsetsCount_; @@ -29,9 +33,9 @@ struct TypeInfo { int implementedInterfacesCount_; void* const* vtable_; // TODO: place vtable at the end of TypeInfo to eliminate the indirection const MethodTableRecord* openMethods_; - int openMethodsCount_; + uint32_t openMethodsCount_; const FieldTableRecord* fields_; - int fieldsCount_; + uint32_t fieldsCount_; }; #ifdef __cplusplus diff --git a/runtime/src/main/cpp/Types.cpp b/runtime/src/main/cpp/Types.cpp new file mode 100644 index 00000000000..9f0b8898183 --- /dev/null +++ b/runtime/src/main/cpp/Types.cpp @@ -0,0 +1,69 @@ +#include "Types.h" + +namespace { +// Use 'echo -n "kotlin.ByteArray" | shasum -| xargs ~/bin/as_array' to generate hash. + +const TypeInfo implAnyTypeInfo = { + // kotlin.Any + { 0x6a, 0x97, 0xc2, 0x61, 0x64, 0x3a, 0xc6, 0x5e, 0x2a, 0x03, + 0xf9, 0xc1, 0x4c, 0x1f, 0x66, 0x9a, 0x44, 0xb2, 0x6b, 0x11 }, + 0, // instanceSize_ + nullptr, // superType_ + nullptr, // objOffsets + 0, // objOffsetsCount_ + nullptr, // implementedInterfaces_ + 0, // implementedInterfacesCount_ + nullptr, // vtable_ + nullptr, // openMethods_ + 0, // openMethodsCount_ + nullptr, // fields_ + 0 // fieldsCount_ +}; + +const TypeInfo implByteArrayTypeInfo = { + // kotlin.ByteArray + { 0x9e, 0x23, 0xa6, 0xa6, 0x91, 0x9b, 0x6b, 0x0a, 0x00, 0xc5, + 0x35, 0xe8, 0xd9, 0xd1, 0xa3, 0xb6, 0xc5, 0xc6, 0xd7, 0x65 }, + -1, // instanceSize_, array of 1 byte elements + &implAnyTypeInfo, // superType_ + nullptr, // objOffsets + 0, // objOffsetsCount_ + // TODO: actually, shall implement cloneable, it seems. + nullptr, // implementedInterfaces_ + 0, // implementedInterfacesCount_ + nullptr, // vtable_ + nullptr, // openMethods_ + 0, // openMethodsCount_ + nullptr, // fields_ + 0 // fieldsCount_ +}; + +const TypeInfo implStringTypeInfo = { + { 0x5b, 0xac, 0x68, 0xed, 0x43, 0xd4, 0x81, 0x4a, 0x14, 0x18, + 0x10, 0xfa, 0x65, 0x09, 0xb9, 0xef, 0x20, 0x0a, 0xba, 0x78 }, // kotlin.String + -1, // instanceSize_, Strings are treated as array of 1 byte elements + &implAnyTypeInfo, // superType_ + nullptr, // objOffsets + 0, // objOffsetsCount_ + nullptr, // implementedInterfaces_ + 0, // implementedInterfacesCount_ + nullptr, // vtable_ + nullptr, // openMethods_ + 0, // openMethodsCount_ + nullptr, // fields_ + 0 // fieldsCount_ +}; + +} // namespace + +#ifdef __cplusplus +extern "C" { +#endif + +const TypeInfo* theAnyTypeInfo = &implAnyTypeInfo; +const TypeInfo* theByteArrayTypeInfo = &implByteArrayTypeInfo; +const TypeInfo* theStringTypeInfo = &implStringTypeInfo; + +#ifdef __cplusplus +} +#endif diff --git a/runtime/src/main/cpp/Types.h b/runtime/src/main/cpp/Types.h new file mode 100644 index 00000000000..d0226a505c5 --- /dev/null +++ b/runtime/src/main/cpp/Types.h @@ -0,0 +1,18 @@ +#ifndef RUNTIME_TYPES_H +#define RUNTIME_TYPES_H + +#include "TypeInfo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const TypeInfo* theAnyTypeInfo; +extern const TypeInfo* theByteArrayTypeInfo; +extern const TypeInfo* theStringTypeInfo; + +#ifdef __cplusplus +} +#endif + +#endif // RUNTIME_TYPES_H diff --git a/runtime/src/main/kotlin/kotlin_native/String.kt b/runtime/src/main/kotlin/kotlin_native/String.kt new file mode 100644 index 00000000000..9f0c08b885e --- /dev/null +++ b/runtime/src/main/kotlin/kotlin_native/String.kt @@ -0,0 +1,28 @@ +package kotlin_native + +class ByteArray(size: Int) { + public val size: Int + + external public operator fun get(index: Int): Byte + + external public operator fun set(index: Int, value: Byte): Unit +} + +class String { + companion object { + external fun fromUtf8Array(ByteArray: array) : String + } + external public operator fun plus(other: Any?): String + + public val length: Int + get() = getStringLength() + + // Can be O(N). + external public fun get(index: Int): Char + + // external public fun subSequence(startIndex: Int, endIndex: Int): CharSequence + + external public fun compareTo(other: String): Int + + external private fun getStringLength(): Int +} \ No newline at end of file