Map returned Unit to void in more cases when producing framework (#2968)
Including function-typed parameters and return types.
This commit is contained in:
committed by
GitHub
parent
f6544c40bb
commit
9b8b94717f
+7
@@ -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)) }
|
||||
|
||||
|
||||
+1
@@ -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) {
|
||||
|
||||
+17
-12
@@ -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)
|
||||
|
||||
+136
-43
@@ -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
|
||||
)
|
||||
+55
-34
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+1
-13
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -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
|
||||
|
||||
+47
-8
@@ -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>()
|
||||
|
||||
+21
-5
@@ -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))
|
||||
|
||||
|
||||
+1
-1
@@ -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 -------
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user