Simplify interop stubs and runtime. (#2627)

This commit is contained in:
Nikolay Igotti
2019-02-12 12:33:45 +03:00
committed by GitHub
parent 550163cc7b
commit 072f0fb9c4
17 changed files with 336 additions and 257 deletions
@@ -68,33 +68,30 @@ internal object nativeMemUtils {
}
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
val clazz = ByteArray::class.java
val baseOffset = unsafe.arrayBaseOffset(clazz).toLong();
unsafe.copyMemory(null, source.address, dest, baseOffset, length.toLong())
unsafe.copyMemory(null, source.address, dest, byteArrayBaseOffset, length.toLong())
}
fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) {
val clazz = ByteArray::class.java
val baseOffset = unsafe.arrayBaseOffset(clazz).toLong();
unsafe.copyMemory(source, baseOffset, null, dest.address, length.toLong())
unsafe.copyMemory(source, byteArrayBaseOffset, null, dest.address, length.toLong())
}
fun getCharArray(source: NativePointed, dest: CharArray, length: Int) {
val clazz = CharArray::class.java
val baseOffset = unsafe.arrayBaseOffset(clazz).toLong();
unsafe.copyMemory(null, source.address, dest, baseOffset, length.toLong() * 2)
unsafe.copyMemory(null, source.address, dest, charArrayBaseOffset, length.toLong() * 2)
}
fun putCharArray(source: CharArray, dest: NativePointed, length: Int) {
val clazz = CharArray::class.java
val baseOffset = unsafe.arrayBaseOffset(clazz).toLong();
unsafe.copyMemory(source, baseOffset, null, dest.address, length.toLong() * 2)
unsafe.copyMemory(source, charArrayBaseOffset, null, dest.address, length.toLong() * 2)
}
fun zeroMemory(dest: NativePointed, length: Int): Unit = unsafe.setMemory(dest.address, length.toLong(), 0)
fun zeroMemory(dest: NativePointed, length: Int): Unit =
unsafe.setMemory(dest.address, length.toLong(), 0)
fun copyMemory(dest: NativePointed, length: Int, src: NativePointed) =
unsafe.copyMemory(src.address, dest.address, length.toLong())
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
inline fun<reified T> allocateInstance(): T {
inline fun <reified T> allocateInstance(): T {
return unsafe.allocateInstance(T::class.java) as T
}
@@ -115,4 +112,7 @@ internal object nativeMemUtils {
isAccessible = true
return@with this.get(null) as Unsafe
}
private val byteArrayBaseOffset = unsafe.arrayBaseOffset(ByteArray::class.java).toLong()
private val charArrayBaseOffset = unsafe.arrayBaseOffset(CharArray::class.java).toLong()
}
@@ -23,7 +23,8 @@ typealias NativePtr = Long
internal typealias NonNullNativePtr = NativePtr
@PublishedApi internal fun NonNullNativePtr.toNativePtr() = this
internal fun NativePtr.toNonNull(): NonNullNativePtr = this
val nativeNullPtr: NativePtr = 0L
public val nativeNullPtr: NativePtr = 0L
// TODO: the functions below should eventually be intrinsified
@@ -82,4 +82,4 @@ inline fun <reified R : Number> Long.narrow(): R = when (R::class.java) {
inline fun <reified R : Number> Number.invalidNarrowing(): R {
throw Error("unable to narrow ${this.javaClass.simpleName} \"${this}\" to ${R::class.java.simpleName}")
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
* Copyright 2010-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,13 +24,13 @@ package kotlinx.cinterop
*
* TODO: the behavior of [equals], [hashCode] and [toString] differs on Native and JVM backends.
*/
open class NativePointed internal constructor(rawPtr: NonNullNativePtr) {
public open class NativePointed internal constructor(rawPtr: NonNullNativePtr) {
var rawPtr = rawPtr.toNativePtr()
internal set
}
// `null` value of `NativePointed?` is mapped to `nativeNullPtr`.
val NativePointed?.rawPtr: NativePtr
public val NativePointed?.rawPtr: NativePtr
get() = if (this != null) this.rawPtr else nativeNullPtr
/**
@@ -38,22 +38,22 @@ val NativePointed?.rawPtr: NativePtr
*
* @param T must not be abstract
*/
inline fun <reified T : NativePointed> interpretPointed(ptr: NativePtr): T = interpretNullablePointed<T>(ptr)!!
public inline fun <reified T : NativePointed> interpretPointed(ptr: NativePtr): T = interpretNullablePointed<T>(ptr)!!
private class OpaqueNativePointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
fun interpretOpaquePointed(ptr: NativePtr): NativePointed = interpretPointed<OpaqueNativePointed>(ptr)
fun interpretNullableOpaquePointed(ptr: NativePtr): NativePointed? = interpretNullablePointed<OpaqueNativePointed>(ptr)
public fun interpretOpaquePointed(ptr: NativePtr): NativePointed = interpretPointed<OpaqueNativePointed>(ptr)
public fun interpretNullableOpaquePointed(ptr: NativePtr): NativePointed? = interpretNullablePointed<OpaqueNativePointed>(ptr)
/**
* Changes the interpretation of the pointed data or code.
*/
inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpretPointed(this.rawPtr)
public inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpretPointed(this.rawPtr)
/**
* C data or code.
*/
abstract class CPointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
public abstract class CPointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
/**
* Represents a reference to (possibly empty) sequence of C values.
@@ -67,26 +67,28 @@ abstract class CPointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
* There are also other implementations of [CValuesRef] that provide temporary pointer,
* e.g. Kotlin Native specific [refTo] functions to pass primitive arrays directly to native.
*/
abstract class CValuesRef<T : CPointed> {
public abstract class CValuesRef<T : CPointed> {
/**
* If this reference is [CPointer], returns this pointer.
* Otherwise copies the referenced values to [placement] and returns the pointer to the copy.
* If this reference is [CPointer], returns this pointer, otherwise
* allocate storage value in the scope and return it.
*/
abstract fun getPointer(scope: AutofreeScope): CPointer<T>
public abstract fun getPointer(scope: AutofreeScope): CPointer<T>
}
/**
* The (possibly empty) sequence of immutable C values.
* It is self-contained and doesn't depend on native memory.
*/
abstract class CValues<T : CVariable> : CValuesRef<T>() {
public abstract class CValues<T : CVariable> : CValuesRef<T>() {
/**
* Copies the values to [placement] and returns the pointer to the copy.
*/
abstract override fun getPointer(scope: AutofreeScope): CPointer<T>
public override fun getPointer(scope: AutofreeScope): CPointer<T> {
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
// TODO: optimize
override fun equals(other: Any?): Boolean {
public override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is CValues<*>) return false
@@ -106,7 +108,7 @@ abstract class CValues<T : CVariable> : CValuesRef<T>() {
return true
}
override fun hashCode(): Int {
public override fun hashCode(): Int {
var result = 0
for (byte in this.getBytes()) {
result = result * 31 + byte
@@ -114,10 +116,17 @@ abstract class CValues<T : CVariable> : CValuesRef<T>() {
return result
}
abstract val size: Int
public abstract val size: Int
public abstract val align: Int
/**
* Copy the referenced values to [placement] and return placement pointer.
*/
public abstract fun place(placement: CPointer<T>): CPointer<T>
}
fun <T : CVariable> CValues<T>.placeTo(scope: AutofreeScope) = this.getPointer(scope)
public fun <T : CVariable> CValues<T>.placeTo(scope: AutofreeScope) = this.getPointer(scope)
/**
* The single immutable C value.
@@ -125,18 +134,18 @@ fun <T : CVariable> CValues<T>.placeTo(scope: AutofreeScope) = this.getPointer(s
*
* TODO: consider providing an adapter instead of subtyping [CValues].
*/
abstract class CValue<T : CVariable> : CValues<T>()
public abstract class CValue<T : CVariable> : CValues<T>()
/**
* C pointer.
*/
class CPointer<T : CPointed> internal constructor(@PublishedApi internal val value: NonNullNativePtr) : CValuesRef<T>() {
public class CPointer<T : CPointed> internal constructor(@PublishedApi internal val value: NonNullNativePtr) : CValuesRef<T>() {
// TODO: replace by [value].
@Suppress("NOTHING_TO_INLINE")
inline val rawValue: NativePtr get() = value.toNativePtr()
public inline val rawValue: NativePtr get() = value.toNativePtr()
override fun equals(other: Any?): Boolean {
public override fun equals(other: Any?): Boolean {
if (this === other) {
return true // fast path
}
@@ -144,19 +153,19 @@ class CPointer<T : CPointed> internal constructor(@PublishedApi internal val val
return (other is CPointer<*>) && (rawValue == other.rawValue)
}
override fun hashCode(): Int {
public override fun hashCode(): Int {
return rawValue.hashCode()
}
override fun toString() = this.cPointerToString()
public override fun toString() = this.cPointerToString()
override fun getPointer(scope: AutofreeScope) = this
public override fun getPointer(scope: AutofreeScope) = this
}
/**
* Returns the pointer to this data or code.
*/
val <T : CPointed> T.ptr: CPointer<T>
public val <T : CPointed> T.ptr: CPointer<T>
get() = interpretCPointer(this.rawPtr)!!
/**
@@ -164,33 +173,33 @@ val <T : CPointed> T.ptr: CPointer<T>
*
* @param T must not be abstract
*/
inline val <reified T : CPointed> CPointer<T>.pointed: T
public inline val <reified T : CPointed> CPointer<T>.pointed: T
get() = interpretPointed<T>(this.rawValue)
// `null` value of `CPointer?` is mapped to `nativeNullPtr`
val CPointer<*>?.rawValue: NativePtr
public val CPointer<*>?.rawValue: NativePtr
get() = if (this != null) this.rawValue else nativeNullPtr
fun <T : CPointed> CPointer<*>.reinterpret(): CPointer<T> = interpretCPointer(this.rawValue)!!
public fun <T : CPointed> CPointer<*>.reinterpret(): CPointer<T> = interpretCPointer(this.rawValue)!!
fun <T : CPointed> CPointer<T>?.toLong() = this.rawValue.toLong()
public fun <T : CPointed> CPointer<T>?.toLong() = this.rawValue.toLong()
fun <T : CPointed> Long.toCPointer(): CPointer<T>? = interpretCPointer(nativeNullPtr + this)
public fun <T : CPointed> Long.toCPointer(): CPointer<T>? = interpretCPointer(nativeNullPtr + this)
/**
* The [CPointed] without any specified interpretation.
*/
abstract class COpaque(rawPtr: NativePtr) : CPointed(rawPtr) // TODO: should it correspond to COpaquePointer?
public abstract class COpaque(rawPtr: NativePtr) : CPointed(rawPtr) // TODO: should it correspond to COpaquePointer?
/**
* The pointer with an opaque type.
*/
typealias COpaquePointer = CPointer<out CPointed> // FIXME
public typealias COpaquePointer = CPointer<out CPointed> // FIXME
/**
* The variable containing a [COpaquePointer].
*/
typealias COpaquePointerVar = CPointerVarOf<COpaquePointer>
public typealias COpaquePointerVar = CPointerVarOf<COpaquePointer>
/**
* The C data variable located in memory.
@@ -198,7 +207,7 @@ typealias COpaquePointerVar = CPointerVarOf<COpaquePointer>
* The non-abstract subclasses should represent the (complete) C data type and thus specify size and alignment.
* Each such subclass must have a companion object which is a [Type].
*/
abstract class CVariable(rawPtr: NativePtr) : CPointed(rawPtr) {
public abstract class CVariable(rawPtr: NativePtr) : CPointed(rawPtr) {
/**
* The (complete) C data type.
@@ -207,7 +216,7 @@ abstract class CVariable(rawPtr: NativePtr) : CPointed(rawPtr) {
* @param align the alignments in bytes that is enough for this data type.
* It may be greater than actually required for simplicity.
*/
open class Type(val size: Long, val align: Int) {
public open class Type(val size: Long, val align: Int) {
init {
require(size % align == 0L)
@@ -216,24 +225,24 @@ abstract class CVariable(rawPtr: NativePtr) : CPointed(rawPtr) {
}
}
inline fun <reified T : CVariable> sizeOf() = typeOf<T>().size
inline fun <reified T : CVariable> alignOf() = typeOf<T>().align
public inline fun <reified T : CVariable> sizeOf() = typeOf<T>().size
public inline fun <reified T : CVariable> alignOf() = typeOf<T>().align
/**
* Returns the member of this [CStructVar] which is located by given offset in bytes.
*/
inline fun <reified T : CPointed> CStructVar.memberAt(offset: Long): T {
public inline fun <reified T : CPointed> CStructVar.memberAt(offset: Long): T {
return interpretPointed<T>(this.rawPtr + offset)
}
inline fun <reified T : CVariable> CStructVar.arrayMemberAt(offset: Long): CArrayPointer<T> {
public inline fun <reified T : CVariable> CStructVar.arrayMemberAt(offset: Long): CArrayPointer<T> {
return interpretCPointer<T>(this.rawPtr + offset)!!
}
/**
* The C struct-typed variable located in memory.
*/
abstract class CStructVar(rawPtr: NativePtr) : CVariable(rawPtr) {
public abstract class CStructVar(rawPtr: NativePtr) : CVariable(rawPtr) {
open class Type(size: Long, align: Int) : CVariable.Type(size, align)
}
@@ -245,83 +254,84 @@ sealed class CPrimitiveVar(rawPtr: NativePtr) : CVariable(rawPtr) {
open class Type(size: Int) : CVariable.Type(size.toLong(), align = size)
}
interface CEnum {
val value: Any
public interface CEnum {
public val value: Any
}
abstract class CEnumVar(rawPtr: NativePtr) : CPrimitiveVar(rawPtr)
public abstract class CEnumVar(rawPtr: NativePtr) : CPrimitiveVar(rawPtr)
// generics below are used for typedef support
// these classes are not supposed to be used directly, instead the typealiases are provided.
@Suppress("FINAL_UPPER_BOUND")
class BooleanVarOf<T : Boolean>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class BooleanVarOf<T : Boolean>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(1)
}
@Suppress("FINAL_UPPER_BOUND")
class ByteVarOf<T : Byte>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class ByteVarOf<T : Byte>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(1)
}
@Suppress("FINAL_UPPER_BOUND")
class ShortVarOf<T : Short>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class ShortVarOf<T : Short>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(2)
}
@Suppress("FINAL_UPPER_BOUND")
class IntVarOf<T : Int>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class IntVarOf<T : Int>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(4)
}
@Suppress("FINAL_UPPER_BOUND")
class LongVarOf<T : Long>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class LongVarOf<T : Long>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(8)
}
@Suppress("FINAL_UPPER_BOUND")
class UByteVarOf<T : UByte>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class UByteVarOf<T : UByte>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(1)
}
@Suppress("FINAL_UPPER_BOUND")
class UShortVarOf<T : UShort>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class UShortVarOf<T : UShort>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(2)
}
@Suppress("FINAL_UPPER_BOUND")
class UIntVarOf<T : UInt>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class UIntVarOf<T : UInt>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(4)
}
@Suppress("FINAL_UPPER_BOUND")
class ULongVarOf<T : ULong>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class ULongVarOf<T : ULong>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(8)
}
@Suppress("FINAL_UPPER_BOUND")
class FloatVarOf<T : Float>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class FloatVarOf<T : Float>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(4)
}
@Suppress("FINAL_UPPER_BOUND")
class DoubleVarOf<T : Double>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
public class DoubleVarOf<T : Double>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(8)
}
typealias BooleanVar = BooleanVarOf<Boolean>
typealias ByteVar = ByteVarOf<Byte>
typealias ShortVar = ShortVarOf<Short>
typealias IntVar = IntVarOf<Int>
typealias LongVar = LongVarOf<Long>
typealias UByteVar = UByteVarOf<UByte>
typealias UShortVar = UShortVarOf<UShort>
typealias UIntVar = UIntVarOf<UInt>
typealias ULongVar = ULongVarOf<ULong>
typealias FloatVar = FloatVarOf<Float>
typealias DoubleVar = DoubleVarOf<Double>
public typealias BooleanVar = BooleanVarOf<Boolean>
public typealias ByteVar = ByteVarOf<Byte>
public typealias ShortVar = ShortVarOf<Short>
public typealias IntVar = IntVarOf<Int>
public typealias LongVar = LongVarOf<Long>
public typealias UByteVar = UByteVarOf<UByte>
public typealias UShortVar = UShortVarOf<UShort>
public typealias UIntVar = UIntVarOf<UInt>
public typealias ULongVar = ULongVarOf<ULong>
public typealias FloatVar = FloatVarOf<Float>
public typealias DoubleVar = DoubleVarOf<Double>
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : Boolean> BooleanVarOf<T>.value: T
public var <T : Boolean> BooleanVarOf<T>.value: T
get() {
val byte = nativeMemUtils.getByte(this)
return byte.toBoolean() as T
@@ -329,78 +339,78 @@ var <T : Boolean> BooleanVarOf<T>.value: T
set(value) = nativeMemUtils.putByte(this, value.toByte())
@Suppress("NOTHING_TO_INLINE")
inline fun Boolean.toByte(): Byte = if (this) 1 else 0
public inline fun Boolean.toByte(): Byte = if (this) 1 else 0
@Suppress("NOTHING_TO_INLINE")
inline fun Byte.toBoolean() = (this - 0 != 0)
public inline fun Byte.toBoolean() = (this.toInt() != 0)
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : Byte> ByteVarOf<T>.value: T
public var <T : Byte> ByteVarOf<T>.value: T
get() = nativeMemUtils.getByte(this) as T
set(value) = nativeMemUtils.putByte(this, value)
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : Short> ShortVarOf<T>.value: T
public var <T : Short> ShortVarOf<T>.value: T
get() = nativeMemUtils.getShort(this) as T
set(value) = nativeMemUtils.putShort(this, value)
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : Int> IntVarOf<T>.value: T
public var <T : Int> IntVarOf<T>.value: T
get() = nativeMemUtils.getInt(this) as T
set(value) = nativeMemUtils.putInt(this, value)
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : Long> LongVarOf<T>.value: T
public var <T : Long> LongVarOf<T>.value: T
get() = nativeMemUtils.getLong(this) as T
set(value) = nativeMemUtils.putLong(this, value)
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : UByte> UByteVarOf<T>.value: T
public var <T : UByte> UByteVarOf<T>.value: T
get() = nativeMemUtils.getByte(this).toUByte() as T
set(value) = nativeMemUtils.putByte(this, value.toByte())
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : UShort> UShortVarOf<T>.value: T
public var <T : UShort> UShortVarOf<T>.value: T
get() = nativeMemUtils.getShort(this).toUShort() as T
set(value) = nativeMemUtils.putShort(this, value.toShort())
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : UInt> UIntVarOf<T>.value: T
public var <T : UInt> UIntVarOf<T>.value: T
get() = nativeMemUtils.getInt(this).toUInt() as T
set(value) = nativeMemUtils.putInt(this, value.toInt())
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : ULong> ULongVarOf<T>.value: T
public var <T : ULong> ULongVarOf<T>.value: T
get() = nativeMemUtils.getLong(this).toULong() as T
set(value) = nativeMemUtils.putLong(this, value.toLong())
// TODO: ensure native floats have the appropriate binary representation
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : Float> FloatVarOf<T>.value: T
public var <T : Float> FloatVarOf<T>.value: T
get() = nativeMemUtils.getFloat(this) as T
set(value) = nativeMemUtils.putFloat(this, value)
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
var <T : Double> DoubleVarOf<T>.value: T
public var <T : Double> DoubleVarOf<T>.value: T
get() = nativeMemUtils.getDouble(this) as T
set(value) = nativeMemUtils.putDouble(this, value)
class CPointerVarOf<T : CPointer<*>>(rawPtr: NativePtr) : CVariable(rawPtr) {
public class CPointerVarOf<T : CPointer<*>>(rawPtr: NativePtr) : CVariable(rawPtr) {
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
}
/**
* The C data variable containing the pointer to `T`.
*/
typealias CPointerVar<T> = CPointerVarOf<CPointer<T>>
public typealias CPointerVar<T> = CPointerVarOf<CPointer<T>>
/**
* The value of this variable.
*/
@Suppress("UNCHECKED_CAST")
inline var <P : CPointer<*>> CPointerVarOf<P>.value: P?
public inline var <P : CPointer<*>> CPointerVarOf<P>.value: P?
get() = interpretCPointer<CPointed>(nativeMemUtils.getNativePtr(this)) as P?
set(value) = nativeMemUtils.putNativePtr(this, value.rawValue)
@@ -409,13 +419,13 @@ inline var <P : CPointer<*>> CPointerVarOf<P>.value: P?
*
* @param T must not be abstract
*/
inline var <reified T : CPointed, reified P : CPointer<T>> CPointerVarOf<P>.pointed: T?
public inline var <reified T : CPointed, reified P : CPointer<T>> CPointerVarOf<P>.pointed: T?
get() = this.value?.pointed
set(value) {
this.value = value?.ptr as P?
}
inline operator fun <reified T : CVariable> CPointer<T>.get(index: Long): T {
public inline operator fun <reified T : CVariable> CPointer<T>.get(index: Long): T {
val offset = if (index == 0L) {
0L // optimization for JVM impl which uses reflection for now.
} else {
@@ -424,40 +434,40 @@ inline operator fun <reified T : CVariable> CPointer<T>.get(index: Long): T {
return interpretPointed(this.rawValue + offset)
}
inline operator fun <reified T : CVariable> CPointer<T>.get(index: Int): T = this.get(index.toLong())
public inline operator fun <reified T : CVariable> CPointer<T>.get(index: Int): T = this.get(index.toLong())
@Suppress("NOTHING_TO_INLINE")
@JvmName("plus\$CPointer")
inline operator fun <T : CPointerVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
public inline operator fun <T : CPointerVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
interpretCPointer(this.rawValue + index * pointerSize)
@Suppress("NOTHING_TO_INLINE")
@JvmName("plus\$CPointer")
inline operator fun <T : CPointerVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
public inline operator fun <T : CPointerVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong()
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.get(index: Int): T? =
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.get(index: Int): T? =
(this + index)!!.pointed.value
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Int, value: T?) {
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Int, value: T?) {
(this + index)!!.pointed.value = value
}
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.get(index: Long): T? =
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.get(index: Long): T? =
(this + index)!!.pointed.value
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Long, value: T?) {
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Long, value: T?) {
(this + index)!!.pointed.value = value
}
typealias CArrayPointer<T> = CPointer<T>
typealias CArrayPointerVar<T> = CPointerVar<T>
public typealias CArrayPointer<T> = CPointer<T>
public typealias CArrayPointerVar<T> = CPointerVar<T>
/**
* The C function.
*/
class CFunction<T : Function<*>>(rawPtr: NativePtr) : CPointed(rawPtr)
public class CFunction<T : Function<*>>(rawPtr: NativePtr) : CPointed(rawPtr)
@@ -16,21 +16,21 @@
package kotlinx.cinterop
interface NativePlacement {
public interface NativePlacement {
fun alloc(size: Long, align: Int): NativePointed
public fun alloc(size: Long, align: Int): NativePointed
fun alloc(size: Int, align: Int) = alloc(size.toLong(), align)
public fun alloc(size: Int, align: Int): NativePointed = alloc(size.toLong(), align)
}
interface NativeFreeablePlacement : NativePlacement {
fun free(mem: NativePtr)
public interface NativeFreeablePlacement : NativePlacement {
public fun free(mem: NativePtr)
}
fun NativeFreeablePlacement.free(pointer: CPointer<*>) = this.free(pointer.rawValue)
fun NativeFreeablePlacement.free(pointed: NativePointed) = this.free(pointed.rawPtr)
public fun NativeFreeablePlacement.free(pointer: CPointer<*>) = this.free(pointer.rawValue)
public fun NativeFreeablePlacement.free(pointed: NativePointed) = this.free(pointed.rawPtr)
object nativeHeap : NativeFreeablePlacement {
public object nativeHeap : NativeFreeablePlacement {
override fun alloc(size: Long, align: Int) = nativeMemUtils.alloc(size, align)
override fun free(mem: NativePtr) = nativeMemUtils.free(mem)
@@ -38,7 +38,7 @@ object nativeHeap : NativeFreeablePlacement {
private typealias Deferred = () -> Unit
open class DeferScope {
public open class DeferScope {
@PublishedApi
internal var topDeferred: Deferred? = null
@@ -65,11 +65,11 @@ open class DeferScope {
}
}
abstract class AutofreeScope : DeferScope(), NativePlacement {
public abstract class AutofreeScope : DeferScope(), NativePlacement {
abstract override fun alloc(size: Long, align: Int): NativePointed
}
open class ArenaBase(private val parent: NativeFreeablePlacement = nativeHeap) : AutofreeScope() {
public open class ArenaBase(private val parent: NativeFreeablePlacement = nativeHeap) : AutofreeScope() {
private var lastChunk: NativePointed? = null
@@ -97,7 +97,7 @@ open class ArenaBase(private val parent: NativeFreeablePlacement = nativeHeap) :
}
class Arena(parent: NativeFreeablePlacement = nativeHeap) : ArenaBase(parent) {
public class Arena(parent: NativeFreeablePlacement = nativeHeap) : ArenaBase(parent) {
fun clear() = this.clearImpl()
}
@@ -106,7 +106,7 @@ class Arena(parent: NativeFreeablePlacement = nativeHeap) : ArenaBase(parent) {
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.alloc(): T =
public inline fun <reified T : CVariable> NativePlacement.alloc(): T =
alloc(typeOf<T>()).reinterpret()
@PublishedApi
@@ -118,7 +118,7 @@ internal fun NativePlacement.alloc(type: CVariable.Type): NativePointed =
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.alloc(initialize: T.() -> Unit): T =
public inline fun <reified T : CVariable> NativePlacement.alloc(initialize: T.() -> Unit): T =
alloc<T>().also { it.initialize() }
/**
@@ -126,7 +126,7 @@ inline fun <reified T : CVariable> NativePlacement.alloc(initialize: T.() -> Uni
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long): CArrayPointer<T> =
public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long): CArrayPointer<T> =
alloc(sizeOf<T>() * length, alignOf<T>()).reinterpret<T>().ptr
/**
@@ -134,7 +134,7 @@ inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long): CAr
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int): CArrayPointer<T> =
public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int): CArrayPointer<T> =
allocArray(length.toLong())
/**
@@ -142,7 +142,7 @@ inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int): CArr
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long,
public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long,
initializer: T.(index: Long)->Unit): CArrayPointer<T> {
val res = allocArray<T>(length)
@@ -158,9 +158,8 @@ inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long,
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int,
initializer: T.(index: Int)->Unit): CArrayPointer<T> =
allocArray(length.toLong()) { index ->
public inline fun <reified T : CVariable> NativePlacement.allocArray(
length: Int, initializer: T.(index: Int)->Unit): CArrayPointer<T> = allocArray(length.toLong()) { index ->
this.initializer(index.toInt())
}
@@ -168,7 +167,7 @@ inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int,
/**
* Allocates C array of pointers to given elements.
*/
fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(elements: List<T?>): CArrayPointer<CPointerVar<T>> {
public fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(elements: List<T?>): CArrayPointer<CPointerVar<T>> {
val res = allocArray<CPointerVar<T>>(elements.size)
elements.forEachIndexed { index, value ->
res[index] = value?.ptr
@@ -179,22 +178,21 @@ 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?) =
public fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(vararg elements: T?) =
allocArrayOfPointersTo(listOf(*elements))
/**
* Allocates C array of given values.
*/
inline fun <reified T : CPointer<*>>
public inline fun <reified T : CPointer<*>>
NativePlacement.allocArrayOf(vararg elements: T?): CArrayPointer<CPointerVarOf<T>> {
return allocArrayOf(listOf(*elements))
}
/**
* Allocates C array of given values.
*/
inline fun <reified T : CPointer<*>>
public inline fun <reified T : CPointer<*>>
NativePlacement.allocArrayOf(elements: List<T?>): CArrayPointer<CPointerVarOf<T>> {
val res = allocArray<CPointerVarOf<T>>(elements.size)
@@ -206,13 +204,13 @@ inline fun <reified T : CPointer<*>>
return res
}
fun NativePlacement.allocArrayOf(elements: ByteArray): CArrayPointer<ByteVar> {
public fun NativePlacement.allocArrayOf(elements: ByteArray): CArrayPointer<ByteVar> {
val result = allocArray<ByteVar>(elements.size)
nativeMemUtils.putByteArray(elements, result.pointed, elements.size)
return result
}
fun NativePlacement.allocArrayOf(vararg elements: Float): CArrayPointer<FloatVar> {
public fun NativePlacement.allocArrayOf(vararg elements: Float): CArrayPointer<FloatVar> {
val res = allocArray<FloatVar>(elements.size)
var index = 0
while (index < elements.size) {
@@ -222,48 +220,67 @@ fun NativePlacement.allocArrayOf(vararg elements: Float): CArrayPointer<FloatVar
return res
}
fun <T : CPointed> NativePlacement.allocPointerTo() = alloc<CPointerVar<T>>()
public fun <T : CPointed> NativePlacement.allocPointerTo() = alloc<CPointerVar<T>>()
fun <T : CVariable> zeroValue(size: Int, align: Int): CValue<T> = object : CValue<T>() {
@PublishedApi
internal class ZeroValue<T: CVariable>(private val sizeBytes: Int, private val alignBytes: Int): CValue<T>() {
// Optimization to avoid unneeded virtual calls in base class implementation.
override fun getPointer(scope: AutofreeScope): CPointer<T> {
val result = scope.alloc(size, align)
nativeMemUtils.zeroMemory(result, size)
return interpretCPointer(result.rawPtr)!!
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
override val size get() = size
override fun place(placement: CPointer<T>): CPointer<T> {
nativeMemUtils.zeroMemory(interpretPointed(placement.rawValue), sizeBytes)
return placement
}
override val size get() = sizeBytes
override val align get() = alignBytes
}
@Suppress("NOTHING_TO_INLINE")
public inline fun <T : CVariable> zeroValue(size: Int, align: Int): CValue<T> = ZeroValue(size, align)
inline fun <reified T : CVariable> zeroValue(): CValue<T> =
zeroValue<T>(sizeOf<T>().toInt(), alignOf<T>())
public inline fun <reified T : CVariable> zeroValue(): CValue<T> = zeroValue<T>(sizeOf<T>().toInt(), alignOf<T>())
inline fun <reified T : CVariable> cValue(): CValue<T> = zeroValue<T>()
public inline fun <reified T : CVariable> cValue(): CValue<T> = zeroValue<T>()
private fun <T : CPointed> NativePlacement.placeBytes(bytes: ByteArray, align: Int): CPointer<T> {
val result = this.alloc(size = bytes.size, align = align)
nativeMemUtils.putByteArray(bytes, result, bytes.size)
return interpretCPointer(result.rawPtr)!!
}
fun <T : CVariable> CPointed.readValues(size: Int, align: Int): CValues<T> {
public fun <T : CVariable> CPointed.readValues(size: Int, align: Int): CValues<T> {
val bytes = ByteArray(size)
nativeMemUtils.getByteArray(this, bytes, size)
return object : CValues<T>() {
override fun getPointer(scope: AutofreeScope): CPointer<T> = scope.placeBytes(bytes, align)
override val size get() = bytes.size
return object : CValue<T>() {
// Optimization to avoid unneeded virtual calls in base class implementation.
override fun getPointer(scope: AutofreeScope): CPointer<T> {
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
override fun place(placement: CPointer<T>): CPointer<T> {
nativeMemUtils.putByteArray(bytes, interpretPointed(placement.rawValue), bytes.size)
return placement
}
override val size get() = size
override val align get() = align
}
}
inline fun <reified T : CVariable> T.readValues(count: Int): CValues<T> =
public inline fun <reified T : CVariable> T.readValues(count: Int): CValues<T> =
this.readValues<T>(size = count * sizeOf<T>().toInt(), align = alignOf<T>())
fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> {
public fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> {
val bytes = ByteArray(size.toInt())
nativeMemUtils.getByteArray(this, bytes, size.toInt())
return object : CValue<T>() {
override fun getPointer(scope: AutofreeScope): CPointer<T> = scope.placeBytes(bytes, align)
override val size get() = bytes.size
override fun place(placement: CPointer<T>): CPointer<T> {
nativeMemUtils.putByteArray(bytes, interpretPointed(placement.rawValue), bytes.size)
return placement
}
// Optimization to avoid unneeded virtual calls in base class implementation.
public override fun getPointer(scope: AutofreeScope): CPointer<T> {
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
override val size get() = size.toInt()
override val align get() = align
}
}
@@ -272,138 +289,140 @@ fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> {
// Note: can't be declared as property due to possible clash with a struct field.
// TODO: find better name.
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(typeOf<T>())
public inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(typeOf<T>())
fun CValue<*>.write(location: NativePtr) {
// TODO: probably CValue must be redesigned.
val fakeScope = object : AutofreeScope() {
var used = false
override fun alloc(size: Long, align: Int): NativePointed {
assert(!used)
used = true
return interpretPointed<ByteVar>(location)
}
}
this.getPointer(fakeScope)
assert(fakeScope.used)
public fun <T: CVariable> CValue<T>.write(location: NativePtr) {
this.place(interpretCPointer(location)!!)
}
// TODO: optimize
fun <T : CVariable> CValues<T>.getBytes(): ByteArray = memScoped {
public fun <T : CVariable> CValues<T>.getBytes(): ByteArray = memScoped {
val result = ByteArray(size)
nativeMemUtils.getByteArray(
source = this@getBytes.placeTo(memScope).reinterpret<ByteVar>().pointed,
dest = result,
length = result.size
)
result
}
/**
* Calls the [block] with temporary copy if this value as receiver.
*/
inline fun <reified T : CStructVar, R> CValue<T>.useContents(block: T.() -> R): R = memScoped {
public inline fun <reified T : CStructVar, R> CValue<T>.useContents(block: T.() -> R): R = memScoped {
this@useContents.placeTo(memScope).pointed.block()
}
inline fun <reified T : CStructVar> CValue<T>.copy(modify: T.() -> Unit): CValue<T> = useContents {
public inline fun <reified T : CStructVar> CValue<T>.copy(modify: T.() -> Unit): CValue<T> = useContents {
this.modify()
this.readValue()
}
inline fun <reified T : CStructVar> cValue(initialize: T.() -> Unit): CValue<T> =
public inline fun <reified T : CStructVar> cValue(initialize: T.() -> Unit): CValue<T> =
zeroValue<T>().copy(modify = initialize)
inline fun <reified T : CVariable> createValues(count: Int, initializer: T.(index: Int) -> Unit) = memScoped {
public inline fun <reified T : CVariable> createValues(count: Int, initializer: T.(index: Int) -> Unit) = memScoped {
val array = allocArray<T>(count, initializer)
array[0].readValues(count)
}
fun cValuesOf(vararg elements: Byte): CValues<ByteVar> = object : CValues<ByteVar>() {
override fun getPointer(scope: AutofreeScope) = scope.allocArrayOf(elements)
override val size get() = 1 * elements.size
}
// TODO: optimize other [cValuesOf] methods:
fun cValuesOf(vararg elements: Byte): CValues<ByteVar> = object : CValues<ByteVar>() {
// Optimization to avoid unneeded virtual calls in base class implementation.
override fun getPointer(scope: AutofreeScope): CPointer<ByteVar> {
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
override fun place(placement: CPointer<ByteVar>): CPointer<ByteVar> {
nativeMemUtils.putByteArray(elements, interpretPointed(placement.rawValue), elements.size)
return placement
}
fun cValuesOf(vararg elements: Short): CValues<ShortVar> =
createValues(elements.size) { index -> this.value = elements[index] }
fun cValuesOf(vararg elements: Int): CValues<IntVar> =
createValues(elements.size) { index -> this.value = elements[index] }
fun cValuesOf(vararg elements: Long): CValues<LongVar> =
createValues(elements.size) { index -> this.value = elements[index] }
fun cValuesOf(vararg elements: Float): CValues<FloatVar> = object : CValues<FloatVar>() {
override fun getPointer(scope: AutofreeScope) = scope.allocArrayOf(*elements)
override val size get() = 4 * elements.size
override val size get() = 1 * elements.size
override val align get() = 1
}
fun cValuesOf(vararg elements: Double): CValues<DoubleVar> =
public fun cValuesOf(vararg elements: Short): CValues<ShortVar> =
createValues(elements.size) { index -> this.value = elements[index] }
fun <T : CPointed> cValuesOf(vararg elements: CPointer<T>?): CValues<CPointerVar<T>> =
public fun cValuesOf(vararg elements: Int): CValues<IntVar> =
createValues(elements.size) { index -> this.value = elements[index] }
fun ByteArray.toCValues() = cValuesOf(*this)
fun ShortArray.toCValues() = cValuesOf(*this)
fun IntArray.toCValues() = cValuesOf(*this)
fun LongArray.toCValues() = cValuesOf(*this)
fun FloatArray.toCValues() = cValuesOf(*this)
fun DoubleArray.toCValues() = cValuesOf(*this)
fun <T : CPointed> Array<CPointer<T>?>.toCValues() = cValuesOf(*this)
public fun cValuesOf(vararg elements: Long): CValues<LongVar> =
createValues(elements.size) { index -> this.value = elements[index] }
fun <T : CPointed> List<CPointer<T>?>.toCValues() = this.toTypedArray().toCValues()
public fun cValuesOf(vararg elements: Float): CValues<FloatVar> =
createValues(elements.size) { index -> this.value = elements[index] }
public fun cValuesOf(vararg elements: Double): CValues<DoubleVar> =
createValues(elements.size) { index -> this.value = elements[index] }
public fun <T : CPointed> cValuesOf(vararg elements: CPointer<T>?): CValues<CPointerVar<T>> =
createValues(elements.size) { index -> this.value = elements[index] }
public fun ByteArray.toCValues() = cValuesOf(*this)
public fun ShortArray.toCValues() = cValuesOf(*this)
public fun IntArray.toCValues() = cValuesOf(*this)
public fun LongArray.toCValues() = cValuesOf(*this)
public fun FloatArray.toCValues() = cValuesOf(*this)
public fun DoubleArray.toCValues() = cValuesOf(*this)
public fun <T : CPointed> Array<CPointer<T>?>.toCValues() = cValuesOf(*this)
public fun <T : CPointed> List<CPointer<T>?>.toCValues() = this.toTypedArray().toCValues()
private class CString(val bytes: ByteArray): CValues<ByteVar>() {
override val size get() = bytes.size + 1
override val align get() = 1
// Optimization to avoid unneeded virtual calls in base class implementation.
override fun getPointer(scope: AutofreeScope): CPointer<ByteVar> {
val result = scope.allocArray<ByteVar>(bytes.size + 1)
nativeMemUtils.putByteArray(bytes, result.pointed, bytes.size)
result[bytes.size] = 0.toByte()
return result
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
override fun place(placement: CPointer<ByteVar>): CPointer<ByteVar> {
nativeMemUtils.putByteArray(bytes, placement.pointed, bytes.size)
placement[bytes.size] = 0.toByte()
return placement
}
}
/**
* @return the value of zero-terminated UTF-8-encoded C string constructed from given [kotlin.String].
*/
val String.cstr: CValues<ByteVar>
public val String.cstr: CValues<ByteVar>
get() = CString(encodeToUtf8(this))
/**
* Convert this list of Kotlin strings to C array of C strings,
* allocating memory for the array and C strings with given [AutofreeScope].
*/
fun List<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer<CPointerVar<ByteVar>> =
public fun List<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer<CPointerVar<ByteVar>> =
autofreeScope.allocArrayOf(this.map { it.cstr.getPointer(autofreeScope) })
/**
* Convert this array of Kotlin strings to C array of C strings,
* allocating memory for the array and C strings with given [AutofreeScope].
*/
fun Array<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer<CPointerVar<ByteVar>> =
public fun Array<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer<CPointerVar<ByteVar>> =
autofreeScope.allocArrayOf(this.map { it.cstr.getPointer(autofreeScope) })
private class WCString(val chars: CharArray): CValues<UShortVar>() {
override val size get() = 2 * (chars.size + 1)
override val align get() = 2
// Optimization to avoid unneeded virtual calls in base class implementation.
override fun getPointer(scope: AutofreeScope): CPointer<UShortVar> {
val result = scope.allocArray<UShortVar>(chars.size + 1)
nativeMemUtils.putCharArray(chars, result.pointed, chars.size)
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
override fun place(placement: CPointer<UShortVar>): CPointer<UShortVar> {
nativeMemUtils.putCharArray(chars, placement.pointed, chars.size)
// TODO: fix, after KT-29627 is fixed.
nativeMemUtils.putShort((result + chars.size)!!.pointed, 0)
return result
nativeMemUtils.putShort((placement + chars.size)!!.pointed, 0)
return placement
}
}
val String.wcstr: CValues<UShortVar>
public val String.wcstr: CValues<UShortVar>
get() = WCString(this.toCharArray())
/**
@@ -412,7 +431,7 @@ val String.wcstr: CValues<UShortVar>
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
*/
// TODO: optimize
fun CPointer<ByteVar>.toKString(): String {
public fun CPointer<ByteVar>.toKString(): String {
val nativeBytes = this
var length = 0
@@ -425,7 +444,7 @@ fun CPointer<ByteVar>.toKString(): String {
return decodeFromUtf8(bytes)
}
class MemScope : ArenaBase() {
public class MemScope : ArenaBase() {
val memScope: MemScope
get() = this
@@ -440,7 +459,7 @@ class MemScope : ArenaBase() {
* Runs given [block] providing allocation of memory
* which will be automatically disposed at the end of this scope.
*/
inline fun <R> memScoped(block: MemScope.()->R): R {
public inline fun <R> memScoped(block: MemScope.()->R): R {
val memScope = MemScope()
try {
return memScope.block()
@@ -449,7 +468,7 @@ inline fun <R> memScoped(block: MemScope.()->R): R {
}
}
fun COpaquePointer.readBytes(count: Int): ByteArray {
public fun COpaquePointer.readBytes(count: Int): ByteArray {
val result = ByteArray(count)
nativeMemUtils.getByteArray(this.reinterpret<ByteVar>().pointed, result, count)
return result
@@ -103,6 +103,17 @@ internal object nativeMemUtils {
}
}
// TODO: optimize
fun copyMemory(dest: NativePointed, length: Int, src: NativePointed): Unit {
val destArray = dest.reinterpret<ByteVar>().ptr
val srcArray = src.reinterpret<ByteVar>().ptr
var index = 0
while (index < length) {
destArray[index] = srcArray[index]
++index
}
}
fun alloc(size: Long, align: Int): NativePointed {
val ptr = malloc(size, align)
if (ptr == nativeNullPtr) {
@@ -157,4 +168,4 @@ private external fun cfree(ptr: NativePtr)
@TypedIntrinsic(IntrinsicType.INTEROP_READ_BITS)
external fun readBits(ptr: NativePtr, offset: Long, size: Int, signed: Boolean): Long
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_BITS)
external fun writeBits(ptr: NativePtr, offset: Long, size: Int, value: Long)
external fun writeBits(ptr: NativePtr, offset: Long, size: Int, value: Long)
@@ -34,14 +34,25 @@ inline fun buildNativeCodeLines(scope: NativeScope, block: NativeCodeBuilder.()
return builder.lines
}
class KotlinCodeBuilder(val scope: KotlinScope) {
private val lines = mutableListOf<String>()
private class Block(val nesting: Int, val start: String, val end: String) {
val prologue = mutableListOf<String>()
val body = mutableListOf<String>()
val epilogue = mutableListOf<String>()
private val freeStack = mutableListOf<String>()
private val nesting get() = freeStack.size
fun indent(line: String) = " ".repeat(nesting) + line
fun indentBraces(line: String) = " ".repeat(nesting - 1) + line
}
class KotlinCodeBuilder(val scope: KotlinScope) {
private val blocks = mutableListOf<Block>()
init {
pushBlock("", "")
}
fun out(line: String) {
lines.add(" ".repeat(nesting) + line)
currentBlock().body += line
}
private var memScoped = false
@@ -52,24 +63,36 @@ class KotlinCodeBuilder(val scope: KotlinScope) {
}
}
fun pushBlock(line: String, free: String = "") {
out(line)
freeStack.add(free)
fun getNativePointer(name: String): String {
return "$name?.getPointer(memScope)"
}
private fun popBlocks() {
while (freeStack.isNotEmpty()) {
val free = freeStack.last()
freeStack.removeAt(freeStack.lastIndex)
out("} $free".trim())
}
fun returnResult(result: String) {
currentBlock().body += "return $result"
}
private fun currentBlock() = blocks.last()
fun pushBlock(start: String, end: String = "}") {
val block = Block(blocks.size, start = start, end = end)
blocks += block
}
private fun emitBlockAndNested(position: Int, lines: MutableList<String>) {
if (position >= blocks.size) return
val block = blocks[position]
if (block.start.isNotEmpty()) lines += block.indentBraces(block.start)
lines += block.prologue.map { block.indent(it) }
lines += block.body.map { block.indent(it) }
emitBlockAndNested(position + 1, lines)
lines += block.epilogue.map { block.indent(it) }
if (block.end.isNotEmpty()) lines += block.indentBraces(block.end)
}
fun build(): List<String> {
this.popBlocks()
val result = this.lines.toList()
this.lines.clear()
return result
val lines = mutableListOf<String>()
emitBlockAndNested(0, lines)
return lines.toList()
}
}
@@ -57,8 +57,9 @@ class MappingBridgeGeneratorImpl(
is RecordType -> {
val mirror = mirror(declarationMapper, returnType)
val tmpVarName = kniRetVal
// We clear in the finally block.
builder.out("val $tmpVarName = nativeHeap.alloc<${mirror.pointedType.render(builder.scope)}>()")
builder.pushBlock("try {", free = "finally { nativeHeap.free($tmpVarName) }")
builder.pushBlock(start = "try {", end = "} finally { nativeHeap.free($tmpVarName) }")
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "$tmpVarName.rawPtr"))
BridgedType.VOID
}
@@ -315,7 +315,7 @@ sealed class TypeInfo {
val objCBlock = "((__bridge $blockType)${nativeValues.last()})"
"$objCBlock(${nativeValues.dropLast(1).joinToString()})"
}.let {
codeBuilder.out("return $it")
codeBuilder.returnResult(it)
}
codeBuilder.build().joinTo(this, separator = "\n")
@@ -341,7 +341,7 @@ sealed class TypeInfo {
error(pointed)
override fun cToBridged(expr: String) = error(pointed)
// TODO: this method must not exist
// TODO: this method must not exist.
override fun constructPointedType(valueType: KotlinType): KotlinClassifierType = error(pointed)
}
}
@@ -231,7 +231,7 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
"$messenger(${messengerArguments.joinToString()})"
}
bodyGenerator.out("return $result")
bodyGenerator.returnResult(result)
this.implementationTemplate = genImplementationTemplate(stubGenerator)
this.bodyLines = bodyGenerator.build()
@@ -191,7 +191,7 @@ class SimpleBridgeGeneratorImpl(
kotlinExpr = "objc_retainAutoreleaseReturnValue($kotlinExpr)"
// (Objective-C does the same for returned pointers).
}
out("return $kotlinExpr")
returnResult(kotlinExpr)
}.forEach {
kotlinLines.add(" $it")
}
@@ -613,7 +613,6 @@ class StubGenerator(
private val cCallSymbolName: String?
init {
// TODO: support dumpShims
val kotlinParameters = mutableListOf<Pair<String, KotlinType>>()
val bodyGenerator = KotlinCodeBuilder(scope = kotlinFile)
val bridgeArguments = mutableListOf<TypedKotlinValue>()
@@ -648,13 +647,12 @@ class StubGenerator(
} else if (representAsValuesRef != null) {
kotlinParameters.add(parameterName to representAsValuesRef)
bodyGenerator.pushMemScoped()
"$parameterName?.getPointer(memScope)"
bodyGenerator.getNativePointer(parameterName)
} else {
val mirror = mirror(parameter.type)
kotlinParameters.add(parameterName to mirror.argType)
parameterName
}
bridgeArguments.add(TypedKotlinValue(parameter.type, bridgeArgument))
}
@@ -667,7 +665,7 @@ class StubGenerator(
) { nativeValues ->
"${func.name}(${nativeValues.joinToString()})"
}
bodyGenerator.out("return $result")
bodyGenerator.returnResult(result)
isCCall = false
cCallSymbolName = null
} else {
@@ -295,6 +295,12 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
return LLVMAddFunction(llvmModule, "llvm.memset.p0i8.i32", functionType)!!
}
private fun importMemcpy(): LLVMValueRef {
val parameterTypes = cValuesOf(int8TypePtr, int8TypePtr, int32Type, int1Type)
val functionType = LLVMFunctionType(LLVMVoidType(), parameterTypes, 4, 0)
return LLVMAddFunction(llvmModule, "llvm.memcpy.p0i8.p0i8.i32", functionType)!!
}
internal fun externalFunction(name: String, type: LLVMTypeRef, origin: CompiledKonanModuleOrigin): LLVMValueRef {
this.imports.add(origin)
@@ -463,6 +469,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
)
val memsetFunction = importMemset()
val memcpyFunction = importMemcpy()
val usedFunctions = mutableListOf<LLVMValueRef>()
val usedGlobals = mutableListOf<LLVMValueRef>()
@@ -77,6 +77,7 @@ internal enum class IntrinsicType {
INTEROP_NARROW,
INTEROP_STATIC_C_FUNCTION,
INTEROP_FUNPTR_INVOKE,
INTEROP_MEMORY_COPY,
// Worker
WORKER_EXECUTE
}
@@ -216,6 +217,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
IntrinsicType.LIST_OF_INTERNAL -> emitListOfInternal(callSite, args)
IntrinsicType.IDENTITY -> emitIdentity(args)
IntrinsicType.GET_CONTINUATION -> emitGetContinuation()
IntrinsicType.INTEROP_MEMORY_COPY -> emitMemoryCopy(callSite, args)
IntrinsicType.RETURN_IF_SUSPEND,
IntrinsicType.INTEROP_BITS_TO_FLOAT,
IntrinsicType.INTEROP_BITS_TO_DOUBLE,
@@ -377,6 +379,12 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
return codegen.theUnitInstanceRef.llvm
}
private fun FunctionGenerationContext.emitMemoryCopy(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
println("memcpy at ${callSite}")
args.map { println(llvm2string(it)) }
TODO("Implement me")
}
private fun extractConstUnsignedInt(value: LLVMValueRef): Long {
assert(LLVMIsConstant(value) != 0)
return LLVMConstIntGetZExtValue(value)
@@ -27,9 +27,8 @@ internal interface ConstPointer : ConstValue {
}
internal fun constPointer(value: LLVMValueRef) = object : ConstPointer {
init {
assert (LLVMIsConstant(value) == 1)
assert(LLVMIsConstant(value) == 1)
}
override val llvm = value
@@ -209,7 +209,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
} else {
methodTableRecords(irClass)
}
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
runtime.methodTableRecordType, methods)
@@ -243,7 +242,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
}
fun vtable(irClass: IrClass): ConstArray {
// TODO: compile-time resolution limits binary compatibility
// TODO: compile-time resolution limits binary compatibility.
val vtableEntries = context.getVtableBuilder(irClass).vtableEntries.map {
val implementation = it.implementation
if (implementation == null || implementation.isExternalObjCClassMethod()) {
@@ -264,7 +263,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
if (previous != null)
throw AssertionError("Duplicate method table entry: functionName = '$functionName', hash = '${nameSignature.value}', entry1 = $previous, entry2 = $it")
// TODO: compile-time resolution limits binary compatibility
// TODO: compile-time resolution limits binary compatibility.
val implementation = it.implementation
val methodEntryPoint = implementation?.entryPointAddress
MethodTableRecord(nameSignature, methodEntryPoint)
@@ -961,6 +961,9 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
)
}
}
IntrinsicType.INTEROP_MEMORY_COPY -> {
TODO("So far unsupported")
}
IntrinsicType.OBJC_INIT_BY -> {
val intrinsic = interop.objCObjectInitBy.name