Interop: implement new version

This commit is contained in:
Svyatoslav Scherbina
2016-11-22 12:47:57 +07:00
parent 3095044420
commit f0df5f9435
10 changed files with 1188 additions and 167 deletions
@@ -10,8 +10,8 @@ private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
override var def: StructDefImpl? = null
}
private class StructDefImpl(size: Long, decl: StructDecl, hasNaturalLayout: Boolean) :
StructDef(size, decl, hasNaturalLayout) {
private class StructDefImpl(size: Long, align: Int, decl: StructDecl, hasNaturalLayout: Boolean) :
StructDef(size, align, decl, hasNaturalLayout) {
override val fields = mutableListOf<Field>()
}
@@ -38,6 +38,11 @@ private class NativeIndexImpl : NativeIndex() {
override val enums: List<EnumDef>
get() = enumById.values.toList()
val typedefById = mutableMapOf<DeclarationID, TypedefDef>()
override val typedefs: List<TypedefDef>
get() = typedefById.values.toList()
val functionByName = mutableMapOf<String, FunctionDecl>()
override val functions: List<FunctionDecl>
@@ -94,6 +99,31 @@ private class NativeIndexImpl : NativeIndex() {
return res
}
fun getTypedef(type: CXType): Type {
assert (type.kind.value == CXType_Typedef)
val declCursor = clang_getTypeDeclaration(type, arena)
val name = clang_getCursorSpelling(declCursor, arena).convertAndDispose()
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor, arena))
if ((underlying is RecordType && underlying.decl.spelling.split(' ').last() == name) ||
(underlying is EnumType && underlying.def.spelling.split(' ').last() == name)) {
// special handling for:
// typedef struct { ... } name;
// typedef enum { ... } name;
// FIXME: implement better solution
return underlying
}
val declId = getDeclarationId(declCursor)
val typedefDef = typedefById.getOrPut(declId) {
TypedefDef(underlying, name)
}
return Typedef(typedefDef)
}
/**
* Computes [StructDef.hasNaturalLayout] property.
*/
@@ -152,10 +182,7 @@ private class NativeIndexImpl : NativeIndex() {
CXType_LongLong -> Int64Type
CXType_Typedef -> {
val declaration = clang_getTypeDeclaration(type, arena)
val underlying = clang_getTypedefDeclUnderlyingType(declaration, arena)
assert (underlying.kind.value != CXType_Invalid)
convertType(underlying)
getTypedef(type)
}
CXType_Record -> RecordType(getStructTypeDecl(type))
@@ -211,9 +238,11 @@ private class NativeIndexImpl : NativeIndex() {
CXIdxEntity_Struct, CXIdxEntity_Union -> {
val structDecl = getStructDeclAt(cursor)
if (clang_isCursorDefinition(cursor) != 0) {
val size = clang_Type_getSizeOf(clang_getCursorType(cursor, arena))
val type = clang_getCursorType(cursor, arena)
val size = clang_Type_getSizeOf(type)
val align = clang_Type_getAlignOf(clang_getCursorType(cursor, arena)).toInt()
val hasNaturalLayout = structHasNaturalLayout(cursor)
structDecl.def = StructDefImpl(size, structDecl, hasNaturalLayout)
structDecl.def = StructDefImpl(size, align, structDecl, hasNaturalLayout)
}
}
@@ -13,6 +13,7 @@ fun buildNativeIndex(headerFile: File, args: List<String>): NativeIndex = buildN
abstract class NativeIndex {
abstract val structs: List<StructDecl>
abstract val enums: List<EnumDef>
abstract val typedefs: List<TypedefDef>
abstract val functions: List<FunctionDecl>
}
@@ -35,7 +36,9 @@ abstract class StructDecl(val spelling: String) {
* @param hasNaturalLayout must be `false` if the struct has unnatural layout, e.g. it is `packed`.
* May be `false` even if the struct has natural layout.
*/
abstract class StructDef(val size: Long, val decl: StructDecl, val hasNaturalLayout: Boolean) {
abstract class StructDef(val size: Long, val align: Int,
val decl: StructDecl,
val hasNaturalLayout: Boolean) {
abstract val fields: List<Field>
}
@@ -63,6 +66,15 @@ class Parameter(val name: String?, val type: Type)
*/
class FunctionDecl(val name: String, val parameters: List<Parameter>, val returnType: Type)
/**
* C typedef definition.
*
* ```
* typedef $aliased $name;
* ```
*/
class TypedefDef(val aliased: Type, val name: String)
/**
* C type.
@@ -103,4 +115,6 @@ interface ArrayType : Type {
data class ConstArrayType(override val elemType: Type, val length: Long) : ArrayType
data class IncompleteArrayType(override val elemType: Type) : ArrayType
data class Typedef(val def: TypedefDef) : Type
object UnsupportedType : Type
+3
View File
@@ -37,8 +37,11 @@ model {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
sourceSets.main.kotlin.srcDirs += "src/jvm/kotlin"
task nativelibs(type: Copy) {
dependsOn 'callbacksSharedLibrary'
@@ -0,0 +1,262 @@
package kotlin_.cinterop
/**
* 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.
*
* Any [StableObjPtr] should be manually [disposed][dispose]
*/
data class StableObjPtr private constructor(val value: COpaquePointer) {
companion object {
/**
* Creates a handle for given object.
*/
fun create(any: Any) = fromValue(newGlobalRef(any))
private fun fromValue(value: NativePtr) = fromValue(CPointer.create(value))
/**
* Creates [StableObjPtr] from given raw value.
*
* @param value must be a [value] of some [StableObjPtr]
*/
fun fromValue(value: COpaquePointer) = StableObjPtr(value)
init {
loadCallbacksLibrary()
}
}
/**
* Disposes the handle. It must not be [used][get] after that.
*/
fun dispose() {
deleteGlobalRef(value.rawValue)
}
/**
* Returns the object this handle was [created][create] for.
*/
fun get(): Any = derefGlobalRef(value.rawValue)
}
/**
* 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) })
}
}
/**
* 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: CArray<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: CArray<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: CArray<COpaquePointerVar>)->Unit
inline fun <reified T : CAdaptedFunctionTypeImpl<*>> CAdaptedFunctionTypeImpl.Companion.of(): T =
T::class.objectInstance!!
private fun loadCallbacksLibrary() {
System.loadLibrary("callbacks")
}
/**
* Reference to `ffi_type` struct instance.
*/
internal class ffi_type(override val rawPtr: NativePtr) : COpaque
/**
* Reference to `ffi_cif` struct instance.
*/
internal class ffi_cif(override val rawPtr: NativePtr) : COpaque
private external fun ffiTypeVoid(): Long
private external fun ffiTypeUInt8(): Long
private external fun ffiTypeSInt8(): Long
private external fun ffiTypeUInt16(): Long
private external fun ffiTypeSInt16(): Long
private external fun ffiTypeUInt32(): Long
private external fun ffiTypeSInt32(): Long
private external fun ffiTypeUInt64(): Long
private external fun ffiTypeSInt64(): Long
private external fun ffiTypePointer(): Long
private external fun ffiTypeStruct0(elements: Long): Long
/**
* Allocates and initializes `ffi_type` describing the struct.
*
* @param elements types of the struct elements
*/
private fun ffiTypeStruct(elementTypes: List<ffi_type>): ffi_type {
val elements = nativeHeap.allocArrayOfPointersTo(*elementTypes.toTypedArray(), null)
val res = ffiTypeStruct0(elements.rawPtr)
if (res == 0L) {
throw OutOfMemoryError()
}
return interpretPointed(res)
}
private external fun ffiCreateCif0(nArgs: Int, rType: Long, argTypes: Long): Long
/**
* Creates and prepares an `ffi_cif`.
*
* @param returnType native function return value type
* @param paramTypes native function parameter types
*
* @return the initialized `ffi_cif`
*/
private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_cif {
val nArgs = paramTypes.size
val argTypes = nativeHeap.allocArrayOfPointersTo(*paramTypes.toTypedArray(), null)
val res = ffiCreateCif0(nArgs, returnType.rawPtr, argTypes.rawPtr)
when (res) {
0L -> throw OutOfMemoryError()
-1L -> throw Error("FFI_BAD_TYPEDEF")
-2L -> throw Error("FFI_BAD_ABI")
-3L -> throw Error("libffi error occurred")
}
return interpretPointed(res)
}
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
ffiFunImpl(interpretPointed(ffiCif),
CPointer.create(ret),
interpretPointed(args),
userData as UserData)
}
/**
* This function is called from native code when a native function created with [ffiCreateClosure] is invoked.
*
* @param ret pointer to memory to be filled with return value of the invoked native function
* @param args pointer to array of pointers to arguments passed to the invoked native function
*/
private fun ffiFunImpl(ffiCif: ffi_cif, ret: COpaquePointer, args: CArray<COpaquePointerVar>,
userData: UserData) {
userData.invoke(ret, args)
}
private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
/**
* Uses libffi to allocate a native function which will call [ffiFunImpl] when invoked.
*
* @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)
when (res) {
0L -> throw OutOfMemoryError()
-1L -> throw Error("libffi error occurred")
}
return res
}
private external fun newGlobalRef(any: Any): Long
private external fun derefGlobalRef(ref: Long): Any
private external fun deleteGlobalRef(ref: Long)
@@ -0,0 +1,67 @@
package kotlin_.cinterop
import sun.misc.Unsafe
private val NativePointed.address: Long
get() = this.rawPtr
private enum class DataModel(val pointerSize: Long) {
_32BIT(4),
_64BIT(8)
}
private val dataModel: DataModel = when (System.getProperty("sun.arch.data.model")) {
null -> TODO()
"32" -> DataModel._32BIT
"64" -> DataModel._64BIT
else -> throw IllegalStateException()
}
internal val pointerSize: Int = dataModel.pointerSize.toInt()
object nativeMemUtils {
fun getByte(mem: NativePointed) = unsafe.getByte(mem.address)
fun putByte(mem: NativePointed, value: Byte) = unsafe.putByte(mem.address, value)
fun getShort(mem: NativePointed) = unsafe.getShort(mem.address)
fun putShort(mem: NativePointed, value: Short) = unsafe.putShort(mem.address, value)
fun getInt(mem: NativePointed) = unsafe.getInt(mem.address)
fun putInt(mem: NativePointed, value: Int) = unsafe.putInt(mem.address, value)
fun getLong(mem: NativePointed) = unsafe.getLong(mem.address)
fun putLong(mem: NativePointed, value: Long) = unsafe.putLong(mem.address, value)
fun getFloat(mem: NativePointed) = unsafe.getFloat(mem.address)
fun putFloat(mem: NativePointed, value: Float) = unsafe.putFloat(mem.address, value)
fun getDouble(mem: NativePointed) = unsafe.getDouble(mem.address)
fun putDouble(mem: NativePointed, value: Double) = unsafe.putDouble(mem.address, value)
fun getPtr(mem: NativePointed): NativePtr = when (dataModel) {
DataModel._32BIT -> getInt(mem).toLong()
DataModel._64BIT -> getLong(mem)
}
fun putPtr(mem: NativePointed, value: NativePtr) = when (dataModel) {
DataModel._32BIT -> putInt(mem, value.toInt())
DataModel._64BIT -> putLong(mem, value)
}
internal class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
fun alloc(size: Long, align: Int): NativePointed {
val address = unsafe.allocateMemory(size)
if (address % align != 0L) TODO(align.toString())
return interpretPointed<NativeAllocated>(address)
}
fun free(mem: NativePointed) {
unsafe.freeMemory(mem.rawPtr)
}
private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) {
isAccessible = true
return@with this.get(null) as Unsafe
}
}
@@ -0,0 +1,30 @@
package kotlin_.cinterop
import kotlin.reflect.companionObjectInstance
import kotlin.reflect.primaryConstructor
typealias NativePtr = Long
val nativeNullPtr: NativePtr = 0L
// TODO: the functions below should eventually be intrinsified
inline fun <reified T : CVariable> CVariable.Type.Companion.of() = T::class.companionObjectInstance as CVariable.Type
/**
* Returns interpretation of entity with given pointer.
*
* @param T must not be abstract
*/
inline fun <reified T : NativePointed> interpretPointed(ptr: NativePtr): T {
return ensuringNotNull(ptr) {
val kClass = T::class
val primaryConstructor = kClass.primaryConstructor
if (primaryConstructor == null) {
throw IllegalArgumentException("${kClass.simpleName} doesn't have a constructor")
}
(primaryConstructor as (NativePtr) -> T)(ptr)
}
}
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
T::class.objectInstance!!
@@ -0,0 +1,331 @@
package kotlin_.cinterop
/**
* The entity which has an associated native pointer.
* Subtypes are supposed to represent interpretations of the pointed data or code.
*
* This interface is likely to be handled by compiler magic and shouldn't be subtyped by arbitrary classes.
*/
interface NativePointed {
val rawPtr: NativePtr
}
// `null` value of `NativePointed?` is mapped to `nativeNullPtr`.
val NativePointed?.rawPtr: NativePtr
get() = this?.rawPtr ?: nativeNullPtr
/**
* Returns interpretation of entity with given pointer, or `null` if it is null.
*
* @param T must not be abstract
*/
inline fun <reified T : NativePointed> interpretNullablePointed(ptr: NativePtr): T? {
return ifNotNull(ptr) {
interpretPointed<T>(it)
}
}
/**
* Applies the function to the pointer if it is not null, otherwise returns `null`.
*/
inline fun <T> ifNotNull(ptr: NativePtr, function: (NativePtr)->T): T? {
return if (ptr == nativeNullPtr) {
null
} else {
function(ptr)
}
}
/**
* Applies the function to the pointer ensuring that it is not null.
*/
inline fun <T> ensuringNotNull(ptr: NativePtr, function: (NativePtr)->T): T {
if (ptr == nativeNullPtr) {
throw IllegalArgumentException()
} else {
return function(ptr)
}
}
/**
* Changes the interpretation of the pointed data or code.
*/
inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpretPointed(this.rawPtr)
/**
* C data or code.
*/
interface CPointed : NativePointed
/**
* C pointer.
*/
class CPointer<T : CPointed> private constructor(val rawValue: NativePtr) {
companion object {
fun <T : CPointed> create(rawValue: NativePtr) = ensuringNotNull(rawValue) {
CPointer<T>(rawValue)
}
fun <T : CPointed> createNullable(rawValue: NativePtr) = ifNotNull(rawValue) {
CPointer<T>(it)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) {
return true // fast path
}
return (other is CPointer<*>) && (rawValue == other.rawValue)
}
override fun hashCode(): Int {
return rawValue.hashCode()
}
override fun toString(): String {
val hex = "%x".format(rawValue)
return "CPointer(raw=0x$hex)"
}
}
/**
* Returns the pointer to this data or code.
*/
val <T : CPointed> T.ptr: CPointer<T>
get() = CPointer.create(this.rawPtr)
/**
* Returns the corresponding [CPointed].
*
* @param T must not be abstract
*/
inline val <reified T : CPointed> CPointer<T>.pointed: T
get() = interpretPointed<T>(this.rawValue)
// `null` value of `CPointer?` is mapped to `nativeNullPtr`
val CPointer<*>?.rawValue: NativePtr
get() = this?.rawValue ?: nativeNullPtr
fun <T : CPointed> CPointer<*>.reinterpret() = this as CPointer<T>
/**
* The [CPointed] without any specified interpretation.
*/
interface COpaque : CPointed // TODO: should it correspond to COpaquePointer?
/**
* The pointer with an opaque type.
*/
typealias COpaquePointer = CPointer<out CPointed> // FIXME
/**
* The variable containing a [COpaquePointer].
*/
typealias COpaquePointerVar = CPointerVarWithValueMappedTo<COpaquePointer>
/**
* The C data variable located in memory.
*
* The non-abstract subclasses should represent the (complete) C data type and thus specify size and alignment.
* Each such subclass must have a companion object which is a [Type].
*/
interface CVariable : CPointed {
/**
* The (complete) C data type.
*
* @param size the size in bytes of data of this type
* @param align the alignments in bytes that is enough for this data type.
* It may be greater than actually required for simplicity.
*/
open class Type(val size: Long, val align: Int) {
init {
assert (size % align == 0L)
}
companion object
}
companion object {
inline fun <reified T : CVariable> sizeOf() = Type.of<T>().size
inline fun <reified T : CVariable> alignOf() = Type.of<T>().align
}
}
/**
* The C data which is composed from number of members.
*/
interface CAggregate : CPointed
/**
* Returns the member of this [CAggregate] which is located by given offset in bytes.
*/
inline fun <reified T : CPointed> CAggregate.memberAt(offset: Long): T {
return interpretPointed<T>(this.rawPtr + offset)
}
/**
* The C struct-typed variable located in memory.
*/
abstract class CStructVar : CVariable, CAggregate {
open class Type(size: Long, align: Int) : CVariable.Type(size, align)
}
/**
* The C primitive-typed variable located in memory.
*/
sealed class CPrimitiveVar : CVariable {
// aligning by size is obviously enough
open class Type(size: Int, align: Int = size) : CVariable.Type(size.toLong(), align)
}
abstract class CEnumVar : CPrimitiveVar()
// generics below are used for typedef support
// these classes are not supposed to be used directly, instead the typealiases are provided.
class CInt8VarWithValueMappedTo<T : Byte>(override val rawPtr: NativePtr) : CPrimitiveVar() {
companion object : Type(1)
}
class CInt16VarWithValueMappedTo<T : Short>(override val rawPtr: NativePtr) : CPrimitiveVar() {
companion object : Type(2)
}
class CInt32VarWithValueMappedTo<T : Int>(override val rawPtr: NativePtr) : CPrimitiveVar() {
companion object : Type(4)
}
class CInt64VarWithValueMappedTo<T : Long>(override val rawPtr: NativePtr) : CPrimitiveVar() {
companion object : Type(8)
}
class CFloat32VarWithValueMappedTo<T : Float>(override val rawPtr: NativePtr) : CPrimitiveVar() {
companion object : Type(4)
}
class CFloat64VarWithValueMappedTo<T : Double>(override val rawPtr: NativePtr) : CPrimitiveVar() {
companion object : Type(8)
}
typealias CInt8Var = CInt8VarWithValueMappedTo<Byte>
typealias CInt16Var = CInt16VarWithValueMappedTo<Short>
typealias CInt32Var = CInt32VarWithValueMappedTo<Int>
typealias CInt64Var = CInt64VarWithValueMappedTo<Long>
typealias CFloat32Var = CFloat32VarWithValueMappedTo<Float>
typealias CFloat64Var = CFloat64VarWithValueMappedTo<Double>
var <T : Byte> CInt8VarWithValueMappedTo<T>.value: T
get() = nativeMemUtils.getByte(this) as T
set(value) = nativeMemUtils.putByte(this, value)
var <T : Short> CInt16VarWithValueMappedTo<T>.value: T
get() = nativeMemUtils.getShort(this) as T
set(value) = nativeMemUtils.putShort(this, value)
var <T : Int> CInt32VarWithValueMappedTo<T>.value: T
get() = nativeMemUtils.getInt(this) as T
set(value) = nativeMemUtils.putInt(this, value)
var <T : Long> CInt64VarWithValueMappedTo<T>.value: T
get() = nativeMemUtils.getLong(this) as T
set(value) = nativeMemUtils.putLong(this, value)
// TODO: ensure native floats have the appropriate binary representation
var <T : Float> CFloat32VarWithValueMappedTo<T>.value: T
get() = nativeMemUtils.getFloat(this) as T
set(value) = nativeMemUtils.putFloat(this, value)
var <T : Double> CFloat64VarWithValueMappedTo<T>.value: T
get() = nativeMemUtils.getDouble(this) as T
set(value) = nativeMemUtils.putDouble(this, value)
class CPointerVarWithValueMappedTo<T : CPointer<*>>(override val rawPtr: NativePtr) : CVariable {
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
}
/**
* The C data variable containing the pointer to `T`.
*/
typealias CPointerVar<T> = CPointerVarWithValueMappedTo<CPointer<T>>
/**
* The value of this variable.
*/
inline var <reified P : CPointer<*>> CPointerVarWithValueMappedTo<P>.value: P?
get() = CPointer.createNullable<CPointed>(nativeMemUtils.getPtr(this)) as P?
set(value) = nativeMemUtils.putPtr(this, value.rawValue)
/**
* The code or data pointed by the value of this variable.
*
* @param T must not be abstract
*/
inline var <reified T : CPointed, reified P : CPointer<T>> CPointerVarWithValueMappedTo<P>.pointed: T?
get() = this.value?.pointed
set(value) {
this.value = value?.ptr as P?
}
class CArray<T : CVariable>(override val rawPtr: NativePtr) : CAggregate
inline fun <reified T : CVariable> CArray<T>.elementOffset(index: Long) = if (index == 0L) {
0L // optimization for JVM impl which uses reflection for now.
} else {
index * CVariable.sizeOf<T>()
}
inline operator fun <reified T : CVariable> CArray<T>.get(index: Long): T = memberAt(elementOffset(index))
inline operator fun <reified T : CVariable> CArray<T>.get(index: Int) = this.get(index.toLong())
/**
* 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
}
/**
* 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
}
/**
* The C function.
*/
class CFunction<T : CFunctionType>(override val rawPtr: NativePtr) : CPointed
/**
* The pointer to [CFunction].
*/
typealias CFunctionPointer<T> = CPointer<CFunction<T>>
/**
* The variable containing a [CFunctionPointer].
*/
typealias CFunctionPointerVar<T> = CPointerVarWithValueMappedTo<CFunctionPointer<T>>
@@ -0,0 +1,222 @@
package kotlin_.cinterop
interface NativePlacement {
fun alloc(size: Long, align: Int): NativePointed
fun alloc(size: Int, align: Int) = alloc(size.toLong(), align)
}
interface NativeFreeablePlacement : NativePlacement {
fun free(mem: NativePointed)
}
object nativeHeap : NativeFreeablePlacement {
override fun alloc(size: Long, align: Int) = nativeMemUtils.alloc(size, align)
override fun free(mem: NativePointed) = nativeMemUtils.free(mem)
}
// TODO: implement optimally
class Arena(private val parent: NativeFreeablePlacement = nativeHeap) : NativePlacement {
private val allocatedChunks = mutableListOf<NativePointed>()
override fun alloc(size: Long, align: Int): NativePointed {
val res = nativeHeap.alloc(size, align)
try {
allocatedChunks.add(res)
return res
} catch (e: Throwable) {
nativeHeap.free(res)
throw e
}
}
fun clear() {
allocatedChunks.forEach {
nativeHeap.free(it)
}
allocatedChunks.clear()
}
}
fun NativePlacement.alloc(size: Int, align: Int) = alloc(size.toLong(), align)
/**
* Allocates variable of given type.
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.alloc(): T =
alloc(CVariable.sizeOf<T>(), CVariable.alignOf<T>()).reinterpret()
/**
* Allocates C array of given elements type and length.
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long): CArray<T> =
alloc(CVariable.sizeOf<T>() * length, CVariable.alignOf<T>()).reinterpret()
/**
* Allocates C array of given elements type and length.
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int): CArray<T> =
allocArray(length.toLong())
/**
* Allocates C array of given elements type and length, and initializes its elements applying given block.
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long,
initializer: T.(Long)->Unit): CArray<T> {
val res = allocArray<T>(length)
(0 until length).forEach {
res[it].initializer(it)
}
return res
}
/**
* Allocates C array of given elements type and length, and initializes its elements applying given block.
*
* @param T must not be abstract
*/
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int, initializer: T.(Long)->Unit) =
allocArray(length.toLong(), initializer)
/**
* Allocates C array of pointers to given elements.
*/
fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(elements: List<T?>): CArray<CPointerVar<T>> {
val res = allocArray<CPointerVar<T>>(elements.size)
elements.forEachIndexed { index, value ->
res[index].value = value?.ptr
}
return res
}
/**
* Allocates C array of pointers to given elements.
*/
fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(vararg elements: T?) =
allocArrayOfPointersTo(elements.toList())
/**
* Allocates C array of given values.
*/
inline fun <reified T : CPointer<*>>
NativePlacement.allocArrayOf(vararg elements: T?): CArray<CPointerVarWithValueMappedTo<T>> {
return allocArrayOf(elements.toList())
}
/**
* Allocates C array of given values.
*/
inline fun <reified T : CPointer<*>>
NativePlacement.allocArrayOf(elements: List<T?>): CArray<CPointerVarWithValueMappedTo<T>> {
val res = allocArray<CPointerVarWithValueMappedTo<T>>(elements.size)
elements.forEachIndexed { index, value ->
res[index].value = value
}
return res
}
fun NativePlacement.allocArrayOf(elements: ByteArray): CArray<CInt8Var> {
val res = allocArray<CInt8Var>(elements.size)
elements.forEachIndexed { i, byte ->
res[i].value = byte
}
return res
}
fun <T : CPointed> NativePlacement.allocPointerTo() = alloc<CPointerVar<T>>()
/**
* The zero-terminated string.
*/
class CString private constructor(override val rawPtr: NativePtr) : CPointed {
companion object {
fun fromArray(array: CArray<CInt8Var>) = CString(array.rawPtr)
}
fun length(): Int {
val array = reinterpret<CArray<CInt8Var>>()
var res = 0
while (array[res].value != 0.toByte()) {
++res
}
return res
}
override fun toString(): String {
val array = reinterpret<CArray<CInt8Var>>()
val bytes = ByteArray(this.length())
bytes.forEachIndexed { i, byte ->
bytes[i] = array[i].value
}
return String(bytes) // TODO: encoding
}
fun asCharPtr() = reinterpret<CInt8Var>()
}
fun CString.Companion.fromString(str: String?, placement: NativePlacement): CString? {
if (str == null) {
return null
}
val bytes = str.toByteArray() // TODO: encoding
val len = bytes.size
val nativeBytes = nativeHeap.allocArray<CInt8Var>(len + 1)
bytes.forEachIndexed { i, byte ->
nativeBytes[i].value = byte
}
nativeBytes[len].value = 0
return CString.fromArray(nativeBytes)
}
fun CPointer<CInt8Var>.asCString() = CString.fromArray(this.reinterpret<CArray<CInt8Var>>().pointed)
fun String.toCString(placement: NativePlacement) = CString.fromString(this, placement)
class MemScope private constructor(private val arena: Arena) : NativePlacement by arena {
val memScope: NativePlacement
get() = this
companion object {
internal inline fun <R> use(block: MemScope.()->R): R {
val memScope = MemScope(Arena())
try {
return memScope.block()
} finally {
memScope.arena.clear()
}
}
}
}
/**
* Runs given [block] providing allocation of memory
* which will be automatically disposed at the end of this scope.
*/
inline fun <R> memScoped(block: MemScope.()->R): R {
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") // TODO: it is a hack
return MemScope.use(block)
}
@@ -0,0 +1,4 @@
/**
* TODO: rename to kotlin
*/
package kotlin_;
@@ -72,7 +72,7 @@ class StubGenerator(
val FunctionType.kotlinName: String
get() {
return usedFunctionTypes.getOrPut(this, {
"NativeFunctionType" + (usedFunctionTypes.size + 1)
"CFunctionType" + (usedFunctionTypes.size + 1)
})
}
@@ -156,6 +156,8 @@ class StubGenerator(
this.def.spelling
}
is Typedef -> this.def.name
else -> throw kotlin.NotImplementedError()
}
}
@@ -173,55 +175,169 @@ class StubGenerator(
}
/**
* Describes the Kotlin native reference type
*
* @param typeName the name of the Kotlin native reference type
* @param typeExpr such Kotlin expression that `typeExpr(ptr)` constructs the reference of this type
* to the value located at `ptr`
* Describes the Kotlin types used to represent some C type.
*/
class NativeRefType(val typeName: String, val typeExpr: String = typeName)
private sealed class TypeMirror(val pointedTypeName: String, val info: TypeInfo) {
/**
* Type to be used in bindings for argument or return value.
*/
abstract val argType: String
/**
* Mirror for C type to be represented in Kotlin as by-value type.
*/
class ByValue(pointedTypeName: String, info: TypeInfo, val valueTypeName: String) :
TypeMirror(pointedTypeName, info) {
override val argType: String
get() = valueTypeName + if (info is TypeInfo.Pointer) "?" else ""
}
/**
* Mirror for C type to be represented in Kotlin as by-ref type.
*/
class ByRef(pointedTypeName: String, info: TypeInfo) : TypeMirror(pointedTypeName, info) {
override val argType: String
get() = pointedTypeName
}
}
/**
* Returns the Kotlin type which describes the reference to value of given (C) type.
* Describes various type conversions for [TypeMirror].
*/
fun getKotlinTypeForRefTo(type: Type): NativeRefType = when (type) {
is Int8Type, is UInt8Type -> NativeRefType("Int8Box")
is Int16Type, is UInt16Type -> NativeRefType("Int16Box")
is Int32Type, is UInt32Type -> NativeRefType("Int32Box")
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
is Int64Type, is UInt64Type -> NativeRefType("Int64Box")
private sealed class TypeInfo {
/**
* The conversion from [TypeMirror.argType] to [jniType].
*/
abstract fun argToJni(name: String): String
is RecordType -> NativeRefType("${type.kotlinName}")
/**
* The conversion from [jniType] to [TypeMirror.argType].
*/
abstract fun argFromJni(name: String): String
/**
* The type to be used for passing [TypeMirror.argType] through JNI.
*/
abstract val jniType: String
/**
* If this info is for [TypeMirror.ByValue], then this method describes how to
* construct pointed-type from value type.
*/
abstract fun constructPointedType(valueType: String): String
class Primitive(override val jniType: String, val varTypeName: String) : TypeInfo() {
override fun argToJni(name: String) = name
override fun argFromJni(name: String) = name
override fun constructPointedType(valueType: String) = "${varTypeName}WithValueMappedTo<$valueType>"
}
class Enum(val className: String, val baseType: String) : TypeInfo() {
override fun argToJni(name: String) = "$name.value"
override fun argFromJni(name: String) = "$className.byValue($name)"
override val jniType: String
get() = baseType
override fun constructPointedType(valueType: String) = "$className.Var" // TODO: improve
}
class Pointer(val pointee: String) : TypeInfo() {
override fun argToJni(name: String) = "$name.rawValue"
override fun argFromJni(name: String) = "CPointer.createNullable<$pointee>($name)"
override val jniType: String
get() = "Long"
override fun constructPointedType(valueType: String) = "CPointerVarWithValueMappedTo<$valueType>"
}
class ByRef(val pointed: String) : TypeInfo() {
override fun argToJni(name: String) = "$name.rawPtr"
override fun argFromJni(name: String) = "interpretPointed<$pointed>($name)"
override val jniType: String
get() = "Long"
override fun constructPointedType(valueType: String): String {
// TODO: this method must not exist
throw UnsupportedOperationException()
}
}
}
private fun mirror(type: PrimitiveType): TypeMirror {
val varTypeName = when (type) {
is Int8Type, is UInt8Type -> "CInt8Var"
is Int16Type, is UInt16Type -> "CInt16Var"
is Int32Type, is UInt32Type -> "CInt32Var"
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
is Int64Type, is UInt64Type -> "CInt64Var"
else -> TODO(type.toString())
}
val info = TypeInfo.Primitive(type.kotlinType, varTypeName)
return TypeMirror.ByValue(varTypeName, info, type.kotlinType)
}
private fun byRefTypeMirror(pointedTypeName: String) : TypeMirror.ByRef {
val info = TypeInfo.ByRef(pointedTypeName)
return TypeMirror.ByRef(pointedTypeName, info)
}
private fun mirror(type: Type): TypeMirror = when (type) {
is PrimitiveType -> mirror(type)
is RecordType -> byRefTypeMirror(type.kotlinName)
is EnumType -> if (type.isKotlinEnum) {
NativeRefType("${type.kotlinName}.ref")
val className = type.kotlinName
val info = TypeInfo.Enum(className, type.def.baseType.kotlinType)
TypeMirror.ByValue("$className.Var", info, className)
} else {
getKotlinTypeForRefTo(type.def.baseType)
mirror(type.def.baseType)
}
is PointerType -> {
val pointeeType = type.pointeeType
if (pointeeType is VoidType) {
NativeRefType("NativePtrBox")
val info = TypeInfo.Pointer("COpaque")
TypeMirror.ByValue("COpaquePointerVar", info, "COpaquePointer")
} else if (pointeeType is FunctionType) {
NativeRefType("NativeFunctionBox<${getKotlinFunctionType(pointeeType)}>", "${pointeeType.kotlinName}.ref")
val kotlinName = pointeeType.kotlinName
val info = TypeInfo.Pointer("CFunction<$kotlinName>")
TypeMirror.ByValue("CFunctionPointerVar<$kotlinName>", info, "CFunctionPointer<$kotlinName>")
} else {
val pointeeRefType = getKotlinTypeForRefTo(pointeeType)
NativeRefType("RefBox<${pointeeRefType.typeName}>", "${pointeeRefType.typeExpr}.ref")
val pointeeMirror = mirror(pointeeType)
val info = TypeInfo.Pointer(pointeeMirror.pointedTypeName)
TypeMirror.ByValue("CPointerVar<${pointeeMirror.pointedTypeName}>", info,
"CPointer<${pointeeMirror.pointedTypeName}>")
}
}
is ConstArrayType -> {
val elemRefType = getKotlinTypeForRefTo(type.elemType)
NativeRefType("NativeArray<${elemRefType.typeName}>", "array[${type.length}](${elemRefType.typeExpr})")
is ArrayType -> {
val elemMirror = mirror(type.elemType)
byRefTypeMirror("CArray<${elemMirror.pointedTypeName}>")
}
is IncompleteArrayType -> {
val elemRefType = getKotlinTypeForRefTo(type.elemType)
NativeRefType("NativeArray<${elemRefType.typeName}>", "array(${elemRefType.typeExpr})")
is Typedef -> {
val baseType = mirror(type.def.aliased)
val name = type.def.name
when (baseType) {
is TypeMirror.ByValue -> TypeMirror.ByValue("${name}Var", baseType.info, name)
is TypeMirror.ByRef -> TypeMirror.ByRef(name, baseType.info)
}
}
else -> throw NotImplementedError()
else -> TODO(type.toString())
}
/**
@@ -248,85 +364,36 @@ class StubGenerator(
val conv: ((String) -> String) = { it },
val kotlinType: String = kotlinJniBridgeType)
/**
* Constructs [OutValueBinding] for the value represented by given native reference type in Kotlin.
*/
fun outValueRefBinding(refType: NativeRefType) = OutValueBinding(
kotlinType = refType.typeName + "?",
kotlinConv = { "$it.getNativePtr().asLong()" },
kotlinJniBridgeType = "Long"
)
/**
* Constructs [InValueBinding] for the value represented by given native reference type in Kotlin.
*/
fun inValueRefBinding(refType: NativeRefType) = InValueBinding(
kotlinJniBridgeType = "Long",
conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})" },
kotlinType = refType.typeName + "?"
)
/**
* Constructs [OutValueBinding] for the value of given C type.
*/
fun getOutValueBinding(type: Type): OutValueBinding = when (type) {
is PrimitiveType -> OutValueBinding(type.kotlinType)
is PointerType -> {
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
OutValueBinding(
kotlinType = "NativePtr?",
kotlinConv = { "$it.asLong()" },
kotlinJniBridgeType = "Long"
)
} else {
outValueRefBinding(getKotlinTypeForRefTo(type.pointeeType))
}
fun getOutValueBinding(type: Type): OutValueBinding {
if (type is ArrayType) {
// array-typed C function argument or return value is actually a pointer to array.
return getOutValueBinding(PointerType(type))
}
is EnumType -> if (type.isKotlinEnum) {
OutValueBinding(
kotlinType = type.kotlinName,
kotlinConv = { "$it.value" },
kotlinJniBridgeType = type.def.baseType.kotlinType
)
} else {
getOutValueBinding(type.def.baseType)
}
val mirror = mirror(type)
is ArrayType -> outValueRefBinding(getKotlinTypeForRefTo(type))
else -> throw NotImplementedError()
return OutValueBinding(
kotlinType = mirror.argType,
kotlinConv = mirror.info::argToJni,
kotlinJniBridgeType = mirror.info.jniType
)
}
fun getCFunctionParamBinding(type: Type): OutValueBinding {
when (type) {
is PointerType -> {
val pointeeType = type.pointeeType
when (pointeeType) {
is FunctionType -> return OutValueBinding(
kotlinType = "(" + getKotlinFunctionType(pointeeType) + ")?",
kotlinConv = { "$it?.staticAsNative(${pointeeType.kotlinName}).asLong()" },
kotlinJniBridgeType = "Long"
)
when (type.pointeeType) {
is Int8Type -> return OutValueBinding(
kotlinType = "String?",
kotlinConv = { name -> "$name?.toCString(memScope).getNativePtr().asLong()" },
kotlinConv = { name -> "$name?.toCString(memScope).rawPtr" },
memScoped = true,
kotlinJniBridgeType = "Long"
)
}
}
is RecordType -> {
val refType = getKotlinTypeForRefTo(type)
// pointer will be converted to value in C code
return OutValueBinding(
kotlinType = refType.typeName,
kotlinConv = { "$it.getNativePtr().asLong()" },
kotlinJniBridgeType = "Long"
)
}
}
return getOutValueBinding(type)
@@ -347,65 +414,30 @@ class StubGenerator(
/**
* Constructs [InValueBinding] for the value of given C type.
*/
fun getInValueBinding(type: Type): InValueBinding = when (type) {
is PrimitiveType -> InValueBinding(type.kotlinType)
is PointerType -> {
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
InValueBinding(
kotlinJniBridgeType = "Long",
conv = { "NativePtr.byValue($it)" },
kotlinType = "NativePtr?"
)
} else {
inValueRefBinding(getKotlinTypeForRefTo(type.pointeeType))
}
fun getInValueBinding(type: Type): InValueBinding {
if (type is ArrayType) {
// array-typed C function argument or return value is actually a pointer to array.
return getInValueBinding(PointerType(type))
}
is EnumType -> if (type.isKotlinEnum) {
InValueBinding(
kotlinJniBridgeType = type.def.baseType.kotlinType,
conv = { "${type.kotlinName}.byValue($it)" },
kotlinType = type.kotlinName
)
} else {
getInValueBinding(type.def.baseType)
}
val mirror = mirror(type)
is ArrayType -> inValueRefBinding(getKotlinTypeForRefTo(type))
else -> throw NotImplementedError()
return InValueBinding(
kotlinJniBridgeType = mirror.info.jniType,
conv = mirror.info::argFromJni,
kotlinType = mirror.argType
)
}
fun getCFunctionRetValBinding(type: Type): InValueBinding {
when (type) {
is VoidType -> return InValueBinding("Unit")
is RecordType -> {
val refType = getKotlinTypeForRefTo(type)
return InValueBinding(
kotlinJniBridgeType = "Long",
conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})!!" },
kotlinType = refType.typeName
)
}
}
return getInValueBinding(type)
}
fun getCallbackParamBinding(type: Type): InValueBinding {
when (type) {
is RecordType -> {
val refType = getKotlinTypeForRefTo(type)
return InValueBinding(
kotlinJniBridgeType = "Long",
conv = { throw UnsupportedOperationException() },
kotlinType = refType.typeName
)
}
}
return getInValueBinding(type)
}
@@ -427,17 +459,19 @@ class StubGenerator(
}
val className = decl.kotlinName
block("class $className(ptr: NativePtr) : NativeStruct(ptr)") {
block("class $className(override val rawPtr: NativePtr) : CStructVar()") {
out("")
out("companion object : Type<$className>(${def.size}, ::$className)")
out("companion object : Type(${def.size}, ${def.align})") // FIXME: align
out("")
def.fields.forEach { field ->
try {
if (field.offset < 0) throw NotImplementedError();
assert(field.offset % 8 == 0L)
val offset = field.offset / 8
val fieldRefType = getKotlinTypeForRefTo(field.type)
out("val ${field.name} by ${fieldRefType.typeExpr} at $offset")
val fieldRefType = mirror(field.type)
out("val ${field.name}: ${fieldRefType.pointedTypeName}")
out(" get() = memberAt($offset)")
out("")
} catch (e: Throwable) {
println("Warning: cannot generate definition for field $className.${field.name}")
}
@@ -450,9 +484,7 @@ class StubGenerator(
*/
private fun generateForwardStruct(s: StructDecl) {
val className = s.kotlinName
block("class $className(ptr: NativePtr) : NativeRef(ptr)") {
out("companion object : Type<$className>(::$className)")
}
out("class $className(override val rawPtr: NativePtr) : COpaque")
}
/**
@@ -464,7 +496,7 @@ class StubGenerator(
return
}
val baseRefType = getKotlinTypeForRefTo(e.baseType)
val baseTypeMirror = mirror(e.baseType)
block("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType})") {
e.values.forEach {
@@ -476,11 +508,11 @@ class StubGenerator(
out("fun byValue(value: ${e.baseType.kotlinType}) = ${e.kotlinName}.values().find { it.value == value }!!")
}
out("")
block("class ref(ptr: NativePtr) : NativeRef(ptr)") {
out("companion object : TypeWithSize<ref>(${baseRefType.typeExpr}.size, ::ref)")
block("class Var(override val rawPtr: NativePtr) : CEnumVar()") {
out("companion object : Type(${baseTypeMirror.pointedTypeName}.size.toInt())")
out("var value: ${e.kotlinName}")
out(" get() = byValue(${baseRefType.typeExpr}.byPtr(ptr).value)")
out(" set(value) { ${baseRefType.typeExpr}.byPtr(ptr).value = value.value }")
out(" get() = byValue(this.reinterpret<${baseTypeMirror.pointedTypeName}>().value)")
out(" set(value) { this.reinterpret<${baseTypeMirror.pointedTypeName}>().value = value.value }")
}
}
}
@@ -504,6 +536,23 @@ class StubGenerator(
}
}
private fun generateTypedef(def: TypedefDef) {
val mirror = mirror(Typedef(def))
val baseMirror = mirror(def.aliased)
when (baseMirror) {
is TypeMirror.ByValue -> {
val name = def.name
val varTypeName = mirror.info.constructPointedType(name)
out("typealias ${mirror.pointedTypeName} = $varTypeName")
out("typealias ${(mirror as TypeMirror.ByValue).valueTypeName} = ${baseMirror.valueTypeName}")
}
is TypeMirror.ByRef -> {
out("typealias ${mirror.pointedTypeName} = ${baseMirror.pointedTypeName}")
}
}
}
/**
* Constructs [InValueBinding] for return value of Kotlin binding for given C function.
*/
@@ -519,12 +568,11 @@ class StubGenerator(
val retValType = func.returnType
if (retValType is RecordType) {
val retValRefType = getKotlinTypeForRefTo(retValType)
val typeExpr = retValRefType.typeExpr
val retValMirror = mirror(retValType)
paramBindings.add(OutValueBinding(
kotlinType = "Placement",
kotlinConv = { name -> "$name.alloc($typeExpr.size).asLong()" },
kotlinType = "NativePlacement",
kotlinConv = { name -> "$name.alloc<${retValMirror.pointedTypeName}>().rawPtr" },
kotlinJniBridgeType = "Long"
))
}
@@ -653,13 +701,13 @@ class StubGenerator(
val constructorArgsStr = constructorArgs.joinToString(", ")
block("object $name : NativeFunctionType<$kotlinFunctionType>($constructorArgsStr)") {
block("override fun invoke(function: $kotlinFunctionType, args: NativeArray<NativePtrBox>, ret: NativePtr)") {
block("object $name : CAdaptedFunctionTypeImpl<$kotlinFunctionType>($constructorArgsStr)") {
block("override fun invoke(function: $kotlinFunctionType, args: CArray<COpaquePointerVar>, ret: COpaquePointer)") {
val args = type.parameterTypes.mapIndexed { i, paramType ->
val refType = getKotlinTypeForRefTo(paramType)
val ref = "args[$i].value.asRef(${refType.typeExpr})!!"
val pointedTypeName = mirror(paramType).pointedTypeName
val ref = "args[$i].value!!.reinterpret<$pointedTypeName>().pointed"
when (paramType) {
is RecordType -> "$ref"
is RecordType -> ref
else -> "$ref.value"
}
}.joinToString(", ")
@@ -670,8 +718,8 @@ class StubGenerator(
is RecordType -> throw NotImplementedError()
is VoidType -> {} // nothing to do
else -> {
val retRefType = getKotlinTypeForRefTo(type.returnType)
out("${retRefType.typeExpr}.byPtr(ret).value = res")
val pointedTypeName = mirror(type.returnType).pointedTypeName
out("ret.reinterpret<$pointedTypeName>().pointed.value = res")
}
}
@@ -702,7 +750,7 @@ class StubGenerator(
out("package $pkgName")
out("")
}
out("import kotlin_native.interop.*")
out("import kotlin_.cinterop.*")
out("")
functionsToBind.forEach {
@@ -732,6 +780,17 @@ class StubGenerator(
out("")
}
nativeIndex.typedefs.forEach { t ->
try {
transaction {
generateTypedef(t)
out("")
}
} catch (e: Throwable) {
println("Warning: cannot generate typedef ${t.name}")
}
}
usedFunctionTypes.entries.forEach {
generateFunctionType(it.key, it.value)
out("")