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.
typealias ObjCObjectMeta = ObjCClass
interface ObjCProtocol : ObjCObject
@ExportTypeInfo("theForeignObjCObjectTypeInfo")
@kotlin.native.internal.Frozen
internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper
@@ -191,6 +191,7 @@ object KotlinTypes {
val objCObjectMeta by InteropClassifier
val objCClass by InteropClassifier
val objCClassOf by InteropClassifier
val objCProtocol by InteropClassifier
val cValuesRef by InteropClassifier
@@ -490,6 +490,11 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
supers.add(classifier.type)
}
if (!isMeta && container.isProtocolClass()) {
// TODO: map Protocol type to ObjCProtocol instead.
supers.add(KotlinTypes.objCProtocol.type)
}
val keywords = when (container) {
is ObjCClass -> "open class"
is ObjCProtocol -> "interface"
@@ -690,3 +695,8 @@ private fun genProtocolGetter(
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 objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject"))
private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass"))
private val objCProtocolFqName = interopPackageName.child(Name.identifier("ObjCProtocol"))
internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
private val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor")
@@ -70,6 +71,12 @@ fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().a
it.fqNameSafe == objCClassFqName
}
fun IrClass.isObjCProtocolClass(): Boolean =
this.fqNameSafe == objCProtocolFqName
fun ClassDescriptor.isObjCProtocolClass(): Boolean =
this.fqNameSafe == objCProtocolFqName
fun FunctionDescriptor.isObjCClassMethod() =
this.containingDeclaration.let { it is ClassDescriptor && it.isObjCClass() }
@@ -9,7 +9,6 @@ package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
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.descriptors.*
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.backend.konan.descriptors.resolveFakeOverride
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.descriptors.konan.CompiledKonanModuleOrigin
internal class CodeGenerator(override val context: Context) : ContextUtils {
@@ -832,12 +832,11 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
assert(!irClass.isInterface)
return if (irClass.isExternalObjCClass()) {
context.llvm.imports.add(irClass.llvmSymbolOrigin)
val llvmSymbolOrigin = irClass.llvmSymbolOrigin
if (irClass.isObjCMetaClass()) {
val name = irClass.descriptor.getExternalObjCMetaClassBinaryName()
val objCClass = load(codegen.objCDataGenerator!!.genClassRef(name).llvm)
val objCClass = getObjCClass(name, llvmSymbolOrigin)
val getClass = context.llvm.externalFunction(
"object_getClass",
@@ -847,7 +846,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler)
} else {
load(codegen.objCDataGenerator!!.genClassRef(irClass.descriptor.getExternalObjCClassBinaryName()).llvm)
getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin)
}
} else {
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() {
if (!context.shouldContainDebugInfo()) return
currentPositionHolder.resetBuilderDebugLocation()
@@ -1403,6 +1403,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
call(isClass, listOf(objCObject)).let {
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 {
kTrue
}
@@ -801,16 +801,15 @@ internal class ObjCExportTranslatorImpl(
// TODO: more precise types can be used.
if (descriptor.isObjCMetaClass()) return ObjCMetaClassType
if (descriptor.isObjCProtocolClass()) return foreignClassType("Protocol")
if (descriptor.isExternalObjCClass() || descriptor.isObjCForwardDeclaration()) {
return if (descriptor.isInterface) {
val name = descriptor.name.asString().removeSuffix("Protocol")
generator?.referenceProtocol(name)
ObjCProtocolType(name)
foreignProtocolType(name)
} else {
val name = descriptor.name.asString()
generator?.referenceClass(name)
ObjCClassType(name)
foreignClassType(name)
}
}
@@ -821,6 +820,16 @@ internal class ObjCExportTranslatorImpl(
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(
functionType: KotlinType,
objCExportScope: ObjCExportScope,
@@ -205,3 +205,11 @@ NSObject* createNSObject() {
-(int)hashCode:(int)p;
-(BOOL)equals;
@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 ObjCClassOf<*>)
assertFalse(Any() is ObjCProtocol)
assertTrue(getPrinterProtocolRaw() is ObjCProtocol)
val printerProtocol = getPrinterProtocol()!!
assertTrue(printerProtocol.asAny() is ObjCProtocol)
assertEquals(3u, ("foo" as NSString).length())
assertEquals(4u, ((1..4).joinToString("") as NSString).length())
assertEquals(2u, (listOf(0, 1) as NSArray).count())