Interop/Runtime: add support for Kotlin Native
Also add minor improvements, fix bugs and workaround missing features.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
91aab11e01
commit
49815f72e0
@@ -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) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user