diff --git a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmUtils.kt b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmUtils.kt index 4379ac7784e..e38e19ce54d 100644 --- a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmUtils.kt +++ b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmUtils.kt @@ -4,4 +4,46 @@ internal fun decodeFromUtf8(bytes: ByteArray) = String(bytes) internal fun encodeToUtf8(str: String) = str.toByteArray() fun bitsToFloat(bits: Int): Float = java.lang.Float.intBitsToFloat(bits) -fun bitsToDouble(bits: Long): Double = java.lang.Double.longBitsToDouble(bits) \ No newline at end of file +fun bitsToDouble(bits: Long): Double = java.lang.Double.longBitsToDouble(bits) + +// TODO: the functions below should eventually be intrinsified + +inline fun Number.signExtend(): R { + val returnValueClass = R::class.java + when (returnValueClass) { + java.lang.Byte::class.java -> if (this is Byte) { + return this.toByte() as R + } + java.lang.Short::class.java -> if (this is Byte || this is Short) { + return this.toShort() as R + } + java.lang.Integer::class.java -> if (this is Byte || this is Short || this is Int) { + return this.toInt() as R + } + java.lang.Long::class.java -> if (this is Byte || this is Short || this is Int || this is Long) { + return this.toLong() as R + } + } + + throw Error("unable to sign extend ${this.javaClass.simpleName} \"$this\" to ${returnValueClass.simpleName}") +} + +inline fun Number.narrow(): R { + val returnValueClass = R::class.java + when (returnValueClass) { + java.lang.Byte::class.java -> if (this is Byte || this is Short || this is Int || this is Long) { + return this.toByte() as R + } + java.lang.Short::class.java -> if (this is Short || this is Int || this is Long) { + return this.toShort() as R + } + java.lang.Integer::class.java -> if (this is Int || this is Long) { + return this.toInt() as R + } + java.lang.Long::class.java -> if (this is Long) { + return this.toLong() as R + } + } + + throw Error("unable to narrow ${this.javaClass.simpleName} \"$this\" to ${returnValueClass.simpleName}") +} diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt index 840f0ca137c..007e4a70f05 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt @@ -21,4 +21,10 @@ fun encodeToUtf8(str: String): ByteArray { external fun bitsToFloat(bits: Int): Float @Intrinsic -external fun bitsToDouble(bits: Long): Double \ No newline at end of file +external fun bitsToDouble(bits: Long): Double + +@Intrinsic +external fun Number.signExtend(): R + +@Intrinsic +external fun Number.narrow(): R 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 887b9d783d5..2b3ed2aab68 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 @@ -132,6 +132,10 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) { fun isTriviallyAdaptedFunctionType(type: KotlinType): Boolean = type.isSubtypeOf(trivallyAdaptedFunctionTypeType) + + val signExtend = packageScope.getContributedFunctions("signExtend").single() + + val narrow = packageScope.getContributedFunctions("narrow").single() } private fun MemberScope.getContributedVariables(name: String) = 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 5c711fad0c1..b2b3db510f1 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 @@ -7,6 +7,7 @@ import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.ValueType import org.jetbrains.kotlin.backend.konan.isRepresentedAs import org.jetbrains.kotlin.backend.konan.reportCompilationError +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.builders.IrBuilder @@ -239,6 +240,50 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB } } + interop.signExtend, interop.narrow -> { + + val integerTypePredicates = arrayOf( + KotlinBuiltIns::isByte, KotlinBuiltIns::isShort, KotlinBuiltIns::isInt, KotlinBuiltIns::isLong + ) + + val receiver = expression.extensionReceiver!! + val typeOperand = expression.getSingleTypeArgument() + + val receiverTypeIndex = integerTypePredicates.indexOfFirst { it(receiver.type) } + val typeOperandIndex = integerTypePredicates.indexOfFirst { it(typeOperand) } + + if (receiverTypeIndex == -1) { + context.reportCompilationError("Receiver's type ${receiver.type} is not an integer type", + irFile, receiver) + } + + if (typeOperandIndex == -1) { + context.reportCompilationError("Type argument $typeOperand is not an integer type", + irFile, expression) + } + + when (descriptor) { + interop.signExtend -> if (receiverTypeIndex > typeOperandIndex) { + context.reportCompilationError("unable to sign extend ${receiver.type} to $typeOperand", + irFile, expression) + } + + interop.narrow -> if (receiverTypeIndex < typeOperandIndex) { + context.reportCompilationError("unable to narrow ${receiver.type} to $typeOperand", + irFile, expression) + } + + else -> throw Error() + } + + val conversionDescriptor = receiver.type.memberScope.getContributedFunctions( + Name.identifier("to$typeOperand"), NoLookupLocation.FROM_BACKEND).single() + + builder.irCall(conversionDescriptor).apply { + dispatchReceiver = receiver + } + } + else -> expression } } diff --git a/backend.native/tests/interop/basics/echo_server.kt b/backend.native/tests/interop/basics/echo_server.kt index bec850a660e..090e277bbb4 100644 --- a/backend.native/tests/interop/basics/echo_server.kt +++ b/backend.native/tests/interop/basics/echo_server.kt @@ -20,7 +20,7 @@ fun main(args: Array) { with(serverAddr) { memset(this.ptr, 0, sockaddr_in.size) - sin_family.value = AF_INET.toByte() + sin_family.value = AF_INET.narrow() sin_addr.s_addr.value = htons(0).toInt() sin_port.value = htons(port) }