Improve types mapping for imported Objective-C APIs
* id -> Any * NSArray -> List * NSMutableArray -> MutableList * NSSet -> Set * NSDictionary -> Map
This commit is contained in:
committed by
SvyatoslavScherbina
parent
be4a811bb7
commit
52d0e47999
@@ -38,7 +38,7 @@ private fun ObjCObjectBase.superInitCheck(superInitCallResult: ObjCObject?) {
|
||||
if (superInitCallResult == null)
|
||||
throw RuntimeException("Super initialization failed")
|
||||
|
||||
if (superInitCallResult.rawPtr() != this.rawPtr())
|
||||
if (superInitCallResult.objcPtr() != this.objcPtr())
|
||||
throw UnsupportedOperationException("Super initializer has replaced object")
|
||||
}
|
||||
|
||||
@@ -46,25 +46,25 @@ private fun ObjCObjectBase.superInitCheck(superInitCallResult: ObjCObject?) {
|
||||
fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (this as T) // TODO: make private
|
||||
|
||||
@SymbolName("Kotlin_Interop_refFromObjC")
|
||||
external fun <T : ObjCObject?> interpretObjCPointerOrNull(rawPtr: NativePtr): T?
|
||||
external fun <T> interpretObjCPointerOrNull(objcPtr: NativePtr): T?
|
||||
|
||||
inline fun <T : ObjCObject> interpretObjCPointer(rawPtr: NativePtr): T = interpretObjCPointerOrNull<T>(rawPtr)!!
|
||||
inline fun <T : Any> interpretObjCPointer(objcPtr: NativePtr): T = interpretObjCPointerOrNull<T>(objcPtr)!!
|
||||
|
||||
@SymbolName("Kotlin_Interop_refToObjC")
|
||||
external fun ObjCObject?.rawPtr(): NativePtr
|
||||
external fun Any?.objcPtr(): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_Interop_createKotlinObjectHolder")
|
||||
external fun createKotlinObjectHolder(any: Any?): NativePtr
|
||||
|
||||
inline fun <reified T : Any> unwrapKotlinObjectHolder(holder: ObjCObject?): T {
|
||||
return unwrapKotlinObjectHolderImpl(holder!!.rawPtr()) as T
|
||||
inline fun <reified T : Any> unwrapKotlinObjectHolder(holder: Any?): T {
|
||||
return unwrapKotlinObjectHolderImpl(holder!!.objcPtr()) as T
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@SymbolName("Kotlin_Interop_unwrapKotlinObjectHolder")
|
||||
external internal fun unwrapKotlinObjectHolderImpl(ptr: NativePtr): Any
|
||||
|
||||
class ObjCObjectVar<T : ObjCObject?>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
class ObjCObjectVar<T>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||
}
|
||||
|
||||
@@ -139,9 +139,11 @@ private external fun <T : ObjCObject> getObjCClass(): NativePtr
|
||||
|
||||
// Konan runtme:
|
||||
|
||||
@Deprecated("Use plain Kotlin cast of String to NSString", level = DeprecationLevel.WARNING)
|
||||
@SymbolName("Kotlin_Interop_CreateNSStringFromKString")
|
||||
external fun CreateNSStringFromKString(str: String?): NativePtr
|
||||
|
||||
@Deprecated("Use plain Kotlin cast of NSString to String", level = DeprecationLevel.WARNING)
|
||||
@SymbolName("Kotlin_Interop_CreateKStringFromNSString")
|
||||
external fun CreateKStringFromNSString(ptr: NativePtr): String?
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@ inline fun <R> autoreleasepool(block: () -> R): R {
|
||||
fun <T : ObjCObject> ObjCObject.reinterpret() = @Suppress("DEPRECATION") this.uncheckedCast<T>()
|
||||
|
||||
// TODO: null checks
|
||||
var <T : ObjCObject?> ObjCObjectVar<T>.value: T
|
||||
var <T> ObjCObjectVar<T>.value: T
|
||||
@Suppress("DEPRECATION") get() =
|
||||
interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>()
|
||||
|
||||
set(value) = nativeMemUtils.putNativePtr(this, value.rawPtr())
|
||||
set(value) = nativeMemUtils.putNativePtr(this, value.objcPtr())
|
||||
|
||||
/**
|
||||
* Makes Kotlin method in Objective-C class accessible through Objective-C dispatch
|
||||
|
||||
@@ -44,7 +44,7 @@ private tailrec fun convertArgument(
|
||||
// If it is fixed argument, then it is not C string because it must have been already converted;
|
||||
// then treat it as NSString.
|
||||
// TODO: handle fixed NSString arguments in the stub instead.
|
||||
interpretCPointer<COpaque>(CreateNSStringFromKString(argument))
|
||||
interpretCPointer<COpaque>(argument.objcPtr())
|
||||
} else {
|
||||
// It is passed as variadic argument; no type information available, so treat it as C string.
|
||||
argument.cstr.getPointer(additionalPlacement)
|
||||
@@ -93,7 +93,7 @@ private tailrec fun convertArgument(
|
||||
is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement)
|
||||
|
||||
is ForeignObjCObject -> {
|
||||
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer((argument as ObjCObject).rawPtr())
|
||||
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer(argument.objcPtr())
|
||||
FFI_TYPE_KIND_POINTER
|
||||
}
|
||||
|
||||
|
||||
+16
-9
@@ -168,6 +168,12 @@ object KotlinTypes {
|
||||
val string by BuiltInType
|
||||
val any by BuiltInType
|
||||
|
||||
val list by CollectionClassifier
|
||||
val mutableList by CollectionClassifier
|
||||
val set by CollectionClassifier
|
||||
val map by CollectionClassifier
|
||||
|
||||
|
||||
val nativePtr by InteropType
|
||||
|
||||
val cOpaque by InteropType
|
||||
@@ -198,20 +204,21 @@ object KotlinTypes {
|
||||
|
||||
val cValue by InteropClassifier
|
||||
|
||||
private object BuiltInType {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): KotlinClassifierType =
|
||||
Classifier.topLevel("kotlin", property.name.capitalize()).type
|
||||
}
|
||||
|
||||
private object InteropClassifier {
|
||||
private open class ClassifierAtPackage(val pkg: String) {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): Classifier =
|
||||
Classifier.topLevel("kotlinx.cinterop", property.name.capitalize())
|
||||
Classifier.topLevel(pkg, property.name.capitalize())
|
||||
}
|
||||
|
||||
private object InteropType {
|
||||
private open class TypeAtPackage(val pkg: String) {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): KotlinClassifierType =
|
||||
InteropClassifier.getValue(thisRef, property).type
|
||||
Classifier.topLevel(pkg, property.name.capitalize()).type
|
||||
}
|
||||
|
||||
private object BuiltInType : TypeAtPackage("kotlin")
|
||||
private object CollectionClassifier : ClassifierAtPackage("kotlin.collections")
|
||||
|
||||
private object InteropClassifier : ClassifierAtPackage("kotlinx.cinterop")
|
||||
private object InteropType : TypeAtPackage("kotlinx.cinterop")
|
||||
}
|
||||
|
||||
abstract class KotlinFile(
|
||||
|
||||
+18
-23
@@ -192,7 +192,7 @@ sealed class TypeInfo {
|
||||
}
|
||||
|
||||
class ObjCPointerInfo(val kotlinType: KotlinType, val type: ObjCPointer) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = "$expr.rawPtr()"
|
||||
override fun argToBridged(expr: String) = "$expr.objcPtr()"
|
||||
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||
"interpretObjCPointerOrNull<${kotlinType.render(scope)}>($expr)" +
|
||||
@@ -204,20 +204,6 @@ sealed class TypeInfo {
|
||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.objCObjectVar.typeWith(valueType)
|
||||
}
|
||||
|
||||
class NSString(val type: ObjCPointer) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = "CreateNSStringFromKString($expr)"
|
||||
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||
"CreateKStringFromNSString($expr)" + if (type.isNullable) "" else "!!"
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
get() = BridgedType.OBJC_POINTER
|
||||
|
||||
override fun constructPointedType(valueType: KotlinType): KotlinClassifierType {
|
||||
return KotlinTypes.objCStringVarOf.typeWith(valueType)
|
||||
}
|
||||
}
|
||||
|
||||
class ObjCBlockPointerInfo(val kotlinType: KotlinFunctionType, val type: ObjCBlockPointer) : TypeInfo() {
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
@@ -487,20 +473,29 @@ internal fun ObjCClass.isNSStringSubclass(): Boolean = this.baseClass?.isNSStrin
|
||||
|
||||
private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue {
|
||||
if (type is ObjCObjectPointer && type.def.isNSStringOrSubclass()) {
|
||||
val info = TypeInfo.NSString(type)
|
||||
return objCMirror(KotlinTypes.string, info, type.isNullable)
|
||||
val valueType = KotlinTypes.string
|
||||
return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable)
|
||||
}
|
||||
|
||||
val clazz = when (type) {
|
||||
is ObjCIdType -> type.protocols.firstOrNull()?.let { declarationMapper.getKotlinClassFor(it) }
|
||||
?: KotlinTypes.objCObject
|
||||
is ObjCClassPointer -> KotlinTypes.objCClass
|
||||
is ObjCObjectPointer -> declarationMapper.getKotlinClassFor(type.def)
|
||||
val valueType = when (type) {
|
||||
is ObjCIdType -> {
|
||||
type.protocols.firstOrNull()?.let { declarationMapper.getKotlinClassFor(it) }?.type
|
||||
?: KotlinTypes.any
|
||||
}
|
||||
is ObjCClassPointer -> KotlinTypes.objCClass.type
|
||||
is ObjCObjectPointer -> {
|
||||
when (type.def.name) {
|
||||
"NSArray" -> KotlinTypes.list.typeWith(StarProjection)
|
||||
"NSMutableArray" -> KotlinTypes.mutableList.typeWith(KotlinTypes.any.makeNullable())
|
||||
"NSSet" -> KotlinTypes.set.typeWith(StarProjection)
|
||||
"NSDictionary" -> KotlinTypes.map.typeWith(KotlinTypes.any.makeNullable(), StarProjection)
|
||||
else -> declarationMapper.getKotlinClassFor(type.def).type
|
||||
}
|
||||
}
|
||||
is ObjCInstanceType -> TODO(type.toString()) // Must have already been handled.
|
||||
is ObjCBlockPointer -> return objCBlockPointerMirror(declarationMapper, type)
|
||||
}
|
||||
|
||||
val valueType = clazz.type
|
||||
return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -506,11 +506,11 @@ class ObjCPropertyStub(
|
||||
}
|
||||
val result = mutableListOf(
|
||||
"$modifiers$kind $receiver${property.name.asSimpleName()}: $kotlinType",
|
||||
" get() = ${getterStub.bridgeName}(nativeNullPtr, this.rawPtr())"
|
||||
" get() = ${getterStub.bridgeName}(nativeNullPtr, this.objcPtr())"
|
||||
)
|
||||
|
||||
property.setter?.let {
|
||||
result.add(" set(value) = ${setterStub!!.bridgeName}(nativeNullPtr, this.rawPtr(), value)")
|
||||
result.add(" set(value) = ${setterStub!!.bridgeName}(nativeNullPtr, this.objcPtr(), value)")
|
||||
}
|
||||
|
||||
return result.asSequence()
|
||||
|
||||
+1
-4
@@ -166,10 +166,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
|
||||
val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single()
|
||||
|
||||
val objCObjectRawPtr = packageScope.getContributedFunctions("rawPtr").single {
|
||||
val extensionReceiverType = it.extensionReceiverParameter?.type
|
||||
extensionReceiverType != null && TypeUtils.getClassDescriptor(extensionReceiverType) == objCObject
|
||||
}
|
||||
val objCObjectRawPtr = packageScope.getContributedFunctions("objcPtr").single()
|
||||
|
||||
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user