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
@@ -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<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
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"
@@ -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 =
@@ -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")
}
+1 -1
View File
@@ -4,7 +4,7 @@ fun main(args: Array<String>) {
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<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")
}
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<String>) {
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)
}
}
@@ -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;
}
@@ -7,7 +7,7 @@ fun main(args: Array<String>) {
return
}
val port = atoi(args[0]).toShort()
val port = atoi(args[0]).toUShort()
memScoped {
@@ -19,13 +19,13 @@ fun main(args: Array<String>) {
.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<String>) {
.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.
@@ -1,5 +1,6 @@
import kotlinx.cinterop.*
import cfunptr.*
import kotlin.test.*
fun main(args: Array<String>) {
val atoiPtr = getAtoiPtr()!!
@@ -23,6 +24,8 @@ fun main(args: Array<String>) {
printIntPtr(isIntPositivePtr(42).ifThenOneElseZero())
printIntPtr(isIntPositivePtr(-42).ifThenOneElseZero())
assertEquals(getMaxUIntGetter()!!(), UInt.MAX_VALUE)
}
fun Boolean.ifThenOneElseZero() = if (this) 1 else 0
@@ -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<String>) {
}
// 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)
+2 -2
View File
@@ -22,8 +22,8 @@ fun main(args: Array<String>) {
val destPtr = alloc<CArrayPointerVar<ByteVar>>()
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")
+3 -3
View File
@@ -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 }
@@ -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<UByteVar>()
val golden = immutableBlobOf(0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21, 0x00).asCPointer()
fun main(args: Array<String>) = memScoped {
@@ -10,9 +10,9 @@ fun main(args: Array<String>) = memScoped {
buffer.usePinned { pinned ->
val z = alloc<z_stream>().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)
+2 -2
View File
@@ -1,8 +1,8 @@
import kotlinx.cinterop.signExtend
import kotlinx.cinterop.convert
import platform.posix.*
fun main(args: Array<String>) {
// 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")
}
+2 -2
View File
@@ -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<size_t>()
val size: size_t = 17.convert<size_t>()
val e = fabs(1.toDouble())
println("and symbols from posix available: $size; $e")
}