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
This commit is contained in:
Svyatoslav Scherbina
2018-09-17 10:16:37 +03:00
committed by SvyatoslavScherbina
parent 4ec1ec05cf
commit 803620f423
8 changed files with 59 additions and 23 deletions
@@ -60,7 +60,8 @@ private class ObjCProtocolImpl(
private class ObjCClassImpl( private class ObjCClassImpl(
name: String, name: String,
override val location: Location, override val location: Location,
override val isForwardDeclaration: Boolean override val isForwardDeclaration: Boolean,
override val binaryName: String?
) : ObjCClass(name), ObjCContainerImpl { ) : ObjCClass(name), ObjCContainerImpl {
override val protocols = mutableListOf<ObjCProtocol>() override val protocols = mutableListOf<ObjCProtocol>()
override val methods = mutableListOf<ObjCMethod>() override val methods = mutableListOf<ObjCMethod>()
@@ -302,12 +303,13 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
if (isObjCInterfaceDeclForward(cursor)) { if (isObjCInterfaceDeclForward(cursor)) {
return objCClassRegistry.getOrPut(cursor) { return objCClassRegistry.getOrPut(cursor) {
ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = true) ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = true, binaryName = null)
} }
} }
return objCClassRegistry.getOrPut(cursor, { 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) addChildrenToObjCContainer(cursor, it)
} }
@@ -336,6 +338,14 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
} }
} }
private fun getObjCBinaryName(cursor: CValue<CXCursor>): 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<CXCursor>): ObjCCategoryImpl? { private fun getObjCCategoryAt(cursor: CValue<CXCursor>): ObjCCategoryImpl? {
assert(cursor.kind == CXCursorKind.CXCursor_ObjCCategoryDecl) { cursor.kind } assert(cursor.kind == CXCursorKind.CXCursor_ObjCCategoryDecl) { cursor.kind }
@@ -163,6 +163,7 @@ data class ObjCProperty(val name: String, val getter: ObjCMethod, val setter: Ob
} }
abstract class ObjCClass(name: String) : ObjCClassOrProtocol(name) { abstract class ObjCClass(name: String) : ObjCClassOrProtocol(name) {
abstract val binaryName: String?
abstract val baseClass: ObjCClass? abstract val baseClass: ObjCClass?
} }
abstract class ObjCProtocol(name: String) : ObjCClassOrProtocol(name) abstract class ObjCProtocol(name: String) : ObjCClassOrProtocol(name)
@@ -38,6 +38,14 @@ internal fun CValue<CXString>.convertAndDispose(): String {
} }
} }
internal fun CPointer<CXStringSet>.convertAndDispose(): Set<String> = 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<CXCursor>) = internal fun getCursorSpelling(cursor: CValue<CXCursor>) =
clang_getCursorSpelling(cursor).convertAndDispose() clang_getCursorSpelling(cursor).convertAndDispose()
@@ -94,7 +94,7 @@ typealias ObjCBlockVar<T> = ObjCNotImplementedVar<T>
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY) @Retention(AnnotationRetention.BINARY)
annotation class ExternalObjCClass(val protocolGetter: String = "") annotation class ExternalObjCClass(val protocolGetter: String = "", val binaryName: String = "")
@Target(AnnotationTarget.FUNCTION) @Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY) @Retention(AnnotationRetention.BINARY)
@@ -505,21 +505,28 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
val name = stubGenerator.kotlinFile.declare(classifier) val name = stubGenerator.kotlinFile.declare(classifier)
val externalObjCClassAnnotationName = "@ExternalObjCClass" val externalObjCClassAnnotationName = "@ExternalObjCClass"
val externalObjCClassAnnotation: String
if (container is ObjCProtocol) { val externalObjCClassAnnotation: String = when (container) {
protocolGetter = if (metaContainerStub != null) { is ObjCProtocol -> {
metaContainerStub.protocolGetter!! protocolGetter = if (metaContainerStub != null) {
} else { metaContainerStub.protocolGetter!!
val nativeBacked = object : NativeBacked {} } else {
// TODO: handle the case when protocol getter stub can't be compiled. val nativeBacked = object : NativeBacked {}
genProtocolGetter(stubGenerator, nativeBacked, container) // 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" this.classHeader = "$externalObjCClassAnnotation $keywords $name : $supersString"
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.backend.konan 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.findPackageView
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue 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.constructedClass
import org.jetbrains.kotlin.backend.konan.irasdescriptors.getExternalObjCMethodInfo import org.jetbrains.kotlin.backend.konan.irasdescriptors.getExternalObjCMethodInfo
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isReal 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.constants.BooleanValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers 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.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
@@ -229,4 +228,15 @@ fun inferObjCSelector(descriptor: FunctionDescriptor): String = if (descriptor.v
append(':') append(':')
} }
} }
} }
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")
@@ -754,7 +754,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
context.llvm.imports.add(irClass.llvmSymbolOrigin) context.llvm.imports.add(irClass.llvmSymbolOrigin)
if (irClass.isObjCMetaClass()) { if (irClass.isObjCMetaClass()) {
val name = irClass.name.asString().removeSuffix("Meta") val name = irClass.descriptor.getExternalObjCMetaClassBinaryName()
val objCClass = load(codegen.objCDataGenerator!!.genClassRef(name).llvm) val objCClass = load(codegen.objCDataGenerator!!.genClassRef(name).llvm)
@@ -766,7 +766,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler) call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler)
} else { } else {
load(codegen.objCDataGenerator!!.genClassRef(irClass.name.asString()).llvm) load(codegen.objCDataGenerator!!.genClassRef(irClass.descriptor.getExternalObjCClassBinaryName()).llvm)
} }
} else { } else {
if (irClass.isObjCMetaClass()) { if (irClass.isObjCMetaClass()) {
@@ -31,7 +31,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
val superclassName = irClass.getSuperClassNotAny()!!.let { val superclassName = irClass.getSuperClassNotAny()!!.let {
context.llvm.imports.add(it.llvmSymbolOrigin) context.llvm.imports.add(it.llvmSymbolOrigin)
it.name.asString() it.descriptor.getExternalObjCClassBinaryName()
} }
val protocolNames = irClass.getSuperInterfaces().map { val protocolNames = irClass.getSuperInterfaces().map {
context.llvm.imports.add(it.llvmSymbolOrigin) context.llvm.imports.add(it.llvmSymbolOrigin)