Represent Objective-C block pointers in methods as Kotlin functions
Also do some refactoring.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
caf6719fc4
commit
e8f97b0436
+16
-1
@@ -530,7 +530,7 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
|
|
||||||
CXType_ObjCSel -> PointerType(VoidType)
|
CXType_ObjCSel -> PointerType(VoidType)
|
||||||
|
|
||||||
CXType_BlockPointer -> objCType { ObjCIdType(getNullability(type, typeAttributes), getProtocols(type)) }
|
CXType_BlockPointer -> objCType { convertBlockPointerType(type, typeAttributes) }
|
||||||
|
|
||||||
else -> UnsupportedType
|
else -> UnsupportedType
|
||||||
}
|
}
|
||||||
@@ -572,6 +572,21 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun convertBlockPointerType(type: CValue<CXType>, typeAttributes: CValue<CXTypeAttributes>?): ObjCPointer {
|
||||||
|
val kind = type.kind
|
||||||
|
assert(kind == CXType_BlockPointer)
|
||||||
|
|
||||||
|
val pointee = clang_getPointeeType(type)
|
||||||
|
val nullability = getNullability(type, typeAttributes)
|
||||||
|
|
||||||
|
// TODO: also use nullability attributes of parameters and return value.
|
||||||
|
|
||||||
|
val functionType = convertFunctionType(pointee) as? FunctionType
|
||||||
|
?: return ObjCIdType(nullability, protocols = emptyList())
|
||||||
|
|
||||||
|
return ObjCBlockPointer(nullability, functionType.parameterTypes, functionType.returnType)
|
||||||
|
}
|
||||||
|
|
||||||
private val TARGET_ATTRIBUTE = "__target__"
|
private val TARGET_ATTRIBUTE = "__target__"
|
||||||
|
|
||||||
internal fun tokenizeExtent(cursor: CValue<CXCursor>): List<String> {
|
internal fun tokenizeExtent(cursor: CValue<CXCursor>): List<String> {
|
||||||
|
|||||||
+4
@@ -258,5 +258,9 @@ data class ObjCIdType(
|
|||||||
) : ObjCQualifiedPointer()
|
) : ObjCQualifiedPointer()
|
||||||
|
|
||||||
data class ObjCInstanceType(override val nullability: Nullability) : ObjCPointer()
|
data class ObjCInstanceType(override val nullability: Nullability) : ObjCPointer()
|
||||||
|
data class ObjCBlockPointer(
|
||||||
|
override val nullability: Nullability,
|
||||||
|
val parameterTypes: List<Type>, val returnType: Type
|
||||||
|
) : ObjCPointer()
|
||||||
|
|
||||||
object UnsupportedType : Type
|
object UnsupportedType : Type
|
||||||
@@ -86,18 +86,32 @@ inline val ObjCObject?.rawPtr: NativePtr get() = if (this != null) {
|
|||||||
nativeNullPtr
|
nativeNullPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
@SymbolName("Kotlin_Interop_unwrapKotlinObjectHolder")
|
||||||
|
external internal fun unwrapKotlinObjectHolderImpl(ptr: NativePtr): Any
|
||||||
|
|
||||||
class ObjCObjectVar<T : ObjCObject?>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
class ObjCObjectVar<T : ObjCObject?>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||||
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObjCStringVarOf<T : String?>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
class ObjCNotImplementedVar<T : Any?>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||||
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
var <T : String?> ObjCStringVarOf<T>.value: T
|
var <T : Any?> ObjCNotImplementedVar<T>.value: T
|
||||||
get() = TODO()
|
get() = TODO()
|
||||||
set(value) = TODO()
|
set(value) = TODO()
|
||||||
|
|
||||||
|
typealias ObjCStringVarOf<T> = ObjCNotImplementedVar<T>
|
||||||
|
typealias ObjCBlockVar<T> = ObjCNotImplementedVar<T>
|
||||||
|
|
||||||
@konan.internal.Intrinsic external fun getReceiverOrSuper(receiver: NativePtr, superClass: NativePtr): COpaquePointer?
|
@konan.internal.Intrinsic external fun getReceiverOrSuper(receiver: NativePtr, superClass: NativePtr): COpaquePointer?
|
||||||
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
|||||||
+10
-6
@@ -16,7 +16,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.gen
|
package org.jetbrains.kotlin.native.interop.gen
|
||||||
|
|
||||||
class NativeCodeBuilder {
|
interface NativeScope {
|
||||||
|
val mappingBridgeGenerator: MappingBridgeGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
class NativeCodeBuilder(val scope: NativeScope) {
|
||||||
val lines = mutableListOf<String>()
|
val lines = mutableListOf<String>()
|
||||||
|
|
||||||
fun out(line: String): Unit {
|
fun out(line: String): Unit {
|
||||||
@@ -24,13 +28,13 @@ class NativeCodeBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun buildNativeCodeLines(block: NativeCodeBuilder.() -> Unit): List<String> {
|
inline fun buildNativeCodeLines(scope: NativeScope, block: NativeCodeBuilder.() -> Unit): List<String> {
|
||||||
val builder = NativeCodeBuilder()
|
val builder = NativeCodeBuilder(scope)
|
||||||
builder.block()
|
builder.block()
|
||||||
return builder.lines
|
return builder.lines
|
||||||
}
|
}
|
||||||
|
|
||||||
class KotlinCodeBuilder {
|
class KotlinCodeBuilder(val scope: KotlinScope) {
|
||||||
private val lines = mutableListOf<String>()
|
private val lines = mutableListOf<String>()
|
||||||
|
|
||||||
private val freeStack = mutableListOf<String>()
|
private val freeStack = mutableListOf<String>()
|
||||||
@@ -69,8 +73,8 @@ class KotlinCodeBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun buildKotlinCodeLines(block: KotlinCodeBuilder.() -> Unit): List<String> {
|
inline fun buildKotlinCodeLines(scope: KotlinScope, block: KotlinCodeBuilder.() -> Unit): List<String> {
|
||||||
val builder = KotlinCodeBuilder()
|
val builder = KotlinCodeBuilder(scope)
|
||||||
builder.block()
|
builder.block()
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
|
|||||||
|
|
||||||
if (unwrappedType is ArrayType) {
|
if (unwrappedType is ArrayType) {
|
||||||
kotlinType = (mirror as TypeMirror.ByValue).valueType
|
kotlinType = (mirror as TypeMirror.ByValue).valueType
|
||||||
getter = mirror.info.argFromBridged(getAddressExpression, kotlinScope) + "!!"
|
getter = mirror.info.argFromBridged(getAddressExpression, kotlinScope, nativeBacked = this) + "!!"
|
||||||
setter = null
|
setter = null
|
||||||
} else {
|
} else {
|
||||||
val pointedTypeName = mirror.pointedType.render(kotlinScope)
|
val pointedTypeName = mirror.pointedType.render(kotlinScope)
|
||||||
|
|||||||
+31
-11
@@ -34,6 +34,8 @@ interface KotlinScope {
|
|||||||
* or `null` if the property with given name can't be declared.
|
* or `null` if the property with given name can't be declared.
|
||||||
*/
|
*/
|
||||||
fun declareProperty(name: String): String?
|
fun declareProperty(name: String): String?
|
||||||
|
|
||||||
|
val mappingBridgeGenerator: MappingBridgeGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Classifier(
|
data class Classifier(
|
||||||
@@ -92,14 +94,23 @@ object StarProjection : KotlinTypeArgument {
|
|||||||
override fun render(scope: KotlinScope) = "*"
|
override fun render(scope: KotlinScope) = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface KotlinType : KotlinTypeArgument
|
interface KotlinType : KotlinTypeArgument {
|
||||||
|
val classifier: Classifier
|
||||||
|
fun makeNullableAsSpecified(nullable: Boolean): KotlinType
|
||||||
|
}
|
||||||
|
|
||||||
data class KotlinClassifierType(
|
data class KotlinClassifierType(
|
||||||
val classifier: Classifier,
|
override val classifier: Classifier,
|
||||||
val arguments: List<KotlinTypeArgument>,
|
val arguments: List<KotlinTypeArgument>,
|
||||||
val nullable: Boolean
|
val nullable: Boolean
|
||||||
) : KotlinType {
|
) : KotlinType {
|
||||||
|
|
||||||
|
override fun makeNullableAsSpecified(nullable: Boolean) = if (this.nullable == nullable) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
this.copy(nullable = nullable)
|
||||||
|
}
|
||||||
|
|
||||||
override fun render(scope: KotlinScope): String = buildString {
|
override fun render(scope: KotlinScope): String = buildString {
|
||||||
append(scope.reference(classifier))
|
append(scope.reference(classifier))
|
||||||
if (arguments.isNotEmpty()) {
|
if (arguments.isNotEmpty()) {
|
||||||
@@ -113,24 +124,33 @@ data class KotlinClassifierType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KotlinClassifierType.makeNullableAsSpecified(nullable: Boolean) = if (this.nullable == nullable) {
|
fun KotlinType.makeNullable() = this.makeNullableAsSpecified(true)
|
||||||
this
|
|
||||||
} else {
|
|
||||||
this.copy(nullable = nullable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KotlinClassifierType.makeNullable() = this.makeNullableAsSpecified(true)
|
|
||||||
|
|
||||||
data class KotlinFunctionType(
|
data class KotlinFunctionType(
|
||||||
val parameterTypes: List<KotlinType>,
|
val parameterTypes: List<KotlinType>,
|
||||||
val returnType: KotlinType
|
val returnType: KotlinType,
|
||||||
|
val nullable: Boolean = false
|
||||||
) : KotlinType {
|
) : KotlinType {
|
||||||
|
|
||||||
|
override fun makeNullableAsSpecified(nullable: Boolean) = if (this.nullable == nullable) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
this.copy(nullable = nullable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val classifier by lazy {
|
||||||
|
Classifier.topLevel("kotlin", "Function${parameterTypes.size}")
|
||||||
|
}
|
||||||
|
|
||||||
override fun render(scope: KotlinScope) = buildString {
|
override fun render(scope: KotlinScope) = buildString {
|
||||||
|
if (nullable) append("(")
|
||||||
|
|
||||||
append('(')
|
append('(')
|
||||||
parameterTypes.joinTo(this) { it.render(scope) }
|
parameterTypes.joinTo(this) { it.render(scope) }
|
||||||
append(") -> ")
|
append(") -> ")
|
||||||
append(returnType.render(scope))
|
append(returnType.render(scope))
|
||||||
|
|
||||||
|
if (nullable) append(")?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +214,7 @@ object KotlinTypes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KotlinFile(
|
abstract class KotlinFile(
|
||||||
val pkg: String,
|
val pkg: String,
|
||||||
namesToBeDeclared: List<String>
|
namesToBeDeclared: List<String>
|
||||||
) : KotlinScope {
|
) : KotlinScope {
|
||||||
|
|||||||
+11
-8
@@ -26,8 +26,7 @@ import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
|||||||
*/
|
*/
|
||||||
class MappingBridgeGeneratorImpl(
|
class MappingBridgeGeneratorImpl(
|
||||||
val declarationMapper: DeclarationMapper,
|
val declarationMapper: DeclarationMapper,
|
||||||
val simpleBridgeGenerator: SimpleBridgeGenerator,
|
val simpleBridgeGenerator: SimpleBridgeGenerator
|
||||||
val kotlinScope: KotlinScope
|
|
||||||
) : MappingBridgeGenerator {
|
) : MappingBridgeGenerator {
|
||||||
|
|
||||||
override fun kotlinToNative(
|
override fun kotlinToNative(
|
||||||
@@ -57,7 +56,7 @@ class MappingBridgeGeneratorImpl(
|
|||||||
is RecordType -> {
|
is RecordType -> {
|
||||||
val mirror = mirror(declarationMapper, returnType)
|
val mirror = mirror(declarationMapper, returnType)
|
||||||
val tmpVarName = kniRetVal
|
val tmpVarName = kniRetVal
|
||||||
builder.out("val $tmpVarName = nativeHeap.alloc<${mirror.pointedType.render(kotlinScope)}>()")
|
builder.out("val $tmpVarName = nativeHeap.alloc<${mirror.pointedType.render(builder.scope)}>()")
|
||||||
builder.pushBlock("try {", free = "finally { nativeHeap.free($tmpVarName) }")
|
builder.pushBlock("try {", free = "finally { nativeHeap.free($tmpVarName) }")
|
||||||
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "$tmpVarName.rawPtr"))
|
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "$tmpVarName.rawPtr"))
|
||||||
BridgedType.VOID
|
BridgedType.VOID
|
||||||
@@ -78,7 +77,11 @@ class MappingBridgeGeneratorImpl(
|
|||||||
if (unwrappedType is RecordType) {
|
if (unwrappedType is RecordType) {
|
||||||
nativeValues.add("*(${unwrappedType.decl.spelling}*)${bridgeNativeValues[index]}")
|
nativeValues.add("*(${unwrappedType.decl.spelling}*)${bridgeNativeValues[index]}")
|
||||||
} else {
|
} else {
|
||||||
nativeValues.add(mirror(declarationMapper, type).info.cFromBridged(bridgeNativeValues[index]))
|
nativeValues.add(
|
||||||
|
mirror(declarationMapper, type).info.cFromBridged(
|
||||||
|
bridgeNativeValues[index], scope, nativeBacked
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +110,7 @@ class MappingBridgeGeneratorImpl(
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val mirror = mirror(declarationMapper, returnType)
|
val mirror = mirror(declarationMapper, returnType)
|
||||||
mirror.info.argFromBridged(callExpr, kotlinScope)
|
mirror.info.argFromBridged(callExpr, builder.scope, nativeBacked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,12 +162,12 @@ class MappingBridgeGeneratorImpl(
|
|||||||
nativeValues.forEachIndexed { index, (type, _) ->
|
nativeValues.forEachIndexed { index, (type, _) ->
|
||||||
val mirror = mirror(declarationMapper, type)
|
val mirror = mirror(declarationMapper, type)
|
||||||
if (type.unwrapTypedefs() is RecordType) {
|
if (type.unwrapTypedefs() is RecordType) {
|
||||||
val pointedTypeName = mirror.pointedType.render(kotlinScope)
|
val pointedTypeName = mirror.pointedType.render(this.scope)
|
||||||
kotlinValues.add(
|
kotlinValues.add(
|
||||||
"interpretPointed<$pointedTypeName>(${bridgeKotlinValues[index]}).readValue()"
|
"interpretPointed<$pointedTypeName>(${bridgeKotlinValues[index]}).readValue()"
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
kotlinValues.add(mirror.info.argFromBridged(bridgeKotlinValues[index], kotlinScope))
|
kotlinValues.add(mirror.info.argFromBridged(bridgeKotlinValues[index], this.scope, nativeBacked))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +192,7 @@ class MappingBridgeGeneratorImpl(
|
|||||||
kniRetVal
|
kniRetVal
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
mirror(declarationMapper, returnType).info.cFromBridged(callExpr)
|
mirror(declarationMapper, returnType).info.cFromBridged(callExpr, builder.scope, nativeBacked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+176
-32
@@ -88,15 +88,15 @@ sealed class TypeMirror(val pointedType: KotlinClassifierType, val info: TypeInf
|
|||||||
/**
|
/**
|
||||||
* Mirror for C type to be represented in Kotlin as by-value type.
|
* Mirror for C type to be represented in Kotlin as by-value type.
|
||||||
*/
|
*/
|
||||||
class ByValue(pointedType: KotlinClassifierType, info: TypeInfo, val valueType: KotlinClassifierType) :
|
class ByValue(
|
||||||
TypeMirror(pointedType, info) {
|
pointedType: KotlinClassifierType,
|
||||||
|
info: TypeInfo,
|
||||||
|
val valueType: KotlinType,
|
||||||
|
val nullable: Boolean = (info is TypeInfo.Pointer)
|
||||||
|
) : TypeMirror(pointedType, info) {
|
||||||
|
|
||||||
override val argType: KotlinType
|
override val argType: KotlinType
|
||||||
get() = if ((info is TypeInfo.Pointer || (info is TypeInfo.ObjCPointerInfo && info.type.isNullable))) {
|
get() = valueType.makeNullableAsSpecified(nullable)
|
||||||
valueType.makeNullable()
|
|
||||||
} else {
|
|
||||||
valueType
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,11 +119,19 @@ sealed class TypeInfo {
|
|||||||
/**
|
/**
|
||||||
* The conversion from [bridgedType] to [TypeMirror.argType].
|
* The conversion from [bridgedType] to [TypeMirror.argType].
|
||||||
*/
|
*/
|
||||||
abstract fun argFromBridged(expr: KotlinExpression, scope: KotlinScope): KotlinExpression
|
abstract fun argFromBridged(
|
||||||
|
expr: KotlinExpression,
|
||||||
|
scope: KotlinScope,
|
||||||
|
nativeBacked: NativeBacked
|
||||||
|
): KotlinExpression
|
||||||
|
|
||||||
abstract val bridgedType: BridgedType
|
abstract val bridgedType: BridgedType
|
||||||
|
|
||||||
open fun cFromBridged(expr: NativeExpression): NativeExpression = expr
|
open fun cFromBridged(
|
||||||
|
expr: NativeExpression,
|
||||||
|
scope: NativeScope,
|
||||||
|
nativeBacked: NativeBacked
|
||||||
|
): NativeExpression = expr
|
||||||
|
|
||||||
open fun cToBridged(expr: NativeExpression): NativeExpression = expr
|
open fun cToBridged(expr: NativeExpression): NativeExpression = expr
|
||||||
|
|
||||||
@@ -136,7 +144,7 @@ sealed class TypeInfo {
|
|||||||
class Primitive(override val bridgedType: BridgedType, val varClass: Classifier) : TypeInfo() {
|
class Primitive(override val bridgedType: BridgedType, val varClass: Classifier) : TypeInfo() {
|
||||||
|
|
||||||
override fun argToBridged(expr: KotlinExpression) = expr
|
override fun argToBridged(expr: KotlinExpression) = expr
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = expr
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) = expr
|
||||||
|
|
||||||
override fun constructPointedType(valueType: KotlinType) = varClass.typeWith(valueType)
|
override fun constructPointedType(valueType: KotlinType) = varClass.typeWith(valueType)
|
||||||
}
|
}
|
||||||
@@ -144,11 +152,13 @@ sealed class TypeInfo {
|
|||||||
class Boolean : TypeInfo() {
|
class Boolean : TypeInfo() {
|
||||||
override fun argToBridged(expr: KotlinExpression) = "$expr.toByte()"
|
override fun argToBridged(expr: KotlinExpression) = "$expr.toByte()"
|
||||||
|
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = "$expr.toBoolean()"
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
|
"$expr.toBoolean()"
|
||||||
|
|
||||||
override val bridgedType: BridgedType get() = BridgedType.BYTE
|
override val bridgedType: BridgedType get() = BridgedType.BYTE
|
||||||
|
|
||||||
override fun cFromBridged(expr: NativeExpression) = "($expr) ? 1 : 0"
|
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
||||||
|
"($expr) ? 1 : 0"
|
||||||
|
|
||||||
override fun cToBridged(expr: NativeExpression) = "($expr) ? 1 : 0"
|
override fun cToBridged(expr: NativeExpression) = "($expr) ? 1 : 0"
|
||||||
|
|
||||||
@@ -158,7 +168,7 @@ sealed class TypeInfo {
|
|||||||
class Enum(val clazz: Classifier, override val bridgedType: BridgedType) : TypeInfo() {
|
class Enum(val clazz: Classifier, override val bridgedType: BridgedType) : TypeInfo() {
|
||||||
override fun argToBridged(expr: KotlinExpression) = "$expr.value"
|
override fun argToBridged(expr: KotlinExpression) = "$expr.value"
|
||||||
|
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) =
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
scope.reference(clazz) + ".byValue($expr)"
|
scope.reference(clazz) + ".byValue($expr)"
|
||||||
|
|
||||||
override fun constructPointedType(valueType: KotlinType) =
|
override fun constructPointedType(valueType: KotlinType) =
|
||||||
@@ -169,13 +179,14 @@ sealed class TypeInfo {
|
|||||||
class Pointer(val pointee: KotlinType) : TypeInfo() {
|
class Pointer(val pointee: KotlinType) : TypeInfo() {
|
||||||
override fun argToBridged(expr: String) = "$expr.rawValue"
|
override fun argToBridged(expr: String) = "$expr.rawValue"
|
||||||
|
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) =
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
"interpretCPointer<${pointee.render(scope)}>($expr)"
|
"interpretCPointer<${pointee.render(scope)}>($expr)"
|
||||||
|
|
||||||
override val bridgedType: BridgedType
|
override val bridgedType: BridgedType
|
||||||
get() = BridgedType.NATIVE_PTR
|
get() = BridgedType.NATIVE_PTR
|
||||||
|
|
||||||
override fun cFromBridged(expr: String) = "(void*)$expr" // Note: required for JVM
|
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
||||||
|
"(void*)$expr" // Note: required for JVM
|
||||||
|
|
||||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.cPointerVarOf.typeWith(valueType)
|
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.cPointerVarOf.typeWith(valueType)
|
||||||
}
|
}
|
||||||
@@ -183,7 +194,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) =
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
"interpretObjCPointerOrNull<${kotlinType.render(scope)}>($expr)" +
|
"interpretObjCPointerOrNull<${kotlinType.render(scope)}>($expr)" +
|
||||||
if (type.isNullable) "" else "!!"
|
if (type.isNullable) "" else "!!"
|
||||||
|
|
||||||
@@ -196,8 +207,8 @@ sealed class TypeInfo {
|
|||||||
class NSString(val type: ObjCPointer) : TypeInfo() {
|
class NSString(val type: ObjCPointer) : TypeInfo() {
|
||||||
override fun argToBridged(expr: String) = "CreateNSStringFromKString($expr)"
|
override fun argToBridged(expr: String) = "CreateNSStringFromKString($expr)"
|
||||||
|
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = "CreateKStringFromNSString($expr)" +
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
if (type.isNullable) "" else "!!"
|
"CreateKStringFromNSString($expr)" + if (type.isNullable) "" else "!!"
|
||||||
|
|
||||||
override val bridgedType: BridgedType
|
override val bridgedType: BridgedType
|
||||||
get() = BridgedType.OBJC_POINTER
|
get() = BridgedType.OBJC_POINTER
|
||||||
@@ -207,11 +218,129 @@ sealed class TypeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ObjCBlockPointerInfo(val kotlinType: KotlinFunctionType, val type: ObjCBlockPointer) : TypeInfo() {
|
||||||
|
|
||||||
|
override val bridgedType: BridgedType
|
||||||
|
get() = BridgedType.OBJC_POINTER
|
||||||
|
|
||||||
|
// When passing Kotlin function as block pointer from Kotlin to native,
|
||||||
|
// it first gets wrapped by a holder in [argToBridged],
|
||||||
|
// and then converted to block in [cFromBridged].
|
||||||
|
|
||||||
|
override fun argToBridged(expr: KotlinExpression): KotlinExpression = "createKotlinObjectHolder($expr)"
|
||||||
|
|
||||||
|
override fun cFromBridged(
|
||||||
|
expr: NativeExpression,
|
||||||
|
scope: NativeScope,
|
||||||
|
nativeBacked: NativeBacked
|
||||||
|
): NativeExpression {
|
||||||
|
val mappingBridgeGenerator = scope.mappingBridgeGenerator
|
||||||
|
|
||||||
|
val blockParameters = type.parameterTypes.mapIndexed { index, it ->
|
||||||
|
"p$index" to it.getStringRepresentation()
|
||||||
|
}.joinToString { "${it.second} ${it.first}" }
|
||||||
|
|
||||||
|
val blockReturnType = type.returnType.getStringRepresentation()
|
||||||
|
|
||||||
|
val kniFunction = "kniFunction"
|
||||||
|
|
||||||
|
val codeBuilder = NativeCodeBuilder(scope)
|
||||||
|
|
||||||
|
return buildString {
|
||||||
|
append("({ ") // Statement expression begins.
|
||||||
|
append("id $kniFunction = $expr; ") // Note: it gets captured below.
|
||||||
|
append("($kniFunction == nil) ? nil : ")
|
||||||
|
append("(id)") // Cast the block to `id`.
|
||||||
|
append("^$blockReturnType($blockParameters) {") // Block begins.
|
||||||
|
|
||||||
|
// As block body, generate the code which simply bridges to Kotlin and calls the Kotlin function:
|
||||||
|
mappingBridgeGenerator.nativeToKotlin(
|
||||||
|
codeBuilder,
|
||||||
|
nativeBacked,
|
||||||
|
type.returnType,
|
||||||
|
type.parameterTypes.mapIndexed { index, it ->
|
||||||
|
TypedNativeValue(it, "p$index")
|
||||||
|
} + TypedNativeValue(ObjCIdType(ObjCPointer.Nullability.Nullable, emptyList()), kniFunction)
|
||||||
|
) { kotlinValues ->
|
||||||
|
val kotlinFunctionType = kotlinType.render(this.scope)
|
||||||
|
val kotlinFunction = "unwrapKotlinObjectHolder<$kotlinFunctionType>(${kotlinValues.last()})"
|
||||||
|
"$kotlinFunction(${kotlinValues.dropLast(1).joinToString()})"
|
||||||
|
}.let {
|
||||||
|
codeBuilder.out("return $it;")
|
||||||
|
}
|
||||||
|
|
||||||
|
codeBuilder.lines.joinTo(this, separator = " ")
|
||||||
|
|
||||||
|
append(" };") // Block ends.
|
||||||
|
append(" })") // Statement expression ends.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When passing block pointer as Kotlin function from native to Kotlin,
|
||||||
|
// it is converted to Kotlin function in [cFromBridged].
|
||||||
|
|
||||||
|
override fun cToBridged(expr: NativeExpression): NativeExpression = expr
|
||||||
|
|
||||||
|
override fun argFromBridged(
|
||||||
|
expr: KotlinExpression,
|
||||||
|
scope: KotlinScope,
|
||||||
|
nativeBacked: NativeBacked
|
||||||
|
): KotlinExpression {
|
||||||
|
val mappingBridgeGenerator = scope.mappingBridgeGenerator
|
||||||
|
|
||||||
|
val funParameters = type.parameterTypes.mapIndexed { index, it ->
|
||||||
|
"p$index" to kotlinType.parameterTypes[index]
|
||||||
|
}.joinToString { "${it.first}: ${it.second.render(scope)}" }
|
||||||
|
|
||||||
|
val funReturnType = kotlinType.returnType.render(scope)
|
||||||
|
|
||||||
|
val codeBuilder = KotlinCodeBuilder(scope)
|
||||||
|
val kniBlockPtr = "kniBlockPtr"
|
||||||
|
|
||||||
|
|
||||||
|
// Build the anonymous function expression:
|
||||||
|
val anonymousFun = buildString {
|
||||||
|
append("fun($funParameters): $funReturnType {\n") // Anonymous function begins.
|
||||||
|
|
||||||
|
// As function body, generate the code which simply bridges to native and calls the block:
|
||||||
|
mappingBridgeGenerator.kotlinToNative(
|
||||||
|
codeBuilder,
|
||||||
|
nativeBacked,
|
||||||
|
type.returnType,
|
||||||
|
type.parameterTypes.mapIndexed { index, it ->
|
||||||
|
TypedKotlinValue(it, "p$index")
|
||||||
|
} + TypedKotlinValue(PointerType(VoidType), "interpretCPointer<COpaque>($kniBlockPtr)")
|
||||||
|
|
||||||
|
) { nativeValues ->
|
||||||
|
val type = type
|
||||||
|
val blockType = blockTypeStringRepresentation(type)
|
||||||
|
val objCBlock = "((__bridge $blockType)${nativeValues.last()})"
|
||||||
|
"$objCBlock(${nativeValues.dropLast(1).joinToString()})"
|
||||||
|
}.let {
|
||||||
|
codeBuilder.out("return $it")
|
||||||
|
}
|
||||||
|
|
||||||
|
codeBuilder.build().joinTo(this, separator = "\n")
|
||||||
|
append("}") // Anonymous function ends.
|
||||||
|
}
|
||||||
|
|
||||||
|
val nullOutput = if (type.isNullable) "null" else "throw NullPointerException()"
|
||||||
|
|
||||||
|
return "$expr.let { $kniBlockPtr -> if (kniBlockPtr == nativeNullPtr) $nullOutput else $anonymousFun }"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun constructPointedType(valueType: KotlinType): KotlinClassifierType {
|
||||||
|
return Classifier.topLevel("kotlinx.cinterop", "ObjCBlockVar").typeWith(valueType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ByRef(val pointed: KotlinType) : TypeInfo() {
|
class ByRef(val pointed: KotlinType) : TypeInfo() {
|
||||||
override fun argToBridged(expr: String) = error(pointed)
|
override fun argToBridged(expr: String) = error(pointed)
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = error(pointed)
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
|
error(pointed)
|
||||||
override val bridgedType: BridgedType get() = error(pointed)
|
override val bridgedType: BridgedType get() = error(pointed)
|
||||||
override fun cFromBridged(expr: String) = error(pointed)
|
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
||||||
|
error(pointed)
|
||||||
override fun cToBridged(expr: String) = error(pointed)
|
override fun cToBridged(expr: String) = error(pointed)
|
||||||
|
|
||||||
// TODO: this method must not exist
|
// TODO: this method must not exist
|
||||||
@@ -327,7 +456,8 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
|||||||
is TypeMirror.ByValue -> TypeMirror.ByValue(
|
is TypeMirror.ByValue -> TypeMirror.ByValue(
|
||||||
Classifier.topLevel(pkg, "${name}Var").type,
|
Classifier.topLevel(pkg, "${name}Var").type,
|
||||||
baseType.info,
|
baseType.info,
|
||||||
Classifier.topLevel(pkg, name).type
|
Classifier.topLevel(pkg, name).type,
|
||||||
|
nullable = baseType.nullable
|
||||||
)
|
)
|
||||||
|
|
||||||
is TypeMirror.ByRef -> TypeMirror.ByRef(Classifier.topLevel(pkg, name).type, baseType.info)
|
is TypeMirror.ByRef -> TypeMirror.ByRef(Classifier.topLevel(pkg, name).type, baseType.info)
|
||||||
@@ -343,8 +473,7 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
|||||||
private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue {
|
private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue {
|
||||||
if (type is ObjCObjectPointer && type.def.name == "NSString") {
|
if (type is ObjCObjectPointer && type.def.name == "NSString") {
|
||||||
val info = TypeInfo.NSString(type)
|
val info = TypeInfo.NSString(type)
|
||||||
val valueType = KotlinTypes.string.makeNullableAsSpecified(type.isNullable)
|
return objCMirror(KotlinTypes.string, info, type.isNullable)
|
||||||
return TypeMirror.ByValue(info.constructPointedType(valueType), info, valueType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val clazz = when (type) {
|
val clazz = when (type) {
|
||||||
@@ -353,21 +482,35 @@ private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPo
|
|||||||
is ObjCClassPointer -> KotlinTypes.objCClass
|
is ObjCClassPointer -> KotlinTypes.objCClass
|
||||||
is ObjCObjectPointer -> declarationMapper.getKotlinClassFor(type.def)
|
is ObjCObjectPointer -> declarationMapper.getKotlinClassFor(type.def)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
return objCPointerMirror(clazz, type)
|
val valueType = clazz.type
|
||||||
|
return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun objCPointerMirror(clazz: Classifier, type: ObjCPointer): TypeMirror.ByValue {
|
private fun objCBlockPointerMirror(declarationMapper: DeclarationMapper, type: ObjCBlockPointer): TypeMirror.ByValue {
|
||||||
val kotlinType = clazz.type
|
val returnType = if (type.returnType.unwrapTypedefs() is VoidType) {
|
||||||
val pointedType = KotlinTypes.objCObjectVar.typeWith(kotlinType.makeNullableAsSpecified(type.isNullable))
|
KotlinTypes.unit
|
||||||
return TypeMirror.ByValue(
|
} else {
|
||||||
pointedType,
|
mirror(declarationMapper, type.returnType).argType
|
||||||
TypeInfo.ObjCPointerInfo(kotlinType, type),
|
}
|
||||||
kotlinType
|
val kotlinType = KotlinFunctionType(
|
||||||
|
type.parameterTypes.map { mirror(declarationMapper, it).argType },
|
||||||
|
returnType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val info = TypeInfo.ObjCBlockPointerInfo(kotlinType, type)
|
||||||
|
return objCMirror(kotlinType, info, type.isNullable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun objCMirror(valueType: KotlinType, info: TypeInfo, nullable: Boolean) = TypeMirror.ByValue(
|
||||||
|
info.constructPointedType(valueType.makeNullableAsSpecified(nullable)),
|
||||||
|
info,
|
||||||
|
valueType.makeNullable(), // All typedefs to Objective-C pointers would be nullable for simplicity
|
||||||
|
nullable
|
||||||
|
)
|
||||||
|
|
||||||
fun getKotlinFunctionType(declarationMapper: DeclarationMapper, type: FunctionType): KotlinFunctionType {
|
fun getKotlinFunctionType(declarationMapper: DeclarationMapper, type: FunctionType): KotlinFunctionType {
|
||||||
val returnType = if (type.returnType.unwrapTypedefs() is VoidType) {
|
val returnType = if (type.returnType.unwrapTypedefs() is VoidType) {
|
||||||
KotlinTypes.unit
|
KotlinTypes.unit
|
||||||
@@ -376,7 +519,8 @@ fun getKotlinFunctionType(declarationMapper: DeclarationMapper, type: FunctionTy
|
|||||||
}
|
}
|
||||||
return KotlinFunctionType(
|
return KotlinFunctionType(
|
||||||
type.parameterTypes.map { mirror(declarationMapper, it).argType },
|
type.parameterTypes.map { mirror(declarationMapper, it).argType },
|
||||||
returnType
|
returnType,
|
||||||
|
nullable = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -92,7 +92,7 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
|||||||
private val bridgeHeader: String
|
private val bridgeHeader: String
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val bodyGenerator = KotlinCodeBuilder()
|
val bodyGenerator = KotlinCodeBuilder(scope = stubGenerator.kotlinFile)
|
||||||
|
|
||||||
val kotlinParameters = mutableListOf<Pair<String, KotlinType>>()
|
val kotlinParameters = mutableListOf<Pair<String, KotlinType>>()
|
||||||
val kotlinObjCBridgeParameters = mutableListOf<Pair<String, KotlinType>>()
|
val kotlinObjCBridgeParameters = mutableListOf<Pair<String, KotlinType>>()
|
||||||
@@ -215,7 +215,7 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
|||||||
|
|
||||||
private fun genImplementationTemplate(stubGenerator: StubGenerator): String = when (container) {
|
private fun genImplementationTemplate(stubGenerator: StubGenerator): String = when (container) {
|
||||||
is ObjCClassOrProtocol -> {
|
is ObjCClassOrProtocol -> {
|
||||||
val codeBuilder = NativeCodeBuilder()
|
val codeBuilder = NativeCodeBuilder(stubGenerator.simpleBridgeGenerator.topLevelNativeScope)
|
||||||
|
|
||||||
val result = codeBuilder.genMethodImp(stubGenerator, this, method, container)
|
val result = codeBuilder.genMethodImp(stubGenerator, this, method, container)
|
||||||
stubGenerator.simpleBridgeGenerator.insertNativeBridge(this, emptyList(), codeBuilder.lines)
|
stubGenerator.simpleBridgeGenerator.insertNativeBridge(this, emptyList(), codeBuilder.lines)
|
||||||
|
|||||||
+2
@@ -44,6 +44,8 @@ interface NativeBacked
|
|||||||
*/
|
*/
|
||||||
interface SimpleBridgeGenerator {
|
interface SimpleBridgeGenerator {
|
||||||
|
|
||||||
|
val topLevelNativeScope: NativeScope
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the expression to convert given Kotlin values to native counterparts, pass through the bridge,
|
* Generates the expression to convert given Kotlin values to native counterparts, pass through the bridge,
|
||||||
* use inside the native code produced by [block] and then return the result back.
|
* use inside the native code produced by [block] and then return the result back.
|
||||||
|
|||||||
+19
-10
@@ -26,7 +26,8 @@ class SimpleBridgeGeneratorImpl(
|
|||||||
private val pkgName: String,
|
private val pkgName: String,
|
||||||
private val jvmFileClassName: String,
|
private val jvmFileClassName: String,
|
||||||
private val libraryForCStubs: NativeLibrary,
|
private val libraryForCStubs: NativeLibrary,
|
||||||
private val kotlinScope: KotlinScope
|
override val topLevelNativeScope: NativeScope,
|
||||||
|
private val topLevelKotlinScope: KotlinScope
|
||||||
) : SimpleBridgeGenerator {
|
) : SimpleBridgeGenerator {
|
||||||
|
|
||||||
private var nextUniqueId = 0
|
private var nextUniqueId = 0
|
||||||
@@ -70,7 +71,7 @@ class SimpleBridgeGeneratorImpl(
|
|||||||
|
|
||||||
val kotlinFunctionName = "kniBridge${nextUniqueId++}"
|
val kotlinFunctionName = "kniBridge${nextUniqueId++}"
|
||||||
val kotlinParameters = kotlinValues.withIndex().joinToString {
|
val kotlinParameters = kotlinValues.withIndex().joinToString {
|
||||||
"p${it.index}: ${it.value.type.kotlinType.render(kotlinScope)}"
|
"p${it.index}: ${it.value.type.kotlinType.render(topLevelKotlinScope)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
val callExpr = "$kotlinFunctionName(${kotlinValues.joinToString { it.value }})"
|
val callExpr = "$kotlinFunctionName(${kotlinValues.joinToString { it.value }})"
|
||||||
@@ -113,7 +114,7 @@ class SimpleBridgeGeneratorImpl(
|
|||||||
}
|
}
|
||||||
nativeLines.add(cFunctionHeader + " {")
|
nativeLines.add(cFunctionHeader + " {")
|
||||||
|
|
||||||
buildNativeCodeLines {
|
buildNativeCodeLines(topLevelNativeScope) {
|
||||||
val cExpr = block(cFunctionParameters.takeLast(kotlinValues.size).map { (name, _) -> name })
|
val cExpr = block(cFunctionParameters.takeLast(kotlinValues.size).map { (name, _) -> name })
|
||||||
if (returnType != BridgedType.VOID) {
|
if (returnType != BridgedType.VOID) {
|
||||||
out("return ($cReturnType)$cExpr;")
|
out("return ($cReturnType)$cExpr;")
|
||||||
@@ -131,7 +132,7 @@ class SimpleBridgeGeneratorImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
nativeLines.add("}")
|
nativeLines.add("}")
|
||||||
val kotlinReturnType = returnType.kotlinType.render(kotlinScope)
|
val kotlinReturnType = returnType.kotlinType.render(topLevelKotlinScope)
|
||||||
kotlinLines.add("private external fun $kotlinFunctionName($kotlinParameters): $kotlinReturnType")
|
kotlinLines.add("private external fun $kotlinFunctionName($kotlinParameters): $kotlinReturnType")
|
||||||
|
|
||||||
val nativeBridge = NativeBridge(kotlinLines, nativeLines)
|
val nativeBridge = NativeBridge(kotlinLines, nativeLines)
|
||||||
@@ -156,7 +157,9 @@ class SimpleBridgeGeneratorImpl(
|
|||||||
val kotlinParameters = nativeValues.withIndex().map {
|
val kotlinParameters = nativeValues.withIndex().map {
|
||||||
"p${it.index}" to it.value.type.kotlinType
|
"p${it.index}" to it.value.type.kotlinType
|
||||||
}
|
}
|
||||||
val joinedKotlinParameters = kotlinParameters.joinToString { "${it.first}: ${it.second.render(kotlinScope)}" }
|
val joinedKotlinParameters = kotlinParameters.joinToString {
|
||||||
|
"${it.first}: ${it.second.render(topLevelKotlinScope)}"
|
||||||
|
}
|
||||||
|
|
||||||
val cFunctionParameters = nativeValues.withIndex().map {
|
val cFunctionParameters = nativeValues.withIndex().map {
|
||||||
"p${it.index}" to it.value.type.nativeType
|
"p${it.index}" to it.value.type.nativeType
|
||||||
@@ -169,10 +172,10 @@ class SimpleBridgeGeneratorImpl(
|
|||||||
val cFunctionHeader = "$cReturnType $symbolName($joinedCParameters)"
|
val cFunctionHeader = "$cReturnType $symbolName($joinedCParameters)"
|
||||||
|
|
||||||
nativeLines.add("$cFunctionHeader;")
|
nativeLines.add("$cFunctionHeader;")
|
||||||
val kotlinReturnType = returnType.kotlinType.render(kotlinScope)
|
val kotlinReturnType = returnType.kotlinType.render(topLevelKotlinScope)
|
||||||
kotlinLines.add("private fun $kotlinFunctionName($joinedKotlinParameters): $kotlinReturnType {")
|
kotlinLines.add("private fun $kotlinFunctionName($joinedKotlinParameters): $kotlinReturnType {")
|
||||||
|
|
||||||
buildKotlinCodeLines {
|
buildKotlinCodeLines(topLevelKotlinScope) {
|
||||||
var kotlinExpr = block(kotlinParameters.map { (name, _) -> name })
|
var kotlinExpr = block(kotlinParameters.map { (name, _) -> name })
|
||||||
if (returnType == BridgedType.OBJC_POINTER) {
|
if (returnType == BridgedType.OBJC_POINTER) {
|
||||||
// The Kotlin code may lose the ownership on this pointer after returning from the bridge,
|
// The Kotlin code may lose the ownership on this pointer after returning from the bridge,
|
||||||
@@ -207,13 +210,19 @@ class SimpleBridgeGeneratorImpl(
|
|||||||
nativeBridges.map { it.second.nativeLines }
|
nativeBridges.map { it.second.nativeLines }
|
||||||
.mapFragmentIsCompilable(libraryForCStubs)
|
.mapFragmentIsCompilable(libraryForCStubs)
|
||||||
.forEachIndexed { index, isCompilable ->
|
.forEachIndexed { index, isCompilable ->
|
||||||
if (isCompilable) {
|
if (!isCompilable) {
|
||||||
includedBridges.add(nativeBridges[index].second)
|
|
||||||
} else {
|
|
||||||
excludedClients.add(nativeBridges[index].first)
|
excludedClients.add(nativeBridges[index].first)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nativeBridges.mapNotNullTo(includedBridges) { (nativeBacked, nativeBridge) ->
|
||||||
|
if (nativeBacked in excludedClients) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
nativeBridge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: exclude unused bridges.
|
// TODO: exclude unused bridges.
|
||||||
|
|
||||||
return object : NativeBridges {
|
return object : NativeBridges {
|
||||||
|
|||||||
+17
-1
@@ -51,6 +51,7 @@ fun Type.getStringRepresentation(): String = when (this) {
|
|||||||
is ObjCClassPointer -> "Class$protocolQualifier"
|
is ObjCClassPointer -> "Class$protocolQualifier"
|
||||||
is ObjCObjectPointer -> "${def.name}$protocolQualifier*"
|
is ObjCObjectPointer -> "${def.name}$protocolQualifier*"
|
||||||
is ObjCInstanceType -> TODO(this.toString()) // Must have already been handled.
|
is ObjCInstanceType -> TODO(this.toString()) // Must have already been handled.
|
||||||
|
is ObjCBlockPointer -> "id"
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw kotlin.NotImplementedError()
|
else -> throw kotlin.NotImplementedError()
|
||||||
@@ -63,4 +64,19 @@ tailrec fun Type.unwrapTypedefs(): Type = if (this is Typedef) {
|
|||||||
this.def.aliased.unwrapTypedefs()
|
this.def.aliased.unwrapTypedefs()
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun blockTypeStringRepresentation(type: ObjCBlockPointer): String {
|
||||||
|
return buildString {
|
||||||
|
append(type.returnType.getStringRepresentation())
|
||||||
|
append("(^)")
|
||||||
|
append("(")
|
||||||
|
val blockParameters = if (type.parameterTypes.isEmpty()) {
|
||||||
|
"void"
|
||||||
|
} else {
|
||||||
|
type.parameterTypes.joinToString { it.getStringRepresentation() }
|
||||||
|
}
|
||||||
|
append(blockParameters)
|
||||||
|
append(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+20
-5
@@ -160,7 +160,10 @@ class StubGenerator(
|
|||||||
|
|
||||||
private val macroConstantsByName = nativeIndex.macroConstants.associateBy { it.name }
|
private val macroConstantsByName = nativeIndex.macroConstants.associateBy { it.name }
|
||||||
|
|
||||||
val kotlinFile = KotlinFile(pkgName, namesToBeDeclared = computeNamesToBeDeclared())
|
val kotlinFile = object : KotlinFile(pkgName, namesToBeDeclared = computeNamesToBeDeclared()) {
|
||||||
|
override val mappingBridgeGenerator: MappingBridgeGenerator
|
||||||
|
get() = this@StubGenerator.mappingBridgeGenerator
|
||||||
|
}
|
||||||
|
|
||||||
private fun computeNamesToBeDeclared(): MutableList<String> {
|
private fun computeNamesToBeDeclared(): MutableList<String> {
|
||||||
return mutableListOf<String>().apply {
|
return mutableListOf<String>().apply {
|
||||||
@@ -402,7 +405,8 @@ class StubGenerator(
|
|||||||
val readBitsExpr =
|
val readBitsExpr =
|
||||||
"readBits(this.rawPtr, ${field.offset}, ${field.size}, $signed).${rawType.convertor!!}()"
|
"readBits(this.rawPtr, ${field.offset}, ${field.size}, $signed).${rawType.convertor!!}()"
|
||||||
|
|
||||||
out(" get() = ${typeInfo.argFromBridged(readBitsExpr, kotlinFile)}")
|
val getExpr = typeInfo.argFromBridged(readBitsExpr, kotlinFile, object : NativeBacked {})
|
||||||
|
out(" get() = $getExpr")
|
||||||
|
|
||||||
val rawValue = typeInfo.argToBridged("value")
|
val rawValue = typeInfo.argToBridged("value")
|
||||||
val setExpr = "writeBits(this.rawPtr, ${field.offset}, ${field.size}, $rawValue.toLong())"
|
val setExpr = "writeBits(this.rawPtr, ${field.offset}, ${field.size}, $rawValue.toLong())"
|
||||||
@@ -594,7 +598,7 @@ class StubGenerator(
|
|||||||
init {
|
init {
|
||||||
// TODO: support dumpShims
|
// TODO: support dumpShims
|
||||||
val kotlinParameters = mutableListOf<Pair<String, KotlinType>>()
|
val kotlinParameters = mutableListOf<Pair<String, KotlinType>>()
|
||||||
val bodyGenerator = KotlinCodeBuilder()
|
val bodyGenerator = KotlinCodeBuilder(scope = kotlinFile)
|
||||||
val bridgeArguments = mutableListOf<TypedKotlinValue>()
|
val bridgeArguments = mutableListOf<TypedKotlinValue>()
|
||||||
|
|
||||||
func.parameters.forEachIndexed { index, parameter ->
|
func.parameters.forEachIndexed { index, parameter ->
|
||||||
@@ -864,6 +868,7 @@ class StubGenerator(
|
|||||||
add("UNUSED_PARAMETER") // For constructors.
|
add("UNUSED_PARAMETER") // For constructors.
|
||||||
add("MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED") // Workaround for multiple-inherited properties.
|
add("MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED") // Workaround for multiple-inherited properties.
|
||||||
add("EXTENSION_SHADOWED_BY_MEMBER") // For Objective-C categories represented as extensions.
|
add("EXTENSION_SHADOWED_BY_MEMBER") // For Objective-C categories represented as extensions.
|
||||||
|
add("REDUNDANT_NULLABLE") // This warning appears due to Obj-C typedef nullability incomplete support.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -976,9 +981,19 @@ class StubGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val simpleBridgeGenerator: SimpleBridgeGenerator =
|
val simpleBridgeGenerator: SimpleBridgeGenerator =
|
||||||
SimpleBridgeGeneratorImpl(platform, pkgName, jvmFileClassName, libraryForCStubs, kotlinFile)
|
SimpleBridgeGeneratorImpl(
|
||||||
|
platform,
|
||||||
|
pkgName,
|
||||||
|
jvmFileClassName,
|
||||||
|
libraryForCStubs,
|
||||||
|
topLevelNativeScope = object : NativeScope {
|
||||||
|
override val mappingBridgeGenerator: MappingBridgeGenerator
|
||||||
|
get() = this@StubGenerator.mappingBridgeGenerator
|
||||||
|
},
|
||||||
|
topLevelKotlinScope = kotlinFile
|
||||||
|
)
|
||||||
|
|
||||||
val mappingBridgeGenerator: MappingBridgeGenerator =
|
val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator, kotlinFile)
|
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2199,7 +2199,7 @@ if (isMac()) {
|
|||||||
|
|
||||||
task interop_objc_smoke(type: RunInteropKonanTest) {
|
task interop_objc_smoke(type: RunInteropKonanTest) {
|
||||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
goldValue = "Hello, World!\nKotlin says: Hello, everybody!\nHello from Kotlin\n2, 1\n" +
|
goldValue = "84\nFoo\nHello, World!\nKotlin says: Hello, everybody!\nHello from Kotlin\n2, 1\n" +
|
||||||
"true\ntrue\nDeallocated\nDeallocated\n"
|
"true\ntrue\nDeallocated\nDeallocated\n"
|
||||||
|
|
||||||
source = "interop/objc/smoke.kt"
|
source = "interop/objc/smoke.kt"
|
||||||
|
|||||||
@@ -25,3 +25,14 @@
|
|||||||
@end;
|
@end;
|
||||||
|
|
||||||
void replacePairElements(id <MutablePair> pair, int first, int second);
|
void replacePairElements(id <MutablePair> pair, int first, int second);
|
||||||
|
|
||||||
|
int invoke1(int arg, int (^block)(int)) {
|
||||||
|
return block(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke2(void (^block)(void)) {
|
||||||
|
block();
|
||||||
|
}
|
||||||
|
|
||||||
|
int (^getSupplier(int x))(void);
|
||||||
|
Class (^ _Nonnull getClassGetter(NSObject* obj))(void);
|
||||||
|
|||||||
@@ -8,7 +8,17 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
|
println(
|
||||||
|
getSupplier(
|
||||||
|
invoke1(42) { it * 2 }
|
||||||
|
)!!()
|
||||||
|
)
|
||||||
|
|
||||||
val foo = Foo()
|
val foo = Foo()
|
||||||
|
|
||||||
|
val classGetter = getClassGetter(foo)
|
||||||
|
invoke2 { println(classGetter()) }
|
||||||
|
|
||||||
foo.hello()
|
foo.hello()
|
||||||
foo.name = "everybody"
|
foo.name = "everybody"
|
||||||
foo.helloWithPrinter(object : NSObject(), PrinterProtocol {
|
foo.helloWithPrinter(object : NSObject(), PrinterProtocol {
|
||||||
|
|||||||
@@ -47,3 +47,13 @@ void replacePairElements(id <MutablePair> pair, int first, int second) {
|
|||||||
[pair update:0 add:(first - pair.first)];
|
[pair update:0 add:(first - pair.first)];
|
||||||
[pair update:1 sub:(pair.second - second)];
|
[pair update:1 sub:(pair.second - second)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int (^getSupplier(int x))(void) {
|
||||||
|
return ^{
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Class (^ _Nonnull getClassGetter(NSObject* obj))() {
|
||||||
|
return ^{ return obj.class; };
|
||||||
|
}
|
||||||
|
|||||||
@@ -103,6 +103,51 @@ id MissingInitImp(id self, SEL _cmd) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: rework the interface to reduce the number of virtual calls
|
||||||
|
// in Kotlin_Interop_createKotlinObjectHolder and Kotlin_Interop_unwrapKotlinObjectHolder
|
||||||
|
@interface KotlinObjectHolder : NSObject
|
||||||
|
-(id)initWithRef:(KRef)ref;
|
||||||
|
-(KRef)ref;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation KotlinObjectHolder {
|
||||||
|
KRef ref_;
|
||||||
|
};
|
||||||
|
|
||||||
|
-(id)initWithRef:(KRef)ref {
|
||||||
|
if (self = [super init]) {
|
||||||
|
UpdateRef(&ref_, ref);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(KRef)ref {
|
||||||
|
return ref_;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)dealloc {
|
||||||
|
UpdateRef(&ref_, nullptr);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end;
|
||||||
|
|
||||||
|
id Kotlin_Interop_createKotlinObjectHolder(KRef any) {
|
||||||
|
if (any == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[[KotlinObjectHolder alloc] initWithRef:any] autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) {
|
||||||
|
if (holder == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [((KotlinObjectHolder*)holder) ref];
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
#else // KONAN_OBJC_INTEROP
|
#else // KONAN_OBJC_INTEROP
|
||||||
@@ -134,6 +179,16 @@ KBoolean Kotlin_Interop_ObjCEquals(KNativePtr ptr, KNativePtr otherPtr) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* Kotlin_Interop_createKotlinObjectHolder(KRef any) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
KRef Kotlin_Interop_unwrapKotlinObjectHolder(void* holder) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
#endif // KONAN_OBJC_INTEROP
|
#endif // KONAN_OBJC_INTEROP
|
||||||
Reference in New Issue
Block a user