Very basic string support. (#10)
String, ByteArray support, type declarations.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#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<ContainerHeader*>(
|
||||
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<ContainerHeader*>(
|
||||
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<ObjHeader*>(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<ArrayHeader*>(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
|
||||
|
||||
@@ -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<uint8_t*>(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<uint8_t*>(obj + 1) + index;
|
||||
}
|
||||
|
||||
inline const uint8_t* ByteArrayAddressOfElementAt(const ArrayHeader* obj, int32_t index) {
|
||||
return reinterpret_cast<const uint8_t*>(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<uint8_t*>(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<ArrayHeader*>(
|
||||
reinterpret_cast<uint8_t*>(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
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <string.h>
|
||||
|
||||
#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<uint32_t>(index) >= obj->count_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
return *ByteArrayAddressOfElementAt(obj, index);
|
||||
}
|
||||
|
||||
void Kotlin_ByteArray_set(ArrayHeader* obj, int32_t index, KByte value) {
|
||||
if (static_cast<uint32_t>(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<uint32_t>(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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user