Add ObjCProtocol type to interop runtime

This commit is contained in:
Svyatoslav Scherbina
2019-05-30 10:24:21 +03:00
committed by SvyatoslavScherbina
parent 3c8e12328c
commit d1222907d4
9 changed files with 64 additions and 9 deletions
@@ -27,6 +27,8 @@ interface ObjCClass : ObjCObject
interface ObjCClassOf<T : ObjCObject> : ObjCClass // TODO: T should be added to ObjCClass and all meta-classes instead. interface ObjCClassOf<T : ObjCObject> : ObjCClass // TODO: T should be added to ObjCClass and all meta-classes instead.
typealias ObjCObjectMeta = ObjCClass typealias ObjCObjectMeta = ObjCClass
interface ObjCProtocol : ObjCObject
@ExportTypeInfo("theForeignObjCObjectTypeInfo") @ExportTypeInfo("theForeignObjCObjectTypeInfo")
@kotlin.native.internal.Frozen @kotlin.native.internal.Frozen
internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper
@@ -191,6 +191,7 @@ object KotlinTypes {
val objCObjectMeta by InteropClassifier val objCObjectMeta by InteropClassifier
val objCClass by InteropClassifier val objCClass by InteropClassifier
val objCClassOf by InteropClassifier val objCClassOf by InteropClassifier
val objCProtocol by InteropClassifier
val cValuesRef by InteropClassifier val cValuesRef by InteropClassifier
@@ -490,6 +490,11 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
supers.add(classifier.type) supers.add(classifier.type)
} }
if (!isMeta && container.isProtocolClass()) {
// TODO: map Protocol type to ObjCProtocol instead.
supers.add(KotlinTypes.objCProtocol.type)
}
val keywords = when (container) { val keywords = when (container) {
is ObjCClass -> "open class" is ObjCClass -> "open class"
is ObjCProtocol -> "interface" is ObjCProtocol -> "interface"
@@ -690,3 +695,8 @@ private fun genProtocolGetter(
return functionName return functionName
} }
private fun ObjCClassOrProtocol.isProtocolClass(): Boolean = when (this) {
is ObjCClass -> (name == "Protocol" || binaryName == "Protocol")
is ObjCProtocol -> false
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes
internal val interopPackageName = InteropFqNames.packageName internal val interopPackageName = InteropFqNames.packageName
internal val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject")) internal val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject"))
private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass")) private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass"))
private val objCProtocolFqName = interopPackageName.child(Name.identifier("ObjCProtocol"))
internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass")) internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod")) private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
private val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor") private val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor")
@@ -70,6 +71,12 @@ fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().a
it.fqNameSafe == objCClassFqName it.fqNameSafe == objCClassFqName
} }
fun IrClass.isObjCProtocolClass(): Boolean =
this.fqNameSafe == objCProtocolFqName
fun ClassDescriptor.isObjCProtocolClass(): Boolean =
this.fqNameSafe == objCProtocolFqName
fun FunctionDescriptor.isObjCClassMethod() = fun FunctionDescriptor.isObjCClassMethod() =
this.containingDeclaration.let { it is ClassDescriptor && it.isObjCClass() } this.containingDeclaration.let { it is ClassDescriptor && it.isObjCClass() }
@@ -9,7 +9,6 @@ package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.* import kotlinx.cinterop.*
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.konan.* 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.backend.konan.llvm.objc.*
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.* 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.konan.target.*
import org.jetbrains.kotlin.backend.konan.descriptors.resolveFakeOverride import org.jetbrains.kotlin.backend.konan.descriptors.resolveFakeOverride
import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.descriptors.konan.CompiledKonanModuleOrigin
internal class CodeGenerator(override val context: Context) : ContextUtils { internal class CodeGenerator(override val context: Context) : ContextUtils {
@@ -832,12 +832,11 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
assert(!irClass.isInterface) assert(!irClass.isInterface)
return if (irClass.isExternalObjCClass()) { return if (irClass.isExternalObjCClass()) {
context.llvm.imports.add(irClass.llvmSymbolOrigin) val llvmSymbolOrigin = irClass.llvmSymbolOrigin
if (irClass.isObjCMetaClass()) { if (irClass.isObjCMetaClass()) {
val name = irClass.descriptor.getExternalObjCMetaClassBinaryName() val name = irClass.descriptor.getExternalObjCMetaClassBinaryName()
val objCClass = getObjCClass(name, llvmSymbolOrigin)
val objCClass = load(codegen.objCDataGenerator!!.genClassRef(name).llvm)
val getClass = context.llvm.externalFunction( val getClass = context.llvm.externalFunction(
"object_getClass", "object_getClass",
@@ -847,7 +846,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.descriptor.getExternalObjCClassBinaryName()).llvm) getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin)
} }
} else { } else {
if (irClass.isObjCMetaClass()) { 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() { fun resetDebugLocation() {
if (!context.shouldContainDebugInfo()) return if (!context.shouldContainDebugInfo()) return
currentPositionHolder.resetBuilderDebugLocation() currentPositionHolder.resetBuilderDebugLocation()
@@ -1403,6 +1403,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
call(isClass, listOf(objCObject)).let { call(isClass, listOf(objCObject)).let {
functionGenerationContext.icmpNe(it, Int8(0).llvm) functionGenerationContext.icmpNe(it, Int8(0).llvm)
} }
} else if (dstClass.isObjCProtocolClass()) {
// Note: it is not clear whether this class should be looked up this way.
// clang does the same, however swiftc uses dynamic lookup.
val protocolClass =
functionGenerationContext.getObjCClass("Protocol", context.standardLlvmSymbolsOrigin)
call(
context.llvm.Kotlin_Interop_IsObjectKindOfClass,
listOf(objCObject, protocolClass)
)
} else { } else {
kTrue kTrue
} }
@@ -801,16 +801,15 @@ internal class ObjCExportTranslatorImpl(
// TODO: more precise types can be used. // TODO: more precise types can be used.
if (descriptor.isObjCMetaClass()) return ObjCMetaClassType if (descriptor.isObjCMetaClass()) return ObjCMetaClassType
if (descriptor.isObjCProtocolClass()) return foreignClassType("Protocol")
if (descriptor.isExternalObjCClass() || descriptor.isObjCForwardDeclaration()) { if (descriptor.isExternalObjCClass() || descriptor.isObjCForwardDeclaration()) {
return if (descriptor.isInterface) { return if (descriptor.isInterface) {
val name = descriptor.name.asString().removeSuffix("Protocol") val name = descriptor.name.asString().removeSuffix("Protocol")
generator?.referenceProtocol(name) foreignProtocolType(name)
ObjCProtocolType(name)
} else { } else {
val name = descriptor.name.asString() val name = descriptor.name.asString()
generator?.referenceClass(name) foreignClassType(name)
ObjCClassType(name)
} }
} }
@@ -821,6 +820,16 @@ internal class ObjCExportTranslatorImpl(
return ObjCIdType return ObjCIdType
} }
private fun foreignProtocolType(name: String): ObjCProtocolType {
generator?.referenceProtocol(name)
return ObjCProtocolType(name)
}
private fun foreignClassType(name: String): ObjCClassType {
generator?.referenceClass(name)
return ObjCClassType(name)
}
internal fun mapFunctionTypeIgnoringNullability( internal fun mapFunctionTypeIgnoringNullability(
functionType: KotlinType, functionType: KotlinType,
objCExportScope: ObjCExportScope, objCExportScope: ObjCExportScope,
@@ -205,3 +205,11 @@ NSObject* createNSObject() {
-(int)hashCode:(int)p; -(int)hashCode:(int)p;
-(BOOL)equals; -(BOOL)equals;
@end; @end;
id getPrinterProtocolRaw() {
return @protocol(Printer);
}
Protocol* getPrinterProtocol() {
return @protocol(Printer);
}
@@ -151,6 +151,11 @@ fun testTypeOps() {
assertTrue(NSObject.asAny() is ObjCClass) assertTrue(NSObject.asAny() is ObjCClass)
assertTrue(NSObject.asAny() is ObjCClassOf<*>) assertTrue(NSObject.asAny() is ObjCClassOf<*>)
assertFalse(Any() is ObjCProtocol)
assertTrue(getPrinterProtocolRaw() is ObjCProtocol)
val printerProtocol = getPrinterProtocol()!!
assertTrue(printerProtocol.asAny() is ObjCProtocol)
assertEquals(3u, ("foo" as NSString).length()) assertEquals(3u, ("foo" as NSString).length())
assertEquals(4u, ((1..4).joinToString("") as NSString).length()) assertEquals(4u, ((1..4).joinToString("") as NSString).length())
assertEquals(2u, (listOf(0, 1) as NSArray).count()) assertEquals(2u, (listOf(0, 1) as NSArray).count())