Interop/Runtime: add support for Kotlin Native

Also add minor improvements, fix bugs and workaround missing features.
This commit is contained in:
Svyatoslav Scherbina
2017-01-31 13:13:30 +07:00
committed by SvyatoslavScherbina
parent 91aab11e01
commit 49815f72e0
10 changed files with 164 additions and 48 deletions
@@ -150,9 +150,6 @@ abstract class CAdaptedFunctionTypeImpl<F : Function<*>>
private typealias UserData = (ret: COpaquePointer, args: CArray<COpaquePointerVar>)->Unit
inline fun <reified T : CAdaptedFunctionTypeImpl<*>> CAdaptedFunctionTypeImpl.Companion.of(): T =
T::class.objectInstance!!
private fun loadCallbacksLibrary() {
System.loadLibrary("callbacks")
}
@@ -38,12 +38,12 @@ object nativeMemUtils {
fun getDouble(mem: NativePointed) = unsafe.getDouble(mem.address)
fun putDouble(mem: NativePointed, value: Double) = unsafe.putDouble(mem.address, value)
fun getPtr(mem: NativePointed): NativePtr = when (dataModel) {
fun getNativePtr(mem: NativePointed): NativePtr = when (dataModel) {
DataModel._32BIT -> getInt(mem).toLong()
DataModel._64BIT -> getLong(mem)
}
fun putPtr(mem: NativePointed, value: NativePtr) = when (dataModel) {
fun putNativePtr(mem: NativePointed, value: NativePtr) = when (dataModel) {
DataModel._32BIT -> putInt(mem, value.toInt())
DataModel._64BIT -> putLong(mem, value)
}
@@ -8,7 +8,7 @@ val nativeNullPtr: NativePtr = 0L
// TODO: the functions below should eventually be intrinsified
inline fun <reified T : CVariable> CVariable.Type.Companion.of() = T::class.companionObjectInstance as CVariable.Type
inline fun <reified T : CVariable> typeOf() = T::class.companionObjectInstance as CVariable.Type
/**
* Returns interpretation of entity with given pointer.
@@ -27,4 +27,6 @@ inline fun <reified T : NativePointed> interpretPointed(ptr: NativePtr): T {
}
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
T::class.objectInstance!!
T::class.objectInstance!!
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=0x%x)".format(rawValue)
@@ -0,0 +1,4 @@
package kotlinx.cinterop
internal fun decodeFromUtf8(bytes: ByteArray) = String(bytes)
internal fun encodeToUtf8(str: String) = str.toByteArray()
@@ -83,9 +83,7 @@ class CPointer<T : CPointed> private constructor(val rawValue: NativePtr) {
return rawValue.hashCode()
}
override fun toString(): String {
return "CPointer(raw=0x%x)".format(rawValue)
}
override fun toString() = this.cPointerToString()
}
/**
@@ -141,18 +139,15 @@ interface CVariable : CPointed {
open class Type(val size: Long, val align: Int) {
init {
assert (size % align == 0L)
require(size % align == 0L)
}
companion object
}
companion object {
inline fun <reified T : CVariable> sizeOf() = Type.of<T>().size
inline fun <reified T : CVariable> alignOf() = Type.of<T>().align
}
}
inline fun <reified T : CVariable> sizeOf() = typeOf<T>().size
inline fun <reified T : CVariable> alignOf() = typeOf<T>().align
/**
* The C data which is composed from several members.
*/
@@ -177,7 +172,7 @@ abstract class CStructVar : CVariable, CAggregate {
*/
sealed class CPrimitiveVar : CVariable {
// aligning by size is obviously enough
open class Type(size: Int, align: Int = size) : CVariable.Type(size.toLong(), align)
open class Type(size: Int) : CVariable.Type(size.toLong(), align = size)
}
abstract class CEnumVar : CPrimitiveVar()
@@ -256,8 +251,8 @@ typealias CPointerVar<T> = CPointerVarWithValueMappedTo<CPointer<T>>
* The value of this variable.
*/
inline var <reified P : CPointer<*>> CPointerVarWithValueMappedTo<P>.value: P?
get() = CPointer.createNullable<CPointed>(nativeMemUtils.getPtr(this)) as P?
set(value) = nativeMemUtils.putPtr(this, value.rawValue)
get() = CPointer.createNullable<CPointed>(nativeMemUtils.getNativePtr(this)) as P?
set(value) = nativeMemUtils.putNativePtr(this, value.rawValue)
/**
* The code or data pointed by the value of this variable.
@@ -275,7 +270,7 @@ class CArray<T : CVariable>(override val rawPtr: NativePtr) : CAggregate
inline fun <reified T : CVariable> CArray<T>.elementOffset(index: Long) = if (index == 0L) {
0L // optimization for JVM impl which uses reflection for now.
} else {
index * CVariable.sizeOf<T>()
index * sizeOf<T>()
}
inline operator fun <reified T : CVariable> CArray<T>.get(index: Long): T = memberAt(elementOffset(index))
@@ -20,22 +20,22 @@ object nativeHeap : NativeFreeablePlacement {
// TODO: implement optimally
class Arena(private val parent: NativeFreeablePlacement = nativeHeap) : NativePlacement {
private val allocatedChunks = mutableListOf<NativePointed>()
private val allocatedChunks = ArrayList<NativePointed>()
override fun alloc(size: Long, align: Int): NativePointed {
val res = nativeHeap.alloc(size, align)
val res = parent.alloc(size, align)
try {
allocatedChunks.add(res)
return res
} catch (e: Throwable) {
nativeHeap.free(res)
parent.free(res)
throw e
}
}
fun clear() {
allocatedChunks.forEach {
nativeHeap.free(it)
parent.free(it)
}
allocatedChunks.clear()
@@ -51,7 +51,7 @@ fun NativePlacement.alloc(size: Int, align: Int) = alloc(size.toLong(), align)
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.alloc(): T =
alloc(CVariable.sizeOf<T>(), CVariable.alignOf<T>()).reinterpret()
alloc(sizeOf<T>(), alignOf<T>()).reinterpret()
/**
* Allocates C array of given elements type and length.
@@ -59,7 +59,7 @@ inline fun <reified T : CVariable> NativePlacement.alloc(): T =
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long): CArray<T> =
alloc(CVariable.sizeOf<T>() * length, CVariable.alignOf<T>()).reinterpret()
alloc(sizeOf<T>() * length, alignOf<T>()).reinterpret()
/**
* Allocates C array of given elements type and length.
@@ -78,7 +78,7 @@ inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long,
initializer: T.(index: Long)->Unit): CArray<T> {
val res = allocArray<T>(length)
(0 until length).forEach { index ->
(0 .. length - 1).forEach { index ->
res[index].initializer(index)
}
@@ -112,7 +112,7 @@ fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(elements: List<T?>): C
* Allocates C array of pointers to given elements.
*/
fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(vararg elements: T?) =
allocArrayOfPointersTo(elements.toList())
allocArrayOfPointersTo(listOf(*elements))
/**
* Allocates C array of given values.
@@ -120,7 +120,7 @@ fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(vararg elements: T?) =
inline fun <reified T : CPointer<*>>
NativePlacement.allocArrayOf(vararg elements: T?): CArray<CPointerVarWithValueMappedTo<T>> {
return allocArrayOf(elements.toList())
return allocArrayOf(listOf(*elements))
}
/**
@@ -139,8 +139,9 @@ inline fun <reified T : CPointer<*>>
fun NativePlacement.allocArrayOf(elements: ByteArray): CArray<CInt8Var> {
val res = allocArray<CInt8Var>(elements.size)
elements.forEachIndexed { i, byte ->
res[i].value = byte
var index = 0
for (byte in elements) {
res[index++].value = byte
}
return res
}
@@ -173,7 +174,7 @@ class CString private constructor(override val rawPtr: NativePtr) : CPointed {
val bytes = ByteArray(len)
nativeMemUtils.getByteArray(array[0], bytes, len)
return String(bytes) // TODO: encoding
return decodeFromUtf8(bytes) // TODO: encoding
}
fun asCharPtr() = reinterpret<CInt8Var>()
@@ -184,7 +185,7 @@ fun CString.Companion.fromString(str: String?, placement: NativePlacement): CStr
return null
}
val bytes = str.toByteArray() // TODO: encoding
val bytes = encodeToUtf8(str) // TODO: encoding
val len = bytes.size
val nativeBytes = nativeHeap.allocArray<CInt8Var>(len + 1)
@@ -197,20 +198,16 @@ fun CString.Companion.fromString(str: String?, placement: NativePlacement): CStr
fun CPointer<CInt8Var>.asCString() = CString.fromArray(this.reinterpret<CArray<CInt8Var>>().pointed)
fun String.toCString(placement: NativePlacement) = CString.fromString(this, placement)
class MemScope private constructor(private val arena: Arena) : NativePlacement by arena {
class MemScope : NativePlacement {
private val arena = Arena()
override fun alloc(size: Long, align: Int) = arena.alloc(size, align)
fun clear() = arena.clear()
val memScope: NativePlacement
get() = this
companion object {
internal inline fun <R> use(block: MemScope.()->R): R {
val memScope = MemScope(Arena())
try {
return memScope.block()
} finally {
memScope.arena.clear()
}
}
}
}
/**
@@ -218,6 +215,10 @@ class MemScope private constructor(private val arena: Arena) : NativePlacement b
* which will be automatically disposed at the end of this scope.
*/
inline fun <R> memScoped(block: MemScope.()->R): R {
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") // TODO: it is a hack
return MemScope.use(block)
val memScope = MemScope()
try {
return memScope.block()
} finally {
memScope.clear()
}
}
@@ -0,0 +1,66 @@
package kotlinx.cinterop
import konan.internal.Intrinsic
internal inline val pointerSize: Int
get() = getPointerSize()
@Intrinsic external fun getPointerSize(): Int
// TODO: do not use singleton because it leads to init-check on any access.
object nativeMemUtils {
@Intrinsic external fun getByte(mem: NativePointed): Byte
@Intrinsic external fun putByte(mem: NativePointed, value: Byte)
@Intrinsic external fun getShort(mem: NativePointed): Short
@Intrinsic external fun putShort(mem: NativePointed, value: Short)
@Intrinsic external fun getInt(mem: NativePointed): Int
@Intrinsic external fun putInt(mem: NativePointed, value: Int)
@Intrinsic external fun getLong(mem: NativePointed): Long
@Intrinsic external fun putLong(mem: NativePointed, value: Long)
@Intrinsic external fun getFloat(mem: NativePointed): Float
@Intrinsic external fun putFloat(mem: NativePointed, value: Float)
@Intrinsic external fun getDouble(mem: NativePointed): Double
@Intrinsic external fun putDouble(mem: NativePointed, value: Double)
@Intrinsic external fun getNativePtr(mem: NativePointed): NativePtr
@Intrinsic external fun putNativePtr(mem: NativePointed, value: NativePtr)
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
val sourceArray: CArray<CInt8Var> = source.reinterpret()
for (index in 0 .. length - 1) {
dest[index] = sourceArray[index].value
}
}
fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) {
val destArray: CArray<CInt8Var> = dest.reinterpret()
for (index in 0 .. length - 1) {
destArray[index].value = source[index]
}
}
private class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
fun alloc(size: Long, align: Int): NativePointed {
val ptr = malloc(size, align)
if (ptr == nativeNullPtr) {
throw OutOfMemoryError("unable to allocate native memory")
}
return NativeAllocated(ptr)
}
fun free(mem: NativePointed) {
free(mem.rawPtr)
}
}
@SymbolName("Kotlin_interop_malloc")
private external fun malloc(size: Long, align: Int): NativePtr
@SymbolName("Kotlin_interop_free")
private external fun free(ptr: NativePtr)
@@ -0,0 +1,26 @@
package kotlinx.cinterop
import konan.internal.Intrinsic
class NativePtr private constructor() {
@Intrinsic external operator fun plus(offset: Long): NativePtr
}
inline val nativeNullPtr: NativePtr
get() = getNativeNullPtr()
@Intrinsic external fun getNativeNullPtr(): NativePtr
fun <T : CVariable> typeOf(): CVariable.Type = throw Error("typeOf() is called with erased argument")
/**
* Returns interpretation of entity with given pointer.
*
* @param T must not be abstract
*/
@Intrinsic external fun <T : NativePointed> interpretPointed(ptr: NativePtr): T
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
TODO("CAdaptedFunctionType.getInstanceOf")
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=$rawValue)"
@@ -0,0 +1,16 @@
package kotlinx.cinterop
internal fun decodeFromUtf8(bytes: ByteArray): String = kotlin.text.fromUtf8Array(bytes, 0, bytes.size)
fun encodeToUtf8(str: String): ByteArray {
val result = ByteArray(str.length)
for (index in 0 .. str.length - 1) {
val char = str[index]
if (char.toInt() >= 128) {
TODO("non-ASCII char")
}
result[index] = char.toByte()
}
return result
}
@@ -154,3 +154,12 @@ public class NoWhenBranchMatchedException : RuntimeException {
constructor(s: String) : super(s) {
}
}
public class OutOfMemoryError : Error {
constructor() : super() {
}
constructor(s: String) : super(s) {
}
}