diff --git a/INTEROP.md b/INTEROP.md index f12289c9d22..af7f9a5212b 100644 --- a/INTEROP.md +++ b/INTEROP.md @@ -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 neither implicit integer casts nor C-style integer casts (e.g. `(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 -have the same or greater size. -The `narrow` converts the integer value to smaller one (possibly changing the -value due to loosing significant bits), so the result must have the same or -less size. +`.convert<${type}>` has the same semantics as one of the +`.toByte`, `.toShort`, `.toInt`, `.toLong`, +`.toUByte`, `.toUShort`, `.toUInt` or `.toULong` +methods, depending on `type`. -Any allowed `.signExtend<${type}>` or `.narrow<${type}>` have the same -semantics as one of the `.toByte`, `.toShort`, `.toInt` or `.toLong` methods, -depending on `type`. - -The example of using `signExtend`: +The example of using `convert`: ``` fun zeroMemory(buffer: COpaquePointer, size: Int) { - memset(buffer, 0, size.signExtend()) + memset(buffer, 0, size.convert()) } ``` diff --git a/Interop/Runtime/build.gradle b/Interop/Runtime/build.gradle index 4a6446108f3..c597789df22 100644 --- a/Interop/Runtime/build.gradle +++ b/Interop/Runtime/build.gradle @@ -72,6 +72,12 @@ dependencies { sourceSets.main.kotlin.srcDirs += "src/jvm/kotlin" +compileKotlin { + kotlinOptions { + freeCompilerArgs = ['-Xuse-experimental=kotlin.ExperimentalUnsignedTypes'] + } +} + task nativelibs(type: Copy) { dependsOn 'callbacksSharedLibrary' diff --git a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt index 71ece4c1be4..b322ce9d4d4 100644 --- a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt +++ b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt @@ -121,7 +121,7 @@ private fun getEnumCType(classifier: KClass<*>): CEnumType? { } @Suppress("UNCHECKED_CAST") - return CEnumType(rawValueCType as CType) + return CEnumType(rawValueCType as CType) } private fun getArgOrRetValCType(type: KType): CType<*> { @@ -346,7 +346,7 @@ private class Struct(val size: Long, val align: Int, elementTypes: List override fun write(location: NativePtr, value: CValue<*>) = value.write(location) } -private class CEnumType(private val rawValueCType: CType) : CType(rawValueCType.ffiType) { +private class CEnumType(private val rawValueCType: CType) : CType(rawValueCType.ffiType) { override fun read(location: NativePtr): CEnum { TODO("enum-typed callback parameters") diff --git a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Generated.kt b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Generated.kt index 18c3e5b2f34..1ecf951711c 100644 --- a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Generated.kt +++ b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Generated.kt @@ -26,16 +26,20 @@ inline operator fun > CPointer?.plus(index: Long): CPointer< inline operator fun > CPointer?.plus(index: Int): CPointer? = this + index.toLong() +@JvmName("get\$Byte") inline operator fun CPointer>.get(index: Int): T = (this + index)!!.pointed.value +@JvmName("set\$Byte") inline operator fun CPointer>.set(index: Int, value: T) { (this + index)!!.pointed.value = value } +@JvmName("get\$Byte") inline operator fun CPointer>.get(index: Long): T = (this + index)!!.pointed.value +@JvmName("set\$Byte") inline operator fun CPointer>.set(index: Long, value: T) { (this + index)!!.pointed.value = value } @@ -48,16 +52,20 @@ inline operator fun > CPointer?.plus(index: Long): CPointer inline operator fun > CPointer?.plus(index: Int): CPointer? = this + index.toLong() +@JvmName("get\$Short") inline operator fun CPointer>.get(index: Int): T = (this + index)!!.pointed.value +@JvmName("set\$Short") inline operator fun CPointer>.set(index: Int, value: T) { (this + index)!!.pointed.value = value } +@JvmName("get\$Short") inline operator fun CPointer>.get(index: Long): T = (this + index)!!.pointed.value +@JvmName("set\$Short") inline operator fun CPointer>.set(index: Long, value: T) { (this + index)!!.pointed.value = value } @@ -70,16 +78,20 @@ inline operator fun > CPointer?.plus(index: Long): CPointer> CPointer?.plus(index: Int): CPointer? = this + index.toLong() +@JvmName("get\$Int") inline operator fun CPointer>.get(index: Int): T = (this + index)!!.pointed.value +@JvmName("set\$Int") inline operator fun CPointer>.set(index: Int, value: T) { (this + index)!!.pointed.value = value } +@JvmName("get\$Int") inline operator fun CPointer>.get(index: Long): T = (this + index)!!.pointed.value +@JvmName("set\$Int") inline operator fun CPointer>.set(index: Long, value: T) { (this + index)!!.pointed.value = value } @@ -92,20 +104,128 @@ inline operator fun > CPointer?.plus(index: Long): CPointer< inline operator fun > CPointer?.plus(index: Int): CPointer? = this + index.toLong() +@JvmName("get\$Long") inline operator fun CPointer>.get(index: Int): T = (this + index)!!.pointed.value +@JvmName("set\$Long") inline operator fun CPointer>.set(index: Int, value: T) { (this + index)!!.pointed.value = value } +@JvmName("get\$Long") inline operator fun CPointer>.get(index: Long): T = (this + index)!!.pointed.value +@JvmName("set\$Long") inline operator fun CPointer>.set(index: Long, value: T) { (this + index)!!.pointed.value = value } +@JvmName("plus\$UByte") +inline operator fun > CPointer?.plus(index: Long): CPointer? = + interpretCPointer(this.rawValue + index * 1) + +@JvmName("plus\$UByte") +inline operator fun > CPointer?.plus(index: Int): CPointer? = + this + index.toLong() + +@JvmName("get\$UByte") +inline operator fun CPointer>.get(index: Int): T = + (this + index)!!.pointed.value + +@JvmName("set\$UByte") +inline operator fun CPointer>.set(index: Int, value: T) { + (this + index)!!.pointed.value = value +} + +@JvmName("get\$UByte") +inline operator fun CPointer>.get(index: Long): T = + (this + index)!!.pointed.value + +@JvmName("set\$UByte") +inline operator fun CPointer>.set(index: Long, value: T) { + (this + index)!!.pointed.value = value +} + +@JvmName("plus\$UShort") +inline operator fun > CPointer?.plus(index: Long): CPointer? = + interpretCPointer(this.rawValue + index * 2) + +@JvmName("plus\$UShort") +inline operator fun > CPointer?.plus(index: Int): CPointer? = + this + index.toLong() + +@JvmName("get\$UShort") +inline operator fun CPointer>.get(index: Int): T = + (this + index)!!.pointed.value + +@JvmName("set\$UShort") +inline operator fun CPointer>.set(index: Int, value: T) { + (this + index)!!.pointed.value = value +} + +@JvmName("get\$UShort") +inline operator fun CPointer>.get(index: Long): T = + (this + index)!!.pointed.value + +@JvmName("set\$UShort") +inline operator fun CPointer>.set(index: Long, value: T) { + (this + index)!!.pointed.value = value +} + +@JvmName("plus\$UInt") +inline operator fun > CPointer?.plus(index: Long): CPointer? = + interpretCPointer(this.rawValue + index * 4) + +@JvmName("plus\$UInt") +inline operator fun > CPointer?.plus(index: Int): CPointer? = + this + index.toLong() + +@JvmName("get\$UInt") +inline operator fun CPointer>.get(index: Int): T = + (this + index)!!.pointed.value + +@JvmName("set\$UInt") +inline operator fun CPointer>.set(index: Int, value: T) { + (this + index)!!.pointed.value = value +} + +@JvmName("get\$UInt") +inline operator fun CPointer>.get(index: Long): T = + (this + index)!!.pointed.value + +@JvmName("set\$UInt") +inline operator fun CPointer>.set(index: Long, value: T) { + (this + index)!!.pointed.value = value +} + +@JvmName("plus\$ULong") +inline operator fun > CPointer?.plus(index: Long): CPointer? = + interpretCPointer(this.rawValue + index * 8) + +@JvmName("plus\$ULong") +inline operator fun > CPointer?.plus(index: Int): CPointer? = + this + index.toLong() + +@JvmName("get\$ULong") +inline operator fun CPointer>.get(index: Int): T = + (this + index)!!.pointed.value + +@JvmName("set\$ULong") +inline operator fun CPointer>.set(index: Int, value: T) { + (this + index)!!.pointed.value = value +} + +@JvmName("get\$ULong") +inline operator fun CPointer>.get(index: Long): T = + (this + index)!!.pointed.value + +@JvmName("set\$ULong") +inline operator fun CPointer>.set(index: Long, value: T) { + (this + index)!!.pointed.value = value +} + @JvmName("plus\$Float") inline operator fun > CPointer?.plus(index: Long): CPointer? = interpretCPointer(this.rawValue + index * 4) @@ -114,16 +234,20 @@ inline operator fun > CPointer?.plus(index: Long): CPointer inline operator fun > CPointer?.plus(index: Int): CPointer? = this + index.toLong() +@JvmName("get\$Float") inline operator fun CPointer>.get(index: Int): T = (this + index)!!.pointed.value +@JvmName("set\$Float") inline operator fun CPointer>.set(index: Int, value: T) { (this + index)!!.pointed.value = value } +@JvmName("get\$Float") inline operator fun CPointer>.get(index: Long): T = (this + index)!!.pointed.value +@JvmName("set\$Float") inline operator fun CPointer>.set(index: Long, value: T) { (this + index)!!.pointed.value = value } @@ -136,16 +260,20 @@ inline operator fun > CPointer?.plus(index: Long): CPointe inline operator fun > CPointer?.plus(index: Int): CPointer? = this + index.toLong() +@JvmName("get\$Double") inline operator fun CPointer>.get(index: Int): T = (this + index)!!.pointed.value +@JvmName("set\$Double") inline operator fun CPointer>.set(index: Int, value: T) { (this + index)!!.pointed.value = value } +@JvmName("get\$Double") inline operator fun CPointer>.get(index: Long): T = (this + index)!!.pointed.value +@JvmName("set\$Double") inline operator fun CPointer>.set(index: Long, value: T) { (this + index)!!.pointed.value = value } @@ -163,16 +291,20 @@ echo "@JvmName(\"plus\\\$$1\")" echo "inline operator fun > CPointer?.plus(index: Int): CPointer? =" echo " this + index.toLong()" echo +echo "@JvmName(\"get\\\$$1\")" echo "inline operator fun CPointer<${1}VarOf>.get(index: Int): T =" echo " (this + index)!!.pointed.value" echo +echo "@JvmName(\"set\\\$$1\")" echo "inline operator fun CPointer<${1}VarOf>.set(index: Int, value: T) {" echo " (this + index)!!.pointed.value = value" echo '}' echo +echo "@JvmName(\"get\\\$$1\")" echo "inline operator fun CPointer<${1}VarOf>.get(index: Long): T =" echo " (this + index)!!.pointed.value" echo +echo "@JvmName(\"set\\\$$1\")" echo "inline operator fun CPointer<${1}VarOf>.set(index: Long, value: T) {" echo " (this + index)!!.pointed.value = value" echo '}' @@ -183,6 +315,10 @@ gen Byte 1 gen Short 2 gen Int 4 gen Long 8 +gen UByte 1 +gen UShort 2 +gen UInt 4 +gen ULong 8 gen Float 4 gen Double 8 diff --git a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt index ad30ff38329..0fff26ad5a1 100644 --- a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt +++ b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Types.kt @@ -246,7 +246,7 @@ sealed class CPrimitiveVar(rawPtr: NativePtr) : CVariable(rawPtr) { } interface CEnum { - val value: Number + val value: Any } abstract class CEnumVar(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) @@ -278,6 +278,26 @@ class LongVarOf(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) { companion object : Type(8) } +@Suppress("FINAL_UPPER_BOUND") +class UByteVarOf(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) { + companion object : Type(1) +} + +@Suppress("FINAL_UPPER_BOUND") +class UShortVarOf(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) { + companion object : Type(2) +} + +@Suppress("FINAL_UPPER_BOUND") +class UIntVarOf(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) { + companion object : Type(4) +} + +@Suppress("FINAL_UPPER_BOUND") +class ULongVarOf(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) { + companion object : Type(8) +} + @Suppress("FINAL_UPPER_BOUND") class FloatVarOf(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) { companion object : Type(4) @@ -293,6 +313,10 @@ typealias ByteVar = ByteVarOf typealias ShortVar = ShortVarOf typealias IntVar = IntVarOf typealias LongVar = LongVarOf +typealias UByteVar = UByteVarOf +typealias UShortVar = UShortVarOf +typealias UIntVar = UIntVarOf +typealias ULongVar = ULongVarOf typealias FloatVar = FloatVarOf typealias DoubleVar = DoubleVarOf @@ -330,6 +354,26 @@ var LongVarOf.value: T get() = nativeMemUtils.getLong(this) as T set(value) = nativeMemUtils.putLong(this, value) +@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST") +var UByteVarOf.value: T + get() = nativeMemUtils.getByte(this).toUByte() as T + set(value) = nativeMemUtils.putByte(this, value.toByte()) + +@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST") +var UShortVarOf.value: T + get() = nativeMemUtils.getShort(this).toUShort() as T + set(value) = nativeMemUtils.putShort(this, value.toShort()) + +@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST") +var UIntVarOf.value: T + get() = nativeMemUtils.getInt(this).toUInt() as T + set(value) = nativeMemUtils.putInt(this, value.toInt()) + +@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST") +var ULongVarOf.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") diff --git a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt index 2a9ff2b2b0d..a9fb3f83c04 100644 --- a/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt +++ b/Interop/Runtime/src/main/kotlin/kotlinx/cinterop/Utils.kt @@ -321,6 +321,22 @@ fun cValuesOf(vararg elements: Int): CValues = fun cValuesOf(vararg elements: Long): CValues = createValues(elements.size) { index -> this.value = elements[index] } +@JvmName("cValuesOfUnsigned") +fun cValuesOf(vararg elements: UByte): CValues = + createValues(elements.size) { index -> this.value = elements[index] } + +@JvmName("cValuesOfUnsigned") +fun cValuesOf(vararg elements: UShort): CValues = + createValues(elements.size) { index -> this.value = elements[index] } + +@JvmName("cValuesOfUnsigned") +fun cValuesOf(vararg elements: UInt): CValues = + createValues(elements.size) { index -> this.value = elements[index] } + +@JvmName("cValuesOfUnsigned") +fun cValuesOf(vararg elements: ULong): CValues = + createValues(elements.size) { index -> this.value = elements[index] } + fun cValuesOf(vararg elements: Float): CValues = object : CValues() { override fun getPointer(scope: AutofreeScope) = scope.allocArrayOf(*elements) override val size get() = 4 * elements.size diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/FunctionPointers.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/FunctionPointers.kt index 4e4dd0a3f43..95e60c084f0 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/FunctionPointers.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/FunctionPointers.kt @@ -102,6 +102,34 @@ private fun invokeImplLongRet(ptr: COpaquePointer, vararg args: Any?): Long = me resultBuffer.value } +@ExportForCompiler +private fun invokeImplUByteRet(ptr: COpaquePointer, vararg args: Any?): UByte = memScoped { + val resultBuffer = allocFfiReturnValueBuffer(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) + 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) + 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) + callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT64, args, null, memScope) + resultBuffer.value +} + @ExportForCompiler private fun invokeImplFloatRet(ptr: COpaquePointer, vararg args: Any?): Float = memScoped { val resultBuffer = allocFfiReturnValueBuffer(FloatVar) diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt index 31b1246e371..7b943f47ee3 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt @@ -28,12 +28,23 @@ external fun bitsToFloat(bits: Int): Float @Intrinsic external fun bitsToDouble(bits: Long): Double +// TODO: deprecate. @Intrinsic external fun Number.signExtend(): R +// TODO: deprecate. @Intrinsic external fun Number.narrow(): R +@Intrinsic external fun Byte.convert(): R +@Intrinsic external fun Short.convert(): R +@Intrinsic external fun Int.convert(): R +@Intrinsic external fun Long.convert(): R +@Intrinsic external fun UByte.convert(): R +@Intrinsic external fun UShort.convert(): R +@Intrinsic external fun UInt.convert(): R +@Intrinsic external fun ULong.convert(): R + @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE) @Retention(AnnotationRetention.SOURCE) internal annotation class JvmName(val name: String) \ No newline at end of file diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt index ac184f33fe2..8f5a3e2f1fb 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt @@ -56,6 +56,19 @@ fun IntArray.refTo(index: Int): CValuesRef = this.usingPinned { addressO fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) fun LongArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } +// TODO: pinning of unsigned arrays involves boxing as they are inline classes wrapping signed arrays. +fun Pinned.addressOf(index: Int): CPointer = this.get().addressOfElement(index) +fun UByteArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.get().addressOfElement(index) +fun UShortArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.get().addressOfElement(index) +fun UIntArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + +fun Pinned.addressOf(index: Int): CPointer = this.get().addressOfElement(index) +fun ULongArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } + fun Pinned.addressOf(index: Int): CPointer = this.addressOfElement(index) fun FloatArray.refTo(index: Int): CValuesRef = this.usingPinned { addressOf(index) } @@ -79,3 +92,15 @@ private external fun getAddressOfElement(array: Any, index: Int): COpaquePointer @Suppress("NOTHING_TO_INLINE") private inline fun

Pinned<*>.addressOfElement(index: Int): CPointer

= getAddressOfElement(this.get(), index).reinterpret() + +@SymbolName("Kotlin_Arrays_getAddressOfElement") +private external fun UByteArray.addressOfElement(index: Int): CPointer + +@SymbolName("Kotlin_Arrays_getAddressOfElement") +private external fun UShortArray.addressOfElement(index: Int): CPointer + +@SymbolName("Kotlin_Arrays_getAddressOfElement") +private external fun UIntArray.addressOfElement(index: Int): CPointer + +@SymbolName("Kotlin_Arrays_getAddressOfElement") +private external fun ULongArray.addressOfElement(index: Int): CPointer diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt index bbe60645858..90fb9448c21 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt @@ -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_DOUBLE: FfiTypeKind = 6 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( argument: Any?, isVariadic: Boolean, location: COpaquePointer, additionalPlacement: AutofreeScope -): FfiTypeKind = when (argument) { +): FfiTypeKind = when (argument) { // FIXME: optimize is CValuesRef<*>? -> { location.reinterpret>()[0] = argument?.getPointer(additionalPlacement) FFI_TYPE_KIND_POINTER @@ -79,6 +83,30 @@ private tailrec fun convertArgument( FFI_TYPE_KIND_SINT16 } + is UInt -> { + location.reinterpret()[0] = argument + FFI_TYPE_KIND_UINT32 + } + + is ULong -> { + location.reinterpret()[0] = argument + FFI_TYPE_KIND_UINT64 + } + + is UByte -> if (isVariadic) { + convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement) + } else { + location.reinterpret()[0] = argument + FFI_TYPE_KIND_UINT8 + } + + is UShort -> if (isVariadic) { + convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement) + } else { + location.reinterpret()[0] = argument + FFI_TYPE_KIND_UINT16 + } + is Double -> { location.reinterpret()[0] = argument FFI_TYPE_KIND_DOUBLE diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt index e0d5b4b4c5f..c8f80bad246 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt @@ -162,6 +162,10 @@ object KotlinTypes { val short by BuiltInType val int 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 double by BuiltInType val unit by BuiltInType diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt index 715cf85d575..2cccefc4d17 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt @@ -23,8 +23,12 @@ interface DeclarationMapper { fun isMappedToStrict(enumDef: EnumDef): Boolean fun getKotlinNameForValue(enumDef: EnumDef): String fun getPackageFor(declaration: TypeDeclaration): String + + val useUnsignedTypes: Boolean } +fun DeclarationMapper.isMappedToSigned(integerType: IntegerType): Boolean = integerType.isSigned || !useUnsignedTypes + fun DeclarationMapper.getKotlinClassFor( objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean = false @@ -41,38 +45,46 @@ fun DeclarationMapper.getKotlinClassFor( return Classifier.topLevel(pkg, className) } -val PrimitiveType.kotlinType: KotlinClassifierType - get() = when (this) { - is CharType -> KotlinTypes.byte +fun PrimitiveType.getKotlinType(declarationMapper: DeclarationMapper): KotlinClassifierType = when (this) { + 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. - is IntegerType -> when (this.size) { +// TODO: C primitive types should probably be generated as type aliases for Kotlin types. + is IntegerType -> if (declarationMapper.isMappedToSigned(this)) { + when (this.size) { 1 -> KotlinTypes.byte 2 -> KotlinTypes.short 4 -> KotlinTypes.int 8 -> KotlinTypes.long else -> TODO(this.toString()) } - - is FloatingType -> when (this.size) { - 4 -> KotlinTypes.float - 8 -> KotlinTypes.double + } else { + when (this.size) { + 1 -> KotlinTypes.uByte + 2 -> KotlinTypes.uShort + 4 -> KotlinTypes.uInt + 8 -> KotlinTypes.uLong else -> TODO(this.toString()) } - - else -> throw NotImplementedError() } -private val PrimitiveType.bridgedType: BridgedType - get() { - val kotlinType = this.kotlinType - return BridgedType.values().single { - it.kotlinType == kotlinType - } + is FloatingType -> when (this.size) { + 4 -> KotlinTypes.float + 8 -> KotlinTypes.double + else -> TODO(this.toString()) } + 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 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) { is CharType -> "ByteVar" is BoolType -> "BooleanVar" - is IntegerType -> when (type.size) { - 1 -> "ByteVar" - 2 -> "ShortVar" - 4 -> "IntVar" - 8 -> "LongVar" - else -> TODO(type.toString()) + is IntegerType -> if (declarationMapper.isMappedToSigned(type)) { + when (type.size) { + 1 -> "ByteVar" + 2 -> "ShortVar" + 4 -> "IntVar" + 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) { 4 -> "FloatVar" @@ -359,9 +381,9 @@ fun mirrorPrimitiveType(type: PrimitiveType): TypeMirror.ByValue { val info = if (type == BoolType) { TypeInfo.Boolean() } 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 { @@ -370,7 +392,7 @@ private fun byRefTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.ByRe } 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) @@ -380,7 +402,7 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when when { 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 info = TypeInfo.Enum(clazz, bridgedType) TypeMirror.ByValue(clazz.nested("Var").type, info, clazz.type) diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGenerator.kt index e7030a350d3..9c2eaf4cb91 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGenerator.kt @@ -24,6 +24,10 @@ enum class BridgedType(val kotlinType: KotlinClassifierType, val convertor: Stri SHORT(KotlinTypes.short, "toShort"), INT(KotlinTypes.int, "toInt"), LONG(KotlinTypes.long, "toLong"), + UBYTE(KotlinTypes.uByte, "toUByte"), + USHORT(KotlinTypes.uShort, "toUShort"), + UINT(KotlinTypes.uInt, "toUInt"), + ULONG(KotlinTypes.uLong, "toULong"), FLOAT(KotlinTypes.float, "toFloat"), DOUBLE(KotlinTypes.double, "toDouble"), NATIVE_PTR(KotlinTypes.nativePtr), diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt index 5bf772db786..d1d676f7644 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt @@ -38,6 +38,10 @@ class SimpleBridgeGeneratorImpl( BridgedType.SHORT -> "jshort" BridgedType.INT -> "jint" BridgedType.LONG -> "jlong" + BridgedType.UBYTE -> "jbyte" + BridgedType.USHORT -> "jshort" + BridgedType.UINT -> "jint" + BridgedType.ULONG -> "jlong" BridgedType.FLOAT -> "jfloat" BridgedType.DOUBLE -> "jdouble" BridgedType.NATIVE_PTR -> "jlong" @@ -49,6 +53,10 @@ class SimpleBridgeGeneratorImpl( BridgedType.SHORT -> "int16_t" BridgedType.INT -> "int32_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.DOUBLE -> "double" BridgedType.NATIVE_PTR -> "void*" diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt index 12549b72168..870140c60ee 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt @@ -156,6 +156,12 @@ class StubGenerator( override fun getPackageFor(declaration: TypeDeclaration): String { 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) @@ -472,7 +478,8 @@ class StubGenerator( block("enum class ${kotlinFile.declare(clazz)}(override val value: $baseKotlinType) : CEnum") { canonicalConstants.forEach { - out("${it.name.asSimpleName()}(${it.value}),") + val literal = integerLiteral(e.baseType, it.value)!! + out("${it.name.asSimpleName()}($literal),") } out(";") out("") @@ -720,20 +727,33 @@ class StubGenerator( } 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) { return "${value + 1} - 1" // Workaround for "The value is out of range" compile error. } - val unwrappedType = type.unwrapTypedefs() - if (unwrappedType !is PrimitiveType) { - return null - } - - val narrowedValue: Number = when (unwrappedType.kotlinType) { - KotlinTypes.byte -> value.toByte() - KotlinTypes.short -> value.toShort() - KotlinTypes.int -> value.toInt() - KotlinTypes.long -> value + val narrowedValue: Number = when (size) { + 1 -> value.toByte() + 2 -> value.toShort() + 4 -> value.toInt() + 8 -> value else -> return null } diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index 6bc268cada5..94fd8dea08f 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -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.Argument +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.config.AnalysisFlag class K2NativeCompilerArguments : CommonCompilerArguments() { // First go the options interesting to the general public. @@ -147,5 +149,11 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { description = "Paths to friend modules" ) var friendModules: String? = null + + override fun configureAnalysisFlags(collector: MessageCollector): MutableMap, Any> = + super.configureAnalysisFlags(collector).also { + val useExperimental = it[AnalysisFlag.useExperimental] as List<*> + it[AnalysisFlag.useExperimental] = useExperimental + listOf("kotlin.ExperimentalUnsignedTypes") + } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt index 17552a0a0b6..fa0ba5466f9 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt @@ -16,8 +16,10 @@ package org.jetbrains.kotlin.backend.konan +import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies import org.jetbrains.kotlin.descriptors.konan.interop.InteropFqNames import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName @@ -90,6 +92,8 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives: val narrow = packageScope.getContributedFunctions("narrow").single() + val convert = packageScope.getContributedFunctions("convert").toSet() + val readBits = packageScope.getContributedFunctions("readBits").single() val writeBits = packageScope.getContributedFunctions("writeBits").single() @@ -101,6 +105,9 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives: TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == cPointer }.toSet() + private fun KonanBuiltIns.getUnsignedClass(unsignedType: UnsignedType): ClassDescriptor = + this.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!! + val invokeImpls = mapOf( builtIns.unit to "invokeImplUnitRet", builtIns.boolean to "invokeImplBooleanRet", @@ -108,6 +115,10 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives: builtIns.short to "invokeImplShortRet", builtIns.int to "invokeImplIntRet", 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.double to "invokeImplDoubleRet", cPointer to "invokeImplPointerRet" diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index f666ddbfcb1..ce74ac1006a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -144,6 +144,35 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val val uInt = unsignedClass(UnsignedType.UINT) 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 interopNativePointedGetRawPointer = diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt index e4bca3aa981..aec3e7d2ad1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt @@ -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.synthesizedName import org.jetbrains.kotlin.backend.konan.irasdescriptors.* +import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor 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 -> { // Replace by `invokeImpl${type}Ret`: @@ -968,6 +998,10 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB return } + if (UnsignedTypes.isUnsignedType(this.toKotlinType()) && !this.containsNull()) { + return + } + if (this.getClass()?.descriptor == interop.cPointer) { return } @@ -976,7 +1010,7 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB } 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") } diff --git a/backend.native/tests/interop/basics/3.kt b/backend.native/tests/interop/basics/3.kt index 5ed1ba182cc..0204d37521a 100644 --- a/backend.native/tests/interop/basics/3.kt +++ b/backend.native/tests/interop/basics/3.kt @@ -4,7 +4,7 @@ fun main(args: Array) { val values = intArrayOf(14, 12, 9, 13, 8) 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()[0] val bValue = b!!.reinterpret()[0] diff --git a/backend.native/tests/interop/basics/bf.kt b/backend.native/tests/interop/basics/bf.kt index c8456097819..f2325406ef7 100644 --- a/backend.native/tests/interop/basics/bf.kt +++ b/backend.native/tests/interop/basics/bf.kt @@ -6,7 +6,7 @@ fun assertEquals(value1: Any?, value2: Any?) { 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, 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)) } -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.x2 = x2 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 } -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.x5 = x5 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 } -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) 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: - 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) - 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) } @@ -66,9 +66,9 @@ fun main(args: Array) { for (x1 in -1L..0L) for (x2 in B2.values()) 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 (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) } } diff --git a/backend.native/tests/interop/basics/cfunptr.def b/backend.native/tests/interop/basics/cfunptr.def index 526b2ca983e..d9538950da1 100644 --- a/backend.native/tests/interop/basics/cfunptr.def +++ b/backend.native/tests/interop/basics/cfunptr.def @@ -51,4 +51,12 @@ typedef _Bool (*isIntPositivePtrType)(int); static isIntPositivePtrType getIsIntPositivePtr() { return &__isIntPositive; +} + +static unsigned int getMaxUInt(void) { + return 0xffffffff; +} + +static typeof(&getMaxUInt) getMaxUIntGetter() { + return &getMaxUInt; } \ No newline at end of file diff --git a/backend.native/tests/interop/basics/echo_server.kt b/backend.native/tests/interop/basics/echo_server.kt index e7c7f1b4d56..f8130ceb342 100644 --- a/backend.native/tests/interop/basics/echo_server.kt +++ b/backend.native/tests/interop/basics/echo_server.kt @@ -7,7 +7,7 @@ fun main(args: Array) { return } - val port = atoi(args[0]).toShort() + val port = atoi(args[0]).toUShort() memScoped { @@ -19,13 +19,13 @@ fun main(args: Array) { .ensureUnixCallResult { it >= 0 } with(serverAddr) { - memset(this.ptr, 0, sockaddr_in.size) - sin_family = AF_INET.narrow() - sin_addr.s_addr = htons(0).toInt() + memset(this.ptr, 0, sockaddr_in.size.convert()) + sin_family = AF_INET.convert() + sin_addr.s_addr = htons(0u).convert() 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 } listen(listenFd, 10) @@ -35,21 +35,21 @@ fun main(args: Array) { .ensureUnixCallResult { it >= 0 } while (true) { - val length = read(commFd, buffer, bufferLength) + val length = read(commFd, buffer, bufferLength.convert()) .ensureUnixCallResult { it >= 0 } if (length == 0L) { break } - write(commFd, buffer, length) + write(commFd, buffer, length.convert()) .ensureUnixCallResult { it >= 0 } } } } // 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 { perror(null) // TODO: store error message to exception instead. diff --git a/backend.native/tests/interop/basics/funptr.kt b/backend.native/tests/interop/basics/funptr.kt index c28452b18bb..0f187632116 100644 --- a/backend.native/tests/interop/basics/funptr.kt +++ b/backend.native/tests/interop/basics/funptr.kt @@ -1,5 +1,6 @@ import kotlinx.cinterop.* import cfunptr.* +import kotlin.test.* fun main(args: Array) { val atoiPtr = getAtoiPtr()!! @@ -23,6 +24,8 @@ fun main(args: Array) { printIntPtr(isIntPositivePtr(42).ifThenOneElseZero()) printIntPtr(isIntPositivePtr(-42).ifThenOneElseZero()) + + assertEquals(getMaxUIntGetter()!!(), UInt.MAX_VALUE) } fun Boolean.ifThenOneElseZero() = if (this) 1 else 0 \ No newline at end of file diff --git a/backend.native/tests/interop/basics/opengl_teapot.kt b/backend.native/tests/interop/basics/opengl_teapot.kt index 6ce2b02f494..b1f75af466e 100644 --- a/backend.native/tests/interop/basics/opengl_teapot.kt +++ b/backend.native/tests/interop/basics/opengl_teapot.kt @@ -13,7 +13,7 @@ private val windowHeight = 480 fun display() { // 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() // Define a viewing transformation @@ -40,13 +40,13 @@ fun display() { fun initialize() { // select projection matrix - glMatrixMode(GL_PROJECTION) + glMatrixMode(GL_PROJECTION.convert()) // set the viewport glViewport(0, 0, windowWidth, windowHeight) // set matrix mode - glMatrixMode(GL_PROJECTION) + glMatrixMode(GL_PROJECTION.convert()) // reset projection matrix glLoadIdentity() @@ -56,29 +56,29 @@ fun initialize() { gluPerspective(45.0, aspect, 1.0, 500.0) // specify which matrix is the current matrix - glMatrixMode(GL_MODELVIEW) - glShadeModel(GL_SMOOTH) + glMatrixMode(GL_MODELVIEW.convert()) + glShadeModel(GL_SMOOTH.convert()) // specify the clear value for the depth buffer glClearDepth(1.0) - glEnable(GL_DEPTH_TEST) - glDepthFunc(GL_LEQUAL) + glEnable(GL_DEPTH_TEST.convert()) + glDepthFunc(GL_LEQUAL.convert()) // 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)) - glLightfv(GL_LIGHT0, GL_DIFFUSE, cValuesOf(0.6f, 0.6f, 0.6f, 1.0f)) - glLightfv(GL_LIGHT0, GL_SPECULAR, cValuesOf(0.7f, 0.7f, 0.3f, 1.0f)) + glLightModelfv(GL_LIGHT_MODEL_AMBIENT.convert(), cValuesOf(0.1f, 0.1f, 0.1f, 1.0f)) + glLightfv(GL_LIGHT0.convert(), GL_DIFFUSE.convert(), cValuesOf(0.6f, 0.6f, 0.6f, 1.0f)) + glLightfv(GL_LIGHT0.convert(), GL_SPECULAR.convert(), cValuesOf(0.7f, 0.7f, 0.3f, 1.0f)) - glEnable(GL_LIGHT0) - glEnable(GL_COLOR_MATERIAL) - glShadeModel(GL_SMOOTH) - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE) - glDepthFunc(GL_LEQUAL) - glEnable(GL_DEPTH_TEST) - glEnable(GL_LIGHTING) - glEnable(GL_LIGHT0) + glEnable(GL_LIGHT0.convert()) + glEnable(GL_COLOR_MATERIAL.convert()) + glShadeModel(GL_SMOOTH.convert()) + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE.convert(), GL_FALSE) + glDepthFunc(GL_LEQUAL.convert()) + glEnable(GL_DEPTH_TEST.convert()) + glEnable(GL_LIGHTING.convert()) + glEnable(GL_LIGHT0.convert()) glClearColor(0.0f, 0.0f, 0.0f, 1.0f) } @@ -90,7 +90,7 @@ fun main(args: Array) { } // Display Mode - glutInitDisplayMode(GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH) + glutInitDisplayMode((GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH).convert()) // Set window size glutInitWindowSize(windowWidth, windowHeight) diff --git a/backend.native/tests/interop/libiconv.kt b/backend.native/tests/interop/libiconv.kt index 539e5168652..8e4ef7bce64 100644 --- a/backend.native/tests/interop/libiconv.kt +++ b/backend.native/tests/interop/libiconv.kt @@ -22,8 +22,8 @@ fun main(args: Array) { val destPtr = alloc>() destPtr.value = destBytes - sourceLength.value = sourceByteArray.size.signExtend(); - destLength.value = golden.size.signExtend(); + sourceLength.value = sourceByteArray.size.convert() + destLength.value = golden.size.convert() val conversion = iconv_open("UTF-8", "LATIN1") diff --git a/backend.native/tests/interop/objc/smoke.kt b/backend.native/tests/interop/objc/smoke.kt index a3c588ac007..a1f596dc064 100644 --- a/backend.native/tests/interop/objc/smoke.kt +++ b/backend.native/tests/interop/objc/smoke.kt @@ -118,9 +118,9 @@ fun testTypeOps() { assertTrue(NSValue.asAny() is NSObjectProtocolMeta) assertFalse(NSValue.asAny() is NSObjectProtocol) // Must be true, but not implemented properly yet. - assertEquals(3, ("foo" as NSString).length()) - assertEquals(4, ((1..4).joinToString("") as NSString).length()) - assertEquals(2, (listOf(0, 1) as NSArray).count()) + assertEquals(3u, ("foo" as NSString).length()) + assertEquals(4u, ((1..4).joinToString("") as NSString).length()) + assertEquals(2u, (listOf(0, 1) as NSArray).count()) assertEquals(42L, (42 as NSNumber).longLongValue()) assertFails { "bar" as NSNumber } diff --git a/backend.native/tests/interop/platform_zlib.kt b/backend.native/tests/interop/platform_zlib.kt index 436dd96e143..9c1ab294a2a 100644 --- a/backend.native/tests/interop/platform_zlib.kt +++ b/backend.native/tests/interop/platform_zlib.kt @@ -2,7 +2,7 @@ import kotlinx.cinterop.* 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() val golden = immutableBlobOf(0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21, 0x00).asCPointer() fun main(args: Array) = memScoped { @@ -10,9 +10,9 @@ fun main(args: Array) = memScoped { buffer.usePinned { pinned -> val z = alloc().apply { next_in = source - avail_in = 8 - next_out = pinned.addressOf(0) - avail_out = buffer.size + avail_in = 8u + next_out = pinned.addressOf(0).reinterpret() + avail_out = buffer.size.toUInt() }.ptr if (inflateInit2(z, -15) == Z_OK && inflate(z, Z_FINISH) == Z_STREAM_END && inflateEnd(z) == Z_OK) diff --git a/backend.native/tests/link/default/default.kt b/backend.native/tests/link/default/default.kt index c7b3f8d88f2..cb7f1ba5ca0 100644 --- a/backend.native/tests/link/default/default.kt +++ b/backend.native/tests/link/default/default.kt @@ -1,8 +1,8 @@ -import kotlinx.cinterop.signExtend +import kotlinx.cinterop.convert import platform.posix.* fun main(args: Array) { // Just check the typealias is in scope. - val sizet: size_t = 0.signExtend() + val sizet: size_t = 0.convert() println("sizet = $sizet") } diff --git a/backend.native/tests/link/purge1/lib.kt b/backend.native/tests/link/purge1/lib.kt index 79039bf502b..b0c3a572c7d 100644 --- a/backend.native/tests/link/purge1/lib.kt +++ b/backend.native/tests/link/purge1/lib.kt @@ -1,9 +1,9 @@ -import kotlinx.cinterop.signExtend +import kotlinx.cinterop.convert import platform.posix.* fun foo() { println("linked library") - val size: size_t = 17.signExtend() + val size: size_t = 17.convert() val e = fabs(1.toDouble()) println("and symbols from posix available: $size; $e") } diff --git a/runtime/src/main/cpp/Interop.cpp b/runtime/src/main/cpp/Interop.cpp index aa2f44bf4da..57843602238 100644 --- a/runtime/src/main/cpp/Interop.cpp +++ b/runtime/src/main/cpp/Interop.cpp @@ -40,6 +40,10 @@ const FfiTypeKind FFI_TYPE_KIND_SINT64 = 4; const FfiTypeKind FFI_TYPE_KIND_FLOAT = 5; const FfiTypeKind FFI_TYPE_KIND_DOUBLE = 6; 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) { switch (typeKind) { @@ -51,6 +55,10 @@ ffi_type* convertFfiTypeKindToType(FfiTypeKind typeKind) { case FFI_TYPE_KIND_FLOAT: return &ffi_type_float; case FFI_TYPE_KIND_DOUBLE: return &ffi_type_double; 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; } diff --git a/samples/curl/src/main/kotlin/org/konan/libcurl/CUrl.kt b/samples/curl/src/main/kotlin/org/konan/libcurl/CUrl.kt index 7fee60f2965..d30140a41fe 100644 --- a/samples/curl/src/main/kotlin/org/konan/libcurl/CUrl.kt +++ b/samples/curl/src/main/kotlin/org/konan/libcurl/CUrl.kt @@ -45,7 +45,7 @@ fun CPointer.toKString(length: Int): String { } fun header_callback(buffer: CPointer?, size: size_t, nitems: size_t, userdata: COpaquePointer?): size_t { - if (buffer == null) return 0 + if (buffer == null) return 0u if (userdata != null) { val header = buffer.toKString((size * nitems).toInt()).trim() val curl = userdata.asStableRef().get() @@ -56,7 +56,7 @@ fun header_callback(buffer: CPointer?, size: size_t, nitems: size_t, us fun write_callback(buffer: CPointer?, size: size_t, nitems: size_t, userdata: COpaquePointer?): size_t { - if (buffer == null) return 0 + if (buffer == null) return 0u if (userdata != null) { val data = buffer.toKString((size * nitems).toInt()).trim() val curl = userdata.asStableRef().get() diff --git a/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitCommit.kt b/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitCommit.kt index 09ace0d1e3e..fec70d4883f 100644 --- a/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitCommit.kt +++ b/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitCommit.kt @@ -32,11 +32,11 @@ class GitCommit(val repository: GitRepository, val commit: CPointer) } val parents: List get() = memScoped { - val count = git_commit_parentcount(commit) + val count = git_commit_parentcount(commit).toInt() val result = ArrayList(count) for (index in 0..count - 1) { val commitPtr = allocPointerTo() - git_commit_parent(commitPtr.ptr, commit, index).errorCheck() + git_commit_parent(commitPtr.ptr, commit, index.toUInt()).errorCheck() result.add(GitCommit(repository, commitPtr.value!!)) } result diff --git a/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitDiff.kt b/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitDiff.kt index f513f8e5a64..c8918bfbc7b 100644 --- a/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitDiff.kt +++ b/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitDiff.kt @@ -21,10 +21,10 @@ import libgit2.* class GitDiff(val repository: GitRepository, val handle: CPointer) { fun deltas(): List { - val size = git_diff_num_deltas(handle) - val results = ArrayList(size.toInt()) + val size = git_diff_num_deltas(handle).toInt() + val results = ArrayList(size) 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!!)) } return results diff --git a/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitTree.kt b/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitTree.kt index 8a06143c825..3bd4707f5dd 100644 --- a/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitTree.kt +++ b/samples/gitchurn/src/main/kotlin/org/konan/libgit/GitTree.kt @@ -23,10 +23,10 @@ class GitTree(val repository: GitRepository, val handle: CPointer) { fun close() = git_tree_free(handle) fun entries(): List = memScoped { - val size = git_tree_entrycount(handle) - val entries = ArrayList(size.toInt()) + val size = git_tree_entrycount(handle).toInt() + val entries = ArrayList(size) 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 entry = when (entryType) { GIT_OBJ_TREE -> memScoped { diff --git a/samples/gtk/src/main/kotlin/Main.kt b/samples/gtk/src/main/kotlin/Main.kt index 6784869d622..b93b53b501d 100644 --- a/samples/gtk/src/main/kotlin/Main.kt +++ b/samples/gtk/src/main/kotlin/Main.kt @@ -19,7 +19,7 @@ import gtk3.* // Note that all callback parameters must be primitive types or nullable C pointers. fun > g_signal_connect(obj: CPointer<*>, actionName: String, - action: CPointer, data: gpointer? = null, connect_flags: Int = 0) { + action: CPointer, data: gpointer? = null, connect_flags: GConnectFlags = 0u) { g_signal_connect_data(obj.reinterpret(), actionName, action.reinterpret(), data = data, destroy_data = null, connect_flags = connect_flags) diff --git a/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt b/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt index 1c3cf659986..f8e828e9f6f 100644 --- a/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt +++ b/samples/nonBlockingEchoServer/src/main/kotlin/EchoServer.kt @@ -32,16 +32,16 @@ fun main(args: Array) { val serverAddr = alloc() val listenFd = socket(AF_INET, SOCK_STREAM, 0) - .ensureUnixCallResult { it >= 0 } + .ensureUnixCallResult { !it.isMinusOne() } with(serverAddr) { - memset(this.ptr, 0, sockaddr_in.size) - sin_family = AF_INET.narrow() - sin_addr.s_addr = posix_htons(0).toInt() - sin_port = posix_htons(port) + memset(this.ptr, 0, sockaddr_in.size.convert()) + sin_family = AF_INET.convert() + sin_addr.s_addr = posix_htons(0).convert() + 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 } fcntl(listenFd, F_SETFL, O_NONBLOCK) @@ -53,8 +53,8 @@ fun main(args: Array) { var connectionId = 0 acceptClientsAndRun(listenFd) { memScoped { - val bufferLength = 100L - val buffer = allocArray(bufferLength) + val bufferLength = 100uL + val buffer = allocArray(bufferLength.toLong()) val connectionIdString = "#${++connectionId}: ".cstr val connectionIdBytes = connectionIdString.ptr @@ -62,10 +62,10 @@ fun main(args: Array) { while (true) { val length = read(buffer, bufferLength) - if (length == 0L) + if (length == 0uL) break - write(connectionIdBytes, connectionIdString.size.toLong()) + write(connectionIdBytes, connectionIdString.size.toULong()) write(buffer, length) } } catch (e: IOException) { @@ -80,19 +80,19 @@ sealed class WaitingFor { class Accept : WaitingFor() class Read(val data: CArrayPointer, - val length: Long, - val continuation: Continuation) : WaitingFor() + val length: ULong, + val continuation: Continuation) : WaitingFor() class Write(val data: CArrayPointer, - val length: Long, + val length: ULong, val continuation: Continuation) : WaitingFor() } class Client(val clientFd: Int, val waitingList: MutableMap) { - suspend fun read(data: CArrayPointer, dataLength: Long): Long { + suspend fun read(data: CArrayPointer, dataLength: ULong): ULong { val length = read(clientFd, data, dataLength) if (length >= 0) - return length + return length.toULong() if (posix_errno() != EWOULDBLOCK) throw IOException(getUnixError()) // Save continuation and suspend. @@ -101,7 +101,7 @@ class Client(val clientFd: Int, val waitingList: MutableMap) { } } - suspend fun write(data: CArrayPointer, length: Long) { + suspend fun write(data: CArrayPointer, length: ULong) { val written = write(clientFd, data, length) if (written >= 0) return @@ -153,7 +153,7 @@ fun acceptClientsAndRun(serverFd: Int, block: suspend Client.() -> Unit) { // Accept new client. val clientFd = accept(serverFd, null, null) - if (clientFd < 0) { + if (clientFd.isMinusOne()) { if (posix_errno() != EWOULDBLOCK) throw Error(getUnixError()) break@loop @@ -173,7 +173,7 @@ fun acceptClientsAndRun(serverFd: Int, block: suspend Client.() -> Unit) { val length = read(socketFd, waitingFor.data, waitingFor.length) if (length < 0) // Read error. waitingFor.continuation.resumeWithException(IOException(getUnixError())) - waitingFor.continuation.resume(length) + waitingFor.continuation.resume(length.toULong()) } is WaitingFor.Write -> { if (errorOccured) @@ -210,3 +210,14 @@ inline fun Long.ensureUnixCallResult(predicate: (Long) -> Boolean): Long { } 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) diff --git a/samples/objc/src/main/kotlin/Window.kt b/samples/objc/src/main/kotlin/Window.kt index 739aee5d6e0..463ea9a526b 100644 --- a/samples/objc/src/main/kotlin/Window.kt +++ b/samples/objc/src/main/kotlin/Window.kt @@ -24,7 +24,7 @@ private fun runApp() { app.run() } -data class Data(val stamp: Long) +data class Data(val stamp: ULong) private class Controller : NSObject() { private val asyncQueue = dispatch_queue_create("com.jetbrains.CustomQueue", null) @@ -33,7 +33,7 @@ private class Controller : NSObject() { fun onClick() { // Execute some async action on button click. dispatch_async_f(asyncQueue, detachObjectGraph { - Data(clock_gettime_nsec_np(CLOCK_REALTIME)) + Data(clock_gettime_nsec_np(CLOCK_REALTIME.convert())) }, staticCFunction { it -> initRuntimeIfNeeded() diff --git a/samples/opengl/src/main/kotlin/OpenGlTeapot.kt b/samples/opengl/src/main/kotlin/OpenGlTeapot.kt index a152e3bcb25..68ae3b217e1 100644 --- a/samples/opengl/src/main/kotlin/OpenGlTeapot.kt +++ b/samples/opengl/src/main/kotlin/OpenGlTeapot.kt @@ -29,7 +29,7 @@ private val windowHeight = 480 fun display() { // 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() // Define a viewing transformation @@ -56,13 +56,13 @@ fun display() { fun initialize() { // select projection matrix - glMatrixMode(GL_PROJECTION) + glMatrixMode(GL_PROJECTION.convert()) // set the viewport glViewport(0, 0, windowWidth, windowHeight) // set matrix mode - glMatrixMode(GL_PROJECTION) + glMatrixMode(GL_PROJECTION.convert()) // reset projection matrix glLoadIdentity() @@ -72,29 +72,29 @@ fun initialize() { gluPerspective(45.0, aspect, 1.0, 500.0) // specify which matrix is the current matrix - glMatrixMode(GL_MODELVIEW) - glShadeModel(GL_SMOOTH) + glMatrixMode(GL_MODELVIEW.convert()) + glShadeModel(GL_SMOOTH.convert()) // specify the clear value for the depth buffer glClearDepth(1.0) - glEnable(GL_DEPTH_TEST) - glDepthFunc(GL_LEQUAL) + glEnable(GL_DEPTH_TEST.convert()) + glDepthFunc(GL_LEQUAL.convert()) // 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)) - glLightfv(GL_LIGHT0, GL_DIFFUSE, cValuesOf(0.6f, 0.6f, 0.6f, 1.0f)) - glLightfv(GL_LIGHT0, GL_SPECULAR, cValuesOf(0.7f, 0.7f, 0.3f, 1.0f)) + glLightModelfv(GL_LIGHT_MODEL_AMBIENT.convert(), cValuesOf(0.1f, 0.1f, 0.1f, 1.0f)) + glLightfv(GL_LIGHT0.convert(), GL_DIFFUSE.convert(), cValuesOf(0.6f, 0.6f, 0.6f, 1.0f)) + glLightfv(GL_LIGHT0.convert(), GL_SPECULAR.convert(), cValuesOf(0.7f, 0.7f, 0.3f, 1.0f)) - glEnable(GL_LIGHT0) - glEnable(GL_COLOR_MATERIAL) - glShadeModel(GL_SMOOTH) - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE) - glDepthFunc(GL_LEQUAL) - glEnable(GL_DEPTH_TEST) - glEnable(GL_LIGHTING) - glEnable(GL_LIGHT0) + glEnable(GL_LIGHT0.convert()) + glEnable(GL_COLOR_MATERIAL.convert()) + glShadeModel(GL_SMOOTH.convert()) + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE.convert(), GL_FALSE) + glDepthFunc(GL_LEQUAL.convert()) + glEnable(GL_DEPTH_TEST.convert()) + glEnable(GL_LIGHTING.convert()) + glEnable(GL_LIGHT0.convert()) glClearColor(0.0f, 0.0f, 0.0f, 1.0f) } @@ -106,7 +106,7 @@ fun main(args: Array) { } // Display Mode - glutInitDisplayMode(GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH) + glutInitDisplayMode((GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH).convert()) // Set window size glutInitWindowSize(windowWidth, windowHeight) diff --git a/samples/socket/src/main/kotlin/EchoServer.kt b/samples/socket/src/main/kotlin/EchoServer.kt index 8ad80cb6c67..0dd13f44841 100644 --- a/samples/socket/src/main/kotlin/EchoServer.kt +++ b/samples/socket/src/main/kotlin/EchoServer.kt @@ -35,35 +35,35 @@ fun main(args: Array) { val serverAddr = alloc() val listenFd = socket(AF_INET, SOCK_STREAM, 0) - .ensureUnixCallResult("socket") { it >= 0 } + .ensureUnixCallResult("socket") { !it.isMinusOne() } with(serverAddr) { - memset(this.ptr, 0, sockaddr_in.size) - sin_family = AF_INET.narrow() - sin_port = posix_htons(port) + memset(this.ptr, 0, sockaddr_in.size.convert()) + sin_family = AF_INET.convert() + 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 } listen(listenFd, 10) .ensureUnixCallResult("listen") { it == 0 } val commFd = accept(listenFd, null, null) - .ensureUnixCallResult("accept") { it >= 0 } + .ensureUnixCallResult("accept") { !it.isMinusOne() } buffer.usePinned { pinned -> 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 } if (length == 0) { break } - send(commFd, prefixBuffer.refTo(0), prefixBuffer.size.signExtend(), 0) + send(commFd, prefixBuffer.refTo(0), prefixBuffer.size.convert(), 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 } } } @@ -83,3 +83,14 @@ inline fun Long.ensureUnixCallResult(op: String, predicate: (Long) -> Boolean): } 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) diff --git a/samples/tensorflow/src/main/kotlin/HelloTensorflow.kt b/samples/tensorflow/src/main/kotlin/HelloTensorflow.kt index 84fae1d6e27..1938494f8e0 100644 --- a/samples/tensorflow/src/main/kotlin/HelloTensorflow.kt +++ b/samples/tensorflow/src/main/kotlin/HelloTensorflow.kt @@ -1,4 +1,5 @@ import kotlinx.cinterop.* +import platform.posix.size_t import tensorflow.* typealias Status = CPointer @@ -31,13 +32,13 @@ fun scalarTensor(value: Int): Tensor { return TF_NewTensor(TF_INT32, dims = null, num_dims = 0, - data = data, len = IntVar.size, + data = data, len = IntVar.size.convert(), deallocator = staticCFunction { dataToFree, _, _ -> nativeHeap.free(dataToFree!!.reinterpret()) }, deallocator_arg = null)!! } 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() != TF_TensorByteSize(this)) { throw Error("Tensor is not of type int.") } if (0 != TF_NumDims(this)) { diff --git a/samples/videoplayer/src/main/kotlin/DecoderWorker.kt b/samples/videoplayer/src/main/kotlin/DecoderWorker.kt index 7f70b938b09..f2a07d2beb6 100644 --- a/samples/videoplayer/src/main/kotlin/DecoderWorker.kt +++ b/samples/videoplayer/src/main/kotlin/DecoderWorker.kt @@ -42,13 +42,13 @@ class AudioFrame(val buffer: CPointer, var position: Int, val size: private fun Int.checkAVError() { if (this != 0) { 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()}") } } private val AVFormatContext.codecs: List - 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? = if (index < 0) null else streams?.get(index)?.pointed @@ -124,7 +124,7 @@ private class VideoDecoder( dispose = ::sws_freeContext ) 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(100) @@ -158,7 +158,7 @@ private class VideoDecoder( val buffer = av_buffer_alloc(scaledFrameSize)!! val ts = av_frame_get_best_effort_timestamp(videoFrame.ptr) * 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)) } } @@ -204,11 +204,11 @@ private class AudioDecoder( channels = output.channels sample_rate = output.sampleRate format = output.sampleFormat - channel_layout = output.channelLayout.signExtend() + channel_layout = output.channelLayout.convert() } with (audioCodecContext) { - setResampleOpt("in_channel_layout", channel_layout.narrow()) + setResampleOpt("in_channel_layout", channel_layout.convert()) setResampleOpt("out_channel_layout", output.channelLayout) setResampleOpt("in_sample_rate", sample_rate) setResampleOpt("out_sample_rate", output.sampleRate) @@ -255,7 +255,7 @@ private class AudioDecoder( val buffer = av_buffer_alloc(audioFrameSize)!! val ts = av_frame_get_best_effort_timestamp(audioFrame.ptr) * 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)) } } diff --git a/samples/videoplayer/src/main/kotlin/SDLAudio.kt b/samples/videoplayer/src/main/kotlin/SDLAudio.kt index 4cca17fb045..d81be1f96c5 100644 --- a/samples/videoplayer/src/main/kotlin/SDLAudio.kt +++ b/samples/videoplayer/src/main/kotlin/SDLAudio.kt @@ -27,7 +27,7 @@ enum class SampleFormat { data class AudioOutput(val sampleRate: Int, val channels: Int, val sampleFormat: SampleFormat) private fun SampleFormat.toSDLFormat(): SDL_AudioFormat? = when (this) { - SampleFormat.S16 -> AUDIO_S16SYS.narrow() + SampleFormat.S16 -> AUDIO_S16SYS.convert() SampleFormat.INVALID -> null } @@ -45,9 +45,9 @@ class SDLAudio(val player: VideoPlayer) : DisposableContainer() { val spec = alloc().apply { freq = audio.sampleRate format = audioFormat - channels = audio.channels.narrow() - silence = 0 - samples = 4096 + channels = audio.channels.convert() + silence = 0u + samples = 4096u userdata = threadData callback = staticCFunction(::audioCallback) } @@ -89,12 +89,12 @@ private fun audioCallback(userdata: COpaquePointer?, buffer: CPointer? val frame = decoder.nextAudioFrame(length - outPosition) if (frame != null) { 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() outPosition += toCopy } else { // println("Decoder returned nothing!") - memset(buffer + outPosition, 0, (length - outPosition).signExtend()) + memset(buffer + outPosition, 0, (length - outPosition).convert()) break } } diff --git a/samples/videoplayer/src/main/kotlin/SDLVideo.kt b/samples/videoplayer/src/main/kotlin/SDLVideo.kt index 0e24c328060..082853337c3 100644 --- a/samples/videoplayer/src/main/kotlin/SDLVideo.kt +++ b/samples/videoplayer/src/main/kotlin/SDLVideo.kt @@ -81,7 +81,7 @@ class SDLRendererWindow(windowPos: Dimensions, videoSize: Dimensions) : Disposab SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), 0, videoSize.w, videoSize.h), ::SDL_DestroyTexture) 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() init { diff --git a/samples/videoplayer/src/main/kotlin/VideoPlayer.kt b/samples/videoplayer/src/main/kotlin/VideoPlayer.kt index a47a67b9d6c..d1d60ad5ba9 100644 --- a/samples/videoplayer/src/main/kotlin/VideoPlayer.kt +++ b/samples/videoplayer/src/main/kotlin/VideoPlayer.kt @@ -71,7 +71,7 @@ class VideoPlayer(val requestedSize: Dimensions?) : DisposableContainer() { } 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 } @@ -127,7 +127,7 @@ class VideoPlayer(val requestedSize: Dimensions?) : DisposableContainer() { lastFrameTime += frameDuration // try to maintain perfect frame rate // Wait for next frame, if needed if (passedTime < frameDuration) { - usleep((1000_000 * (frameDuration - passedTime)).toInt()) + usleep((1000_000 * (frameDuration - passedTime)).toInt().toUInt()) } else if (passedTime > frameDuration * 1.5){ 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) { audio.pause() input.check() - usleep(1 * 1000) + usleep(1u * 1000u) } audio.resume() } @@ -152,14 +152,14 @@ class VideoPlayer(val requestedSize: Dimensions?) : DisposableContainer() { if (!decoder.audioVideoSynced()) { println("Resynchronizing video with audio") while (!decoder.audioVideoSynced() && state == State.PLAYING) { - usleep(500) + usleep(500u) input.check() } } } } else { // For pure sound, playback is driven by demand. - usleep(10 * 1000) + usleep(10u * 1000u) } } } diff --git a/samples/win32/src/main/kotlin/MessageBox.kt b/samples/win32/src/main/kotlin/MessageBox.kt index 537e26b27e2..5403ad9a64e 100644 --- a/samples/win32/src/main/kotlin/MessageBox.kt +++ b/samples/win32/src/main/kotlin/MessageBox.kt @@ -1,6 +1,7 @@ +import kotlinx.cinterop.* import platform.windows.* fun main(args: Array) { MessageBoxW(null, "Konan говорит:\nЗДРАВСТВУЙ МИР!\n", - "Заголовок окна", MB_YESNOCANCEL or MB_ICONQUESTION) + "Заголовок окна", (MB_YESNOCANCEL or MB_ICONQUESTION).convert()) }