Use unsigned types in interop (#1913)

This commit is contained in:
SvyatoslavScherbina
2018-08-24 18:30:56 +03:00
committed by GitHub
parent 29cddb46bf
commit 8f1b94f38b
46 changed files with 665 additions and 192 deletions
+11 -15
View File
@@ -468,28 +468,24 @@ Sometimes the C libraries have function parameters or struct fields of
platform-dependent type, e.g. `long` or `size_t`. Kotlin itself doesn't provide platform-dependent type, e.g. `long` or `size_t`. Kotlin itself doesn't provide
neither implicit integer casts nor C-style integer casts (e.g. neither implicit integer casts nor C-style integer casts (e.g.
`(size_t) intValue`), so to make writing portable code in such cases easier, `(size_t) intValue`), so to make writing portable code in such cases easier,
the following methods are provided: `convert` method is provided:
* `fun ${type1}.signExtend<${type2}>(): ${type2}` ```
* `fun ${type1}.narrow<${type2}>(): ${type2}` fun ${type1}.convert<${type2}>(): ${type2}
```
where each of `type1` and `type2` must be an integral type. where each of `type1` and `type2` must be an integral type, either signed or unsigned.
The `signExtend` converts the integer value to more wide, i.e. the result must `.convert<${type}>` has the same semantics as one of the
have the same or greater size. `.toByte`, `.toShort`, `.toInt`, `.toLong`,
The `narrow` converts the integer value to smaller one (possibly changing the `.toUByte`, `.toUShort`, `.toUInt` or `.toULong`
value due to loosing significant bits), so the result must have the same or methods, depending on `type`.
less size.
Any allowed `.signExtend<${type}>` or `.narrow<${type}>` have the same The example of using `convert`:
semantics as one of the `.toByte`, `.toShort`, `.toInt` or `.toLong` methods,
depending on `type`.
The example of using `signExtend`:
``` ```
fun zeroMemory(buffer: COpaquePointer, size: Int) { fun zeroMemory(buffer: COpaquePointer, size: Int) {
memset(buffer, 0, size.signExtend<size_t>()) memset(buffer, 0, size.convert<size_t>())
} }
``` ```
+6
View File
@@ -72,6 +72,12 @@ dependencies {
sourceSets.main.kotlin.srcDirs += "src/jvm/kotlin" sourceSets.main.kotlin.srcDirs += "src/jvm/kotlin"
compileKotlin {
kotlinOptions {
freeCompilerArgs = ['-Xuse-experimental=kotlin.ExperimentalUnsignedTypes']
}
}
task nativelibs(type: Copy) { task nativelibs(type: Copy) {
dependsOn 'callbacksSharedLibrary' dependsOn 'callbacksSharedLibrary'
@@ -121,7 +121,7 @@ private fun getEnumCType(classifier: KClass<*>): CEnumType? {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return CEnumType(rawValueCType as CType<Number>) return CEnumType(rawValueCType as CType<Any>)
} }
private fun getArgOrRetValCType(type: KType): CType<*> { private fun getArgOrRetValCType(type: KType): CType<*> {
@@ -346,7 +346,7 @@ private class Struct(val size: Long, val align: Int, elementTypes: List<CType<*>
override fun write(location: NativePtr, value: CValue<*>) = value.write(location) override fun write(location: NativePtr, value: CValue<*>) = value.write(location)
} }
private class CEnumType(private val rawValueCType: CType<Number>) : CType<CEnum>(rawValueCType.ffiType) { private class CEnumType(private val rawValueCType: CType<Any>) : CType<CEnum>(rawValueCType.ffiType) {
override fun read(location: NativePtr): CEnum { override fun read(location: NativePtr): CEnum {
TODO("enum-typed callback parameters") TODO("enum-typed callback parameters")
@@ -26,16 +26,20 @@ inline operator fun <T : ByteVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<
inline operator fun <T : ByteVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? = inline operator fun <T : ByteVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong() this + index.toLong()
@JvmName("get\$Byte")
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.get(index: Int): T = inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Byte")
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.set(index: Int, value: T) { inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@JvmName("get\$Byte")
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.get(index: Long): T = inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Byte")
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.set(index: Long, value: T) { inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@@ -48,16 +52,20 @@ inline operator fun <T : ShortVarOf<*>> CPointer<T>?.plus(index: Long): CPointer
inline operator fun <T : ShortVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? = inline operator fun <T : ShortVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong() this + index.toLong()
@JvmName("get\$Short")
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.get(index: Int): T = inline operator fun <T : Short> CPointer<ShortVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Short")
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.set(index: Int, value: T) { inline operator fun <T : Short> CPointer<ShortVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@JvmName("get\$Short")
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.get(index: Long): T = inline operator fun <T : Short> CPointer<ShortVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Short")
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.set(index: Long, value: T) { inline operator fun <T : Short> CPointer<ShortVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@@ -70,16 +78,20 @@ inline operator fun <T : IntVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T
inline operator fun <T : IntVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? = inline operator fun <T : IntVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong() this + index.toLong()
@JvmName("get\$Int")
inline operator fun <T : Int> CPointer<IntVarOf<T>>.get(index: Int): T = inline operator fun <T : Int> CPointer<IntVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Int")
inline operator fun <T : Int> CPointer<IntVarOf<T>>.set(index: Int, value: T) { inline operator fun <T : Int> CPointer<IntVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@JvmName("get\$Int")
inline operator fun <T : Int> CPointer<IntVarOf<T>>.get(index: Long): T = inline operator fun <T : Int> CPointer<IntVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Int")
inline operator fun <T : Int> CPointer<IntVarOf<T>>.set(index: Long, value: T) { inline operator fun <T : Int> CPointer<IntVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@@ -92,20 +104,128 @@ inline operator fun <T : LongVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<
inline operator fun <T : LongVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? = inline operator fun <T : LongVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong() this + index.toLong()
@JvmName("get\$Long")
inline operator fun <T : Long> CPointer<LongVarOf<T>>.get(index: Int): T = inline operator fun <T : Long> CPointer<LongVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Long")
inline operator fun <T : Long> CPointer<LongVarOf<T>>.set(index: Int, value: T) { inline operator fun <T : Long> CPointer<LongVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@JvmName("get\$Long")
inline operator fun <T : Long> CPointer<LongVarOf<T>>.get(index: Long): T = inline operator fun <T : Long> CPointer<LongVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Long")
inline operator fun <T : Long> CPointer<LongVarOf<T>>.set(index: Long, value: T) { inline operator fun <T : Long> CPointer<LongVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@JvmName("plus\$UByte")
inline operator fun <T : UByteVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
interpretCPointer(this.rawValue + index * 1)
@JvmName("plus\$UByte")
inline operator fun <T : UByteVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong()
@JvmName("get\$UByte")
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value
@JvmName("set\$UByte")
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("get\$UByte")
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value
@JvmName("set\$UByte")
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("plus\$UShort")
inline operator fun <T : UShortVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
interpretCPointer(this.rawValue + index * 2)
@JvmName("plus\$UShort")
inline operator fun <T : UShortVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong()
@JvmName("get\$UShort")
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value
@JvmName("set\$UShort")
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("get\$UShort")
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value
@JvmName("set\$UShort")
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("plus\$UInt")
inline operator fun <T : UIntVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
interpretCPointer(this.rawValue + index * 4)
@JvmName("plus\$UInt")
inline operator fun <T : UIntVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong()
@JvmName("get\$UInt")
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value
@JvmName("set\$UInt")
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("get\$UInt")
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value
@JvmName("set\$UInt")
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("plus\$ULong")
inline operator fun <T : ULongVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
interpretCPointer(this.rawValue + index * 8)
@JvmName("plus\$ULong")
inline operator fun <T : ULongVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong()
@JvmName("get\$ULong")
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value
@JvmName("set\$ULong")
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("get\$ULong")
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value
@JvmName("set\$ULong")
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value
}
@JvmName("plus\$Float") @JvmName("plus\$Float")
inline operator fun <T : FloatVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? = inline operator fun <T : FloatVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
interpretCPointer(this.rawValue + index * 4) interpretCPointer(this.rawValue + index * 4)
@@ -114,16 +234,20 @@ inline operator fun <T : FloatVarOf<*>> CPointer<T>?.plus(index: Long): CPointer
inline operator fun <T : FloatVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? = inline operator fun <T : FloatVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong() this + index.toLong()
@JvmName("get\$Float")
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.get(index: Int): T = inline operator fun <T : Float> CPointer<FloatVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Float")
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.set(index: Int, value: T) { inline operator fun <T : Float> CPointer<FloatVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@JvmName("get\$Float")
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.get(index: Long): T = inline operator fun <T : Float> CPointer<FloatVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Float")
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.set(index: Long, value: T) { inline operator fun <T : Float> CPointer<FloatVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@@ -136,16 +260,20 @@ inline operator fun <T : DoubleVarOf<*>> CPointer<T>?.plus(index: Long): CPointe
inline operator fun <T : DoubleVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? = inline operator fun <T : DoubleVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
this + index.toLong() this + index.toLong()
@JvmName("get\$Double")
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.get(index: Int): T = inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.get(index: Int): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Double")
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.set(index: Int, value: T) { inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.set(index: Int, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@JvmName("get\$Double")
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.get(index: Long): T = inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.get(index: Long): T =
(this + index)!!.pointed.value (this + index)!!.pointed.value
@JvmName("set\$Double")
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.set(index: Long, value: T) { inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.set(index: Long, value: T) {
(this + index)!!.pointed.value = value (this + index)!!.pointed.value = value
} }
@@ -163,16 +291,20 @@ echo "@JvmName(\"plus\\\$$1\")"
echo "inline operator fun <T : ${1}VarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =" echo "inline operator fun <T : ${1}VarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? ="
echo " this + index.toLong()" echo " this + index.toLong()"
echo echo
echo "@JvmName(\"get\\\$$1\")"
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.get(index: Int): T =" echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.get(index: Int): T ="
echo " (this + index)!!.pointed.value" echo " (this + index)!!.pointed.value"
echo echo
echo "@JvmName(\"set\\\$$1\")"
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.set(index: Int, value: T) {" echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.set(index: Int, value: T) {"
echo " (this + index)!!.pointed.value = value" echo " (this + index)!!.pointed.value = value"
echo '}' echo '}'
echo echo
echo "@JvmName(\"get\\\$$1\")"
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.get(index: Long): T =" echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.get(index: Long): T ="
echo " (this + index)!!.pointed.value" echo " (this + index)!!.pointed.value"
echo echo
echo "@JvmName(\"set\\\$$1\")"
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.set(index: Long, value: T) {" echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.set(index: Long, value: T) {"
echo " (this + index)!!.pointed.value = value" echo " (this + index)!!.pointed.value = value"
echo '}' echo '}'
@@ -183,6 +315,10 @@ gen Byte 1
gen Short 2 gen Short 2
gen Int 4 gen Int 4
gen Long 8 gen Long 8
gen UByte 1
gen UShort 2
gen UInt 4
gen ULong 8
gen Float 4 gen Float 4
gen Double 8 gen Double 8
@@ -246,7 +246,7 @@ sealed class CPrimitiveVar(rawPtr: NativePtr) : CVariable(rawPtr) {
} }
interface CEnum { interface CEnum {
val value: Number val value: Any
} }
abstract class CEnumVar(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) abstract class CEnumVar(rawPtr: NativePtr) : CPrimitiveVar(rawPtr)
@@ -278,6 +278,26 @@ class LongVarOf<T : Long>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(8) companion object : Type(8)
} }
@Suppress("FINAL_UPPER_BOUND")
class UByteVarOf<T : UByte>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(1)
}
@Suppress("FINAL_UPPER_BOUND")
class UShortVarOf<T : UShort>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(2)
}
@Suppress("FINAL_UPPER_BOUND")
class UIntVarOf<T : UInt>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(4)
}
@Suppress("FINAL_UPPER_BOUND")
class ULongVarOf<T : ULong>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(8)
}
@Suppress("FINAL_UPPER_BOUND") @Suppress("FINAL_UPPER_BOUND")
class FloatVarOf<T : Float>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) { class FloatVarOf<T : Float>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
companion object : Type(4) companion object : Type(4)
@@ -293,6 +313,10 @@ typealias ByteVar = ByteVarOf<Byte>
typealias ShortVar = ShortVarOf<Short> typealias ShortVar = ShortVarOf<Short>
typealias IntVar = IntVarOf<Int> typealias IntVar = IntVarOf<Int>
typealias LongVar = LongVarOf<Long> 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 FloatVar = FloatVarOf<Float>
typealias DoubleVar = DoubleVarOf<Double> typealias DoubleVar = DoubleVarOf<Double>
@@ -330,6 +354,26 @@ var <T : Long> LongVarOf<T>.value: T
get() = nativeMemUtils.getLong(this) as T get() = nativeMemUtils.getLong(this) as T
set(value) = nativeMemUtils.putLong(this, value) set(value) = nativeMemUtils.putLong(this, value)
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
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
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
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
get() = nativeMemUtils.getLong(this).toULong() as T
set(value) = nativeMemUtils.putLong(this, value.toLong())
// TODO: ensure native floats have the appropriate binary representation // TODO: ensure native floats have the appropriate binary representation
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST") @Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
@@ -321,6 +321,22 @@ fun cValuesOf(vararg elements: Int): CValues<IntVar> =
fun cValuesOf(vararg elements: Long): CValues<LongVar> = fun cValuesOf(vararg elements: Long): CValues<LongVar> =
createValues(elements.size) { index -> this.value = elements[index] } createValues(elements.size) { index -> this.value = elements[index] }
@JvmName("cValuesOfUnsigned")
fun cValuesOf(vararg elements: UByte): CValues<UByteVar> =
createValues(elements.size) { index -> this.value = elements[index] }
@JvmName("cValuesOfUnsigned")
fun cValuesOf(vararg elements: UShort): CValues<UShortVar> =
createValues(elements.size) { index -> this.value = elements[index] }
@JvmName("cValuesOfUnsigned")
fun cValuesOf(vararg elements: UInt): CValues<UIntVar> =
createValues(elements.size) { index -> this.value = elements[index] }
@JvmName("cValuesOfUnsigned")
fun cValuesOf(vararg elements: ULong): CValues<ULongVar> =
createValues(elements.size) { index -> this.value = elements[index] }
fun cValuesOf(vararg elements: Float): CValues<FloatVar> = object : CValues<FloatVar>() { fun cValuesOf(vararg elements: Float): CValues<FloatVar> = object : CValues<FloatVar>() {
override fun getPointer(scope: AutofreeScope) = scope.allocArrayOf(*elements) override fun getPointer(scope: AutofreeScope) = scope.allocArrayOf(*elements)
override val size get() = 4 * elements.size override val size get() = 4 * elements.size
@@ -102,6 +102,34 @@ private fun invokeImplLongRet(ptr: COpaquePointer, vararg args: Any?): Long = me
resultBuffer.value resultBuffer.value
} }
@ExportForCompiler
private fun invokeImplUByteRet(ptr: COpaquePointer, vararg args: Any?): UByte = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<UByteVar>(UByteVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT8, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplUShortRet(ptr: COpaquePointer, vararg args: Any?): UShort = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<UShortVar>(UShortVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT16, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplUIntRet(ptr: COpaquePointer, vararg args: Any?): UInt = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<UIntVar>(UIntVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT32, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler
private fun invokeImplULongRet(ptr: COpaquePointer, vararg args: Any?): ULong = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<ULongVar>(ULongVar)
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT64, args, null, memScope)
resultBuffer.value
}
@ExportForCompiler @ExportForCompiler
private fun invokeImplFloatRet(ptr: COpaquePointer, vararg args: Any?): Float = memScoped { private fun invokeImplFloatRet(ptr: COpaquePointer, vararg args: Any?): Float = memScoped {
val resultBuffer = allocFfiReturnValueBuffer<FloatVar>(FloatVar) val resultBuffer = allocFfiReturnValueBuffer<FloatVar>(FloatVar)
@@ -28,12 +28,23 @@ external fun bitsToFloat(bits: Int): Float
@Intrinsic @Intrinsic
external fun bitsToDouble(bits: Long): Double external fun bitsToDouble(bits: Long): Double
// TODO: deprecate.
@Intrinsic @Intrinsic
external fun <R : Number> Number.signExtend(): R external fun <R : Number> Number.signExtend(): R
// TODO: deprecate.
@Intrinsic @Intrinsic
external fun <R : Number> Number.narrow(): R external fun <R : Number> Number.narrow(): R
@Intrinsic external fun <R : Any> Byte.convert(): R
@Intrinsic external fun <R : Any> Short.convert(): R
@Intrinsic external fun <R : Any> Int.convert(): R
@Intrinsic external fun <R : Any> Long.convert(): R
@Intrinsic external fun <R : Any> UByte.convert(): R
@Intrinsic external fun <R : Any> UShort.convert(): R
@Intrinsic external fun <R : Any> UInt.convert(): R
@Intrinsic external fun <R : Any> ULong.convert(): R
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE)
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
internal annotation class JvmName(val name: String) internal annotation class JvmName(val name: String)
@@ -56,6 +56,19 @@ fun IntArray.refTo(index: Int): CValuesRef<IntVar> = this.usingPinned { addressO
fun Pinned<LongArray>.addressOf(index: Int): CPointer<LongVar> = this.addressOfElement(index) fun Pinned<LongArray>.addressOf(index: Int): CPointer<LongVar> = this.addressOfElement(index)
fun LongArray.refTo(index: Int): CValuesRef<LongVar> = this.usingPinned { addressOf(index) } fun LongArray.refTo(index: Int): CValuesRef<LongVar> = this.usingPinned { addressOf(index) }
// TODO: pinning of unsigned arrays involves boxing as they are inline classes wrapping signed arrays.
fun Pinned<UByteArray>.addressOf(index: Int): CPointer<UByteVar> = this.get().addressOfElement(index)
fun UByteArray.refTo(index: Int): CValuesRef<UByteVar> = this.usingPinned { addressOf(index) }
fun Pinned<UShortArray>.addressOf(index: Int): CPointer<UShortVar> = this.get().addressOfElement(index)
fun UShortArray.refTo(index: Int): CValuesRef<UShortVar> = this.usingPinned { addressOf(index) }
fun Pinned<UIntArray>.addressOf(index: Int): CPointer<UIntVar> = this.get().addressOfElement(index)
fun UIntArray.refTo(index: Int): CValuesRef<UIntVar> = this.usingPinned { addressOf(index) }
fun Pinned<ULongArray>.addressOf(index: Int): CPointer<ULongVar> = this.get().addressOfElement(index)
fun ULongArray.refTo(index: Int): CValuesRef<ULongVar> = this.usingPinned { addressOf(index) }
fun Pinned<FloatArray>.addressOf(index: Int): CPointer<FloatVar> = this.addressOfElement(index) fun Pinned<FloatArray>.addressOf(index: Int): CPointer<FloatVar> = this.addressOfElement(index)
fun FloatArray.refTo(index: Int): CValuesRef<FloatVar> = this.usingPinned { addressOf(index) } fun FloatArray.refTo(index: Int): CValuesRef<FloatVar> = this.usingPinned { addressOf(index) }
@@ -79,3 +92,15 @@ private external fun getAddressOfElement(array: Any, index: Int): COpaquePointer
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
private inline fun <P : CVariable> Pinned<*>.addressOfElement(index: Int): CPointer<P> = private inline fun <P : CVariable> Pinned<*>.addressOfElement(index: Int): CPointer<P> =
getAddressOfElement(this.get(), index).reinterpret() getAddressOfElement(this.get(), index).reinterpret()
@SymbolName("Kotlin_Arrays_getAddressOfElement")
private external fun UByteArray.addressOfElement(index: Int): CPointer<UByteVar>
@SymbolName("Kotlin_Arrays_getAddressOfElement")
private external fun UShortArray.addressOfElement(index: Int): CPointer<UShortVar>
@SymbolName("Kotlin_Arrays_getAddressOfElement")
private external fun UIntArray.addressOfElement(index: Int): CPointer<UIntVar>
@SymbolName("Kotlin_Arrays_getAddressOfElement")
private external fun ULongArray.addressOfElement(index: Int): CPointer<ULongVar>
@@ -30,11 +30,15 @@ const val FFI_TYPE_KIND_SINT64: FfiTypeKind = 4
const val FFI_TYPE_KIND_FLOAT: FfiTypeKind = 5 const val FFI_TYPE_KIND_FLOAT: FfiTypeKind = 5
const val FFI_TYPE_KIND_DOUBLE: FfiTypeKind = 6 const val FFI_TYPE_KIND_DOUBLE: FfiTypeKind = 6
const val FFI_TYPE_KIND_POINTER: FfiTypeKind = 7 const val FFI_TYPE_KIND_POINTER: FfiTypeKind = 7
const val FFI_TYPE_KIND_UINT8: FfiTypeKind = 8
const val FFI_TYPE_KIND_UINT16: FfiTypeKind = 9
const val FFI_TYPE_KIND_UINT32: FfiTypeKind = 10
const val FFI_TYPE_KIND_UINT64: FfiTypeKind = 11
private tailrec fun convertArgument( private tailrec fun convertArgument(
argument: Any?, isVariadic: Boolean, location: COpaquePointer, argument: Any?, isVariadic: Boolean, location: COpaquePointer,
additionalPlacement: AutofreeScope additionalPlacement: AutofreeScope
): FfiTypeKind = when (argument) { ): FfiTypeKind = when (argument) { // FIXME: optimize
is CValuesRef<*>? -> { is CValuesRef<*>? -> {
location.reinterpret<CPointerVar<*>>()[0] = argument?.getPointer(additionalPlacement) location.reinterpret<CPointerVar<*>>()[0] = argument?.getPointer(additionalPlacement)
FFI_TYPE_KIND_POINTER FFI_TYPE_KIND_POINTER
@@ -79,6 +83,30 @@ private tailrec fun convertArgument(
FFI_TYPE_KIND_SINT16 FFI_TYPE_KIND_SINT16
} }
is UInt -> {
location.reinterpret<UIntVar>()[0] = argument
FFI_TYPE_KIND_UINT32
}
is ULong -> {
location.reinterpret<ULongVar>()[0] = argument
FFI_TYPE_KIND_UINT64
}
is UByte -> if (isVariadic) {
convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<UByteVar>()[0] = argument
FFI_TYPE_KIND_UINT8
}
is UShort -> if (isVariadic) {
convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<UShortVar>()[0] = argument
FFI_TYPE_KIND_UINT16
}
is Double -> { is Double -> {
location.reinterpret<DoubleVar>()[0] = argument location.reinterpret<DoubleVar>()[0] = argument
FFI_TYPE_KIND_DOUBLE FFI_TYPE_KIND_DOUBLE
@@ -162,6 +162,10 @@ object KotlinTypes {
val short by BuiltInType val short by BuiltInType
val int by BuiltInType val int by BuiltInType
val long by BuiltInType val long by BuiltInType
val uByte by BuiltInType
val uShort by BuiltInType
val uInt by BuiltInType
val uLong by BuiltInType
val float by BuiltInType val float by BuiltInType
val double by BuiltInType val double by BuiltInType
val unit by BuiltInType val unit by BuiltInType
@@ -23,8 +23,12 @@ interface DeclarationMapper {
fun isMappedToStrict(enumDef: EnumDef): Boolean fun isMappedToStrict(enumDef: EnumDef): Boolean
fun getKotlinNameForValue(enumDef: EnumDef): String fun getKotlinNameForValue(enumDef: EnumDef): String
fun getPackageFor(declaration: TypeDeclaration): String fun getPackageFor(declaration: TypeDeclaration): String
val useUnsignedTypes: Boolean
} }
fun DeclarationMapper.isMappedToSigned(integerType: IntegerType): Boolean = integerType.isSigned || !useUnsignedTypes
fun DeclarationMapper.getKotlinClassFor( fun DeclarationMapper.getKotlinClassFor(
objCClassOrProtocol: ObjCClassOrProtocol, objCClassOrProtocol: ObjCClassOrProtocol,
isMeta: Boolean = false isMeta: Boolean = false
@@ -41,38 +45,46 @@ fun DeclarationMapper.getKotlinClassFor(
return Classifier.topLevel(pkg, className) return Classifier.topLevel(pkg, className)
} }
val PrimitiveType.kotlinType: KotlinClassifierType fun PrimitiveType.getKotlinType(declarationMapper: DeclarationMapper): KotlinClassifierType = when (this) {
get() = when (this) { is CharType -> KotlinTypes.byte
is CharType -> KotlinTypes.byte
is BoolType -> KotlinTypes.boolean is BoolType -> KotlinTypes.boolean
// TODO: C primitive types should probably be generated as type aliases for Kotlin types. // TODO: C primitive types should probably be generated as type aliases for Kotlin types.
is IntegerType -> when (this.size) { is IntegerType -> if (declarationMapper.isMappedToSigned(this)) {
when (this.size) {
1 -> KotlinTypes.byte 1 -> KotlinTypes.byte
2 -> KotlinTypes.short 2 -> KotlinTypes.short
4 -> KotlinTypes.int 4 -> KotlinTypes.int
8 -> KotlinTypes.long 8 -> KotlinTypes.long
else -> TODO(this.toString()) else -> TODO(this.toString())
} }
} else {
is FloatingType -> when (this.size) { when (this.size) {
4 -> KotlinTypes.float 1 -> KotlinTypes.uByte
8 -> KotlinTypes.double 2 -> KotlinTypes.uShort
4 -> KotlinTypes.uInt
8 -> KotlinTypes.uLong
else -> TODO(this.toString()) else -> TODO(this.toString())
} }
else -> throw NotImplementedError()
} }
private val PrimitiveType.bridgedType: BridgedType is FloatingType -> when (this.size) {
get() { 4 -> KotlinTypes.float
val kotlinType = this.kotlinType 8 -> KotlinTypes.double
return BridgedType.values().single { else -> TODO(this.toString())
it.kotlinType == kotlinType
}
} }
else -> throw NotImplementedError()
}
private fun PrimitiveType.getBridgedType(declarationMapper: DeclarationMapper): BridgedType {
val kotlinType = this.getKotlinType(declarationMapper)
return BridgedType.values().single {
it.kotlinType == kotlinType
}
}
internal val ObjCPointer.isNullable: Boolean internal val ObjCPointer.isNullable: Boolean
get() = this.nullability != ObjCPointer.Nullability.NonNull get() = this.nullability != ObjCPointer.Nullability.NonNull
@@ -334,16 +346,26 @@ sealed class TypeInfo {
} }
} }
fun mirrorPrimitiveType(type: PrimitiveType): TypeMirror.ByValue { fun mirrorPrimitiveType(type: PrimitiveType, declarationMapper: DeclarationMapper): TypeMirror.ByValue {
val varClassName = when (type) { val varClassName = when (type) {
is CharType -> "ByteVar" is CharType -> "ByteVar"
is BoolType -> "BooleanVar" is BoolType -> "BooleanVar"
is IntegerType -> when (type.size) { is IntegerType -> if (declarationMapper.isMappedToSigned(type)) {
1 -> "ByteVar" when (type.size) {
2 -> "ShortVar" 1 -> "ByteVar"
4 -> "IntVar" 2 -> "ShortVar"
8 -> "LongVar" 4 -> "IntVar"
else -> TODO(type.toString()) 8 -> "LongVar"
else -> TODO(type.toString())
}
} else {
when (type.size) {
1 -> "UByteVar"
2 -> "UShortVar"
4 -> "UIntVar"
8 -> "ULongVar"
else -> TODO(type.toString())
}
} }
is FloatingType -> when (type.size) { is FloatingType -> when (type.size) {
4 -> "FloatVar" 4 -> "FloatVar"
@@ -359,9 +381,9 @@ fun mirrorPrimitiveType(type: PrimitiveType): TypeMirror.ByValue {
val info = if (type == BoolType) { val info = if (type == BoolType) {
TypeInfo.Boolean() TypeInfo.Boolean()
} else { } else {
TypeInfo.Primitive(type.bridgedType, varClassOf) TypeInfo.Primitive(type.getBridgedType(declarationMapper), varClassOf)
} }
return TypeMirror.ByValue(varClass.type, info, type.kotlinType) return TypeMirror.ByValue(varClass.type, info, type.getKotlinType(declarationMapper))
} }
private fun byRefTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.ByRef { private fun byRefTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.ByRef {
@@ -370,7 +392,7 @@ private fun byRefTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.ByRe
} }
fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) { fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) {
is PrimitiveType -> mirrorPrimitiveType(type) is PrimitiveType -> mirrorPrimitiveType(type, declarationMapper)
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type) is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type)
@@ -380,7 +402,7 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
when { when {
declarationMapper.isMappedToStrict(type.def) -> { declarationMapper.isMappedToStrict(type.def) -> {
val bridgedType = (type.def.baseType.unwrapTypedefs() as PrimitiveType).bridgedType val bridgedType = (type.def.baseType.unwrapTypedefs() as PrimitiveType).getBridgedType(declarationMapper)
val clazz = Classifier.topLevel(pkg, kotlinName) val clazz = Classifier.topLevel(pkg, kotlinName)
val info = TypeInfo.Enum(clazz, bridgedType) val info = TypeInfo.Enum(clazz, bridgedType)
TypeMirror.ByValue(clazz.nested("Var").type, info, clazz.type) TypeMirror.ByValue(clazz.nested("Var").type, info, clazz.type)
@@ -24,6 +24,10 @@ enum class BridgedType(val kotlinType: KotlinClassifierType, val convertor: Stri
SHORT(KotlinTypes.short, "toShort"), SHORT(KotlinTypes.short, "toShort"),
INT(KotlinTypes.int, "toInt"), INT(KotlinTypes.int, "toInt"),
LONG(KotlinTypes.long, "toLong"), LONG(KotlinTypes.long, "toLong"),
UBYTE(KotlinTypes.uByte, "toUByte"),
USHORT(KotlinTypes.uShort, "toUShort"),
UINT(KotlinTypes.uInt, "toUInt"),
ULONG(KotlinTypes.uLong, "toULong"),
FLOAT(KotlinTypes.float, "toFloat"), FLOAT(KotlinTypes.float, "toFloat"),
DOUBLE(KotlinTypes.double, "toDouble"), DOUBLE(KotlinTypes.double, "toDouble"),
NATIVE_PTR(KotlinTypes.nativePtr), NATIVE_PTR(KotlinTypes.nativePtr),
@@ -38,6 +38,10 @@ class SimpleBridgeGeneratorImpl(
BridgedType.SHORT -> "jshort" BridgedType.SHORT -> "jshort"
BridgedType.INT -> "jint" BridgedType.INT -> "jint"
BridgedType.LONG -> "jlong" BridgedType.LONG -> "jlong"
BridgedType.UBYTE -> "jbyte"
BridgedType.USHORT -> "jshort"
BridgedType.UINT -> "jint"
BridgedType.ULONG -> "jlong"
BridgedType.FLOAT -> "jfloat" BridgedType.FLOAT -> "jfloat"
BridgedType.DOUBLE -> "jdouble" BridgedType.DOUBLE -> "jdouble"
BridgedType.NATIVE_PTR -> "jlong" BridgedType.NATIVE_PTR -> "jlong"
@@ -49,6 +53,10 @@ class SimpleBridgeGeneratorImpl(
BridgedType.SHORT -> "int16_t" BridgedType.SHORT -> "int16_t"
BridgedType.INT -> "int32_t" BridgedType.INT -> "int32_t"
BridgedType.LONG -> "int64_t" BridgedType.LONG -> "int64_t"
BridgedType.UBYTE -> "uint8_t"
BridgedType.USHORT -> "uint16_t"
BridgedType.UINT -> "uint32_t"
BridgedType.ULONG -> "uint64_t"
BridgedType.FLOAT -> "float" BridgedType.FLOAT -> "float"
BridgedType.DOUBLE -> "double" BridgedType.DOUBLE -> "double"
BridgedType.NATIVE_PTR -> "void*" BridgedType.NATIVE_PTR -> "void*"
@@ -156,6 +156,12 @@ class StubGenerator(
override fun getPackageFor(declaration: TypeDeclaration): String { override fun getPackageFor(declaration: TypeDeclaration): String {
return imports.getPackage(declaration.location) ?: pkgName return imports.getPackage(declaration.location) ?: pkgName
} }
override val useUnsignedTypes: Boolean
get() = when (platform) {
KotlinPlatform.JVM -> false
KotlinPlatform.NATIVE -> true
}
} }
fun mirror(type: Type): TypeMirror = mirror(declarationMapper, type) fun mirror(type: Type): TypeMirror = mirror(declarationMapper, type)
@@ -472,7 +478,8 @@ class StubGenerator(
block("enum class ${kotlinFile.declare(clazz)}(override val value: $baseKotlinType) : CEnum") { block("enum class ${kotlinFile.declare(clazz)}(override val value: $baseKotlinType) : CEnum") {
canonicalConstants.forEach { canonicalConstants.forEach {
out("${it.name.asSimpleName()}(${it.value}),") val literal = integerLiteral(e.baseType, it.value)!!
out("${it.name.asSimpleName()}($literal),")
} }
out(";") out(";")
out("") out("")
@@ -720,20 +727,33 @@ class StubGenerator(
} }
private fun integerLiteral(type: Type, value: Long): String? { private fun integerLiteral(type: Type, value: Long): String? {
val integerType = type.unwrapTypedefs() as? IntegerType ?: return null
return integerLiteral(integerType.size, declarationMapper.isMappedToSigned(integerType), value)
}
private fun integerLiteral(size: Int, isSigned: Boolean, value: Long): String? {
if (!isSigned) {
val signedLiteral = integerLiteral(size, true, value)
val converter = when (size) {
1 -> "toUByte"
2 -> "toUShort"
4 -> "toUInt"
8 -> "toULong"
else -> return null
}
return "($signedLiteral).$converter()"
}
if (value == Long.MIN_VALUE) { if (value == Long.MIN_VALUE) {
return "${value + 1} - 1" // Workaround for "The value is out of range" compile error. return "${value + 1} - 1" // Workaround for "The value is out of range" compile error.
} }
val unwrappedType = type.unwrapTypedefs() val narrowedValue: Number = when (size) {
if (unwrappedType !is PrimitiveType) { 1 -> value.toByte()
return null 2 -> value.toShort()
} 4 -> value.toInt()
8 -> value
val narrowedValue: Number = when (unwrappedType.kotlinType) {
KotlinTypes.byte -> value.toByte()
KotlinTypes.short -> value.toShort()
KotlinTypes.int -> value.toInt()
KotlinTypes.long -> value
else -> return null else -> return null
} }
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.cli.bc
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.Argument import org.jetbrains.kotlin.cli.common.arguments.Argument
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.AnalysisFlag
class K2NativeCompilerArguments : CommonCompilerArguments() { class K2NativeCompilerArguments : CommonCompilerArguments() {
// First go the options interesting to the general public. // First go the options interesting to the general public.
@@ -147,5 +149,11 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
description = "Paths to friend modules" description = "Paths to friend modules"
) )
var friendModules: String? = null var friendModules: String? = null
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> =
super.configureAnalysisFlags(collector).also {
val useExperimental = it[AnalysisFlag.useExperimental] as List<*>
it[AnalysisFlag.useExperimental] = useExperimental + listOf("kotlin.ExperimentalUnsignedTypes")
}
} }
@@ -16,8 +16,10 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.descriptors.konan.interop.InteropFqNames import org.jetbrains.kotlin.descriptors.konan.interop.InteropFqNames
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -90,6 +92,8 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
val narrow = packageScope.getContributedFunctions("narrow").single() val narrow = packageScope.getContributedFunctions("narrow").single()
val convert = packageScope.getContributedFunctions("convert").toSet()
val readBits = packageScope.getContributedFunctions("readBits").single() val readBits = packageScope.getContributedFunctions("readBits").single()
val writeBits = packageScope.getContributedFunctions("writeBits").single() val writeBits = packageScope.getContributedFunctions("writeBits").single()
@@ -101,6 +105,9 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == cPointer TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == cPointer
}.toSet() }.toSet()
private fun KonanBuiltIns.getUnsignedClass(unsignedType: UnsignedType): ClassDescriptor =
this.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!!
val invokeImpls = mapOf( val invokeImpls = mapOf(
builtIns.unit to "invokeImplUnitRet", builtIns.unit to "invokeImplUnitRet",
builtIns.boolean to "invokeImplBooleanRet", builtIns.boolean to "invokeImplBooleanRet",
@@ -108,6 +115,10 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
builtIns.short to "invokeImplShortRet", builtIns.short to "invokeImplShortRet",
builtIns.int to "invokeImplIntRet", builtIns.int to "invokeImplIntRet",
builtIns.long to "invokeImplLongRet", builtIns.long to "invokeImplLongRet",
builtIns.getUnsignedClass(UnsignedType.UBYTE) to "invokeImplUByteRet",
builtIns.getUnsignedClass(UnsignedType.USHORT) to "invokeImplUShortRet",
builtIns.getUnsignedClass(UnsignedType.UINT) to "invokeImplUIntRet",
builtIns.getUnsignedClass(UnsignedType.ULONG) to "invokeImplULongRet",
builtIns.float to "invokeImplFloatRet", builtIns.float to "invokeImplFloatRet",
builtIns.double to "invokeImplDoubleRet", builtIns.double to "invokeImplDoubleRet",
cPointer to "invokeImplPointerRet" cPointer to "invokeImplPointerRet"
@@ -144,6 +144,35 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val uInt = unsignedClass(UnsignedType.UINT) val uInt = unsignedClass(UnsignedType.UINT)
val uLong = unsignedClass(UnsignedType.ULONG) val uLong = unsignedClass(UnsignedType.ULONG)
val signedIntegerClasses = setOf(byte, short, int, long)
val unsignedIntegerClasses = setOf(uByte, uShort, uInt, uLong)
val allIntegerClasses = signedIntegerClasses + unsignedIntegerClasses
val integerConversions = allIntegerClasses.flatMap { fromClass ->
allIntegerClasses.map { toClass ->
val name = Name.identifier("to${toClass.descriptor.name.asString().capitalize()}")
val descriptor = if (fromClass in signedIntegerClasses && toClass in unsignedIntegerClasses) {
builtInsPackage("kotlin")
.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
.single {
it.dispatchReceiverParameter == null &&
it.extensionReceiverParameter?.type == fromClass.descriptor.defaultType &&
it.valueParameters.isEmpty()
}
} else {
fromClass.descriptor.unsubstitutedMemberScope
.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
.single {
it.extensionReceiverParameter == null && it.valueParameters.isEmpty()
}
}
val symbol = symbolTable.referenceSimpleFunction(descriptor)
(fromClass to toClass) to symbol
}
}.toMap()
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context)) val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
val interopNativePointedGetRawPointer = val interopNativePointedGetRawPointer =
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.irasdescriptors.* import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
@@ -901,6 +902,35 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
} }
} }
in interop.convert -> {
val integerClasses = symbols.allIntegerClasses
val typeOperand = expression.getTypeArgument(0)!!
val receiverType = expression.symbol.owner.extensionReceiverParameter!!.type
val receiverClass = receiverType.classifierOrFail as IrClassSymbol
assert(receiverClass in integerClasses)
if (typeOperand is IrSimpleType && typeOperand.classifier in integerClasses && !typeOperand.hasQuestionMark) {
val typeOperandClass = typeOperand.classifier as IrClassSymbol
val conversion = symbols.integerConversions[receiverClass to typeOperandClass]!!.owner
builder.irCall(conversion).apply {
val valueToConvert = expression.extensionReceiver!!
if (conversion.dispatchReceiverParameter != null) {
dispatchReceiver = valueToConvert
} else {
extensionReceiver = valueToConvert
}
}
} else {
context.reportCompilationError(
"unable to convert ${receiverType.toKotlinType()} to ${typeOperand.toKotlinType()}",
irFile,
expression
)
}
}
in interop.cFunctionPointerInvokes -> { in interop.cFunctionPointerInvokes -> {
// Replace by `invokeImpl${type}Ret`: // Replace by `invokeImpl${type}Ret`:
@@ -968,6 +998,10 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
return return
} }
if (UnsignedTypes.isUnsignedType(this.toKotlinType()) && !this.containsNull()) {
return
}
if (this.getClass()?.descriptor == interop.cPointer) { if (this.getClass()?.descriptor == interop.cPointer) {
return return
} }
@@ -976,7 +1010,7 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
} }
private fun IrType.checkCTypeNullability(reportError: (String) -> Nothing) { private fun IrType.checkCTypeNullability(reportError: (String) -> Nothing) {
if (this.isNullablePrimitiveType()) { if (this.isNullablePrimitiveType() || UnsignedTypes.isUnsignedType(this.toKotlinType()) && this.containsNull()) {
reportError("Type ${this.toKotlinType()} must not be nullable when used in C function signature") reportError("Type ${this.toKotlinType()} must not be nullable when used in C function signature")
} }
+1 -1
View File
@@ -4,7 +4,7 @@ fun main(args: Array<String>) {
val values = intArrayOf(14, 12, 9, 13, 8) val values = intArrayOf(14, 12, 9, 13, 8)
val count = values.size val count = values.size
cstdlib.qsort(values.refTo(0), count.toLong(), IntVar.size, staticCFunction { a, b -> cstdlib.qsort(values.refTo(0), count.toULong(), IntVar.size.convert(), staticCFunction { a, b ->
val aValue = a!!.reinterpret<IntVar>()[0] val aValue = a!!.reinterpret<IntVar>()[0]
val bValue = b!!.reinterpret<IntVar>()[0] val bValue = b!!.reinterpret<IntVar>()[0]
+8 -8
View File
@@ -6,7 +6,7 @@ fun assertEquals(value1: Any?, value2: Any?) {
throw AssertionError("Expected $value1, got $value2") throw AssertionError("Expected $value1, got $value2")
} }
fun check(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) { fun check(s: S, x1: Long, x2: B2, x3: UShort, x4: UInt, x5: Int, x6: Long) {
assertEquals(x1, s.x1) assertEquals(x1, s.x1)
assertEquals(x1, getX1(s.ptr)) assertEquals(x1, getX1(s.ptr))
@@ -26,7 +26,7 @@ fun check(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) {
assertEquals(x6, getX6(s.ptr)) assertEquals(x6, getX6(s.ptr))
} }
fun assign(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) { fun assign(s: S, x1: Long, x2: B2, x3: UShort, x4: UInt, x5: Int, x6: Long) {
s.x1 = x1 s.x1 = x1
s.x2 = x2 s.x2 = x2
s.x3 = x3 s.x3 = x3
@@ -35,7 +35,7 @@ fun assign(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) {
s.x6 = x6 s.x6 = x6
} }
fun assignReversed(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) { fun assignReversed(s: S, x1: Long, x2: B2, x3: UShort, x4: UInt, x5: Int, x6: Long) {
s.x6 = x6 s.x6 = x6
s.x5 = x5 s.x5 = x5
s.x4 = x4 s.x4 = x4
@@ -44,7 +44,7 @@ fun assignReversed(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long
s.x1 = x1 s.x1 = x1
} }
fun test(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) { fun test(s: S, x1: Long, x2: B2, x3: UShort, x4: UInt, x5: Int, x6: Long) {
assign(s, x1, x2, x3, x4, x5, x6) assign(s, x1, x2, x3, x4, x5, x6)
check(s, x1, x2, x3, x4, x5, x6) check(s, x1, x2, x3, x4, x5, x6)
@@ -53,10 +53,10 @@ fun test(s: S, x1: Long, x2: B2, x3: Short, x4: Int, x5: Int, x6: Long) {
// Also check with some insignificant bits modified: // Also check with some insignificant bits modified:
assign(s, x1 + 2, x2, (x3 + 8).toShort(), x4 - 16, x5 + 32, x6 + Long.MIN_VALUE) assign(s, x1 + 2, x2, (x3 + 8u).toUShort(), x4 - 16u, x5 + 32, x6 + Long.MIN_VALUE)
check(s, x1, x2, x3, x4, x5, x6) check(s, x1, x2, x3, x4, x5, x6)
assignReversed(s, x1 + 2, x2, (x3 + 8).toShort(), x4 - 16, x5 + 32, x6 + Long.MIN_VALUE) assignReversed(s, x1 + 2, x2, (x3 + 8u).toUShort(), x4 - 16u, x5 + 32, x6 + Long.MIN_VALUE)
check(s, x1, x2, x3, x4, x5, x6) check(s, x1, x2, x3, x4, x5, x6)
} }
@@ -66,9 +66,9 @@ fun main(args: Array<String>) {
for (x1 in -1L..0L) for (x1 in -1L..0L)
for (x2 in B2.values()) for (x2 in B2.values())
for (x3 in 0..7) for (x3 in 0..7)
for (x4 in intArrayOf(0, 6, 15)) for (x4 in uintArrayOf(0u, 6u, 15u))
for (x5 in intArrayOf(-16, -2, -1, 0, 5, 15)) for (x5 in intArrayOf(-16, -2, -1, 0, 5, 15))
for (x6 in longArrayOf(Long.MIN_VALUE/2, -1L shl 36, -325L, 0, 1L shl 48, Long.MAX_VALUE/2)) for (x6 in longArrayOf(Long.MIN_VALUE/2, -1L shl 36, -325L, 0, 1L shl 48, Long.MAX_VALUE/2))
test(s, x1, x2, x3.toShort(), x4, x5, x6) test(s, x1, x2, x3.toUShort(), x4, x5, x6)
} }
} }
@@ -52,3 +52,11 @@ typedef _Bool (*isIntPositivePtrType)(int);
static isIntPositivePtrType getIsIntPositivePtr() { static isIntPositivePtrType getIsIntPositivePtr() {
return &__isIntPositive; return &__isIntPositive;
} }
static unsigned int getMaxUInt(void) {
return 0xffffffff;
}
static typeof(&getMaxUInt) getMaxUIntGetter() {
return &getMaxUInt;
}
@@ -7,7 +7,7 @@ fun main(args: Array<String>) {
return return
} }
val port = atoi(args[0]).toShort() val port = atoi(args[0]).toUShort()
memScoped { memScoped {
@@ -19,13 +19,13 @@ fun main(args: Array<String>) {
.ensureUnixCallResult { it >= 0 } .ensureUnixCallResult { it >= 0 }
with(serverAddr) { with(serverAddr) {
memset(this.ptr, 0, sockaddr_in.size) memset(this.ptr, 0, sockaddr_in.size.convert())
sin_family = AF_INET.narrow() sin_family = AF_INET.convert()
sin_addr.s_addr = htons(0).toInt() sin_addr.s_addr = htons(0u).convert()
sin_port = htons(port) sin_port = htons(port)
} }
bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.toInt()) bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.toUInt())
.ensureUnixCallResult { it == 0 } .ensureUnixCallResult { it == 0 }
listen(listenFd, 10) listen(listenFd, 10)
@@ -35,21 +35,21 @@ fun main(args: Array<String>) {
.ensureUnixCallResult { it >= 0 } .ensureUnixCallResult { it >= 0 }
while (true) { while (true) {
val length = read(commFd, buffer, bufferLength) val length = read(commFd, buffer, bufferLength.convert())
.ensureUnixCallResult { it >= 0 } .ensureUnixCallResult { it >= 0 }
if (length == 0L) { if (length == 0L) {
break break
} }
write(commFd, buffer, length) write(commFd, buffer, length.convert())
.ensureUnixCallResult { it >= 0 } .ensureUnixCallResult { it >= 0 }
} }
} }
} }
// Not available through interop because declared as macro: // Not available through interop because declared as macro:
fun htons(value: Short) = ((value.toInt() ushr 8) or (value.toInt() shl 8)).toShort() fun htons(value: UShort) = ((value.toInt() ushr 8) or (value.toInt() shl 8)).toUShort()
fun throwUnixError(): Nothing { fun throwUnixError(): Nothing {
perror(null) // TODO: store error message to exception instead. perror(null) // TODO: store error message to exception instead.
@@ -1,5 +1,6 @@
import kotlinx.cinterop.* import kotlinx.cinterop.*
import cfunptr.* import cfunptr.*
import kotlin.test.*
fun main(args: Array<String>) { fun main(args: Array<String>) {
val atoiPtr = getAtoiPtr()!! val atoiPtr = getAtoiPtr()!!
@@ -23,6 +24,8 @@ fun main(args: Array<String>) {
printIntPtr(isIntPositivePtr(42).ifThenOneElseZero()) printIntPtr(isIntPositivePtr(42).ifThenOneElseZero())
printIntPtr(isIntPositivePtr(-42).ifThenOneElseZero()) printIntPtr(isIntPositivePtr(-42).ifThenOneElseZero())
assertEquals(getMaxUIntGetter()!!(), UInt.MAX_VALUE)
} }
fun Boolean.ifThenOneElseZero() = if (this) 1 else 0 fun Boolean.ifThenOneElseZero() = if (this) 1 else 0
@@ -13,7 +13,7 @@ private val windowHeight = 480
fun display() { fun display() {
// Clear Screen and Depth Buffer // Clear Screen and Depth Buffer
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) glClear((GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT).convert())
glLoadIdentity() glLoadIdentity()
// Define a viewing transformation // Define a viewing transformation
@@ -40,13 +40,13 @@ fun display() {
fun initialize() { fun initialize() {
// select projection matrix // select projection matrix
glMatrixMode(GL_PROJECTION) glMatrixMode(GL_PROJECTION.convert())
// set the viewport // set the viewport
glViewport(0, 0, windowWidth, windowHeight) glViewport(0, 0, windowWidth, windowHeight)
// set matrix mode // set matrix mode
glMatrixMode(GL_PROJECTION) glMatrixMode(GL_PROJECTION.convert())
// reset projection matrix // reset projection matrix
glLoadIdentity() glLoadIdentity()
@@ -56,29 +56,29 @@ fun initialize() {
gluPerspective(45.0, aspect, 1.0, 500.0) gluPerspective(45.0, aspect, 1.0, 500.0)
// specify which matrix is the current matrix // specify which matrix is the current matrix
glMatrixMode(GL_MODELVIEW) glMatrixMode(GL_MODELVIEW.convert())
glShadeModel(GL_SMOOTH) glShadeModel(GL_SMOOTH.convert())
// specify the clear value for the depth buffer // specify the clear value for the depth buffer
glClearDepth(1.0) glClearDepth(1.0)
glEnable(GL_DEPTH_TEST) glEnable(GL_DEPTH_TEST.convert())
glDepthFunc(GL_LEQUAL) glDepthFunc(GL_LEQUAL.convert())
// specify implementation-specific hints // specify implementation-specific hints
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) glHint(GL_PERSPECTIVE_CORRECTION_HINT.convert(), GL_NICEST.convert())
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, cValuesOf(0.1f, 0.1f, 0.1f, 1.0f)) glLightModelfv(GL_LIGHT_MODEL_AMBIENT.convert(), cValuesOf(0.1f, 0.1f, 0.1f, 1.0f))
glLightfv(GL_LIGHT0, GL_DIFFUSE, cValuesOf(0.6f, 0.6f, 0.6f, 1.0f)) glLightfv(GL_LIGHT0.convert(), GL_DIFFUSE.convert(), cValuesOf(0.6f, 0.6f, 0.6f, 1.0f))
glLightfv(GL_LIGHT0, GL_SPECULAR, cValuesOf(0.7f, 0.7f, 0.3f, 1.0f)) glLightfv(GL_LIGHT0.convert(), GL_SPECULAR.convert(), cValuesOf(0.7f, 0.7f, 0.3f, 1.0f))
glEnable(GL_LIGHT0) glEnable(GL_LIGHT0.convert())
glEnable(GL_COLOR_MATERIAL) glEnable(GL_COLOR_MATERIAL.convert())
glShadeModel(GL_SMOOTH) glShadeModel(GL_SMOOTH.convert())
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE.convert(), GL_FALSE)
glDepthFunc(GL_LEQUAL) glDepthFunc(GL_LEQUAL.convert())
glEnable(GL_DEPTH_TEST) glEnable(GL_DEPTH_TEST.convert())
glEnable(GL_LIGHTING) glEnable(GL_LIGHTING.convert())
glEnable(GL_LIGHT0) glEnable(GL_LIGHT0.convert())
glClearColor(0.0f, 0.0f, 0.0f, 1.0f) glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
} }
@@ -90,7 +90,7 @@ fun main(args: Array<String>) {
} }
// Display Mode // Display Mode
glutInitDisplayMode(GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH) glutInitDisplayMode((GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH).convert())
// Set window size // Set window size
glutInitWindowSize(windowWidth, windowHeight) glutInitWindowSize(windowWidth, windowHeight)
+2 -2
View File
@@ -22,8 +22,8 @@ fun main(args: Array<String>) {
val destPtr = alloc<CArrayPointerVar<ByteVar>>() val destPtr = alloc<CArrayPointerVar<ByteVar>>()
destPtr.value = destBytes destPtr.value = destBytes
sourceLength.value = sourceByteArray.size.signExtend(); sourceLength.value = sourceByteArray.size.convert()
destLength.value = golden.size.signExtend(); destLength.value = golden.size.convert()
val conversion = iconv_open("UTF-8", "LATIN1") val conversion = iconv_open("UTF-8", "LATIN1")
+3 -3
View File
@@ -118,9 +118,9 @@ fun testTypeOps() {
assertTrue(NSValue.asAny() is NSObjectProtocolMeta) assertTrue(NSValue.asAny() is NSObjectProtocolMeta)
assertFalse(NSValue.asAny() is NSObjectProtocol) // Must be true, but not implemented properly yet. assertFalse(NSValue.asAny() is NSObjectProtocol) // Must be true, but not implemented properly yet.
assertEquals(3, ("foo" as NSString).length()) assertEquals(3u, ("foo" as NSString).length())
assertEquals(4, ((1..4).joinToString("") as NSString).length()) assertEquals(4u, ((1..4).joinToString("") as NSString).length())
assertEquals(2, (listOf(0, 1) as NSArray).count()) assertEquals(2u, (listOf(0, 1) as NSArray).count())
assertEquals(42L, (42 as NSNumber).longLongValue()) assertEquals(42L, (42 as NSNumber).longLongValue())
assertFails { "bar" as NSNumber } assertFails { "bar" as NSNumber }
@@ -2,7 +2,7 @@ import kotlinx.cinterop.*
import platform.zlib.* import platform.zlib.*
val source = immutableBlobOf(0xF3, 0x48, 0xCD, 0xC9, 0xC9, 0x57, 0x04, 0x00).asCPointer() val source = immutableBlobOf(0xF3, 0x48, 0xCD, 0xC9, 0xC9, 0x57, 0x04, 0x00).asCPointer().reinterpret<UByteVar>()
val golden = immutableBlobOf(0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21, 0x00).asCPointer() val golden = immutableBlobOf(0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21, 0x00).asCPointer()
fun main(args: Array<String>) = memScoped { fun main(args: Array<String>) = memScoped {
@@ -10,9 +10,9 @@ fun main(args: Array<String>) = memScoped {
buffer.usePinned { pinned -> buffer.usePinned { pinned ->
val z = alloc<z_stream>().apply { val z = alloc<z_stream>().apply {
next_in = source next_in = source
avail_in = 8 avail_in = 8u
next_out = pinned.addressOf(0) next_out = pinned.addressOf(0).reinterpret()
avail_out = buffer.size avail_out = buffer.size.toUInt()
}.ptr }.ptr
if (inflateInit2(z, -15) == Z_OK && inflate(z, Z_FINISH) == Z_STREAM_END && inflateEnd(z) == Z_OK) if (inflateInit2(z, -15) == Z_OK && inflate(z, Z_FINISH) == Z_STREAM_END && inflateEnd(z) == Z_OK)
+2 -2
View File
@@ -1,8 +1,8 @@
import kotlinx.cinterop.signExtend import kotlinx.cinterop.convert
import platform.posix.* import platform.posix.*
fun main(args: Array<String>) { fun main(args: Array<String>) {
// Just check the typealias is in scope. // Just check the typealias is in scope.
val sizet: size_t = 0.signExtend<size_t>() val sizet: size_t = 0.convert<size_t>()
println("sizet = $sizet") println("sizet = $sizet")
} }
+2 -2
View File
@@ -1,9 +1,9 @@
import kotlinx.cinterop.signExtend import kotlinx.cinterop.convert
import platform.posix.* import platform.posix.*
fun foo() { fun foo() {
println("linked library") println("linked library")
val size: size_t = 17.signExtend<size_t>() val size: size_t = 17.convert<size_t>()
val e = fabs(1.toDouble()) val e = fabs(1.toDouble())
println("and symbols from posix available: $size; $e") println("and symbols from posix available: $size; $e")
} }
+8
View File
@@ -40,6 +40,10 @@ const FfiTypeKind FFI_TYPE_KIND_SINT64 = 4;
const FfiTypeKind FFI_TYPE_KIND_FLOAT = 5; const FfiTypeKind FFI_TYPE_KIND_FLOAT = 5;
const FfiTypeKind FFI_TYPE_KIND_DOUBLE = 6; const FfiTypeKind FFI_TYPE_KIND_DOUBLE = 6;
const FfiTypeKind FFI_TYPE_KIND_POINTER = 7; const FfiTypeKind FFI_TYPE_KIND_POINTER = 7;
const FfiTypeKind FFI_TYPE_KIND_UINT8 = 8;
const FfiTypeKind FFI_TYPE_KIND_UINT16 = 9;
const FfiTypeKind FFI_TYPE_KIND_UINT32 = 10;
const FfiTypeKind FFI_TYPE_KIND_UINT64 = 11;
ffi_type* convertFfiTypeKindToType(FfiTypeKind typeKind) { ffi_type* convertFfiTypeKindToType(FfiTypeKind typeKind) {
switch (typeKind) { switch (typeKind) {
@@ -51,6 +55,10 @@ ffi_type* convertFfiTypeKindToType(FfiTypeKind typeKind) {
case FFI_TYPE_KIND_FLOAT: return &ffi_type_float; case FFI_TYPE_KIND_FLOAT: return &ffi_type_float;
case FFI_TYPE_KIND_DOUBLE: return &ffi_type_double; case FFI_TYPE_KIND_DOUBLE: return &ffi_type_double;
case FFI_TYPE_KIND_POINTER: return &ffi_type_pointer; case FFI_TYPE_KIND_POINTER: return &ffi_type_pointer;
case FFI_TYPE_KIND_UINT8: return &ffi_type_uint8;
case FFI_TYPE_KIND_UINT16: return &ffi_type_uint16;
case FFI_TYPE_KIND_UINT32: return &ffi_type_uint32;
case FFI_TYPE_KIND_UINT64: return &ffi_type_uint64;
default: assert(false); return nullptr; default: assert(false); return nullptr;
} }
@@ -45,7 +45,7 @@ fun CPointer<ByteVar>.toKString(length: Int): String {
} }
fun header_callback(buffer: CPointer<ByteVar>?, size: size_t, nitems: size_t, userdata: COpaquePointer?): size_t { fun header_callback(buffer: CPointer<ByteVar>?, size: size_t, nitems: size_t, userdata: COpaquePointer?): size_t {
if (buffer == null) return 0 if (buffer == null) return 0u
if (userdata != null) { if (userdata != null) {
val header = buffer.toKString((size * nitems).toInt()).trim() val header = buffer.toKString((size * nitems).toInt()).trim()
val curl = userdata.asStableRef<CUrl>().get() val curl = userdata.asStableRef<CUrl>().get()
@@ -56,7 +56,7 @@ fun header_callback(buffer: CPointer<ByteVar>?, size: size_t, nitems: size_t, us
fun write_callback(buffer: CPointer<ByteVar>?, size: size_t, nitems: size_t, userdata: COpaquePointer?): size_t { fun write_callback(buffer: CPointer<ByteVar>?, size: size_t, nitems: size_t, userdata: COpaquePointer?): size_t {
if (buffer == null) return 0 if (buffer == null) return 0u
if (userdata != null) { if (userdata != null) {
val data = buffer.toKString((size * nitems).toInt()).trim() val data = buffer.toKString((size * nitems).toInt()).trim()
val curl = userdata.asStableRef<CUrl>().get() val curl = userdata.asStableRef<CUrl>().get()
@@ -32,11 +32,11 @@ class GitCommit(val repository: GitRepository, val commit: CPointer<git_commit>)
} }
val parents: List<GitCommit> get() = memScoped { val parents: List<GitCommit> get() = memScoped {
val count = git_commit_parentcount(commit) val count = git_commit_parentcount(commit).toInt()
val result = ArrayList<GitCommit>(count) val result = ArrayList<GitCommit>(count)
for (index in 0..count - 1) { for (index in 0..count - 1) {
val commitPtr = allocPointerTo<git_commit>() val commitPtr = allocPointerTo<git_commit>()
git_commit_parent(commitPtr.ptr, commit, index).errorCheck() git_commit_parent(commitPtr.ptr, commit, index.toUInt()).errorCheck()
result.add(GitCommit(repository, commitPtr.value!!)) result.add(GitCommit(repository, commitPtr.value!!))
} }
result result
@@ -21,10 +21,10 @@ import libgit2.*
class GitDiff(val repository: GitRepository, val handle: CPointer<git_diff>) { class GitDiff(val repository: GitRepository, val handle: CPointer<git_diff>) {
fun deltas(): List<GifDiffDelta> { fun deltas(): List<GifDiffDelta> {
val size = git_diff_num_deltas(handle) val size = git_diff_num_deltas(handle).toInt()
val results = ArrayList<GifDiffDelta>(size.toInt()) val results = ArrayList<GifDiffDelta>(size)
for (index in 0..size - 1) { for (index in 0..size - 1) {
val delta = git_diff_get_delta(handle, index) val delta = git_diff_get_delta(handle, index.convert())
results.add(GifDiffDelta(this, delta!!)) results.add(GifDiffDelta(this, delta!!))
} }
return results return results
@@ -23,10 +23,10 @@ class GitTree(val repository: GitRepository, val handle: CPointer<git_tree>) {
fun close() = git_tree_free(handle) fun close() = git_tree_free(handle)
fun entries(): List<GitTreeEntry> = memScoped { fun entries(): List<GitTreeEntry> = memScoped {
val size = git_tree_entrycount(handle) val size = git_tree_entrycount(handle).toInt()
val entries = ArrayList<GitTreeEntry>(size.toInt()) val entries = ArrayList<GitTreeEntry>(size)
for (index in 0..size - 1) { for (index in 0..size - 1) {
val treeEntry = git_tree_entry_byindex(handle, index)!! val treeEntry = git_tree_entry_byindex(handle, index.convert())!!
val entryType = git_tree_entry_type(treeEntry) val entryType = git_tree_entry_type(treeEntry)
val entry = when (entryType) { val entry = when (entryType) {
GIT_OBJ_TREE -> memScoped { GIT_OBJ_TREE -> memScoped {
+1 -1
View File
@@ -19,7 +19,7 @@ import gtk3.*
// Note that all callback parameters must be primitive types or nullable C pointers. // Note that all callback parameters must be primitive types or nullable C pointers.
fun <F : CFunction<*>> g_signal_connect(obj: CPointer<*>, actionName: String, fun <F : CFunction<*>> g_signal_connect(obj: CPointer<*>, actionName: String,
action: CPointer<F>, data: gpointer? = null, connect_flags: Int = 0) { action: CPointer<F>, data: gpointer? = null, connect_flags: GConnectFlags = 0u) {
g_signal_connect_data(obj.reinterpret(), actionName, action.reinterpret(), g_signal_connect_data(obj.reinterpret(), actionName, action.reinterpret(),
data = data, destroy_data = null, connect_flags = connect_flags) data = data, destroy_data = null, connect_flags = connect_flags)
@@ -32,16 +32,16 @@ fun main(args: Array<String>) {
val serverAddr = alloc<sockaddr_in>() val serverAddr = alloc<sockaddr_in>()
val listenFd = socket(AF_INET, SOCK_STREAM, 0) val listenFd = socket(AF_INET, SOCK_STREAM, 0)
.ensureUnixCallResult { it >= 0 } .ensureUnixCallResult { !it.isMinusOne() }
with(serverAddr) { with(serverAddr) {
memset(this.ptr, 0, sockaddr_in.size) memset(this.ptr, 0, sockaddr_in.size.convert())
sin_family = AF_INET.narrow() sin_family = AF_INET.convert()
sin_addr.s_addr = posix_htons(0).toInt() sin_addr.s_addr = posix_htons(0).convert()
sin_port = posix_htons(port) sin_port = posix_htons(port).convert()
} }
bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.toInt()) bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.toUInt())
.ensureUnixCallResult { it == 0 } .ensureUnixCallResult { it == 0 }
fcntl(listenFd, F_SETFL, O_NONBLOCK) fcntl(listenFd, F_SETFL, O_NONBLOCK)
@@ -53,8 +53,8 @@ fun main(args: Array<String>) {
var connectionId = 0 var connectionId = 0
acceptClientsAndRun(listenFd) { acceptClientsAndRun(listenFd) {
memScoped { memScoped {
val bufferLength = 100L val bufferLength = 100uL
val buffer = allocArray<ByteVar>(bufferLength) val buffer = allocArray<ByteVar>(bufferLength.toLong())
val connectionIdString = "#${++connectionId}: ".cstr val connectionIdString = "#${++connectionId}: ".cstr
val connectionIdBytes = connectionIdString.ptr val connectionIdBytes = connectionIdString.ptr
@@ -62,10 +62,10 @@ fun main(args: Array<String>) {
while (true) { while (true) {
val length = read(buffer, bufferLength) val length = read(buffer, bufferLength)
if (length == 0L) if (length == 0uL)
break break
write(connectionIdBytes, connectionIdString.size.toLong()) write(connectionIdBytes, connectionIdString.size.toULong())
write(buffer, length) write(buffer, length)
} }
} catch (e: IOException) { } catch (e: IOException) {
@@ -80,19 +80,19 @@ sealed class WaitingFor {
class Accept : WaitingFor() class Accept : WaitingFor()
class Read(val data: CArrayPointer<ByteVar>, class Read(val data: CArrayPointer<ByteVar>,
val length: Long, val length: ULong,
val continuation: Continuation<Long>) : WaitingFor() val continuation: Continuation<ULong>) : WaitingFor()
class Write(val data: CArrayPointer<ByteVar>, class Write(val data: CArrayPointer<ByteVar>,
val length: Long, val length: ULong,
val continuation: Continuation<Unit>) : WaitingFor() val continuation: Continuation<Unit>) : WaitingFor()
} }
class Client(val clientFd: Int, val waitingList: MutableMap<Int, WaitingFor>) { class Client(val clientFd: Int, val waitingList: MutableMap<Int, WaitingFor>) {
suspend fun read(data: CArrayPointer<ByteVar>, dataLength: Long): Long { suspend fun read(data: CArrayPointer<ByteVar>, dataLength: ULong): ULong {
val length = read(clientFd, data, dataLength) val length = read(clientFd, data, dataLength)
if (length >= 0) if (length >= 0)
return length return length.toULong()
if (posix_errno() != EWOULDBLOCK) if (posix_errno() != EWOULDBLOCK)
throw IOException(getUnixError()) throw IOException(getUnixError())
// Save continuation and suspend. // Save continuation and suspend.
@@ -101,7 +101,7 @@ class Client(val clientFd: Int, val waitingList: MutableMap<Int, WaitingFor>) {
} }
} }
suspend fun write(data: CArrayPointer<ByteVar>, length: Long) { suspend fun write(data: CArrayPointer<ByteVar>, length: ULong) {
val written = write(clientFd, data, length) val written = write(clientFd, data, length)
if (written >= 0) if (written >= 0)
return return
@@ -153,7 +153,7 @@ fun acceptClientsAndRun(serverFd: Int, block: suspend Client.() -> Unit) {
// Accept new client. // Accept new client.
val clientFd = accept(serverFd, null, null) val clientFd = accept(serverFd, null, null)
if (clientFd < 0) { if (clientFd.isMinusOne()) {
if (posix_errno() != EWOULDBLOCK) if (posix_errno() != EWOULDBLOCK)
throw Error(getUnixError()) throw Error(getUnixError())
break@loop break@loop
@@ -173,7 +173,7 @@ fun acceptClientsAndRun(serverFd: Int, block: suspend Client.() -> Unit) {
val length = read(socketFd, waitingFor.data, waitingFor.length) val length = read(socketFd, waitingFor.data, waitingFor.length)
if (length < 0) // Read error. if (length < 0) // Read error.
waitingFor.continuation.resumeWithException(IOException(getUnixError())) waitingFor.continuation.resumeWithException(IOException(getUnixError()))
waitingFor.continuation.resume(length) waitingFor.continuation.resume(length.toULong())
} }
is WaitingFor.Write -> { is WaitingFor.Write -> {
if (errorOccured) if (errorOccured)
@@ -210,3 +210,14 @@ inline fun Long.ensureUnixCallResult(predicate: (Long) -> Boolean): Long {
} }
return this return this
} }
inline fun ULong.ensureUnixCallResult(predicate: (ULong) -> Boolean): ULong {
if (!predicate(this)) {
throw Error(getUnixError())
}
return this
}
private fun Int.isMinusOne() = (this == -1)
private fun Long.isMinusOne() = (this == -1L)
private fun ULong.isMinusOne() = (this == ULong.MAX_VALUE)
+2 -2
View File
@@ -24,7 +24,7 @@ private fun runApp() {
app.run() app.run()
} }
data class Data(val stamp: Long) data class Data(val stamp: ULong)
private class Controller : NSObject() { private class Controller : NSObject() {
private val asyncQueue = dispatch_queue_create("com.jetbrains.CustomQueue", null) private val asyncQueue = dispatch_queue_create("com.jetbrains.CustomQueue", null)
@@ -33,7 +33,7 @@ private class Controller : NSObject() {
fun onClick() { fun onClick() {
// Execute some async action on button click. // Execute some async action on button click.
dispatch_async_f(asyncQueue, detachObjectGraph { dispatch_async_f(asyncQueue, detachObjectGraph {
Data(clock_gettime_nsec_np(CLOCK_REALTIME)) Data(clock_gettime_nsec_np(CLOCK_REALTIME.convert()))
}, staticCFunction { }, staticCFunction {
it -> it ->
initRuntimeIfNeeded() initRuntimeIfNeeded()
+20 -20
View File
@@ -29,7 +29,7 @@ private val windowHeight = 480
fun display() { fun display() {
// Clear Screen and Depth Buffer // Clear Screen and Depth Buffer
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) glClear((GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT).convert())
glLoadIdentity() glLoadIdentity()
// Define a viewing transformation // Define a viewing transformation
@@ -56,13 +56,13 @@ fun display() {
fun initialize() { fun initialize() {
// select projection matrix // select projection matrix
glMatrixMode(GL_PROJECTION) glMatrixMode(GL_PROJECTION.convert())
// set the viewport // set the viewport
glViewport(0, 0, windowWidth, windowHeight) glViewport(0, 0, windowWidth, windowHeight)
// set matrix mode // set matrix mode
glMatrixMode(GL_PROJECTION) glMatrixMode(GL_PROJECTION.convert())
// reset projection matrix // reset projection matrix
glLoadIdentity() glLoadIdentity()
@@ -72,29 +72,29 @@ fun initialize() {
gluPerspective(45.0, aspect, 1.0, 500.0) gluPerspective(45.0, aspect, 1.0, 500.0)
// specify which matrix is the current matrix // specify which matrix is the current matrix
glMatrixMode(GL_MODELVIEW) glMatrixMode(GL_MODELVIEW.convert())
glShadeModel(GL_SMOOTH) glShadeModel(GL_SMOOTH.convert())
// specify the clear value for the depth buffer // specify the clear value for the depth buffer
glClearDepth(1.0) glClearDepth(1.0)
glEnable(GL_DEPTH_TEST) glEnable(GL_DEPTH_TEST.convert())
glDepthFunc(GL_LEQUAL) glDepthFunc(GL_LEQUAL.convert())
// specify implementation-specific hints // specify implementation-specific hints
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) glHint(GL_PERSPECTIVE_CORRECTION_HINT.convert(), GL_NICEST.convert())
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, cValuesOf(0.1f, 0.1f, 0.1f, 1.0f)) glLightModelfv(GL_LIGHT_MODEL_AMBIENT.convert(), cValuesOf(0.1f, 0.1f, 0.1f, 1.0f))
glLightfv(GL_LIGHT0, GL_DIFFUSE, cValuesOf(0.6f, 0.6f, 0.6f, 1.0f)) glLightfv(GL_LIGHT0.convert(), GL_DIFFUSE.convert(), cValuesOf(0.6f, 0.6f, 0.6f, 1.0f))
glLightfv(GL_LIGHT0, GL_SPECULAR, cValuesOf(0.7f, 0.7f, 0.3f, 1.0f)) glLightfv(GL_LIGHT0.convert(), GL_SPECULAR.convert(), cValuesOf(0.7f, 0.7f, 0.3f, 1.0f))
glEnable(GL_LIGHT0) glEnable(GL_LIGHT0.convert())
glEnable(GL_COLOR_MATERIAL) glEnable(GL_COLOR_MATERIAL.convert())
glShadeModel(GL_SMOOTH) glShadeModel(GL_SMOOTH.convert())
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE.convert(), GL_FALSE)
glDepthFunc(GL_LEQUAL) glDepthFunc(GL_LEQUAL.convert())
glEnable(GL_DEPTH_TEST) glEnable(GL_DEPTH_TEST.convert())
glEnable(GL_LIGHTING) glEnable(GL_LIGHTING.convert())
glEnable(GL_LIGHT0) glEnable(GL_LIGHT0.convert())
glClearColor(0.0f, 0.0f, 0.0f, 1.0f) glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
} }
@@ -106,7 +106,7 @@ fun main(args: Array<String>) {
} }
// Display Mode // Display Mode
glutInitDisplayMode(GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH) glutInitDisplayMode((GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH).convert())
// Set window size // Set window size
glutInitWindowSize(windowWidth, windowHeight) glutInitWindowSize(windowWidth, windowHeight)
+20 -9
View File
@@ -35,35 +35,35 @@ fun main(args: Array<String>) {
val serverAddr = alloc<sockaddr_in>() val serverAddr = alloc<sockaddr_in>()
val listenFd = socket(AF_INET, SOCK_STREAM, 0) val listenFd = socket(AF_INET, SOCK_STREAM, 0)
.ensureUnixCallResult("socket") { it >= 0 } .ensureUnixCallResult("socket") { !it.isMinusOne() }
with(serverAddr) { with(serverAddr) {
memset(this.ptr, 0, sockaddr_in.size) memset(this.ptr, 0, sockaddr_in.size.convert())
sin_family = AF_INET.narrow() sin_family = AF_INET.convert()
sin_port = posix_htons(port) sin_port = posix_htons(port).convert()
} }
bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.toInt()) bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.convert())
.ensureUnixCallResult("bind") { it == 0 } .ensureUnixCallResult("bind") { it == 0 }
listen(listenFd, 10) listen(listenFd, 10)
.ensureUnixCallResult("listen") { it == 0 } .ensureUnixCallResult("listen") { it == 0 }
val commFd = accept(listenFd, null, null) val commFd = accept(listenFd, null, null)
.ensureUnixCallResult("accept") { it >= 0 } .ensureUnixCallResult("accept") { !it.isMinusOne() }
buffer.usePinned { pinned -> buffer.usePinned { pinned ->
while (true) { while (true) {
val length = recv(commFd, pinned.addressOf(0), buffer.size.signExtend(), 0).toInt() val length = recv(commFd, pinned.addressOf(0), buffer.size.convert(), 0).toInt()
.ensureUnixCallResult("read") { it >= 0 } .ensureUnixCallResult("read") { it >= 0 }
if (length == 0) { if (length == 0) {
break break
} }
send(commFd, prefixBuffer.refTo(0), prefixBuffer.size.signExtend(), 0) send(commFd, prefixBuffer.refTo(0), prefixBuffer.size.convert(), 0)
.ensureUnixCallResult("write") { it >= 0 } .ensureUnixCallResult("write") { it >= 0 }
send(commFd, pinned.addressOf(0), length.signExtend(), 0) send(commFd, pinned.addressOf(0), length.convert(), 0)
.ensureUnixCallResult("write") { it >= 0 } .ensureUnixCallResult("write") { it >= 0 }
} }
} }
@@ -83,3 +83,14 @@ inline fun Long.ensureUnixCallResult(op: String, predicate: (Long) -> Boolean):
} }
return this return this
} }
inline fun ULong.ensureUnixCallResult(op: String, predicate: (ULong) -> Boolean): ULong {
if (!predicate(this)) {
throw Error("$op: ${strerror(posix_errno())!!.toKString()}")
}
return this
}
private fun Int.isMinusOne() = (this == -1)
private fun Long.isMinusOne() = (this == -1L)
private fun ULong.isMinusOne() = (this == ULong.MAX_VALUE)
@@ -1,4 +1,5 @@
import kotlinx.cinterop.* import kotlinx.cinterop.*
import platform.posix.size_t
import tensorflow.* import tensorflow.*
typealias Status = CPointer<TF_Status> typealias Status = CPointer<TF_Status>
@@ -31,13 +32,13 @@ fun scalarTensor(value: Int): Tensor {
return TF_NewTensor(TF_INT32, return TF_NewTensor(TF_INT32,
dims = null, num_dims = 0, dims = null, num_dims = 0,
data = data, len = IntVar.size, data = data, len = IntVar.size.convert(),
deallocator = staticCFunction { dataToFree, _, _ -> nativeHeap.free(dataToFree!!.reinterpret<IntVar>()) }, deallocator = staticCFunction { dataToFree, _, _ -> nativeHeap.free(dataToFree!!.reinterpret<IntVar>()) },
deallocator_arg = null)!! deallocator_arg = null)!!
} }
val Tensor.scalarIntValue: Int get() { val Tensor.scalarIntValue: Int get() {
if (TF_INT32 != TF_TensorType(this) || IntVar.size != TF_TensorByteSize(this)) { if (TF_INT32 != TF_TensorType(this) || IntVar.size.convert<size_t>() != TF_TensorByteSize(this)) {
throw Error("Tensor is not of type int.") throw Error("Tensor is not of type int.")
} }
if (0 != TF_NumDims(this)) { if (0 != TF_NumDims(this)) {
@@ -42,13 +42,13 @@ class AudioFrame(val buffer: CPointer<AVBufferRef>, var position: Int, val size:
private fun Int.checkAVError() { private fun Int.checkAVError() {
if (this != 0) { if (this != 0) {
val buffer = ByteArray(1024) val buffer = ByteArray(1024)
av_strerror(this, buffer.refTo(0), buffer.size.signExtend()) av_strerror(this, buffer.refTo(0), buffer.size.convert())
throw Error("AVError: ${buffer.stringFromUtf8()}") throw Error("AVError: ${buffer.stringFromUtf8()}")
} }
} }
private val AVFormatContext.codecs: List<AVCodecContext?> private val AVFormatContext.codecs: List<AVCodecContext?>
get() = List(nb_streams) { streams?.get(it)?.pointed?.codec?.pointed } get() = List(nb_streams.toInt()) { streams?.get(it)?.pointed?.codec?.pointed }
private fun AVFormatContext.streamAt(index: Int): AVStream? = private fun AVFormatContext.streamAt(index: Int): AVStream? =
if (index < 0) null else streams?.get(index)?.pointed if (index < 0) null else streams?.get(index)?.pointed
@@ -124,7 +124,7 @@ private class VideoDecoder(
dispose = ::sws_freeContext dispose = ::sws_freeContext
) )
private val scaledFrameSize = avpicture_get_size(avPixelFormat, windowSize.w, windowSize.h) private val scaledFrameSize = avpicture_get_size(avPixelFormat, windowSize.w, windowSize.h)
private val buffer: ByteArray = ByteArray(scaledFrameSize) private val buffer: UByteArray = UByteArray(scaledFrameSize) { 0u }
private val videoQueue = Queue<VideoFrame>(100) private val videoQueue = Queue<VideoFrame>(100)
@@ -158,7 +158,7 @@ private class VideoDecoder(
val buffer = av_buffer_alloc(scaledFrameSize)!! val buffer = av_buffer_alloc(scaledFrameSize)!!
val ts = av_frame_get_best_effort_timestamp(videoFrame.ptr) * val ts = av_frame_get_best_effort_timestamp(videoFrame.ptr) *
av_q2d(videoCodecContext.time_base.readValue()) av_q2d(videoCodecContext.time_base.readValue())
memcpy(buffer.pointed.data, scaledVideoFrame.data[0], scaledFrameSize.signExtend()) memcpy(buffer.pointed.data, scaledVideoFrame.data[0], scaledFrameSize.convert())
videoQueue.push(VideoFrame(buffer, scaledVideoFrame.linesize[0], ts)) videoQueue.push(VideoFrame(buffer, scaledVideoFrame.linesize[0], ts))
} }
} }
@@ -204,11 +204,11 @@ private class AudioDecoder(
channels = output.channels channels = output.channels
sample_rate = output.sampleRate sample_rate = output.sampleRate
format = output.sampleFormat format = output.sampleFormat
channel_layout = output.channelLayout.signExtend() channel_layout = output.channelLayout.convert()
} }
with (audioCodecContext) { with (audioCodecContext) {
setResampleOpt("in_channel_layout", channel_layout.narrow()) setResampleOpt("in_channel_layout", channel_layout.convert())
setResampleOpt("out_channel_layout", output.channelLayout) setResampleOpt("out_channel_layout", output.channelLayout)
setResampleOpt("in_sample_rate", sample_rate) setResampleOpt("in_sample_rate", sample_rate)
setResampleOpt("out_sample_rate", output.sampleRate) setResampleOpt("out_sample_rate", output.sampleRate)
@@ -255,7 +255,7 @@ private class AudioDecoder(
val buffer = av_buffer_alloc(audioFrameSize)!! val buffer = av_buffer_alloc(audioFrameSize)!!
val ts = av_frame_get_best_effort_timestamp(audioFrame.ptr) * val ts = av_frame_get_best_effort_timestamp(audioFrame.ptr) *
av_q2d(audioCodecContext.time_base.readValue()) av_q2d(audioCodecContext.time_base.readValue())
memcpy(buffer.pointed.data, data[0], audioFrameSize.signExtend()) memcpy(buffer.pointed.data, data[0], audioFrameSize.convert())
audioQueue.push(AudioFrame(buffer, 0, audioFrameSize, ts)) audioQueue.push(AudioFrame(buffer, 0, audioFrameSize, ts))
} }
} }
@@ -27,7 +27,7 @@ enum class SampleFormat {
data class AudioOutput(val sampleRate: Int, val channels: Int, val sampleFormat: SampleFormat) data class AudioOutput(val sampleRate: Int, val channels: Int, val sampleFormat: SampleFormat)
private fun SampleFormat.toSDLFormat(): SDL_AudioFormat? = when (this) { private fun SampleFormat.toSDLFormat(): SDL_AudioFormat? = when (this) {
SampleFormat.S16 -> AUDIO_S16SYS.narrow() SampleFormat.S16 -> AUDIO_S16SYS.convert()
SampleFormat.INVALID -> null SampleFormat.INVALID -> null
} }
@@ -45,9 +45,9 @@ class SDLAudio(val player: VideoPlayer) : DisposableContainer() {
val spec = alloc<SDL_AudioSpec>().apply { val spec = alloc<SDL_AudioSpec>().apply {
freq = audio.sampleRate freq = audio.sampleRate
format = audioFormat format = audioFormat
channels = audio.channels.narrow() channels = audio.channels.convert()
silence = 0 silence = 0u
samples = 4096 samples = 4096u
userdata = threadData userdata = threadData
callback = staticCFunction(::audioCallback) callback = staticCFunction(::audioCallback)
} }
@@ -89,12 +89,12 @@ private fun audioCallback(userdata: COpaquePointer?, buffer: CPointer<Uint8Var>?
val frame = decoder.nextAudioFrame(length - outPosition) val frame = decoder.nextAudioFrame(length - outPosition)
if (frame != null) { if (frame != null) {
val toCopy = minOf(length - outPosition, frame.size - frame.position) val toCopy = minOf(length - outPosition, frame.size - frame.position)
memcpy(buffer + outPosition, frame.buffer.pointed.data + frame.position, toCopy.signExtend()) memcpy(buffer + outPosition, frame.buffer.pointed.data + frame.position, toCopy.convert())
frame.unref() frame.unref()
outPosition += toCopy outPosition += toCopy
} else { } else {
// println("Decoder returned nothing!") // println("Decoder returned nothing!")
memset(buffer + outPosition, 0, (length - outPosition).signExtend()) memset(buffer + outPosition, 0, (length - outPosition).convert())
break break
} }
} }
@@ -81,7 +81,7 @@ class SDLRendererWindow(windowPos: Dimensions, videoSize: Dimensions) : Disposab
SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), 0, videoSize.w, videoSize.h), SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), 0, videoSize.w, videoSize.h),
::SDL_DestroyTexture) ::SDL_DestroyTexture)
private val rect = sdlDisposable("calloc(SDL_Rect)", private val rect = sdlDisposable("calloc(SDL_Rect)",
SDL_calloc(1, SDL_Rect.size), ::SDL_free) SDL_calloc(1u, SDL_Rect.size.convert()), ::SDL_free)
.reinterpret<SDL_Rect>() .reinterpret<SDL_Rect>()
init { init {
@@ -71,7 +71,7 @@ class VideoPlayer(val requestedSize: Dimensions?) : DisposableContainer() {
} }
private fun getTime(): Double { private fun getTime(): Double {
clock_gettime(platform.posix.CLOCK_MONOTONIC, now) clock_gettime(platform.posix.CLOCK_MONOTONIC.convert(), now)
return now.pointed.tv_sec + now.pointed.tv_nsec / 1e9 return now.pointed.tv_sec + now.pointed.tv_nsec / 1e9
} }
@@ -127,7 +127,7 @@ class VideoPlayer(val requestedSize: Dimensions?) : DisposableContainer() {
lastFrameTime += frameDuration // try to maintain perfect frame rate lastFrameTime += frameDuration // try to maintain perfect frame rate
// Wait for next frame, if needed // Wait for next frame, if needed
if (passedTime < frameDuration) { if (passedTime < frameDuration) {
usleep((1000_000 * (frameDuration - passedTime)).toInt()) usleep((1000_000 * (frameDuration - passedTime)).toInt().toUInt())
} else if (passedTime > frameDuration * 1.5){ } else if (passedTime > frameDuration * 1.5){
lastFrameTime = now // we fell behind more than half frame, reset time lastFrameTime = now // we fell behind more than half frame, reset time
} }
@@ -140,7 +140,7 @@ class VideoPlayer(val requestedSize: Dimensions?) : DisposableContainer() {
while (state == State.PAUSED) { while (state == State.PAUSED) {
audio.pause() audio.pause()
input.check() input.check()
usleep(1 * 1000) usleep(1u * 1000u)
} }
audio.resume() audio.resume()
} }
@@ -152,14 +152,14 @@ class VideoPlayer(val requestedSize: Dimensions?) : DisposableContainer() {
if (!decoder.audioVideoSynced()) { if (!decoder.audioVideoSynced()) {
println("Resynchronizing video with audio") println("Resynchronizing video with audio")
while (!decoder.audioVideoSynced() && state == State.PLAYING) { while (!decoder.audioVideoSynced() && state == State.PLAYING) {
usleep(500) usleep(500u)
input.check() input.check()
} }
} }
} }
} else { } else {
// For pure sound, playback is driven by demand. // For pure sound, playback is driven by demand.
usleep(10 * 1000) usleep(10u * 1000u)
} }
} }
} }
+2 -1
View File
@@ -1,6 +1,7 @@
import kotlinx.cinterop.*
import platform.windows.* import platform.windows.*
fun main(args: Array<String>) { fun main(args: Array<String>) {
MessageBoxW(null, "Konan говорит:\nЗДРАВСТВУЙ МИР!\n", MessageBoxW(null, "Konan говорит:\nЗДРАВСТВУЙ МИР!\n",
"Заголовок окна", MB_YESNOCANCEL or MB_ICONQUESTION) "Заголовок окна", (MB_YESNOCANCEL or MB_ICONQUESTION).convert())
} }