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:
Svyatoslav Scherbina
2017-04-20 12:31:53 +03:00
committed by SvyatoslavScherbina
parent cc73c7e009
commit 3f84ba462f
12 changed files with 2532 additions and 1733 deletions
@@ -16,6 +16,15 @@
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.
* 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) {
}
/**
* Describes the type of C function with adapter for Kotlin functions.
*
* The instances of this class are supposed to be Kotlin object declarations (singletons),
* 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) })
}
private fun getFieldCType(type: KType): CType<*> {
val classifier = type.classifier
if (classifier is KClass<*> && classifier.isSubclassOf(CStructVar::class)) {
return getStructCType(classifier)
}
/**
* 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>()
return getArgOrRetValCType(type)
}
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() {
System.loadLibrary("callbacks")
@@ -233,7 +478,7 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_
return interpretPointed(res)
}
@Suppress("UNUSED_PARAMETER")
@Suppress("UNUSED_PARAMETER", "UNUSED")
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
@Suppress("UNCHECKED_CAST")
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
*/
private fun ffiCreateClosure(ffiCif: ffi_cif, userData: UserData): NativePtr {
val res = ffiCreateClosure0(ffiCif.rawPtr, userData)
private fun ffiCreateClosure(ffiCif: ffi_cif, impl: FfiClosureImpl): NativePtr {
val res = ffiCreateClosure0(ffiCif.rawPtr, userData = impl)
when (res) {
0L -> throw OutOfMemoryError()
@@ -52,17 +52,81 @@ fun <T : CPointed> interpretCPointer(rawValue: NativePtr) =
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)
/**
* Returns a pointer to `T`-typed C function which calls given Kotlin *static* function.
* @see CAdaptedFunctionType.fromStatic
*/
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.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class CLength(val value: Int)
@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 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.
*/
class CFunction<T : CFunctionType>(override val rawPtr: NativePtr) : CPointed
class CFunction<T : Function<*>>(override val rawPtr: NativePtr) : CPointed
/**
* The pointer to [CFunction].
* TODO: remove.
* Returns a pointer to C function which calls given Kotlin *static* function.
*
* @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>>
/**
* The variable containing a [CFunctionPointer].
* TODO: remove.
*/
typealias CFunctionPointerVar<T> = CPointerVarOf<CFunctionPointer<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>>
@@ -45,12 +45,3 @@ private external fun disposeStablePointer(pointer: COpaquePointer)
@SymbolName("Kotlin_Interop_derefStablePointer")
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
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
TODO("CAdaptedFunctionType.getInstanceOf 11")
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=$rawValue)"
/**
* 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 <R> staticCFunction(function: () -> R): CPointer<CFunction<() -> R>>
@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>>