Map returned Unit to void in more cases when producing framework (#2968)

Including function-typed parameters and return types.
This commit is contained in:
SvyatoslavScherbina
2019-05-22 10:45:18 +03:00
committed by GitHub
parent f6544c40bb
commit 9b8b94717f
13 changed files with 383 additions and 130 deletions
@@ -134,6 +134,8 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
val interopForeignObjCObject = interopClass("ForeignObjCObject")
// These are possible supertypes of forward declarations - we need to reference them explicitly to force their deserialization.
// TODO: Do it lazily.
val interopCOpaque = symbolTable.referenceClass(context.interopBuiltIns.cOpaque)
@@ -430,6 +432,11 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
.single()
)
private fun interopClass(name: String) = symbolTable.referenceClass(
context.interopBuiltIns.packageScope
.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND) as ClassDescriptor
)
val functions = (0 .. KONAN_FUNCTION_INTERFACES_MAX_PARAMETERS)
.map { symbolTable.referenceClass(builtIns.getFunction(it)) }
@@ -460,6 +460,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val Kotlin_ObjCExport_AbstractMethodCalled by lazyRtFunction
val Kotlin_ObjCExport_RethrowExceptionAsNSError by lazyRtFunction
val Kotlin_ObjCExport_RethrowNSErrorAsException by lazyRtFunction
val Kotlin_ObjCExport_AllocInstanceWithAssociatedObject by lazyRtFunction
val tlsMode by lazy {
when (target) {
@@ -188,16 +188,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
pointerType(runtime.typeInfoType), interfaces)
val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type ->
if (isObjectType(type)) {
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
} else {
null
}
}
val objOffsets = getObjOffsets(bodyType)
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type,
objOffsets.map { Int32(it.toInt()) })
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type, objOffsets)
val objOffsetsCount = if (irClass.descriptor == context.builtIns.array) {
1 // To mark it as non-leaf.
@@ -242,6 +235,15 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
exportTypeInfoIfRequired(irClass, irClass.llvmTypeInfoPtr)
}
private fun getObjOffsets(bodyType: LLVMTypeRef): List<Int32> =
getStructElements(bodyType).mapIndexedNotNull { index, type ->
if (isObjectType(type)) {
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
} else {
null
}
}.map { Int32(it.toInt()) }
fun vtable(irClass: IrClass): ConstArray {
// TODO: compile-time resolution limits binary compatibility.
val vtableEntries = context.getVtableBuilder(irClass).vtableEntries.map {
@@ -314,11 +316,12 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fun generateSyntheticInterfaceImpl(
irClass: IrClass,
methodImpls: Map<IrFunction, ConstPointer>,
bodyType: LLVMTypeRef,
immutable: Boolean = false
): ConstPointer {
assert(irClass.isInterface)
val size = LLVMStoreSizeOfType(llvmTargetData, kObjHeader).toInt()
val size = LLVMStoreSizeOfType(llvmTargetData, bodyType).toInt()
val superClass = context.ir.symbols.any.owner
@@ -328,8 +331,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
pointerType(runtime.typeInfoType), interfaces)
assert(superClass.declarations.all { it !is IrProperty && it !is IrField })
val objOffsetsPtr = NullPointer(int32Type)
val objOffsetsCount = 0
val objOffsets = getObjOffsets(bodyType)
val objOffsetsPtr = staticData.placeGlobalConstArray("", int32Type, objOffsets)
val objOffsetsCount = objOffsets.size
val methods = (methodTableRecords(superClass) + methodImpls.map { (method, impl) ->
assert(method.parent == irClass)
@@ -5,47 +5,133 @@
package org.jetbrains.kotlin.backend.konan.llvm.objcexport
import llvm.LLVMLinkage
import llvm.LLVMSetLinkage
import llvm.LLVMStoreSizeOfType
import llvm.LLVMValueRef
import llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.objcexport.BlockPointerBridge
import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.util.OperatorNameConventions
internal fun ObjCExportCodeGenerator.generateKotlinFunctionImpl(invokeMethod: IrSimpleFunction): ConstPointer {
// TODO: consider also overriding methods of `Any`.
internal fun ObjCExportCodeGenerator.generateBlockToKotlinFunctionConverter(
bridge: BlockPointerBridge
): LLVMValueRef {
val irInterface = symbols.functions[bridge.numberOfParameters].owner
val invokeMethod = irInterface.declarations.filterIsInstance<IrSimpleFunction>()
.single { it.name == OperatorNameConventions.INVOKE }
val numberOfParameters = invokeMethod.valueParameters.size
// Note: we can store Objective-C block pointer as associated object of Kotlin function object itself,
// but only if it is equivalent to its dynamic translation result. If block returns void, then it's not like that:
val useSeparateHolder = bridge.returnsVoid
val function = generateFunction(
val bodyType = if (useSeparateHolder) {
structType(codegen.kObjHeader, codegen.kObjHeaderPtr)
} else {
structType(codegen.kObjHeader)
}
val invokeImpl = generateFunction(
codegen,
codegen.getLlvmFunctionType(invokeMethod),
"invokeFunction$numberOfParameters"
"invokeFunction${bridge.nameSuffix}"
) {
val args = (0 until numberOfParameters).map { index -> kotlinReferenceToObjC(param(index + 1)) }
val args = (0 until bridge.numberOfParameters).map { index ->
kotlinReferenceToObjC(param(index + 1))
}
val rawBlockPtr = callFromBridge(context.llvm.Kotlin_ObjCExport_GetAssociatedObject, listOf(param(0)))
val thisRef = param(0)
val associatedObjectHolder = if (useSeparateHolder) {
val bodyPtr = bitcast(pointerType(bodyType), thisRef)
loadSlot(structGep(bodyPtr, 1), isVar = false)
} else {
thisRef
}
val blockPtr = callFromBridge(
context.llvm.Kotlin_ObjCExport_GetAssociatedObject,
listOf(associatedObjectHolder)
)
val blockLiteralType = codegen.runtime.getStructType("Block_literal_1")
val blockPtr = bitcast(pointerType(blockLiteralType), rawBlockPtr)
val invokePtr = structGep(blockPtr, 3)
val invoke = loadBlockInvoke(blockPtr, bridge)
val result = callFromBridge(invoke, listOf(blockPtr) + args)
val blockInvokeType = functionType(int8TypePtr, false, (0 .. numberOfParameters).map { int8TypePtr })
val invoke = bitcast(pointerType(blockInvokeType), load(invokePtr))
val result = callFromBridge(invoke, listOf(rawBlockPtr) + args)
// TODO: support void-as-Unit.
ret(objCReferenceToKotlin(result, Lifetime.RETURN_VALUE))
val kotlinResult = if (bridge.returnsVoid) {
theUnitInstanceRef.llvm
} else {
objCReferenceToKotlin(result, Lifetime.RETURN_VALUE)
}
ret(kotlinResult)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
}
return constPointer(function)
val typeInfo = rttiGenerator.generateSyntheticInterfaceImpl(
irInterface,
mapOf(invokeMethod to constPointer(invokeImpl)),
bodyType,
immutable = true
)
return generateFunction(
codegen,
functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr),
""
) {
val blockPtr = param(0)
ifThen(icmpEq(blockPtr, kNullInt8Ptr)) {
ret(kNullObjHeaderPtr)
}
val retainedBlockPtr = callFromBridge(retainBlock, listOf(blockPtr))
val result = if (useSeparateHolder) {
val result = allocInstance(typeInfo.llvm, Lifetime.RETURN_VALUE)
val bodyPtr = bitcast(pointerType(bodyType), result)
val holder = allocInstanceWithAssociatedObject(
symbols.interopForeignObjCObject.owner.typeInfoPtr,
retainedBlockPtr,
Lifetime.ARGUMENT
)
storeHeapRef(holder, structGep(bodyPtr, 1))
result
} else {
allocInstanceWithAssociatedObject(typeInfo, retainedBlockPtr, Lifetime.RETURN_VALUE)
}
ret(result)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
}
}
private fun FunctionGenerationContext.loadBlockInvoke(
blockPtr: LLVMValueRef,
bridge: BlockPointerBridge
): LLVMValueRef {
val blockLiteralType = codegen.runtime.getStructType("Block_literal_1")
val invokePtr = structGep(bitcast(pointerType(blockLiteralType), blockPtr), 3)
return bitcast(pointerType(bridge.blockInvokeLlvmType), load(invokePtr))
}
private fun FunctionGenerationContext.allocInstanceWithAssociatedObject(
typeInfo: ConstPointer,
associatedObject: LLVMValueRef,
resultLifetime: Lifetime
): LLVMValueRef = call(
context.llvm.Kotlin_ObjCExport_AllocInstanceWithAssociatedObject,
listOf(typeInfo.llvm, associatedObject),
resultLifetime
)
private val BlockPointerBridge.blockInvokeLlvmType: LLVMTypeRef
get() = functionType(
if (returnsVoid) voidType else int8TypePtr,
false,
(0..numberOfParameters).map { int8TypePtr }
)
private val BlockPointerBridge.nameSuffix: String
get() = numberOfParameters.toString() + if (returnsVoid) "V" else ""
internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjCExportCodeGenerator) {
private val codegen get() = objCExportCodeGenerator.codegen
@@ -94,9 +180,11 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
fun org.jetbrains.kotlin.backend.konan.Context.LongInt(value: Long) =
if (is64Bit()) Int64(value) else Int32(value.toInt())
private fun generateDescriptorForBlockAdapterToFunction(numberOfParameters: Int): ConstValue {
private fun generateDescriptorForBlockAdapterToFunction(bridge: BlockPointerBridge): ConstValue {
val numberOfParameters = bridge.numberOfParameters
val signature = buildString {
append('@')
append(if (bridge.returnsVoid) 'v' else '@')
val pointerSize = codegen.runtime.pointerSize
append(pointerSize * (numberOfParameters + 1))
@@ -132,14 +220,10 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
)
}
private fun ObjCExportCodeGenerator.generateInvoke(numberOfParameters: Int): ConstPointer {
val functionType = functionType(
int8TypePtr,
false,
(0 .. numberOfParameters).map { int8TypePtr }
)
private fun ObjCExportCodeGenerator.generateInvoke(bridge: BlockPointerBridge): ConstPointer {
val numberOfParameters = bridge.numberOfParameters
val result = generateFunction(codegen, functionType, "invokeBlock$numberOfParameters") {
val result = generateFunction(codegen, bridge.blockInvokeLlvmType, "invokeBlock${bridge.nameSuffix}") {
val blockPtr = bitcast(pointerType(blockLiteralType), param(0))
val kotlinFunction = loadSlot(structGep(blockPtr, 1), isVar = false)
@@ -154,7 +238,11 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
val result = callFromBridge(callee, listOf(kotlinFunction) + args, Lifetime.ARGUMENT)
ret(kotlinReferenceToObjC(result))
if (bridge.returnsVoid) {
ret(null)
} else {
ret(kotlinReferenceToObjC(result))
}
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
}
@@ -162,17 +250,22 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
return constPointer(result)
}
fun ObjCExportCodeGenerator.generateConvertFunctionToBlock(numberOfParameters: Int): LLVMValueRef {
fun ObjCExportCodeGenerator.generateConvertFunctionToBlock(bridge: BlockPointerBridge): LLVMValueRef {
val blockDescriptor = codegen.staticData.placeGlobal(
"",
generateDescriptorForBlockAdapterToFunction(numberOfParameters)
generateDescriptorForBlockAdapterToFunction(bridge)
)
return generateFunction(
codegen,
functionType(int8TypePtr, false, codegen.kObjHeaderPtr),
"convertFunction$numberOfParameters"
"convertFunction${bridge.nameSuffix}"
) {
val kotlinRef = param(0)
ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) {
ret(kNullInt8Ptr)
}
val isa = codegen.importGlobal(
"_NSConcreteStackBlock",
int8TypePtr,
@@ -183,7 +276,7 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
val reserved = Int32(0).llvm
val invokeType = pointerType(functionType(voidType, true, int8TypePtr))
val invoke = generateInvoke(numberOfParameters).bitcast(invokeType).llvm
val invoke = generateInvoke(bridge).bitcast(invokeType).llvm
val descriptor = blockDescriptor.llvmGlobal
val blockOnStack = alloca(blockLiteralType)
@@ -197,13 +290,7 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
}
// Note: it is the slot in the block located on stack, so no need to manage it properly:
storeRefUnsafe(param(0), slot)
val retainBlock = context.llvm.externalFunction(
"objc_retainBlock",
functionType(int8TypePtr, false, int8TypePtr),
CurrentKonanModuleOrigin
)
storeRefUnsafe(kotlinRef, slot)
val copiedBlock = callFromBridge(retainBlock, listOf(bitcast(int8TypePtr, blockOnStack)))
@@ -219,3 +306,9 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
}
}
}
private val ObjCExportCodeGenerator.retainBlock get() = context.llvm.externalFunction(
"objc_retainBlock",
functionType(int8TypePtr, false, int8TypePtr),
CurrentKonanModuleOrigin
)
@@ -25,10 +25,9 @@ import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
internal fun TypeBridge.makeNothing() = when (this) {
is ReferenceBridge -> kNullInt8Ptr
is ReferenceBridge, is BlockPointerBridge -> kNullInt8Ptr
is ValueTypeBridge -> LLVMConstNull(this.objCValueType.llvmType)!!
}
@@ -121,23 +120,59 @@ internal class ObjCExportCodeGenerator(
fun FunctionGenerationContext.objCReferenceToKotlin(value: LLVMValueRef, resultLifetime: Lifetime) =
callFromBridge(context.llvm.Kotlin_ObjCExport_refFromObjC, listOf(value), resultLifetime)
private fun FunctionGenerationContext.objCBlockPointerToKotlin(
value: LLVMValueRef,
typeBridge: BlockPointerBridge,
resultLifetime: Lifetime
) = callFromBridge(
blockToKotlinFunctionConverter(typeBridge),
listOf(value),
resultLifetime
)
private val blockToKotlinFunctionConverterCache = mutableMapOf<BlockPointerBridge, LLVMValueRef>()
internal fun blockToKotlinFunctionConverter(bridge: BlockPointerBridge): LLVMValueRef =
blockToKotlinFunctionConverterCache.getOrPut(bridge) {
generateBlockToKotlinFunctionConverter(bridge)
}
private fun FunctionGenerationContext.kotlinFunctionToObjCBlockPointer(
typeBridge: BlockPointerBridge,
value: LLVMValueRef
) = callFromBridge(kotlinFunctionToBlockConverter(typeBridge), listOf(value))
private val blockAdapterToFunctionGenerator = BlockAdapterToFunctionGenerator(this)
private val functionToBlockConverterCache = mutableMapOf<BlockPointerBridge, LLVMValueRef>()
internal fun kotlinFunctionToBlockConverter(bridge: BlockPointerBridge): LLVMValueRef =
functionToBlockConverterCache.getOrPut(bridge) {
blockAdapterToFunctionGenerator.run {
generateConvertFunctionToBlock(bridge)
}
}
fun FunctionGenerationContext.kotlinToObjC(
value: LLVMValueRef,
typeBridge: TypeBridge
): LLVMValueRef = when {
LLVMTypeOf(value) == voidType -> typeBridge.makeNothing()
typeBridge is ReferenceBridge -> kotlinReferenceToObjC(value)
typeBridge is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType)
else -> TODO()
): LLVMValueRef = if (LLVMTypeOf(value) == voidType) {
typeBridge.makeNothing()
} else {
when (typeBridge) {
is ReferenceBridge -> kotlinReferenceToObjC(value)
is BlockPointerBridge -> kotlinFunctionToObjCBlockPointer(typeBridge, value)
is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType)
}
}
fun FunctionGenerationContext.objCToKotlin(
value: LLVMValueRef,
typeBridge: TypeBridge,
resultLifetime: Lifetime
): LLVMValueRef = when (typeBridge) {
// TODO: if we add value type check here, we could bridge on Unit better.
is ReferenceBridge -> objCReferenceToKotlin(value, resultLifetime)
is BlockPointerBridge -> objCBlockPointerToKotlin(value, typeBridge, resultLifetime)
is ValueTypeBridge -> objCToKotlin(value, typeBridge.objCValueType)
}
@@ -426,39 +461,25 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
}
private fun ObjCExportCodeGenerator.emitFunctionConverters() {
val generator = BlockAdapterToFunctionGenerator(this)
(0 .. ObjCExportMapper.maxFunctionTypeParameterCount).forEach { numberOfParameters ->
val converter = generator.run { generateConvertFunctionToBlock(numberOfParameters) }
val converter = kotlinFunctionToBlockConverter(BlockPointerBridge(numberOfParameters, returnsVoid = false))
setObjCExportTypeInfo(symbols.functions[numberOfParameters].owner, constPointer(converter))
}
}
private fun ObjCExportCodeGenerator.generateKotlinFunctionAdapterToBlock(numberOfParameters: Int): ConstPointer {
val irInterface = symbols.functions[numberOfParameters].owner
val invokeMethod = irInterface.declarations.filterIsInstance<IrSimpleFunction>()
.single { it.name == OperatorNameConventions.INVOKE }
val invokeImpl = generateKotlinFunctionImpl(invokeMethod)
return rttiGenerator.generateSyntheticInterfaceImpl(
irInterface,
mapOf(invokeMethod to invokeImpl),
immutable = true
)
}
private fun ObjCExportCodeGenerator.emitKotlinFunctionAdaptersToBlock() {
private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() {
val converters = (0 .. ObjCExportMapper.maxFunctionTypeParameterCount).map {
val bridge = BlockPointerBridge(numberOfParameters = it, returnsVoid = false)
constPointer(blockToKotlinFunctionConverter(bridge))
}
val ptr = staticData.placeGlobalArray(
"",
pointerType(runtime.typeInfoType),
(0 .. ObjCExportMapper.maxFunctionTypeParameterCount).map {
generateKotlinFunctionAdapterToBlock(it)
}
converters.first().llvmType,
converters
).pointer.getElementPtr(0)
// Note: this global replaces the weak global defined in runtime.
staticData.placeGlobal("Kotlin_ObjCExport_functionAdaptersToBlock", ptr, isExported = true)
staticData.placeGlobal("Kotlin_ObjCExport_blockToFunctionConverters", ptr, isExported = true)
}
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
@@ -501,7 +522,7 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
emitFunctionConverters()
emitKotlinFunctionAdaptersToBlock()
emitBlockToKotlinFunctionConverters()
}
private inline fun ObjCExportCodeGenerator.generateObjCImpBy(
@@ -1199,7 +1220,7 @@ private fun MethodBridge.ReturnValue.objCType(context: Context): LLVMTypeRef {
}
private val TypeBridge.objCType: LLVMTypeRef get() = when (this) {
is ReferenceBridge -> int8TypePtr
is ReferenceBridge, is BlockPointerBridge -> int8TypePtr
is ValueTypeBridge -> this.objCValueType.llvmType
}
@@ -1240,7 +1261,7 @@ private val MethodBridgeParameter.objCEncoding: String get() = when (this) {
}
private val TypeBridge.objCEncoding: String get() = when (this) {
ReferenceBridge -> "@"
ReferenceBridge, is BlockPointerBridge -> "@"
is ValueTypeBridge -> this.objCValueType.encoding
}
@@ -6,9 +6,6 @@
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.KotlinType
@@ -116,16 +113,7 @@ internal object CustomTypeMappers {
override val mappedClassId: ClassId = KotlinBuiltIns.getFunctionClassId(parameterCount)
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType {
val functionType = mappedSuperType
val returnType = functionType.getReturnTypeFromFunctionType()
val parameterTypes = listOfNotNull(functionType.getReceiverTypeFromFunctionType()) +
functionType.getValueParameterTypesFromFunctionType().map { it.type }
return ObjCBlockPointerType(
translator.mapReferenceType(returnType, objCExportScope),
parameterTypes.map { translator.mapReferenceType(it, objCExportScope) }
)
return translator.mapFunctionTypeIgnoringNullability(mappedSuperType, objCExportScope, returnsVoid = false)
}
}
}
@@ -14,6 +14,12 @@ import org.jetbrains.kotlin.ir.declarations.IrValueParameter
internal sealed class TypeBridge
internal object ReferenceBridge : TypeBridge()
internal data class BlockPointerBridge(
val numberOfParameters: Int,
val returnsVoid: Boolean
) : TypeBridge()
internal data class ValueTypeBridge(val objCValueType: ObjCValueType) : TypeBridge()
internal sealed class MethodBridgeParameter
@@ -7,7 +7,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.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.ClassId
@@ -596,12 +596,13 @@ internal class ObjCExportTranslatorImpl(
}
internal fun mapReferenceType(kotlinType: KotlinType, objCExportScope: ObjCExportScope): ObjCReferenceType =
mapReferenceTypeIgnoringNullability(kotlinType, objCExportScope).let {
if (kotlinType.binaryRepresentationIsNullable()) {
ObjCNullableReferenceType(it)
} else {
it
}
mapReferenceTypeIgnoringNullability(kotlinType, objCExportScope).withNullabilityOf(kotlinType)
private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KotlinType): ObjCReferenceType =
if (kotlinType.binaryRepresentationIsNullable()) {
ObjCNullableReferenceType(this)
} else {
this
}
internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType {
@@ -695,8 +696,46 @@ internal class ObjCExportTranslatorImpl(
return ObjCIdType
}
internal fun mapFunctionTypeIgnoringNullability(
functionType: KotlinType,
objCExportScope: ObjCExportScope,
returnsVoid: Boolean
): ObjCBlockPointerType {
val parameterTypes = listOfNotNull(functionType.getReceiverTypeFromFunctionType()) +
functionType.getValueParameterTypesFromFunctionType().map { it.type }
return ObjCBlockPointerType(
if (returnsVoid) {
ObjCVoidType
} else {
mapReferenceType(functionType.getReturnTypeFromFunctionType(), objCExportScope)
},
parameterTypes.map { mapReferenceType(it, objCExportScope) }
)
}
private fun mapFunctionType(
kotlinType: KotlinType,
objCExportScope: ObjCExportScope,
typeBridge: BlockPointerBridge
): ObjCReferenceType {
val expectedDescriptor = builtIns.getFunction(typeBridge.numberOfParameters)
// Somewhat similar to mapType:
val functionType = if (TypeUtils.getClassDescriptor(kotlinType) == expectedDescriptor) {
kotlinType
} else {
kotlinType.supertypes().singleOrNull { TypeUtils.getClassDescriptor(it) == expectedDescriptor }
?: expectedDescriptor.defaultType // Should not happen though.
}
return mapFunctionTypeIgnoringNullability(functionType, objCExportScope, typeBridge.returnsVoid)
.withNullabilityOf(kotlinType)
}
private fun mapType(kotlinType: KotlinType, typeBridge: TypeBridge, objCExportScope: ObjCExportScope): ObjCType = when (typeBridge) {
ReferenceBridge -> mapReferenceType(kotlinType, objCExportScope)
is BlockPointerBridge -> mapFunctionType(kotlinType, objCExportScope, typeBridge)
is ValueTypeBridge -> {
when (typeBridge.objCValueType) {
ObjCValueType.BOOL -> ObjCPrimitiveType("BOOL")
@@ -770,7 +809,7 @@ abstract class ObjCExportHeaderGenerator internal constructor(
override fun reportWarning(method: FunctionDescriptor, text: String) =
this@ObjCExportHeaderGenerator.reportWarning(method, text)
},
},
objcGenerics)
private val generatedClasses = mutableSetOf<ClassDescriptor>()
@@ -11,9 +11,7 @@ import org.jetbrains.kotlin.backend.konan.*
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.PrimitiveType
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.descriptorUtil.*
@@ -114,7 +112,9 @@ internal fun ObjCExportMapper.doesThrow(method: FunctionDescriptor): Boolean = m
it.overriddenDescriptors.isEmpty() && it.annotations.hasAnnotation(KonanFqNames.throws)
}
private fun ObjCExportMapper.bridgeType(kotlinType: KotlinType): TypeBridge = kotlinType.unwrapToPrimitiveOrReference(
private fun ObjCExportMapper.bridgeType(
kotlinType: KotlinType
): TypeBridge = kotlinType.unwrapToPrimitiveOrReference<TypeBridge>(
eachInlinedClass = { inlinedClass, _ ->
when (inlinedClass.classId) {
UnsignedType.UBYTE.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_CHAR)
@@ -138,10 +138,26 @@ private fun ObjCExportMapper.bridgeType(kotlinType: KotlinType): TypeBridge = ko
ValueTypeBridge(objCValueType)
},
ifReference = {
ReferenceBridge
if (kotlinType.isFunctionType) {
bridgeFunctionType(kotlinType)
} else {
ReferenceBridge
}
}
)
private fun ObjCExportMapper.bridgeFunctionType(kotlinType: KotlinType): TypeBridge {
// kotlinType.arguments include return type: <P1, P2, ..., Pn, R>
val numberOfParameters = kotlinType.arguments.size - 1
val returnType = kotlinType.getReturnTypeFromFunctionType()
val returnsVoid = returnType.isUnit() || returnType.isNothing()
// Note: this is correct because overriding method can't turn this into false
// neither for a parameter nor for a return type.
return BlockPointerBridge(numberOfParameters, returnsVoid)
}
private fun ObjCExportMapper.bridgeParameter(parameter: ParameterDescriptor): MethodBridgeValueParameter =
MethodBridgeValueParameter.Mapped(bridgeType(parameter.type))
@@ -75,7 +75,7 @@ object ObjCInstanceType : ObjCNonNullReferenceType() {
}
class ObjCBlockPointerType(
val returnType: ObjCReferenceType,
val returnType: ObjCType,
val parameterTypes: List<ObjCReferenceType>
) : ObjCNonNullReferenceType() {
@@ -378,4 +378,46 @@ class GH2959 {
private class PrivateImpl(override val id: Int) : I
fun getI(id: Int): List<I> = listOf(PrivateImpl(id))
}
}
fun runUnitBlock(block: () -> Unit): Boolean {
val blockAny: () -> Any? = block
return blockAny() === Unit
}
fun asUnitBlock(block: () -> Any?): () -> Unit = { block() }
fun runNothingBlock(block: () -> Nothing) = try {
block()
false
} catch (e: Throwable) {
true
}
fun asNothingBlock(block: () -> Any?): () -> Nothing = {
block()
TODO()
}
fun getNullBlock(): (() -> Unit)? = null
fun isBlockNull(block: (() -> Unit)?): Boolean = block == null
interface IntBlocks<T> {
fun getPlusOneBlock(): T
fun callBlock(argument: Int, block: T): Int
}
object IntBlocksImpl : IntBlocks<(Int) -> Int> {
override fun getPlusOneBlock(): (Int) -> Int = { it: Int -> it + 1 }
override fun callBlock(argument: Int, block: (Int) -> Int): Int = block(argument)
}
interface UnitBlockCoercion<T : Any> {
fun coerce(block: () -> Unit): T
fun uncoerce(block: T): () -> Unit
}
object UnitBlockCoercionImpl : UnitBlockCoercion<() -> Unit> {
override fun coerce(block: () -> Unit): () -> Unit = block
override fun uncoerce(block: () -> Unit): () -> Unit = block
}
@@ -350,6 +350,42 @@ func testCharExtensions() throws {
func testLambda() throws {
try assertEquals(actual: ValuesKt.sumLambda(3, 4), expected: 7)
var blockRuns = 0
try assertTrue(ValuesKt.runUnitBlock { blockRuns += 1 })
try assertEquals(actual: blockRuns, expected: 1)
let unitBlock: () -> Void = ValuesKt.asUnitBlock {
blockRuns += 1
return 42
}
try assertTrue(unitBlock() == Void())
try assertEquals(actual: blockRuns, expected: 2)
let nothingBlock: () -> Void = ValuesKt.asNothingBlock { blockRuns += 1 }
try assertTrue(ValuesKt.runNothingBlock(block: nothingBlock))
try assertEquals(actual: blockRuns, expected: 3)
try assertTrue(ValuesKt.getNullBlock() == nil)
try assertTrue(ValuesKt.isBlockNull(block: nil))
// Test dynamic conversion:
let intBlocks = IntBlocksImpl()
try assertEquals(actual: intBlocks.getPlusOneBlock()(1), expected: 2)
try assertEquals(actual: intBlocks.callBlock(argument: 2) { KotlinInt(value: $0.int32Value + 2) }, expected: 4)
// Test round trip with dynamic conversion:
let coercedUnitBlock: () -> KotlinUnit = UnitBlockCoercionImpl().coerce { blockRuns += 1 }
try assertTrue(coercedUnitBlock() === KotlinUnit())
try assertEquals(actual: blockRuns, expected: 4)
let uncoercedUnitBlock: () -> Void = UnitBlockCoercionImpl().uncoerce {
blockRuns += 1
return KotlinUnit()
}
try assertTrue(uncoercedUnitBlock() == Void())
try assertEquals(actual: blockRuns, expected: 5)
}
// -------- Tests for classes and interfaces -------
+12 -13
View File
@@ -76,6 +76,7 @@ struct ObjCTypeAdapter {
};
typedef id (*convertReferenceToObjC)(ObjHeader* obj);
typedef OBJ_GETTER((*convertReferenceFromObjC), id obj);
struct TypeInfoObjCExportAddition {
/*convertReferenceToObjC*/ void* convert;
@@ -108,6 +109,14 @@ inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* type
return result;
}
extern "C" OBJ_GETTER(Kotlin_ObjCExport_AllocInstanceWithAssociatedObject,
const TypeInfo* typeInfo, id associatedObject) RUNTIME_NOTHROW;
extern "C" OBJ_GETTER(Kotlin_ObjCExport_AllocInstanceWithAssociatedObject,
const TypeInfo* typeInfo, id associatedObject) {
RETURN_RESULT_OF(AllocInstanceWithAssociatedObject, typeInfo, associatedObject);
}
static Class getOrCreateClass(const TypeInfo* typeInfo);
static void initializeClass(Class clazz);
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
@@ -249,8 +258,6 @@ extern "C" id Kotlin_ObjCExport_convertUnit(ObjHeader* unitInstance) {
return instance;
}
extern "C" id objc_retainBlock(id self);
extern "C" id Kotlin_ObjCExport_CreateNSStringFromKString(ObjHeader* str) {
KChar* utf16Chars = CharArrayAddressOfElementAt(str->array(), 0);
auto numBytes = str->array()->count_ * sizeof(KChar);
@@ -575,9 +582,9 @@ static const char* getBlockEncoding(id block) {
}
// Note: replaced by compiler in appropriate compilation modes.
__attribute__((weak)) const TypeInfo * const * Kotlin_ObjCExport_functionAdaptersToBlock = nullptr;
__attribute__((weak)) convertReferenceFromObjC* Kotlin_ObjCExport_blockToFunctionConverters = nullptr;
static const TypeInfo* getFunctionTypeInfoForBlock(id block) {
static OBJ_GETTER(blockToKotlinImp, id block, SEL cmd) {
const char* encoding = getBlockEncoding(block);
// TODO: optimize:
@@ -602,15 +609,7 @@ static const TypeInfo* getFunctionTypeInfoForBlock(id block) {
format:@"Blocks with non-reference-typed return value aren't supported (%s)", returnTypeEncoding];
}
// TODO: support Unit-as-void.
return Kotlin_ObjCExport_functionAdaptersToBlock[parameterCount];
}
static OBJ_GETTER(blockToKotlinImp, id self, SEL cmd) {
const TypeInfo* typeInfo = getFunctionTypeInfoForBlock(self);
RETURN_RESULT_OF(AllocInstanceWithAssociatedObject, typeInfo, objc_retainBlock(self));
// TODO: call (Any) constructor?
RETURN_RESULT_OF(Kotlin_ObjCExport_blockToFunctionConverters[parameterCount], block);
}
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);