Rework interop callbacks
* Do not rely on type inference and `CFunctionType`;
* Represent struct-typed parameters and return values as `CValue<*>`
(currently supported only on JVM).
This commit is contained in:
committed by
SvyatoslavScherbina
parent
cc73c7e009
commit
3f84ba462f
+1
-8
@@ -275,14 +275,7 @@ methods available:
|
|||||||
### Callbacks ###
|
### Callbacks ###
|
||||||
|
|
||||||
To convert Kotlin function to pointer to C function,
|
To convert Kotlin function to pointer to C function,
|
||||||
`staticCFunction(::kotlinFunction)` can be used. Currently `staticCFunction`
|
`staticCFunction(::kotlinFunction)` can be used.
|
||||||
heavily relies on type inference, so the expression `staticCFunction(...)`
|
|
||||||
should be either assigned to the variable having proper type explicitly
|
|
||||||
specified, or passed to the function, e.g.
|
|
||||||
|
|
||||||
```
|
|
||||||
glutDisplayFunc(staticCFunction(::display))
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that some function types are not supported currently. For example,
|
Note that some function types are not supported currently. For example,
|
||||||
it is not possible to get pointer to function that receives or returns structs
|
it is not possible to get pointer to function that receives or returns structs
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -137,7 +137,7 @@ internal fun visitChildren(parent: CValue<CXCursor>, visitor: CursorVisitor) {
|
|||||||
clang_visitChildren(parent, staticCFunction { cursor, parent, clientData ->
|
clang_visitChildren(parent, staticCFunction { cursor, parent, clientData ->
|
||||||
@Suppress("NAME_SHADOWING", "UNCHECKED_CAST")
|
@Suppress("NAME_SHADOWING", "UNCHECKED_CAST")
|
||||||
val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor
|
val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor
|
||||||
visitor(cursor.readValue(), parent.readValue())
|
visitor(cursor, parent)
|
||||||
}, clientData)
|
}, clientData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,15 @@
|
|||||||
|
|
||||||
package kotlinx.cinterop
|
package kotlinx.cinterop
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KFunction
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.full.companionObjectInstance
|
||||||
|
import kotlin.reflect.full.declaredMemberProperties
|
||||||
|
import kotlin.reflect.full.isSubclassOf
|
||||||
|
import kotlin.reflect.jvm.reflect
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides a way to create a stable handle to any Kotlin object.
|
* This class provides a way to create a stable handle to any Kotlin object.
|
||||||
* Its [value] can be safely passed to native code e.g. to be received in a Kotlin callback.
|
* Its [value] can be safely passed to native code e.g. to be received in a Kotlin callback.
|
||||||
@@ -59,112 +68,348 @@ data class StableObjPtr private constructor(val value: COpaquePointer) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun getFieldCType(type: KType): CType<*> {
|
||||||
* Describes the type of C function with adapter for Kotlin functions.
|
val classifier = type.classifier
|
||||||
*
|
if (classifier is KClass<*> && classifier.isSubclassOf(CStructVar::class)) {
|
||||||
* The instances of this class are supposed to be Kotlin object declarations (singletons),
|
return getStructCType(classifier)
|
||||||
* because it is required by [CAdaptedFunctionType] and
|
|
||||||
* because creating the instance implies allocating some amount of non-freeable memory for the instance itself
|
|
||||||
* and for any unique Kotlin function "converted" to this type.
|
|
||||||
*
|
|
||||||
* Native function type definition consists in the following:
|
|
||||||
* - Definitions of native function's parameter and return types to be passed into the constructor
|
|
||||||
* - Implementation of [invoke] method which describes how to convert between these types and Kotlin types used in [F]
|
|
||||||
*
|
|
||||||
* @param F Kotlin function type corresponding to given native function type
|
|
||||||
*/
|
|
||||||
abstract class CAdaptedFunctionTypeImpl<F : Function<*>>
|
|
||||||
protected constructor(returnType: CType, vararg paramTypes: CType) : CAdaptedFunctionType<F> {
|
|
||||||
|
|
||||||
override fun fromStatic(function: F): NativePtr {
|
|
||||||
// TODO: optimize synchronization
|
|
||||||
synchronized(cache) {
|
|
||||||
return cache.getOrPut(function, { createFromStatic(function) })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return getArgOrRetValCType(type)
|
||||||
* Describes the C type of a function's parameter or return value.
|
|
||||||
* It is supposed to be constructed using the primitive types (such as [SInt32]) and the [Struct] combinator.
|
|
||||||
*
|
|
||||||
* This description omits the details that are irrelevant for the ABI.
|
|
||||||
*/
|
|
||||||
protected open class CType internal constructor(val ffiType: ffi_type) {
|
|
||||||
internal constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
|
||||||
}
|
|
||||||
|
|
||||||
protected object Void : CType(ffiTypeVoid())
|
|
||||||
protected object UInt8 : CType(ffiTypeUInt8())
|
|
||||||
protected object SInt8 : CType(ffiTypeSInt8())
|
|
||||||
protected object UInt16 : CType(ffiTypeUInt16())
|
|
||||||
protected object SInt16 : CType(ffiTypeSInt16())
|
|
||||||
protected object UInt32 : CType(ffiTypeUInt32())
|
|
||||||
protected object SInt32 : CType(ffiTypeSInt32())
|
|
||||||
protected object UInt64 : CType(ffiTypeUInt64())
|
|
||||||
protected object SInt64 : CType(ffiTypeSInt64())
|
|
||||||
protected object Pointer : CType(ffiTypePointer())
|
|
||||||
|
|
||||||
protected class Struct(vararg elementTypes: CType) : CType(
|
|
||||||
ffiTypeStruct(
|
|
||||||
elementTypes.map { it.ffiType }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method should invoke given Kotlin function.
|
|
||||||
*
|
|
||||||
* @param args array of pointers to arguments to be passed to [function]
|
|
||||||
* @param ret pointer to memory to be filled with return value of [function]
|
|
||||||
*/
|
|
||||||
protected abstract fun invoke(function: F, args: CArrayPointer<COpaquePointerVar>, ret: COpaquePointer)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
init {
|
|
||||||
loadCallbacksLibrary()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val ffiCif = ffiCreateCif(returnType.ffiType, paramTypes.map { it.ffiType })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocates a native function of this type for given Kotlin function.
|
|
||||||
*/
|
|
||||||
private fun createFromStatic(function: F): NativePtr {
|
|
||||||
if (!isStatic(function)) {
|
|
||||||
throw IllegalArgumentException()
|
|
||||||
}
|
|
||||||
|
|
||||||
val impl: UserData = { ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar> ->
|
|
||||||
invoke(function, args, ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ffiCreateClosure(ffiCif, impl)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns `true` if given function is *static* as defined in [fromStatic].
|
|
||||||
*/
|
|
||||||
private fun isStatic(function: Function<*>): Boolean {
|
|
||||||
// TODO: revise
|
|
||||||
try {
|
|
||||||
with(function.javaClass.getDeclaredField("INSTANCE")) {
|
|
||||||
if (!java.lang.reflect.Modifier.isStatic(modifiers) || !java.lang.reflect.Modifier.isFinal(modifiers)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
isAccessible = true // TODO: undo
|
|
||||||
|
|
||||||
return get(null) == function
|
|
||||||
}
|
|
||||||
} catch (e: NoSuchFieldException) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val cache = mutableMapOf<F, NativePtr>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private typealias UserData = (ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar>)->Unit
|
private fun getVariableCType(type: KType): CType<*>? {
|
||||||
|
val classifier = type.classifier
|
||||||
|
return when (classifier) {
|
||||||
|
!is KClass<*> -> null
|
||||||
|
ByteVarOf::class -> SInt8
|
||||||
|
ShortVarOf::class -> SInt16
|
||||||
|
IntVarOf::class -> SInt32
|
||||||
|
LongVarOf::class -> SInt64
|
||||||
|
CPointerVarOf::class -> Pointer
|
||||||
|
// TODO: floats, enums.
|
||||||
|
else -> if (classifier.isSubclassOf(CStructVar::class)) {
|
||||||
|
getStructCType(classifier)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val structTypeCache = ConcurrentHashMap<Class<*>, CType<*>>()
|
||||||
|
|
||||||
|
private fun getStructCType(structClass: KClass<*>): CType<*> = structTypeCache.computeIfAbsent(structClass.java) {
|
||||||
|
// Note that struct classes are not supposed to be user-defined,
|
||||||
|
// so they don't require to be checked strictly.
|
||||||
|
|
||||||
|
val annotations = structClass.annotations
|
||||||
|
val cNaturalStruct = annotations.filterIsInstance<CNaturalStruct>().firstOrNull() ?:
|
||||||
|
error("struct ${structClass.simpleName} has custom layout")
|
||||||
|
|
||||||
|
val propertiesByName = structClass.declaredMemberProperties.groupBy { it.name }
|
||||||
|
|
||||||
|
val fields = cNaturalStruct.fieldNames.map {
|
||||||
|
propertiesByName[it]!!.single()
|
||||||
|
}
|
||||||
|
|
||||||
|
val fieldCTypes = mutableListOf<CType<*>>()
|
||||||
|
|
||||||
|
for (field in fields) {
|
||||||
|
val lengthAnnotation = field.annotations.filterIsInstance<CLength>().firstOrNull()
|
||||||
|
if (lengthAnnotation == null) {
|
||||||
|
val fieldType = getFieldCType(field.returnType)
|
||||||
|
fieldCTypes.add(fieldType)
|
||||||
|
} else {
|
||||||
|
assert(field.returnType.classifier == CPointer::class)
|
||||||
|
val length = lengthAnnotation.value
|
||||||
|
if (length != 0) {
|
||||||
|
val pointed = field.returnType.arguments.single().type!!
|
||||||
|
val pointedCType = getVariableCType(pointed) ?: TODO("array element type '$pointed'")
|
||||||
|
|
||||||
|
// Represent array field as repeated element-typed fields:
|
||||||
|
repeat(length) {
|
||||||
|
fieldCTypes.add(pointedCType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val structType = structClass.companionObjectInstance as CVariable.Type
|
||||||
|
|
||||||
|
Struct(structType.size, structType.align, fieldCTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStructValueCType(type: KType): CType<*> {
|
||||||
|
val structClass = type.arguments.singleOrNull()?.type?.classifier as? KClass<*> ?:
|
||||||
|
error("'$type' type is incomplete")
|
||||||
|
|
||||||
|
return getStructCType(structClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getEnumCType(classifier: KClass<*>): CEnumType? {
|
||||||
|
val rawValueType = classifier.declaredMemberProperties.single().returnType
|
||||||
|
|
||||||
|
val rawValueCType = when (rawValueType.classifier) {
|
||||||
|
Byte::class -> SInt8
|
||||||
|
Short::class -> SInt16
|
||||||
|
Int::class -> SInt32
|
||||||
|
Long::class -> SInt64
|
||||||
|
else -> error("'${classifier.simpleName}' has unexpected value type '$rawValueType'")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return CEnumType(rawValueCType as CType<Number>)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getArgOrRetValCType(type: KType): CType<*> {
|
||||||
|
val classifier = type.classifier
|
||||||
|
|
||||||
|
val result = when (classifier) {
|
||||||
|
!is KClass<*> -> null
|
||||||
|
Unit::class -> Void
|
||||||
|
Byte::class -> SInt8
|
||||||
|
Short::class -> SInt16
|
||||||
|
Int::class -> SInt32
|
||||||
|
Long::class -> SInt64
|
||||||
|
CPointer::class -> Pointer
|
||||||
|
// TODO: floats
|
||||||
|
CValue::class -> getStructValueCType(type)
|
||||||
|
else -> if (classifier.isSubclassOf(CEnum::class)) {
|
||||||
|
getEnumCType(classifier)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} ?: error("$type is not supported in callback signature")
|
||||||
|
|
||||||
|
if (type.isMarkedNullable != (classifier == CPointer::class)) {
|
||||||
|
if (type.isMarkedNullable) {
|
||||||
|
error("$type must not be nullable when used in callback signature")
|
||||||
|
} else {
|
||||||
|
error("$type must be nullable when used in callback signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStaticCFunction(function: Function<*>): CPointer<CFunction<*>> {
|
||||||
|
val errorMessage = "staticCFunction must take an unbound, non-capturing function"
|
||||||
|
|
||||||
|
if (!isStatic(function)) {
|
||||||
|
throw IllegalArgumentException(errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
val kFunction = function as? KFunction<*> ?: function.reflect() ?:
|
||||||
|
throw IllegalArgumentException(errorMessage)
|
||||||
|
|
||||||
|
val returnType = getArgOrRetValCType(kFunction.returnType)
|
||||||
|
val paramTypes = kFunction.parameters.map { getArgOrRetValCType(it.type) }
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return interpretCPointer(createStaticCFunctionImpl(returnType as CType<Any?>, paramTypes, function))!!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `true` if given function is *static* as defined in [staticCFunction].
|
||||||
|
*/
|
||||||
|
private fun isStatic(function: Function<*>): Boolean {
|
||||||
|
// TODO: revise
|
||||||
|
try {
|
||||||
|
with(function.javaClass.getDeclaredField("INSTANCE")) {
|
||||||
|
if (!java.lang.reflect.Modifier.isStatic(modifiers) || !java.lang.reflect.Modifier.isFinal(modifiers)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
isAccessible = true // TODO: undo
|
||||||
|
|
||||||
|
return get(null) == function
|
||||||
|
|
||||||
|
// If the class has static final "INSTANCE" field, and only the value of this field is accepted,
|
||||||
|
// then each class is handled at most once, so these checks prevent memory leaks.
|
||||||
|
}
|
||||||
|
} catch (e: NoSuchFieldException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val createdStaticFunctions = ConcurrentHashMap<Class<*>, CPointer<CFunction<*>>>()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
internal fun <F : Function<*>> staticCFunctionImpl(function: F) =
|
||||||
|
createdStaticFunctions.computeIfAbsent(function.javaClass) {
|
||||||
|
createStaticCFunction(function)
|
||||||
|
} as CPointer<CFunction<F>>
|
||||||
|
|
||||||
|
private val invokeMethods = (0 .. 22).map { arity ->
|
||||||
|
Class.forName("kotlin.jvm.functions.Function$arity").getMethod("invoke",
|
||||||
|
*Array<Class<*>>(arity) { java.lang.Object::class.java })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStaticCFunctionImpl(
|
||||||
|
returnType: CType<Any?>,
|
||||||
|
paramTypes: List<CType<*>>,
|
||||||
|
function: Function<*>
|
||||||
|
): NativePtr {
|
||||||
|
val ffiCif = ffiCreateCif(returnType.ffiType, paramTypes.map { it.ffiType })
|
||||||
|
|
||||||
|
val arity = paramTypes.size
|
||||||
|
val pt = paramTypes.toTypedArray()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val impl: FfiClosureImpl = when (arity) {
|
||||||
|
0 -> {
|
||||||
|
val f = function as () -> Any?
|
||||||
|
ffiClosureImpl(returnType) { args ->
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
val f = function as (Any?) -> Any?
|
||||||
|
ffiClosureImpl(returnType) { args ->
|
||||||
|
f(pt.read(args, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
val f = function as (Any?, Any?) -> Any?
|
||||||
|
ffiClosureImpl(returnType) { args ->
|
||||||
|
f(pt.read(args, 0), pt.read(args, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
val f = function as (Any?, Any?, Any?) -> Any?
|
||||||
|
ffiClosureImpl(returnType) { args ->
|
||||||
|
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
4 -> {
|
||||||
|
val f = function as (Any?, Any?, Any?, Any?) -> Any?
|
||||||
|
ffiClosureImpl(returnType) { args ->
|
||||||
|
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2), pt.read(args, 3))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
5 -> {
|
||||||
|
val f = function as (Any?, Any?, Any?, Any?, Any?) -> Any?
|
||||||
|
ffiClosureImpl(returnType) { args ->
|
||||||
|
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2), pt.read(args, 3), pt.read(args, 4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val invokeMethod = invokeMethods[arity]
|
||||||
|
ffiClosureImpl(returnType) { args ->
|
||||||
|
val arguments = Array(arity) { pt.read(args, it) }
|
||||||
|
invokeMethod.invoke(function, *arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ffiCreateClosure(ffiCif, impl)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun Array<CType<*>>.read(args: CArrayPointer<COpaquePointerVar>, index: Int) =
|
||||||
|
this[index].read(args[index].rawValue)
|
||||||
|
|
||||||
|
private inline fun ffiClosureImpl(
|
||||||
|
returnType: CType<Any?>,
|
||||||
|
crossinline invoke: (args: CArrayPointer<COpaquePointerVar>) -> Any?
|
||||||
|
): FfiClosureImpl {
|
||||||
|
return { ret, args ->
|
||||||
|
val result = invoke(args)
|
||||||
|
returnType.write(ret.rawValue, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the bridge between Kotlin type `T` and the corresponding C type of a function's parameter or return value.
|
||||||
|
* It is supposed to be constructed using the primitive types (such as [SInt32]), the [Struct] combinator
|
||||||
|
* and the [CEnumType] wrapper.
|
||||||
|
*
|
||||||
|
* This description omits the details that are irrelevant for the ABI.
|
||||||
|
*/
|
||||||
|
private abstract class CType<T> internal constructor(val ffiType: ffi_type) {
|
||||||
|
internal constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
||||||
|
abstract fun read(location: NativePtr): T
|
||||||
|
abstract fun write(location: NativePtr, value: T): Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
private object Void : CType<Any?>(ffiTypeVoid()) {
|
||||||
|
override fun read(location: NativePtr) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun write(location: NativePtr, value: Any?) {
|
||||||
|
// nothing to do.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SInt8 : CType<Byte>(ffiTypeSInt8()) {
|
||||||
|
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).value
|
||||||
|
override fun write(location: NativePtr, value: Byte) {
|
||||||
|
interpretPointed<ByteVar>(location).value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SInt16 : CType<Short>(ffiTypeSInt16()) {
|
||||||
|
override fun read(location: NativePtr) = interpretPointed<ShortVar>(location).value
|
||||||
|
override fun write(location: NativePtr, value: Short) {
|
||||||
|
interpretPointed<ShortVar>(location).value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SInt32 : CType<Int>(ffiTypeSInt32()) {
|
||||||
|
override fun read(location: NativePtr) = interpretPointed<IntVar>(location).value
|
||||||
|
override fun write(location: NativePtr, value: Int) {
|
||||||
|
interpretPointed<IntVar>(location).value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SInt64 : CType<Long>(ffiTypeSInt64()) {
|
||||||
|
override fun read(location: NativePtr) = interpretPointed<LongVar>(location).value
|
||||||
|
override fun write(location: NativePtr, value: Long) {
|
||||||
|
interpretPointed<LongVar>(location).value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private object Pointer : CType<CPointer<*>?>(ffiTypePointer()) {
|
||||||
|
override fun read(location: NativePtr) = interpretPointed<CPointerVar<*>>(location).value
|
||||||
|
override fun write(location: NativePtr, value: CPointer<*>?) {
|
||||||
|
interpretPointed<CPointerVar<*>>(location).value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Struct(val size: Long, val align: Int, elementTypes: List<CType<*>>) : CType<CValue<*>>(
|
||||||
|
ffiTypeStruct(
|
||||||
|
elementTypes.map { it.ffiType }
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).readValue<CStructVar>(size, align)
|
||||||
|
|
||||||
|
override fun write(location: NativePtr, value: CValue<*>) {
|
||||||
|
// TODO: probably CValue must be redesigned.
|
||||||
|
val fakePlacement = object : NativePlacement {
|
||||||
|
var used = false
|
||||||
|
override fun alloc(size: Long, align: Int): NativePointed {
|
||||||
|
assert(!used)
|
||||||
|
assert (size == this@Struct.size)
|
||||||
|
assert (align == this@Struct.align)
|
||||||
|
used = true
|
||||||
|
return interpretPointed<ByteVar>(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value.getPointer(fakePlacement)
|
||||||
|
assert (fakePlacement.used)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CEnumType(private val rawValueCType: CType<Number>) : CType<CEnum>(rawValueCType.ffiType) {
|
||||||
|
|
||||||
|
override fun read(location: NativePtr): CEnum {
|
||||||
|
TODO("enum-typed callback parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(location: NativePtr, value: CEnum) {
|
||||||
|
rawValueCType.write(location, value.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private typealias FfiClosureImpl = (ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar>)->Unit
|
||||||
|
private typealias UserData = FfiClosureImpl
|
||||||
|
|
||||||
private fun loadCallbacksLibrary() {
|
private fun loadCallbacksLibrary() {
|
||||||
System.loadLibrary("callbacks")
|
System.loadLibrary("callbacks")
|
||||||
@@ -233,7 +478,7 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_
|
|||||||
return interpretPointed(res)
|
return interpretPointed(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER", "UNUSED")
|
||||||
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
|
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
ffiFunImpl(interpretCPointer(ret)!!,
|
ffiFunImpl(interpretCPointer(ret)!!,
|
||||||
@@ -259,8 +504,8 @@ private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
|
|||||||
*
|
*
|
||||||
* @param ffiCif describes the type of the function to create
|
* @param ffiCif describes the type of the function to create
|
||||||
*/
|
*/
|
||||||
private fun ffiCreateClosure(ffiCif: ffi_cif, userData: UserData): NativePtr {
|
private fun ffiCreateClosure(ffiCif: ffi_cif, impl: FfiClosureImpl): NativePtr {
|
||||||
val res = ffiCreateClosure0(ffiCif.rawPtr, userData)
|
val res = ffiCreateClosure0(ffiCif.rawPtr, userData = impl)
|
||||||
|
|
||||||
when (res) {
|
when (res) {
|
||||||
0L -> throw OutOfMemoryError()
|
0L -> throw OutOfMemoryError()
|
||||||
|
|||||||
@@ -52,17 +52,81 @@ fun <T : CPointed> interpretCPointer(rawValue: NativePtr) =
|
|||||||
CPointer<T>(rawValue)
|
CPointer<T>(rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
|
|
||||||
T::class.objectInstance!!
|
|
||||||
|
|
||||||
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=0x%x)".format(rawValue)
|
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=0x%x)".format(rawValue)
|
||||||
|
|
||||||
/**
|
@Target(AnnotationTarget.PROPERTY)
|
||||||
* Returns a pointer to `T`-typed C function which calls given Kotlin *static* function.
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
* @see CAdaptedFunctionType.fromStatic
|
annotation class CLength(val value: Int)
|
||||||
*/
|
|
||||||
inline fun <reified F : Function<*>, reified T : CAdaptedFunctionType<F>> staticCFunction(body: F): CFunctionPointer<T> {
|
|
||||||
val type = CAdaptedFunctionType.getInstanceOf<T>()
|
|
||||||
return interpretPointed<CFunction<T>>(type.fromStatic(body)).ptr
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class CNaturalStruct(vararg val fieldNames: String)
|
||||||
|
|
||||||
|
fun <R> staticCFunction(function: () -> R): CPointer<CFunction<() -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, R> staticCFunction(function: (P1) -> R): CPointer<CFunction<(P1) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, R> staticCFunction(function: (P1, P2) -> R): CPointer<CFunction<(P1, P2) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, R> staticCFunction(function: (P1, P2, P3) -> R): CPointer<CFunction<(P1, P2, P3) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, R> staticCFunction(function: (P1, P2, P3, P4) -> R): CPointer<CFunction<(P1, P2, P3, P4) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, R> staticCFunction(function: (P1, P2, P3, P4, P5) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|
||||||
|
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>> =
|
||||||
|
staticCFunctionImpl(function)
|
||||||
|
|||||||
@@ -381,43 +381,16 @@ inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Long
|
|||||||
typealias CArrayPointer<T> = CPointer<T>
|
typealias CArrayPointer<T> = CPointer<T>
|
||||||
typealias CArrayPointerVar<T> = CPointerVar<T>
|
typealias CArrayPointerVar<T> = CPointerVar<T>
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of C function.
|
|
||||||
*/
|
|
||||||
interface CFunctionType
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of C function constructed from some Kotlin function, possibly using an adapter.
|
|
||||||
* The (non-abstract) implementation classes are supposed to be object declarations.
|
|
||||||
*/
|
|
||||||
interface CAdaptedFunctionType<F : Function<*>> : CFunctionType {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a raw pointer to C function of this type, which calls given Kotlin *static* function.
|
|
||||||
*
|
|
||||||
* This inconvenient method should not be used directly; use [staticCFunction] instead.
|
|
||||||
*
|
|
||||||
* @param function must be *static*, i.e. an (unbound) reference to a Kotlin function or
|
|
||||||
* a closure which doesn't capture any variable
|
|
||||||
*/
|
|
||||||
fun fromStatic(function: F): NativePtr
|
|
||||||
|
|
||||||
companion object
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The C function.
|
* The C function.
|
||||||
*/
|
*/
|
||||||
class CFunction<T : CFunctionType>(override val rawPtr: NativePtr) : CPointed
|
class CFunction<T : Function<*>>(override val rawPtr: NativePtr) : CPointed
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pointer to [CFunction].
|
* Returns a pointer to C function which calls given Kotlin *static* function.
|
||||||
* TODO: remove.
|
*
|
||||||
|
* @param function must be *static*, i.e. an (unbound) reference to a Kotlin function or
|
||||||
|
* a closure which doesn't capture any variable
|
||||||
*/
|
*/
|
||||||
typealias CFunctionPointer<T> = CPointer<CFunction<T>>
|
@Deprecated("The function type is too general. Supply argument with known arity.", level = DeprecationLevel.ERROR)
|
||||||
|
external fun <F : Function<*>> staticCFunction(function: F): CPointer<CFunction<F>>
|
||||||
/**
|
|
||||||
* The variable containing a [CFunctionPointer].
|
|
||||||
* TODO: remove.
|
|
||||||
*/
|
|
||||||
typealias CFunctionPointerVar<T> = CPointerVarOf<CFunctionPointer<T>>
|
|
||||||
@@ -45,12 +45,3 @@ private external fun disposeStablePointer(pointer: COpaquePointer)
|
|||||||
|
|
||||||
@SymbolName("Kotlin_Interop_derefStablePointer")
|
@SymbolName("Kotlin_Interop_derefStablePointer")
|
||||||
private external fun derefStablePointer(pointer: COpaquePointer): Any
|
private external fun derefStablePointer(pointer: COpaquePointer): Any
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of C function which can be constructed from the appropriate Kotlin function without using any adapter.
|
|
||||||
*/
|
|
||||||
abstract class CTriviallyAdaptedFunctionType<F : Function<*>> : CAdaptedFunctionType<F> {
|
|
||||||
@konan.internal.Intrinsic
|
|
||||||
override final fun fromStatic(function: F): NativePtr =
|
|
||||||
throw Error("CTriviallyAdaptedFunctionType.fromStatic is called virtually")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,21 +34,50 @@ fun <T : CVariable> typeOf(): CVariable.Type = throw Error("typeOf() is called w
|
|||||||
|
|
||||||
@Intrinsic external fun CPointer<*>.getRawValue(): NativePtr
|
@Intrinsic external fun CPointer<*>.getRawValue(): NativePtr
|
||||||
|
|
||||||
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
|
|
||||||
TODO("CAdaptedFunctionType.getInstanceOf 11")
|
|
||||||
|
|
||||||
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=$rawValue)"
|
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=$rawValue)"
|
||||||
|
|
||||||
/**
|
@Intrinsic external fun <R> staticCFunction(function: () -> R): CPointer<CFunction<() -> R>>
|
||||||
* Returns a pointer to `T`-typed C function which calls given Kotlin *static* function.
|
|
||||||
* @see CAdaptedFunctionType.fromStatic
|
|
||||||
*/
|
|
||||||
// TODO: This function is not inline, whereas the same name function in JvmTypes.kt
|
|
||||||
// is inline. We can't make it inline here, because native interop lowering
|
|
||||||
// expects to find and transform it. And native interop lowering is done after
|
|
||||||
// inline expansions.
|
|
||||||
fun <F : Function<*>, T : CAdaptedFunctionType<F>> staticCFunction(body: F): CFunctionPointer<T> {
|
|
||||||
val type = CAdaptedFunctionType.getInstanceOf<T>()
|
|
||||||
return interpretPointed<CFunction<T>>(type.fromStatic(body)).ptr
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, R> staticCFunction(function: (P1) -> R): CPointer<CFunction<(P1) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, R> staticCFunction(function: (P1, P2) -> R): CPointer<CFunction<(P1, P2) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, R> staticCFunction(function: (P1, P2, P3) -> R): CPointer<CFunction<(P1, P2, P3) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, R> staticCFunction(function: (P1, P2, P3, P4) -> R): CPointer<CFunction<(P1, P2, P3, P4) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, R> staticCFunction(function: (P1, P2, P3, P4, P5) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>>
|
||||||
|
|
||||||
|
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>>
|
||||||
+53
-139
@@ -152,15 +152,6 @@ class StubGenerator(
|
|||||||
|
|
||||||
val functionsToBind = nativeIndex.functions.filter { it.name !in excludedFunctions }
|
val functionsToBind = nativeIndex.functions.filter { it.name !in excludedFunctions }
|
||||||
|
|
||||||
private val usedFunctionTypes = mutableMapOf<FunctionType, String>()
|
|
||||||
|
|
||||||
val FunctionType.kotlinName: String
|
|
||||||
get() {
|
|
||||||
return usedFunctionTypes.getOrPut(this, {
|
|
||||||
"CFunctionType" + (usedFunctionTypes.size + 1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private val macroConstantsByName = nativeIndex.macroConstants.associateBy { it.name }
|
private val macroConstantsByName = nativeIndex.macroConstants.associateBy { it.name }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -442,7 +433,7 @@ class StubGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is FunctionType -> byRefTypeMirror("CFunction<${type.kotlinName}>")
|
is FunctionType -> byRefTypeMirror("CFunction<${getKotlinFunctionType(type)}>")
|
||||||
|
|
||||||
is Typedef -> {
|
is Typedef -> {
|
||||||
val baseType = mirror(type.def.aliased)
|
val baseType = mirror(type.def.aliased)
|
||||||
@@ -485,6 +476,17 @@ class StubGenerator(
|
|||||||
* Constructs [OutValueBinding] for the value of given C type.
|
* Constructs [OutValueBinding] for the value of given C type.
|
||||||
*/
|
*/
|
||||||
fun getOutValueBinding(type: Type): OutValueBinding {
|
fun getOutValueBinding(type: Type): OutValueBinding {
|
||||||
|
if (type.unwrapTypedefs() is RecordType) {
|
||||||
|
// TODO: this case should probably be handled more generally.
|
||||||
|
val typeMirror = mirror(type)
|
||||||
|
return OutValueBinding(
|
||||||
|
kotlinType = "CValue<${typeMirror.pointedTypeName}>",
|
||||||
|
kotlinConv = { name -> "$name.getPointer(memScope).rawValue" }, // TODO: eliminate this copying
|
||||||
|
memScoped = true,
|
||||||
|
kotlinJniBridgeType = "NativePtr"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val mirror = mirror(type)
|
val mirror = mirror(type)
|
||||||
|
|
||||||
return OutValueBinding(
|
return OutValueBinding(
|
||||||
@@ -539,16 +541,6 @@ class StubGenerator(
|
|||||||
kotlinJniBridgeType = "NativePtr"
|
kotlinJniBridgeType = "NativePtr"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (type.unwrapTypedefs() is RecordType) {
|
|
||||||
// TODO: this case should probably be handled more generally.
|
|
||||||
val typeMirror = mirror(type)
|
|
||||||
return OutValueBinding(
|
|
||||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>",
|
|
||||||
kotlinConv = { name -> "$name.getPointer(memScope).rawValue" }, // TODO: eliminate this copying
|
|
||||||
memScoped = true,
|
|
||||||
kotlinJniBridgeType = "NativePtr"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return getOutValueBinding(type)
|
return getOutValueBinding(type)
|
||||||
}
|
}
|
||||||
@@ -569,6 +561,15 @@ class StubGenerator(
|
|||||||
* Constructs [InValueBinding] for the value of given C type.
|
* Constructs [InValueBinding] for the value of given C type.
|
||||||
*/
|
*/
|
||||||
fun getInValueBinding(type: Type): InValueBinding {
|
fun getInValueBinding(type: Type): InValueBinding {
|
||||||
|
if (type.unwrapTypedefs() is RecordType) {
|
||||||
|
val typeMirror = mirror(type)
|
||||||
|
return InValueBinding(
|
||||||
|
kotlinJniBridgeType = "NativePtr",
|
||||||
|
conv = { name -> "interpretPointed<${typeMirror.pointedTypeName}>($name).readValue()" },
|
||||||
|
kotlinType = "CValue<${typeMirror.pointedTypeName}>"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val mirror = mirror(type)
|
val mirror = mirror(type)
|
||||||
|
|
||||||
return InValueBinding(
|
return InValueBinding(
|
||||||
@@ -581,15 +582,6 @@ class StubGenerator(
|
|||||||
fun getCFunctionRetValBinding(func: FunctionDecl): InValueBinding {
|
fun getCFunctionRetValBinding(func: FunctionDecl): InValueBinding {
|
||||||
when {
|
when {
|
||||||
func.returnsVoid() -> return InValueBinding("Unit")
|
func.returnsVoid() -> return InValueBinding("Unit")
|
||||||
func.returnsRecord() -> {
|
|
||||||
// TODO: this case should probably be handled more generally.
|
|
||||||
val typeMirror = mirror(func.returnType)
|
|
||||||
return InValueBinding(
|
|
||||||
kotlinJniBridgeType = "NativePtr",
|
|
||||||
conv = { name -> "interpretPointed<${typeMirror.pointedTypeName}>($name).readValue()" },
|
|
||||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return getInValueBinding(func.returnType)
|
return getInValueBinding(func.returnType)
|
||||||
@@ -606,6 +598,23 @@ class StubGenerator(
|
|||||||
getCallbackRetValBinding(type.returnType).kotlinType
|
getCallbackRetValBinding(type.returnType).kotlinType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getArrayLength(type: ArrayType): Long {
|
||||||
|
val unwrappedElementType = type.elemType.unwrapTypedefs()
|
||||||
|
val elementLength = if (unwrappedElementType is ArrayType) {
|
||||||
|
getArrayLength(unwrappedElementType)
|
||||||
|
} else {
|
||||||
|
1L
|
||||||
|
}
|
||||||
|
|
||||||
|
val elementCount = when (type) {
|
||||||
|
is ConstArrayType -> type.length
|
||||||
|
is IncompleteArrayType -> 0L
|
||||||
|
else -> TODO(type.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return elementLength * elementCount
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces to [out] the definition of Kotlin class representing the reference to given struct.
|
* Produces to [out] the definition of Kotlin class representing the reference to given struct.
|
||||||
*/
|
*/
|
||||||
@@ -616,6 +625,12 @@ class StubGenerator(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (platform == KotlinPlatform.JVM) {
|
||||||
|
if (def.hasNaturalLayout) {
|
||||||
|
out("@CNaturalStruct(${def.fields.joinToString { it.name.quoteAsKotlinLiteral() }})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
block("class ${decl.kotlinName.asSimpleName()}(override val rawPtr: NativePtr) : CStructVar()") {
|
block("class ${decl.kotlinName.asSimpleName()}(override val rawPtr: NativePtr) : CStructVar()") {
|
||||||
out("")
|
out("")
|
||||||
out("companion object : Type(${def.size}, ${def.align})") // FIXME: align
|
out("companion object : Type(${def.size}, ${def.align})") // FIXME: align
|
||||||
@@ -628,9 +643,17 @@ class StubGenerator(
|
|||||||
assert(field.offset % 8 == 0L)
|
assert(field.offset % 8 == 0L)
|
||||||
val offset = field.offset / 8
|
val offset = field.offset / 8
|
||||||
val fieldRefType = mirror(field.type)
|
val fieldRefType = mirror(field.type)
|
||||||
if (field.type.unwrapTypedefs() is ArrayType) {
|
val unwrappedFieldType = field.type.unwrapTypedefs()
|
||||||
|
if (unwrappedFieldType is ArrayType) {
|
||||||
val type = (fieldRefType as TypeMirror.ByValue).valueTypeName
|
val type = (fieldRefType as TypeMirror.ByValue).valueTypeName
|
||||||
|
|
||||||
|
if (platform == KotlinPlatform.JVM) {
|
||||||
|
val length = getArrayLength(unwrappedFieldType)
|
||||||
|
|
||||||
|
// TODO: @CLength should probably be used on types instead of properties.
|
||||||
|
out("@CLength($length)")
|
||||||
|
}
|
||||||
|
|
||||||
out("val ${field.name.asSimpleName()}: $type")
|
out("val ${field.name.asSimpleName()}: $type")
|
||||||
out(" get() = arrayMemberAt($offset)")
|
out(" get() = arrayMemberAt($offset)")
|
||||||
} else {
|
} else {
|
||||||
@@ -990,51 +1013,6 @@ class StubGenerator(
|
|||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFfiStructType(elementTypes: List<Type>) =
|
|
||||||
"Struct(" +
|
|
||||||
elementTypes.map { getFfiType(it) }.joinToString(", ") +
|
|
||||||
")"
|
|
||||||
|
|
||||||
private fun getFfiType(type: Type): String {
|
|
||||||
return when(type) {
|
|
||||||
is VoidType -> "Void"
|
|
||||||
is CharType -> "SInt8" // TODO: libffi has separate representation for char type.
|
|
||||||
is IntegerType -> when (type.size) {
|
|
||||||
1 -> if (type.isSigned) "SInt8" else "UInt8"
|
|
||||||
2 -> if (type.isSigned) "SInt16" else "UInt16"
|
|
||||||
4 -> if (type.isSigned) "SInt32" else "UInt32"
|
|
||||||
8 -> if (type.isSigned) "SInt64" else "UInt64"
|
|
||||||
else -> TODO(type.toString())
|
|
||||||
}
|
|
||||||
is PointerType -> "Pointer"
|
|
||||||
is ConstArrayType -> getFfiStructType(
|
|
||||||
Array(type.length.toInt(), { type.elemType }).toList()
|
|
||||||
)
|
|
||||||
is EnumType -> getFfiType(type.def.baseType)
|
|
||||||
is RecordType -> {
|
|
||||||
val def = type.decl.def!!
|
|
||||||
if (!def.hasNaturalLayout) {
|
|
||||||
throw NotImplementedError(type.kotlinName)
|
|
||||||
}
|
|
||||||
getFfiStructType(def.fields.map { it.type })
|
|
||||||
}
|
|
||||||
is Typedef -> getFfiType(type.def.aliased)
|
|
||||||
else -> throw NotImplementedError(type.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getArgFfiType(type: Type) = when (type.unwrapTypedefs()) {
|
|
||||||
is ArrayType -> "Pointer"
|
|
||||||
else -> getFfiType(type)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRetValFfiType(type: Type) = getArgFfiType(type)
|
|
||||||
|
|
||||||
private fun generateFunctionType(type: FunctionType, name: String) = when (platform) {
|
|
||||||
KotlinPlatform.JVM -> generateJvmFunctionType(type, name)
|
|
||||||
KotlinPlatform.NATIVE -> generateNativeFunctionType(type, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns `true` iff the function binding for Kotlin Native
|
* Returns `true` iff the function binding for Kotlin Native
|
||||||
* requires non-trivial Kotlin adapter to convert arguments.
|
* requires non-trivial Kotlin adapter to convert arguments.
|
||||||
@@ -1064,64 +1042,6 @@ class StubGenerator(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FunctionType.requiresAdapterOnNative(): Boolean {
|
|
||||||
assert (platform == KotlinPlatform.NATIVE)
|
|
||||||
return (parameterTypes + returnType).any {
|
|
||||||
val unwrappedType = it.unwrapTypedefs()
|
|
||||||
unwrappedType is RecordType || (unwrappedType is EnumType && unwrappedType.def.isStrictEnum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateNativeFunctionType(type: FunctionType, name: String) {
|
|
||||||
if (type.requiresAdapterOnNative()) {
|
|
||||||
out("object $name : CFunctionType {}")
|
|
||||||
} else {
|
|
||||||
val kotlinFunctionType = getKotlinFunctionType(type)
|
|
||||||
out("object $name : CTriviallyAdaptedFunctionType<$kotlinFunctionType>()")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateJvmFunctionType(type: FunctionType, name: String) {
|
|
||||||
val kotlinFunctionType = getKotlinFunctionType(type)
|
|
||||||
|
|
||||||
val constructorArgs = try {
|
|
||||||
listOf(getRetValFfiType(type.returnType)) +
|
|
||||||
type.parameterTypes.map { getArgFfiType(it) }
|
|
||||||
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
log("Warning: cannot generate definition for function type $name")
|
|
||||||
out("object $name : CFunctionType {}")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val constructorArgsStr = constructorArgs.joinToString(", ")
|
|
||||||
|
|
||||||
block("object $name : CAdaptedFunctionTypeImpl<$kotlinFunctionType>($constructorArgsStr)") {
|
|
||||||
block("override fun invoke(function: $kotlinFunctionType, args: CArrayPointer<COpaquePointerVar>, ret: COpaquePointer)") {
|
|
||||||
val args = type.parameterTypes.mapIndexed { i, paramType ->
|
|
||||||
val pointedTypeName = mirror(paramType).pointedTypeName
|
|
||||||
val ref = "args[$i]!!.reinterpret<$pointedTypeName>().pointed"
|
|
||||||
when (paramType.unwrapTypedefs()) {
|
|
||||||
is RecordType -> ref
|
|
||||||
else -> "$ref.value"
|
|
||||||
}
|
|
||||||
}.joinToString(", ")
|
|
||||||
|
|
||||||
out("val res = function($args)")
|
|
||||||
|
|
||||||
when (type.returnType.unwrapTypedefs()) {
|
|
||||||
is RecordType -> throw NotImplementedError()
|
|
||||||
is VoidType -> {} // nothing to do
|
|
||||||
else -> {
|
|
||||||
val pointedTypeName = mirror(type.returnType).pointedTypeName
|
|
||||||
out("ret.reinterpret<$pointedTypeName>().pointed.value = res")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun integerLiteral(type: Type, value: Long): String? {
|
private fun integerLiteral(type: Type, value: Long): String? {
|
||||||
if (value == Long.MIN_VALUE) {
|
if (value == Long.MIN_VALUE) {
|
||||||
return "${value + 1} - 1" // Workaround for "The value is out of range" compile error.
|
return "${value + 1} - 1" // Workaround for "The value is out of range" compile error.
|
||||||
@@ -1287,12 +1207,6 @@ class StubGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usedFunctionTypes.entries.forEach {
|
|
||||||
stubs.add(
|
|
||||||
generateKotlinFragmentBy { generateFunctionType(it.key, it.value) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stubs
|
return stubs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-14
@@ -144,20 +144,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
|||||||
|
|
||||||
val bitsToDouble = packageScope.getContributedFunctions("bitsToDouble").single()
|
val bitsToDouble = packageScope.getContributedFunctions("bitsToDouble").single()
|
||||||
|
|
||||||
val staticCFunction = packageScope.getContributedFunctions("staticCFunction").single()
|
val staticCFunction = packageScope.getContributedFunctions("staticCFunction").toSet()
|
||||||
|
|
||||||
private val triviallyAdaptedFunctionTypeClass =
|
|
||||||
packageScope.getContributedClassifier("CTriviallyAdaptedFunctionType") as ClassDescriptor
|
|
||||||
|
|
||||||
private val trivallyAdaptedFunctionTypeType =
|
|
||||||
triviallyAdaptedFunctionTypeClass.defaultType.replace(
|
|
||||||
newArguments = listOf(
|
|
||||||
StarProjectionImpl(triviallyAdaptedFunctionTypeClass.declaredTypeParameters.single())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun isTriviallyAdaptedFunctionType(type: KotlinType): Boolean =
|
|
||||||
type.isSubtypeOf(trivallyAdaptedFunctionTypeType)
|
|
||||||
|
|
||||||
val signExtend = packageScope.getContributedFunctions("signExtend").single()
|
val signExtend = packageScope.getContributedFunctions("signExtend").single()
|
||||||
|
|
||||||
|
|||||||
+50
-11
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.lower
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
||||||
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
||||||
import org.jetbrains.kotlin.backend.common.lower.at
|
import org.jetbrains.kotlin.backend.common.lower.at
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
@@ -275,27 +276,42 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
|||||||
builder.readValue(receiver, typeArgument) ?: expression
|
builder.readValue(receiver, typeArgument) ?: expression
|
||||||
}
|
}
|
||||||
|
|
||||||
interop.staticCFunction -> {
|
in interop.staticCFunction -> {
|
||||||
val argument = expression.getValueArgument(0)!!
|
val argument = expression.getValueArgument(0)!!
|
||||||
if (argument !is IrCallableReference || argument.getArguments().isNotEmpty()) {
|
if (argument !is IrCallableReference || argument.getArguments().isNotEmpty()) {
|
||||||
context.reportCompilationError(
|
context.reportCompilationError(
|
||||||
"${interop.staticCFunction.fqNameSafe} must take an unbound, non-capturing function",
|
"${descriptor.fqNameSafe} must take an unbound, non-capturing function",
|
||||||
irFile, expression
|
irFile, expression
|
||||||
)
|
)
|
||||||
// TODO: should probably be reported during analysis.
|
// TODO: should probably be reported during analysis.
|
||||||
}
|
}
|
||||||
|
|
||||||
val cFunctionType = expression.getTypeArgument(descriptor.typeParameters[1])!!
|
val target = argument.descriptor.original
|
||||||
|
val signatureTypes = target.allParameters.map { it.type } + target.returnType!!
|
||||||
|
|
||||||
if (interop.isTriviallyAdaptedFunctionType(cFunctionType)) {
|
signatureTypes.forEachIndexed { index, type ->
|
||||||
IrCallableReferenceImpl(
|
type.ensureSupportedInCallbacks(
|
||||||
builder.startOffset, builder.endOffset,
|
isReturnType = (index == signatureTypes.lastIndex),
|
||||||
expression.type,
|
reportError = { context.reportCompilationError(it, irFile, expression) }
|
||||||
argument.descriptor,
|
)
|
||||||
typeArguments = null)
|
|
||||||
} else {
|
|
||||||
TODO("$cFunctionType requiring non-trivial C adapter")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
descriptor.typeParameters.forEachIndexed { index, typeParameterDescriptor ->
|
||||||
|
val typeArgument = expression.getTypeArgument(typeParameterDescriptor)!!
|
||||||
|
val signatureType = signatureTypes[index]
|
||||||
|
if (typeArgument != signatureType) {
|
||||||
|
context.reportCompilationError(
|
||||||
|
"C function signature element mismatch: expected '$signatureType', got '$typeArgument'",
|
||||||
|
irFile, expression
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IrCallableReferenceImpl(
|
||||||
|
builder.startOffset, builder.endOffset,
|
||||||
|
expression.type,
|
||||||
|
target,
|
||||||
|
typeArguments = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
interop.signExtend, interop.narrow -> {
|
interop.signExtend, interop.narrow -> {
|
||||||
@@ -346,6 +362,29 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KotlinType.ensureSupportedInCallbacks(isReturnType: Boolean, reportError: (String) -> Nothing) {
|
||||||
|
if (isReturnType && KotlinBuiltIns.isUnit(this)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(this)) {
|
||||||
|
if (!this.isMarkedNullable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reportError("Type $this must not be nullable when used in callback signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TypeUtils.getClassDescriptor(this) == interop.cPointer) {
|
||||||
|
if (this.isMarkedNullable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reportError("Type $this must be nullable when used in callback signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
reportError("Type $this is not supported in callback signature")
|
||||||
|
}
|
||||||
|
|
||||||
private fun IrCall.getSingleTypeArgument(): KotlinType {
|
private fun IrCall.getSingleTypeArgument(): KotlinType {
|
||||||
val typeParameter = descriptor.original.typeParameters.single()
|
val typeParameter = descriptor.original.typeParameters.single()
|
||||||
return getTypeArgument(typeParameter)!!
|
return getTypeArgument(typeParameter)!!
|
||||||
|
|||||||
Reference in New Issue
Block a user