Add prelimitary support for using Objective-C types in framework API
This commit is contained in:
committed by
SvyatoslavScherbina
parent
80d026afa5
commit
7d7f0f46e6
@@ -23,9 +23,12 @@ interface ObjCClass : ObjCObject
|
|||||||
typealias ObjCObjectMeta = ObjCClass
|
typealias ObjCObjectMeta = ObjCClass
|
||||||
|
|
||||||
abstract class ObjCObjectBase protected constructor() : ObjCObject {
|
abstract class ObjCObjectBase protected constructor() : ObjCObject {
|
||||||
final override fun equals(other: Any?): Boolean = this.uncheckedCast<ObjCPointerHolder>().equals(other)
|
final override fun equals(other: Any?): Boolean {
|
||||||
final override fun hashCode(): Int = ObjCHashCode(this.rawPtr)
|
val thisAny: Any = this
|
||||||
final override fun toString(): String = ObjCToString(this.rawPtr)
|
return thisAny.equals(other) // Call it virtually because ObjCObjectBase is a fake type.
|
||||||
|
}
|
||||||
|
final override fun hashCode(): Int = ObjCHashCode(this.rawPtr())
|
||||||
|
final override fun toString(): String = ObjCToString(this.rawPtr())
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ObjCObjectBaseMeta protected constructor() : ObjCObjectBase(), ObjCObjectMeta {}
|
abstract class ObjCObjectBaseMeta protected constructor() : ObjCObjectBase(), ObjCObjectMeta {}
|
||||||
@@ -43,7 +46,11 @@ class ObjCPointerHolder(inline val rawPtr: NativePtr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final override fun equals(other: Any?): Boolean =
|
final override fun equals(other: Any?): Boolean =
|
||||||
(other is ObjCPointerHolder) && ObjCEquals(this.rawPtr, other.rawPtr)
|
if (other is ObjCPointerHolder) {
|
||||||
|
ObjCEquals(this.rawPtr, other.rawPtr)
|
||||||
|
} else {
|
||||||
|
other == this
|
||||||
|
}
|
||||||
|
|
||||||
final override fun hashCode(): Int = ObjCHashCode(this.rawPtr)
|
final override fun hashCode(): Int = ObjCHashCode(this.rawPtr)
|
||||||
final override fun toString(): String = ObjCToString(this.rawPtr)
|
final override fun toString(): String = ObjCToString(this.rawPtr)
|
||||||
@@ -61,7 +68,7 @@ private fun ObjCObjectBase.superInitCheck(superInitCallResult: ObjCObject?) {
|
|||||||
if (superInitCallResult == null)
|
if (superInitCallResult == null)
|
||||||
throw RuntimeException("Super initialization failed")
|
throw RuntimeException("Super initialization failed")
|
||||||
|
|
||||||
if (superInitCallResult.rawPtr != this.rawPtr)
|
if (superInitCallResult.rawPtr() != this.rawPtr())
|
||||||
throw UnsupportedOperationException("Super initializer has replaced object")
|
throw UnsupportedOperationException("Super initializer has replaced object")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,18 +86,14 @@ inline fun <T : ObjCObject> interpretObjCPointer(rawPtr: NativePtr): T = if (raw
|
|||||||
throw NullPointerException()
|
throw NullPointerException()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val ObjCObject.rawPtr: NativePtr get() = (this.uncheckedCast<ObjCPointerHolder>()).rawPtr
|
@SymbolName("Kotlin_Interop_refToObjC")
|
||||||
inline val ObjCObject?.rawPtr: NativePtr get() = if (this != null) {
|
external fun ObjCObject?.rawPtr(): NativePtr
|
||||||
(this.uncheckedCast<ObjCPointerHolder>()).rawPtr
|
|
||||||
} else {
|
|
||||||
nativeNullPtr
|
|
||||||
}
|
|
||||||
|
|
||||||
@SymbolName("Kotlin_Interop_createKotlinObjectHolder")
|
@SymbolName("Kotlin_Interop_createKotlinObjectHolder")
|
||||||
external fun createKotlinObjectHolder(any: Any?): NativePtr
|
external fun createKotlinObjectHolder(any: Any?): NativePtr
|
||||||
|
|
||||||
inline fun <reified T : Any> unwrapKotlinObjectHolder(holder: ObjCObject?): T {
|
inline fun <reified T : Any> unwrapKotlinObjectHolder(holder: ObjCObject?): T {
|
||||||
return unwrapKotlinObjectHolderImpl(holder!!.rawPtr) as T
|
return unwrapKotlinObjectHolderImpl(holder!!.rawPtr()) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ fun <T : ObjCObject> ObjCObject.reinterpret() = this.uncheckedCast<T>()
|
|||||||
// TODO: null checks
|
// TODO: null checks
|
||||||
var <T : ObjCObject?> ObjCObjectVar<T>.value: T
|
var <T : ObjCObject?> ObjCObjectVar<T>.value: T
|
||||||
get() = interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>()
|
get() = interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>()
|
||||||
set(value) = nativeMemUtils.putNativePtr(this, value.rawPtr)
|
set(value) = nativeMemUtils.putNativePtr(this, value.rawPtr())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes Kotlin method in Objective-C class accessible through Objective-C dispatch
|
* Makes Kotlin method in Objective-C class accessible through Objective-C dispatch
|
||||||
|
|||||||
+1
-1
@@ -192,7 +192,7 @@ sealed class TypeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ObjCPointerInfo(val kotlinType: KotlinType, val type: ObjCPointer) : TypeInfo() {
|
class ObjCPointerInfo(val kotlinType: KotlinType, val type: ObjCPointer) : TypeInfo() {
|
||||||
override fun argToBridged(expr: String) = "$expr.rawPtr"
|
override fun argToBridged(expr: String) = "$expr.rawPtr()"
|
||||||
|
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
"interpretObjCPointerOrNull<${kotlinType.render(scope)}>($expr)" +
|
"interpretObjCPointerOrNull<${kotlinType.render(scope)}>($expr)" +
|
||||||
|
|||||||
+2
-2
@@ -112,13 +112,13 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
|||||||
|
|
||||||
if (method.nsConsumesSelf) {
|
if (method.nsConsumesSelf) {
|
||||||
// TODO: do this later due to possible exceptions
|
// TODO: do this later due to possible exceptions
|
||||||
bodyGenerator.out("objc_retain($kniReceiverParameter.rawPtr)")
|
bodyGenerator.out("objc_retain($kniReceiverParameter.rawPtr())")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinObjCBridgeParameters.add(kniReceiverParameter to KotlinTypes.objCObject.type)
|
kotlinObjCBridgeParameters.add(kniReceiverParameter to KotlinTypes.objCObject.type)
|
||||||
nativeBridgeArguments.add(
|
nativeBridgeArguments.add(
|
||||||
TypedKotlinValue(voidPtr,
|
TypedKotlinValue(voidPtr,
|
||||||
"getReceiverOrSuper($kniReceiverParameter.rawPtr, $kniSuperClassParameter)"))
|
"getReceiverOrSuper($kniReceiverParameter.rawPtr(), $kniSuperClassParameter)"))
|
||||||
|
|
||||||
val kotlinParameterNames = method.getKotlinParameterNames()
|
val kotlinParameterNames = method.getKotlinParameterNames()
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -172,10 +172,9 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
|||||||
|
|
||||||
val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single()
|
val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single()
|
||||||
|
|
||||||
val objCObjectRawPtr = packageScope.getContributedVariables("rawPtr").single {
|
val objCObjectRawPtr = packageScope.getContributedFunctions("rawPtr").single {
|
||||||
val extensionReceiverType = it.extensionReceiverParameter?.type
|
val extensionReceiverType = it.extensionReceiverParameter?.type
|
||||||
extensionReceiverType != null && !extensionReceiverType.isMarkedNullable &&
|
extensionReceiverType != null && TypeUtils.getClassDescriptor(extensionReceiverType) == objCObject
|
||||||
TypeUtils.getClassDescriptor(extensionReceiverType) == objCObject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()
|
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()
|
||||||
|
|||||||
+1
-1
@@ -117,7 +117,7 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
|||||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectSuperInitCheck)
|
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectSuperInitCheck)
|
||||||
|
|
||||||
val interopObjCObjectRawValueGetter =
|
val interopObjCObjectRawValueGetter =
|
||||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr.getter!!)
|
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr)
|
||||||
|
|
||||||
val interopInvokeImpls = context.interopBuiltIns.invokeImpls.mapValues { (_, function) ->
|
val interopInvokeImpls = context.interopBuiltIns.invokeImpls.mapValues { (_, function) ->
|
||||||
symbolTable.referenceSimpleFunction(function)
|
symbolTable.referenceSimpleFunction(function)
|
||||||
|
|||||||
+9
@@ -440,6 +440,15 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val objCExportEnabled = if (context.config.produce.isNativeBinary) {
|
||||||
|
// Note: this defines the global declared in runtime (if any).
|
||||||
|
staticData.placeGlobal("objCExportEnabled", Int8(0), isExported = true).also {
|
||||||
|
it.setConstant(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val tlsMode by lazy {
|
val tlsMode by lazy {
|
||||||
when (target) {
|
when (target) {
|
||||||
KonanTarget.WASM32,
|
KonanTarget.WASM32,
|
||||||
|
|||||||
+6
@@ -218,6 +218,7 @@ internal class ObjCExportCodeGenerator(
|
|||||||
emitSortedAdapters(placedInterfaceAdapters, "Kotlin_ObjCExport_sortedProtocolAdapters")
|
emitSortedAdapters(placedInterfaceAdapters, "Kotlin_ObjCExport_sortedProtocolAdapters")
|
||||||
|
|
||||||
context.llvm.kObjectReservedTailSize!!.setInitializer(Int32(runtime.pointerSize))
|
context.llvm.kObjectReservedTailSize!!.setInitializer(Int32(runtime.pointerSize))
|
||||||
|
context.llvm.objCExportEnabled!!.setInitializer(Int8(1))
|
||||||
|
|
||||||
dataGenerator.finishModule() // TODO: move to appropriate place.
|
dataGenerator.finishModule() // TODO: move to appropriate place.
|
||||||
}
|
}
|
||||||
@@ -445,6 +446,11 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
|
|||||||
emitBoxConverter(it)
|
emitBoxConverter(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setObjCExportTypeInfo(
|
||||||
|
context.interopBuiltIns.objCPointerHolder,
|
||||||
|
constPointer(codegen.llvmFunction(context.ir.symbols.objCPointerHolderValueGetter.owner))
|
||||||
|
)
|
||||||
|
|
||||||
emitFunctionConverters()
|
emitFunctionConverters()
|
||||||
|
|
||||||
emitKotlinFunctionAdaptersToBlock()
|
emitKotlinFunctionAdaptersToBlock()
|
||||||
|
|||||||
+43
-16
@@ -92,8 +92,11 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
|
|||||||
private val kotlinAnyName = namer.kotlinAnyName
|
private val kotlinAnyName = namer.kotlinAnyName
|
||||||
|
|
||||||
private val stubs = mutableListOf<Stub>()
|
private val stubs = mutableListOf<Stub>()
|
||||||
private val classToName = mutableMapOf<ClassDescriptor, String>()
|
private val classOrInterfaceToName = mutableMapOf<ClassDescriptor, String>()
|
||||||
private val interfaceToName = mutableMapOf<ClassDescriptor, String>()
|
|
||||||
|
internal val classForwardDeclarations = mutableSetOf<String>()
|
||||||
|
internal val protocolForwardDeclarations = mutableSetOf<String>()
|
||||||
|
|
||||||
private val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>()
|
private val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>()
|
||||||
val extraClassesToTranslate = mutableSetOf<ClassDescriptor>()
|
val extraClassesToTranslate = mutableSetOf<ClassDescriptor>()
|
||||||
|
|
||||||
@@ -154,17 +157,11 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translateClassName(descriptor: ClassDescriptor): String {
|
fun translateClassName(descriptor: ClassDescriptor): String = classOrInterfaceToName.getOrPut(descriptor) {
|
||||||
val descriptorToName = if (descriptor.isInterface) interfaceToName else classToName
|
assert(mapper.shouldBeExposed(descriptor))
|
||||||
|
val forwardDeclarations = if (descriptor.isInterface) protocolForwardDeclarations else classForwardDeclarations
|
||||||
|
|
||||||
return descriptorToName.getOrPut(descriptor) {
|
namer.getClassOrProtocolName(descriptor).also { forwardDeclarations += it }
|
||||||
if (!mapper.shouldBeExposed(descriptor)) {
|
|
||||||
context.reportCompilationError("Can't produce ${descriptor.fqNameSafe.asString()} to framework API")
|
|
||||||
throw KonanCompilationException()
|
|
||||||
}
|
|
||||||
|
|
||||||
namer.getClassOrProtocolName(descriptor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateInterface(descriptor: ClassDescriptor) {
|
private fun translateInterface(descriptor: ClassDescriptor) {
|
||||||
@@ -555,13 +552,13 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
|
|||||||
add("#import <Foundation/Foundation.h>")
|
add("#import <Foundation/Foundation.h>")
|
||||||
add("")
|
add("")
|
||||||
|
|
||||||
if (classToName.isNotEmpty()) {
|
if (classForwardDeclarations.isNotEmpty()) {
|
||||||
add("@class ${classToName.values.joinToString()};")
|
add("@class ${classForwardDeclarations.joinToString()};")
|
||||||
add("")
|
add("")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interfaceToName.isNotEmpty()) {
|
if (protocolForwardDeclarations.isNotEmpty()) {
|
||||||
add("@protocol ${interfaceToName.values.joinToString()};")
|
add("@protocol ${protocolForwardDeclarations.joinToString()};")
|
||||||
add("")
|
add("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,6 +787,10 @@ private fun ObjCExportHeaderGenerator.mapReferenceTypeIgnoringNullability(
|
|||||||
return ObjCIdType
|
return ObjCIdType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (classDescriptor.defaultType.isObjCObjectType()) {
|
||||||
|
return mapObjCObjectReferenceTypeIgnoringNullability(classDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
scheduleClassToBeGenerated(classDescriptor)
|
scheduleClassToBeGenerated(classDescriptor)
|
||||||
|
|
||||||
return if (classDescriptor.isInterface) {
|
return if (classDescriptor.isInterface) {
|
||||||
@@ -799,6 +800,32 @@ private fun ObjCExportHeaderGenerator.mapReferenceTypeIgnoringNullability(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private tailrec fun ObjCExportHeaderGenerator.mapObjCObjectReferenceTypeIgnoringNullability(
|
||||||
|
descriptor: ClassDescriptor
|
||||||
|
): ObjCNonNullReferenceType {
|
||||||
|
// TODO: more precise types can be used.
|
||||||
|
|
||||||
|
if (descriptor.isObjCMetaClass()) return ObjCIdType
|
||||||
|
|
||||||
|
if (descriptor.isExternalObjCClass()) {
|
||||||
|
return if (descriptor.isInterface) {
|
||||||
|
val name = descriptor.name.asString().removeSuffix("Protocol")
|
||||||
|
protocolForwardDeclarations += name
|
||||||
|
ObjCProtocolType(name)
|
||||||
|
} else {
|
||||||
|
val name = descriptor.name.asString()
|
||||||
|
classForwardDeclarations += name
|
||||||
|
ObjCClassType(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.isKotlinObjCClass()) {
|
||||||
|
return mapObjCObjectReferenceTypeIgnoringNullability(descriptor.getSuperClassOrAny())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObjCIdType
|
||||||
|
}
|
||||||
|
|
||||||
private fun ObjCExportHeaderGenerator.scheduleClassToBeGenerated(classDescriptor: ClassDescriptor) {
|
private fun ObjCExportHeaderGenerator.scheduleClassToBeGenerated(classDescriptor: ClassDescriptor) {
|
||||||
if (classDescriptor !in generatedClasses) {
|
if (classDescriptor !in generatedClasses) {
|
||||||
extraClassesToTranslate += classDescriptor
|
extraClassesToTranslate += classDescriptor
|
||||||
|
|||||||
+2
@@ -49,6 +49,8 @@ internal fun ObjCExportMapper.getClassIfCategory(descriptor: CallableMemberDescr
|
|||||||
|
|
||||||
val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return null
|
val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return null
|
||||||
|
|
||||||
|
if (extensionReceiverType.isObjCObjectType()) return null
|
||||||
|
|
||||||
val erasedClass = extensionReceiverType.getErasedTypeClass()
|
val erasedClass = extensionReceiverType.getErasedTypeClass()
|
||||||
return if (this.isRepresentedAsObjCInterface(erasedClass)) {
|
return if (this.isRepresentedAsObjCInterface(erasedClass)) {
|
||||||
erasedClass
|
erasedClass
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
#import "Exceptions.h"
|
#import "Exceptions.h"
|
||||||
|
|
||||||
|
// Note: defined by a compiler-generated bitcode.
|
||||||
|
extern "C" const uint8_t objCExportEnabled;
|
||||||
|
|
||||||
struct ObjCToKotlinMethodAdapter {
|
struct ObjCToKotlinMethodAdapter {
|
||||||
const char* selector;
|
const char* selector;
|
||||||
const char* encoding;
|
const char* encoding;
|
||||||
@@ -513,6 +516,16 @@ extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) {
|
|||||||
return Kotlin_ObjCExport_refToObjC_slowpath(obj);
|
return Kotlin_ObjCExport_refToObjC_slowpath(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" ALWAYS_INLINE id Kotlin_Interop_refToObjC(ObjHeader* obj) {
|
||||||
|
if (obj == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
} else if (!objCExportEnabled || obj->type_info() == theObjCPointerHolderTypeInfo) {
|
||||||
|
return *reinterpret_cast<id*>(obj + 1); // First field.
|
||||||
|
} else {
|
||||||
|
return Kotlin_ObjCExport_refToObjC(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) {
|
extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) {
|
||||||
if (obj == nullptr) RETURN_OBJ(nullptr);
|
if (obj == nullptr) RETURN_OBJ(nullptr);
|
||||||
id convertible = (id<ConvertibleToKotlin>)obj;
|
id convertible = (id<ConvertibleToKotlin>)obj;
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ OBJ_GETTER(Kotlin_Interop_ObjCToString, id <NSObject> ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KInt Kotlin_Interop_ObjCHashCode(id <NSObject> ptr) {
|
KInt Kotlin_Interop_ObjCHashCode(id <NSObject> ptr) {
|
||||||
return (KInt) ptr.hash;
|
KLong hash = ptr.hash;
|
||||||
|
return (KInt)(hash ^ (hash >> 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
KBoolean Kotlin_Interop_ObjCEquals(id <NSObject> ptr, id otherPtr) {
|
KBoolean Kotlin_Interop_ObjCEquals(id <NSObject> ptr, id otherPtr) {
|
||||||
|
|||||||
Reference in New Issue
Block a user