diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt index eaa4d5924a3..6d1385f1ada 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt @@ -27,6 +27,8 @@ interface ObjCClass : ObjCObject interface ObjCClassOf : ObjCClass // TODO: T should be added to ObjCClass and all meta-classes instead. typealias ObjCObjectMeta = ObjCClass +interface ObjCProtocol : ObjCObject + @ExportTypeInfo("theForeignObjCObjectTypeInfo") @kotlin.native.internal.Frozen internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt index 4056e145272..707e1f930f7 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt @@ -191,6 +191,7 @@ object KotlinTypes { val objCObjectMeta by InteropClassifier val objCClass by InteropClassifier val objCClassOf by InteropClassifier + val objCProtocol by InteropClassifier val cValuesRef by InteropClassifier diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt index 63d3fc9bf2d..0b42a412002 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt @@ -490,6 +490,11 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator, supers.add(classifier.type) } + if (!isMeta && container.isProtocolClass()) { + // TODO: map Protocol type to ObjCProtocol instead. + supers.add(KotlinTypes.objCProtocol.type) + } + val keywords = when (container) { is ObjCClass -> "open class" is ObjCProtocol -> "interface" @@ -690,3 +695,8 @@ private fun genProtocolGetter( return functionName } + +private fun ObjCClassOrProtocol.isProtocolClass(): Boolean = when (this) { + is ObjCClass -> (name == "Protocol" || binaryName == "Protocol") + is ObjCProtocol -> false +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt index a75efbb0240..06e233e5631 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes internal val interopPackageName = InteropFqNames.packageName internal val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject")) private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass")) +private val objCProtocolFqName = interopPackageName.child(Name.identifier("ObjCProtocol")) internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass")) private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod")) private val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor") @@ -70,6 +71,12 @@ fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().a it.fqNameSafe == objCClassFqName } +fun IrClass.isObjCProtocolClass(): Boolean = + this.fqNameSafe == objCProtocolFqName + +fun ClassDescriptor.isObjCProtocolClass(): Boolean = + this.fqNameSafe == objCProtocolFqName + fun FunctionDescriptor.isObjCClassMethod() = this.containingDeclaration.let { it is ClassDescriptor && it.isObjCClass() } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 4c286460bf1..78aadcd80c1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -9,7 +9,6 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.konan.* -import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.llvm.objc.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.* @@ -17,6 +16,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.backend.konan.descriptors.resolveFakeOverride import org.jetbrains.kotlin.backend.konan.ir.* +import org.jetbrains.kotlin.descriptors.konan.CompiledKonanModuleOrigin internal class CodeGenerator(override val context: Context) : ContextUtils { @@ -832,12 +832,11 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, assert(!irClass.isInterface) return if (irClass.isExternalObjCClass()) { - context.llvm.imports.add(irClass.llvmSymbolOrigin) + val llvmSymbolOrigin = irClass.llvmSymbolOrigin if (irClass.isObjCMetaClass()) { val name = irClass.descriptor.getExternalObjCMetaClassBinaryName() - - val objCClass = load(codegen.objCDataGenerator!!.genClassRef(name).llvm) + val objCClass = getObjCClass(name, llvmSymbolOrigin) val getClass = context.llvm.externalFunction( "object_getClass", @@ -847,7 +846,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler) } else { - load(codegen.objCDataGenerator!!.genClassRef(irClass.descriptor.getExternalObjCClassBinaryName()).llvm) + getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin) } } else { if (irClass.isObjCMetaClass()) { @@ -874,6 +873,11 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, } } + fun getObjCClass(binaryName: String, llvmSymbolOrigin: CompiledKonanModuleOrigin): LLVMValueRef { + context.llvm.imports.add(llvmSymbolOrigin) + return load(codegen.objCDataGenerator!!.genClassRef(binaryName).llvm) + } + fun resetDebugLocation() { if (!context.shouldContainDebugInfo()) return currentPositionHolder.resetBuilderDebugLocation() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index c4bf3bfb18c..a965432f9d6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1403,6 +1403,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map) + assertFalse(Any() is ObjCProtocol) + assertTrue(getPrinterProtocolRaw() is ObjCProtocol) + val printerProtocol = getPrinterProtocol()!! + assertTrue(printerProtocol.asAny() is ObjCProtocol) + assertEquals(3u, ("foo" as NSString).length()) assertEquals(4u, ((1..4).joinToString("") as NSString).length()) assertEquals(2u, (listOf(0, 1) as NSArray).count())