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(
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<ObjCProtocol>()
override val methods = mutableListOf<ObjCMethod>()
@@ -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<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? {
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 val binaryName: String?
abstract val baseClass: ObjCClass?
}
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>) =
clang_getCursorSpelling(cursor).convertAndDispose()
@@ -94,7 +94,7 @@ typealias ObjCBlockVar<T> = ObjCNotImplementedVar<T>
@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)
@@ -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"
@@ -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(':')
}
}
}
}
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)
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()) {
@@ -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)