From 803620f4239733e7e8834eec0c7e828588e837a5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 17 Sep 2018 10:16:37 +0300 Subject: [PATCH] Improve interop with headers generated by Swift Support `objc_runtime_name` attribute on Objective-C classes thus enabling interop with `@objc` Swift classes without explicitly specified name. See https://github.com/JetBrains/kotlin-native/issues/1841#issuecomment-411346727 --- .../kotlin/native/interop/indexer/Indexer.kt | 16 +++++++-- .../native/interop/indexer/NativeIndex.kt | 1 + .../kotlin/native/interop/indexer/Utils.kt | 8 +++++ .../kotlin/kotlinx/cinterop/ObjectiveCImpl.kt | 2 +- .../kotlin/native/interop/gen/ObjCStubs.kt | 33 +++++++++++-------- .../kotlin/backend/konan/ObjCInterop.kt | 16 +++++++-- .../backend/konan/llvm/CodeGenerator.kt | 4 +-- .../llvm/KotlinObjCClassInfoGenerator.kt | 2 +- 8 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt index 169095d229e..2155b14ef59 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt @@ -60,7 +60,8 @@ private class ObjCProtocolImpl( private class ObjCClassImpl( name: String, override val location: Location, - override val isForwardDeclaration: Boolean + override val isForwardDeclaration: Boolean, + override val binaryName: String? ) : ObjCClass(name), ObjCContainerImpl { override val protocols = mutableListOf() override val methods = mutableListOf() @@ -302,12 +303,13 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { if (isObjCInterfaceDeclForward(cursor)) { return objCClassRegistry.getOrPut(cursor) { - ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = true) + ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = true, binaryName = null) } } return objCClassRegistry.getOrPut(cursor, { - ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = false) + ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = false, + binaryName = getObjCBinaryName(cursor).takeIf { it != name }) }) { addChildrenToObjCContainer(cursor, it) } @@ -336,6 +338,14 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { } } + private fun getObjCBinaryName(cursor: CValue): String { + val prefix = "_OBJC_CLASS_\$_" + val symbolName = clang_Cursor_getObjCManglings(cursor)!!.convertAndDispose() + .single { it.startsWith(prefix) } + + return symbolName.substring(prefix.length) + } + private fun getObjCCategoryAt(cursor: CValue): ObjCCategoryImpl? { assert(cursor.kind == CXCursorKind.CXCursor_ObjCCategoryDecl) { cursor.kind } diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt index ee491635ec9..39eff61a6a7 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt @@ -163,6 +163,7 @@ data class ObjCProperty(val name: String, val getter: ObjCMethod, val setter: Ob } abstract class ObjCClass(name: String) : ObjCClassOrProtocol(name) { + abstract val binaryName: String? abstract val baseClass: ObjCClass? } abstract class ObjCProtocol(name: String) : ObjCClassOrProtocol(name) diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt index 94e50f61a0c..591ebe2653d 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt @@ -38,6 +38,14 @@ internal fun CValue.convertAndDispose(): String { } } +internal fun CPointer.convertAndDispose(): Set = try { + (0 until this.pointed.Count).mapTo(mutableSetOf()) { + clang_getCString(this.pointed.Strings!![it].readValue())!!.toKString() + } +} finally { + clang_disposeStringSet(this) +} + internal fun getCursorSpelling(cursor: CValue) = clang_getCursorSpelling(cursor).convertAndDispose() diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt index bb241131823..ff9841a7099 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt @@ -94,7 +94,7 @@ typealias ObjCBlockVar = ObjCNotImplementedVar @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) -annotation class ExternalObjCClass(val protocolGetter: String = "") +annotation class ExternalObjCClass(val protocolGetter: String = "", val binaryName: String = "") @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.BINARY) 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 f70d055badb..9a9948e4dec 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 @@ -505,21 +505,28 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator, val name = stubGenerator.kotlinFile.declare(classifier) val externalObjCClassAnnotationName = "@ExternalObjCClass" - val externalObjCClassAnnotation: String - if (container is ObjCProtocol) { - protocolGetter = if (metaContainerStub != null) { - metaContainerStub.protocolGetter!! - } else { - val nativeBacked = object : NativeBacked {} - // TODO: handle the case when protocol getter stub can't be compiled. - genProtocolGetter(stubGenerator, nativeBacked, container) + val externalObjCClassAnnotation: String = when (container) { + is ObjCProtocol -> { + protocolGetter = if (metaContainerStub != null) { + metaContainerStub.protocolGetter!! + } else { + val nativeBacked = object : NativeBacked {} + // TODO: handle the case when protocol getter stub can't be compiled. + genProtocolGetter(stubGenerator, nativeBacked, container) + } + + externalObjCClassAnnotationName.applyToStrings(protocolGetter) + } + is ObjCClass -> { + protocolGetter = null + val binaryName = container.binaryName + if (binaryName != null) { + externalObjCClassAnnotationName.applyToStrings("", binaryName) + } else { + externalObjCClassAnnotationName + } } - - externalObjCClassAnnotation = externalObjCClassAnnotationName.applyToStrings(protocolGetter) - } else { - protocolGetter = null - externalObjCClassAnnotation = externalObjCClassAnnotationName } this.classHeader = "$externalObjCClassAnnotation $keywords $name : $supersString" 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 1bc88fb488a..d3fb77fbda7 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 @@ -5,9 +5,9 @@ package org.jetbrains.kotlin.backend.konan -import org.jetbrains.kotlin.backend.konan.descriptors.findPackage import org.jetbrains.kotlin.backend.konan.descriptors.findPackageView import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue +import org.jetbrains.kotlin.backend.konan.descriptors.getStringValueOrNull import org.jetbrains.kotlin.backend.konan.irasdescriptors.constructedClass import org.jetbrains.kotlin.backend.konan.irasdescriptors.getExternalObjCMethodInfo import org.jetbrains.kotlin.backend.konan.irasdescriptors.isReal @@ -22,7 +22,6 @@ import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition import org.jetbrains.kotlin.resolve.constants.BooleanValue import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers -import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -229,4 +228,15 @@ fun inferObjCSelector(descriptor: FunctionDescriptor): String = if (descriptor.v append(':') } } -} \ No newline at end of file +} + +fun ClassDescriptor.getExternalObjCClassBinaryName(): String = + this.getExplicitExternalObjCClassBinaryName() + ?: this.name.asString() + +fun ClassDescriptor.getExternalObjCMetaClassBinaryName(): String = + this.getExplicitExternalObjCClassBinaryName() + ?: this.name.asString().removeSuffix("Meta") + +private fun ClassDescriptor.getExplicitExternalObjCClassBinaryName() = + this.annotations.findAnnotation(externalObjCClassFqName)!!.getStringValueOrNull("binaryName") \ No newline at end of file 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 5b61ef90d61..d33101283b7 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 @@ -754,7 +754,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, context.llvm.imports.add(irClass.llvmSymbolOrigin) if (irClass.isObjCMetaClass()) { - val name = irClass.name.asString().removeSuffix("Meta") + val name = irClass.descriptor.getExternalObjCMetaClassBinaryName() val objCClass = load(codegen.objCDataGenerator!!.genClassRef(name).llvm) @@ -766,7 +766,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler) } else { - load(codegen.objCDataGenerator!!.genClassRef(irClass.name.asString()).llvm) + load(codegen.objCDataGenerator!!.genClassRef(irClass.descriptor.getExternalObjCClassBinaryName()).llvm) } } else { if (irClass.isObjCMetaClass()) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt index 33853e1ab24..e56a2274b7d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt @@ -31,7 +31,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con val superclassName = irClass.getSuperClassNotAny()!!.let { context.llvm.imports.add(it.llvmSymbolOrigin) - it.name.asString() + it.descriptor.getExternalObjCClassBinaryName() } val protocolNames = irClass.getSuperInterfaces().map { context.llvm.imports.add(it.llvmSymbolOrigin)