Improve types mapping for imported Objective-C APIs

* id -> Any
* NSArray -> List
* NSMutableArray -> MutableList
* NSSet -> Set
* NSDictionary -> Map
This commit is contained in:
Svyatoslav Scherbina
2018-04-11 10:20:40 +03:00
committed by SvyatoslavScherbina
parent be4a811bb7
commit 52d0e47999
7 changed files with 50 additions and 49 deletions
@@ -38,7 +38,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.objcPtr() != this.objcPtr())
throw UnsupportedOperationException("Super initializer has replaced object") 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 fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (this as T) // TODO: make private
@SymbolName("Kotlin_Interop_refFromObjC") @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") @SymbolName("Kotlin_Interop_refToObjC")
external fun ObjCObject?.rawPtr(): NativePtr external fun Any?.objcPtr(): NativePtr
@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: Any?): T {
return unwrapKotlinObjectHolderImpl(holder!!.rawPtr()) as T return unwrapKotlinObjectHolderImpl(holder!!.objcPtr()) as T
} }
@PublishedApi @PublishedApi
@SymbolName("Kotlin_Interop_unwrapKotlinObjectHolder") @SymbolName("Kotlin_Interop_unwrapKotlinObjectHolder")
external internal fun unwrapKotlinObjectHolderImpl(ptr: NativePtr): Any 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) companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
} }
@@ -139,9 +139,11 @@ private external fun <T : ObjCObject> getObjCClass(): NativePtr
// Konan runtme: // Konan runtme:
@Deprecated("Use plain Kotlin cast of String to NSString", level = DeprecationLevel.WARNING)
@SymbolName("Kotlin_Interop_CreateNSStringFromKString") @SymbolName("Kotlin_Interop_CreateNSStringFromKString")
external fun CreateNSStringFromKString(str: String?): NativePtr external fun CreateNSStringFromKString(str: String?): NativePtr
@Deprecated("Use plain Kotlin cast of NSString to String", level = DeprecationLevel.WARNING)
@SymbolName("Kotlin_Interop_CreateKStringFromNSString") @SymbolName("Kotlin_Interop_CreateKStringFromNSString")
external fun CreateKStringFromNSString(ptr: NativePtr): String? 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>() fun <T : ObjCObject> ObjCObject.reinterpret() = @Suppress("DEPRECATION") this.uncheckedCast<T>()
// TODO: null checks // TODO: null checks
var <T : ObjCObject?> ObjCObjectVar<T>.value: T var <T> ObjCObjectVar<T>.value: T
@Suppress("DEPRECATION") get() = @Suppress("DEPRECATION") get() =
interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>() 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 * 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; // If it is fixed argument, then it is not C string because it must have been already converted;
// then treat it as NSString. // then treat it as NSString.
// TODO: handle fixed NSString arguments in the stub instead. // TODO: handle fixed NSString arguments in the stub instead.
interpretCPointer<COpaque>(CreateNSStringFromKString(argument)) interpretCPointer<COpaque>(argument.objcPtr())
} else { } else {
// It is passed as variadic argument; no type information available, so treat it as C string. // It is passed as variadic argument; no type information available, so treat it as C string.
argument.cstr.getPointer(additionalPlacement) argument.cstr.getPointer(additionalPlacement)
@@ -93,7 +93,7 @@ private tailrec fun convertArgument(
is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement) is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement)
is ForeignObjCObject -> { is ForeignObjCObject -> {
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer((argument as ObjCObject).rawPtr()) location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer(argument.objcPtr())
FFI_TYPE_KIND_POINTER FFI_TYPE_KIND_POINTER
} }
@@ -168,6 +168,12 @@ object KotlinTypes {
val string by BuiltInType val string by BuiltInType
val any 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 nativePtr by InteropType
val cOpaque by InteropType val cOpaque by InteropType
@@ -198,20 +204,21 @@ object KotlinTypes {
val cValue by InteropClassifier val cValue by InteropClassifier
private object BuiltInType { private open class ClassifierAtPackage(val pkg: String) {
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): KotlinClassifierType =
Classifier.topLevel("kotlin", property.name.capitalize()).type
}
private object InteropClassifier {
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): Classifier = 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 = 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( abstract class KotlinFile(
@@ -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.objcPtr()"
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)" +
@@ -204,20 +204,6 @@ sealed class TypeInfo {
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.objCObjectVar.typeWith(valueType) 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() { class ObjCBlockPointerInfo(val kotlinType: KotlinFunctionType, val type: ObjCBlockPointer) : TypeInfo() {
override val bridgedType: BridgedType 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 { private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue {
if (type is ObjCObjectPointer && type.def.isNSStringOrSubclass()) { if (type is ObjCObjectPointer && type.def.isNSStringOrSubclass()) {
val info = TypeInfo.NSString(type) val valueType = KotlinTypes.string
return objCMirror(KotlinTypes.string, info, type.isNullable) return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable)
} }
val clazz = when (type) { val valueType = when (type) {
is ObjCIdType -> type.protocols.firstOrNull()?.let { declarationMapper.getKotlinClassFor(it) } is ObjCIdType -> {
?: KotlinTypes.objCObject type.protocols.firstOrNull()?.let { declarationMapper.getKotlinClassFor(it) }?.type
is ObjCClassPointer -> KotlinTypes.objCClass ?: KotlinTypes.any
is ObjCObjectPointer -> declarationMapper.getKotlinClassFor(type.def) }
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 ObjCInstanceType -> TODO(type.toString()) // Must have already been handled.
is ObjCBlockPointer -> return objCBlockPointerMirror(declarationMapper, type) is ObjCBlockPointer -> return objCBlockPointerMirror(declarationMapper, type)
} }
val valueType = clazz.type
return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable) return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable)
} }
@@ -506,11 +506,11 @@ class ObjCPropertyStub(
} }
val result = mutableListOf( val result = mutableListOf(
"$modifiers$kind $receiver${property.name.asSimpleName()}: $kotlinType", "$modifiers$kind $receiver${property.name.asSimpleName()}: $kotlinType",
" get() = ${getterStub.bridgeName}(nativeNullPtr, this.rawPtr())" " get() = ${getterStub.bridgeName}(nativeNullPtr, this.objcPtr())"
) )
property.setter?.let { 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() return result.asSequence()
@@ -166,10 +166,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single() val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single()
val objCObjectRawPtr = packageScope.getContributedFunctions("rawPtr").single { val objCObjectRawPtr = packageScope.getContributedFunctions("objcPtr").single()
val extensionReceiverType = it.extensionReceiverParameter?.type
extensionReceiverType != null && TypeUtils.getClassDescriptor(extensionReceiverType) == objCObject
}
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single() val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()