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)
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
}
@@ -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(
@@ -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)
}
@@ -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()
@@ -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()