Support unsigned types (#1854)

(cherry picked from commit 7b46f93033146462619376d8d008343c2ee75e00)
This commit is contained in:
SvyatoslavScherbina
2018-08-17 17:48:11 +03:00
committed by Vasily Levchenko
parent c78d070455
commit 49a32e92c1
12 changed files with 315 additions and 60 deletions
@@ -259,6 +259,7 @@ internal fun IrBuiltIns.getKotlinClass(cache: BoxCache): IrClass = when (cache)
BoxCache.LONG -> longClass
}.owner
// TODO: consider adding box caches for unsigned types.
enum class BoxCache {
BOOLEAN, BYTE, SHORT, CHAR, INT, LONG
}
@@ -25,18 +25,16 @@ import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.annotations.*
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.name.isChildOf
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isUnit
@@ -760,14 +758,18 @@ internal class CAdapterGenerator(
typedef _Bool ${prefix}_KBoolean;
#endif
""".trimIndent())
output("typedef char ${prefix}_KByte;")
output("typedef unsigned short ${prefix}_KChar;")
output("typedef short ${prefix}_KShort;")
output("typedef int ${prefix}_KInt;")
output("typedef long long ${prefix}_KLong;")
output("typedef float ${prefix}_KFloat;")
output("typedef double ${prefix}_KDouble;")
output("typedef void* ${prefix}_KNativePtr;")
output("typedef unsigned short ${prefix}_KChar;")
output("typedef signed char ${prefix}_KByte;")
output("typedef short ${prefix}_KShort;")
output("typedef int ${prefix}_KInt;")
output("typedef long long ${prefix}_KLong;")
output("typedef unsigned char ${prefix}_KUByte;")
output("typedef unsigned short ${prefix}_KUShort;")
output("typedef unsigned int ${prefix}_KUInt;")
output("typedef unsigned long long ${prefix}_KULong;")
output("typedef float ${prefix}_KFloat;")
output("typedef double ${prefix}_KDouble;")
output("typedef void* ${prefix}_KNativePtr;")
output("struct ${prefix}_KType;")
output("typedef struct ${prefix}_KType ${prefix}_KType;")
@@ -894,6 +896,15 @@ internal class CAdapterGenerator(
}
}
private val unsignedTypeMapping = UnsignedType.values().associate {
it.classId to when (it) {
UnsignedType.UBYTE -> "${prefix}_KUByte"
UnsignedType.USHORT -> "${prefix}_KUShort"
UnsignedType.UINT -> "${prefix}_KUInt"
UnsignedType.ULONG -> "${prefix}_KULong"
}
}
internal fun isMappedToString(descriptor: ClassDescriptor): Boolean =
isMappedToString(descriptor.defaultType.computeBinaryType())
@@ -926,8 +937,10 @@ internal class CAdapterGenerator(
}
private fun translateNonVoidTypeFull(type: KotlinType): Pair<String, String> = type.unwrapToPrimitiveOrReference(
eachInlinedClass = { _, _ ->
// unsigned types to be handled.
eachInlinedClass = { inlinedClass, _ ->
unsignedTypeMapping[inlinedClass.classId]?.let {
return it to it
}
},
ifPrimitive = { primitiveType, _ ->
primitiveTypeMapping[primitiveType]!!.let { it to it }
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.kotlinNativeInternal
import org.jetbrains.kotlin.backend.konan.llvm.findMainEntryPoint
import org.jetbrains.kotlin.backend.konan.lower.TestProcessor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.config.coroutinesIntrinsicsPackageFqName
import org.jetbrains.kotlin.config.coroutinesPackageFqName
@@ -134,6 +135,14 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val nativePtr = symbolTable.referenceClass(context.nativePtr)
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol =
symbolTable.referenceClass(builtIns.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!!)
val uByte = unsignedClass(UnsignedType.UBYTE)
val uShort = unsignedClass(UnsignedType.USHORT)
val uInt = unsignedClass(UnsignedType.UINT)
val uLong = unsignedClass(UnsignedType.ULONG)
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
val interopNativePointedGetRawPointer =
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.types.KotlinType
@@ -95,8 +96,11 @@ internal class ObjCExportCodeGenerator(
): LLVMValueRef = when (valueType) {
ObjCValueType.BOOL -> zext(value, int8Type) // TODO: zext behaviour may be strange on bit types.
ObjCValueType.CHAR, ObjCValueType.UNSIGNED_SHORT, ObjCValueType.SHORT,
ObjCValueType.INT, ObjCValueType.LONG_LONG, ObjCValueType.FLOAT, ObjCValueType.DOUBLE -> value
ObjCValueType.UNICHAR,
ObjCValueType.CHAR, ObjCValueType.SHORT, ObjCValueType.INT, ObjCValueType.LONG_LONG,
ObjCValueType.UNSIGNED_CHAR, ObjCValueType.UNSIGNED_SHORT, ObjCValueType.UNSIGNED_INT,
ObjCValueType.UNSIGNED_LONG_LONG,
ObjCValueType.FLOAT, ObjCValueType.DOUBLE -> value
}
private fun FunctionGenerationContext.objCToKotlin(
@@ -105,8 +109,11 @@ internal class ObjCExportCodeGenerator(
): LLVMValueRef = when (valueType) {
ObjCValueType.BOOL -> icmpNe(value, Int8(0).llvm)
ObjCValueType.CHAR, ObjCValueType.UNSIGNED_SHORT, ObjCValueType.SHORT,
ObjCValueType.INT, ObjCValueType.LONG_LONG, ObjCValueType.FLOAT, ObjCValueType.DOUBLE -> value
ObjCValueType.UNICHAR,
ObjCValueType.CHAR, ObjCValueType.SHORT, ObjCValueType.INT, ObjCValueType.LONG_LONG,
ObjCValueType.UNSIGNED_CHAR, ObjCValueType.UNSIGNED_SHORT, ObjCValueType.UNSIGNED_INT,
ObjCValueType.UNSIGNED_LONG_LONG,
ObjCValueType.FLOAT, ObjCValueType.DOUBLE -> value
}
fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef) =
@@ -340,22 +347,30 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo(
private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef
get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr)
private fun ObjCExportCodeGenerator.emitBoxConverter(objCValueType: ObjCValueType) {
val symbols = context.ir.symbols
private fun ObjCExportCodeGenerator.emitBoxConverters() {
val irBuiltIns = context.irBuiltIns
val symbols = context.ir.symbols
val boxClass = when (objCValueType) {
ObjCValueType.BOOL -> irBuiltIns.booleanClass
ObjCValueType.UNSIGNED_SHORT -> irBuiltIns.charClass
ObjCValueType.CHAR -> irBuiltIns.byteClass
ObjCValueType.SHORT -> irBuiltIns.shortClass
ObjCValueType.INT -> irBuiltIns.intClass
ObjCValueType.LONG_LONG -> irBuiltIns.longClass
ObjCValueType.FLOAT -> irBuiltIns.floatClass
ObjCValueType.DOUBLE -> irBuiltIns.doubleClass
}.owner
emitBoxConverter(irBuiltIns.booleanClass, ObjCValueType.BOOL, "numberWithBool:")
emitBoxConverter(irBuiltIns.byteClass, ObjCValueType.CHAR, "numberWithChar:")
emitBoxConverter(irBuiltIns.shortClass, ObjCValueType.SHORT, "numberWithShort:")
emitBoxConverter(irBuiltIns.intClass, ObjCValueType.INT, "numberWithInt:")
emitBoxConverter(irBuiltIns.longClass, ObjCValueType.LONG_LONG, "numberWithLongLong:")
emitBoxConverter(symbols.uByte, ObjCValueType.UNSIGNED_CHAR, "numberWithUnsignedChar")
emitBoxConverter(symbols.uShort, ObjCValueType.UNSIGNED_SHORT, "numberWithUnsignedShort:")
emitBoxConverter(symbols.uInt, ObjCValueType.UNSIGNED_INT, "numberWithUnsignedInt:")
emitBoxConverter(symbols.uLong, ObjCValueType.UNSIGNED_LONG_LONG, "numberWithUnsignedLongLong:")
emitBoxConverter(irBuiltIns.floatClass, ObjCValueType.FLOAT, "numberWithFloat:")
emitBoxConverter(irBuiltIns.doubleClass, ObjCValueType.DOUBLE, "numberWithDouble:")
}
val name = "${boxClass.name}To${objCValueType.nsNumberName}"
private fun ObjCExportCodeGenerator.emitBoxConverter(
boxClassSymbol: IrClassSymbol,
objCValueType: ObjCValueType,
nsNumberFactorySelector: String
) {
val boxClass = boxClassSymbol.owner
val name = "${boxClass.name}ToNSNumber"
val converter = generateFunction(codegen, kotlinToObjCFunctionType, name) {
val unboxFunction = context.getUnboxFunction(boxClass).llvmFunction
@@ -368,7 +383,7 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(objCValueType: ObjCValueTyp
val value = kotlinToObjC(kotlinValue, objCValueType)
val nsNumber = genGetSystemClass("NSNumber")
ret(genSendMessage(int8TypePtr, nsNumber, objCValueType.nsNumberFactorySelector, value))
ret(genSendMessage(int8TypePtr, nsNumber, nsNumberFactorySelector, value))
}
LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage)
@@ -446,9 +461,7 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
constPointer(context.llvm.Kotlin_Interop_CreateKotlinMutableDictonaryFromKMap)
)
ObjCValueType.values().forEach {
emitBoxConverter(it)
}
emitBoxConverters()
emitFunctionConverters()
@@ -1091,11 +1104,15 @@ private fun objCFunctionType(context: Context, methodBridge: MethodBridge): LLVM
private val ObjCValueType.llvmType: LLVMTypeRef get() = when (this) {
ObjCValueType.BOOL -> int8Type
ObjCValueType.UNICHAR -> int16Type
ObjCValueType.CHAR -> int8Type
ObjCValueType.UNSIGNED_SHORT -> int16Type
ObjCValueType.SHORT -> int16Type
ObjCValueType.INT -> int32Type
ObjCValueType.LONG_LONG -> int64Type
ObjCValueType.UNSIGNED_CHAR -> int8Type
ObjCValueType.UNSIGNED_SHORT -> int16Type
ObjCValueType.UNSIGNED_INT -> int32Type
ObjCValueType.UNSIGNED_LONG_LONG -> int64Type
ObjCValueType.FLOAT -> LLVMFloatType()!!
ObjCValueType.DOUBLE -> LLVMDoubleType()!!
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
@@ -66,13 +67,18 @@ abstract class ObjCExportHeaderGenerator(
result += CustomTypeMapper.Collection(generator, map, "NSDictionary")
result += CustomTypeMapper.Collection(generator, mutableMap, namer.mutableMapName)
for (descriptor in listOf(boolean, char, byte, short, int, long, float, double)) {
for (descriptor in listOf(boolean, byte, short, int, long, float, double)) {
// TODO: Kotlin code doesn't have any checkcasts on unboxing,
// so it is possible that it expects boxed number of other type and unboxes it incorrectly.
// TODO: NSNumber seem to have different equality semantics.
result += CustomTypeMapper.Simple(descriptor, "NSNumber")
}
for (unsignedType in UnsignedType.values()) {
val descriptor = moduleDescriptor.findClassAcrossModuleDependencies(unsignedType.classId)!!
result += CustomTypeMapper.Simple(descriptor, "NSNumber")
}
result += CustomTypeMapper.Simple(string, "NSString")
(0..mapper.maxFunctionTypeParameterCount).forEach {
@@ -737,11 +743,15 @@ abstract class ObjCExportHeaderGenerator(
is ValueTypeBridge -> {
val cName = when (typeBridge.objCValueType) {
ObjCValueType.BOOL -> "BOOL"
ObjCValueType.UNICHAR -> "unichar"
ObjCValueType.CHAR -> "int8_t"
ObjCValueType.UNSIGNED_SHORT -> "unichar"
ObjCValueType.SHORT -> "int16_t"
ObjCValueType.INT -> "int32_t"
ObjCValueType.LONG_LONG -> "int64_t"
ObjCValueType.UNSIGNED_CHAR -> "uint8_t"
ObjCValueType.UNSIGNED_SHORT -> "uint16_t"
ObjCValueType.UNSIGNED_INT -> "uint32_t"
ObjCValueType.UNSIGNED_LONG_LONG -> "uint64_t"
ObjCValueType.FLOAT -> "float"
ObjCValueType.DOUBLE -> "double"
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.types.KotlinType
@@ -111,13 +112,18 @@ internal fun ObjCExportMapper.doesThrow(method: FunctionDescriptor): Boolean = m
}
private fun ObjCExportMapper.bridgeType(kotlinType: KotlinType): TypeBridge = kotlinType.unwrapToPrimitiveOrReference(
eachInlinedClass = { _, _ ->
// unsigned types to be handled.
eachInlinedClass = { inlinedClass, _ ->
when (inlinedClass.classId) {
UnsignedType.UBYTE.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_CHAR)
UnsignedType.USHORT.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_SHORT)
UnsignedType.UINT.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_INT)
UnsignedType.ULONG.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_LONG_LONG)
}
},
ifPrimitive = { primitiveType, _ ->
val objCValueType = when (primitiveType) {
KonanPrimitiveType.BOOLEAN -> ObjCValueType.BOOL
KonanPrimitiveType.CHAR -> ObjCValueType.UNSIGNED_SHORT
KonanPrimitiveType.CHAR -> ObjCValueType.UNICHAR
KonanPrimitiveType.BYTE -> ObjCValueType.CHAR
KonanPrimitiveType.SHORT -> ObjCValueType.SHORT
KonanPrimitiveType.INT -> ObjCValueType.INT
@@ -219,4 +225,4 @@ internal fun ObjCExportMapper.bridgePropertyType(descriptor: PropertyDescriptor)
assert(isBaseProperty(descriptor))
return bridgeType(descriptor.type)
}
}
@@ -111,27 +111,17 @@ object ObjCVoidType : ObjCType() {
override fun render(attrsAndName: String) = "void".withAttrsAndName(attrsAndName)
}
internal enum class ObjCValueType(
val encoding: String
) {
internal enum class ObjCValueType(val encoding: String) {
BOOL("c"),
UNICHAR("S"),
CHAR("c"),
SHORT("s"),
UNSIGNED_SHORT("S"),
INT("i"),
LONG_LONG("q"),
UNSIGNED_CHAR("C"),
UNSIGNED_SHORT("S"),
UNSIGNED_INT("I"),
UNSIGNED_LONG_LONG("Q"),
FLOAT("f"),
DOUBLE("d")
;
// UNSIGNED_SHORT -> unsignedShort
val nsNumberName = this.name.split('_').mapIndexed { index, s ->
val lower = s.toLowerCase()
if (index > 0) lower.capitalize() else lower
}.joinToString("")
val nsNumberValueSelector get() = "${nsNumberName}Value"
val nsNumberFactorySelector get() = "numberWith${nsNumberName.capitalize()}:"
}