Skip to content

Commit

Permalink
Improve interop with headers generated by Swift
Browse files Browse the repository at this point in the history
Support `objc_runtime_name` attribute on Objective-C classes
thus enabling interop with `@objc` Swift classes without
explicitly specified name.

See JetBrains#1841 (comment)
  • Loading branch information
SvyatoslavScherbina committed Sep 18, 2018
1 parent 4ec1ec0 commit 803620f
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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>()
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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 }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

externalObjCClassAnnotation = externalObjCClassAnnotationName.applyToStrings(protocolGetter)
} else {
protocolGetter = null
externalObjCClassAnnotation = externalObjCClassAnnotationName
externalObjCClassAnnotationName.applyToStrings(protocolGetter)
}
is ObjCClass -> {
protocolGetter = null
val binaryName = container.binaryName
if (binaryName != null) {
externalObjCClassAnnotationName.applyToStrings("", binaryName)
} else {
externalObjCClassAnnotationName
}
}
}

this.classHeader = "$externalObjCClassAnnotation $keywords $name : $supersString"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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")
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 803620f

Please sign in to comment.