diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt index 86dcdb37ad4..4db3c86e6db 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt @@ -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() } @@ -38,6 +38,11 @@ private class NativeIndexImpl : NativeIndex() { override val enums: List get() = enumById.values.toList() + val typedefById = mutableMapOf() + + override val typedefs: List + get() = typedefById.values.toList() + val functionByName = mutableMapOf() override val functions: List @@ -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) } } diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt index 6390ddd6d89..73681c415ef 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt @@ -13,6 +13,7 @@ fun buildNativeIndex(headerFile: File, args: List): NativeIndex = buildN abstract class NativeIndex { abstract val structs: List abstract val enums: List + abstract val typedefs: List abstract val functions: List } @@ -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 } @@ -63,6 +66,15 @@ class Parameter(val name: String?, val type: Type) */ class FunctionDecl(val name: String, val parameters: List, 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 \ No newline at end of file diff --git a/Interop/Runtime/build.gradle b/Interop/Runtime/build.gradle index 957eb22ca99..07ac2f8b004 100644 --- a/Interop/Runtime/build.gradle +++ b/Interop/Runtime/build.gradle @@ -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' diff --git a/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmCallbacks.kt b/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmCallbacks.kt new file mode 100644 index 00000000000..5827836e8e6 --- /dev/null +++ b/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmCallbacks.kt @@ -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> + protected constructor(returnType: CType, vararg paramTypes: CType) : CAdaptedFunctionType { + + 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(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, 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 -> + 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() +} + +private typealias UserData = (ret: COpaquePointer, args: CArray)->Unit + +inline fun > 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 { + 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_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, + 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) \ No newline at end of file diff --git a/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmNativeMem.kt b/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmNativeMem.kt new file mode 100644 index 00000000000..048ded7c1cd --- /dev/null +++ b/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmNativeMem.kt @@ -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(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 + } +} \ No newline at end of file diff --git a/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmTypes.kt b/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmTypes.kt new file mode 100644 index 00000000000..ecf369bc997 --- /dev/null +++ b/Interop/Runtime/src/jvm/kotlin/kotlin_/cinterop/JvmTypes.kt @@ -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 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 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 > CAdaptedFunctionType.Companion.getInstanceOf(): T = + T::class.objectInstance!! \ No newline at end of file diff --git a/Interop/Runtime/src/main/kotlin/kotlin_/cinterop/Types.kt b/Interop/Runtime/src/main/kotlin/kotlin_/cinterop/Types.kt new file mode 100644 index 00000000000..ff3ec8e7b84 --- /dev/null +++ b/Interop/Runtime/src/main/kotlin/kotlin_/cinterop/Types.kt @@ -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 interpretNullablePointed(ptr: NativePtr): T? { + return ifNotNull(ptr) { + interpretPointed(it) + } +} + +/** + * Applies the function to the pointer if it is not null, otherwise returns `null`. + */ +inline fun 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 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 NativePointed.reinterpret(): T = interpretPointed(this.rawPtr) + +/** + * C data or code. + */ +interface CPointed : NativePointed + +/** + * C pointer. + */ +class CPointer private constructor(val rawValue: NativePtr) { + companion object { + fun create(rawValue: NativePtr) = ensuringNotNull(rawValue) { + CPointer(rawValue) + } + + fun createNullable(rawValue: NativePtr) = ifNotNull(rawValue) { + CPointer(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.ptr: CPointer + get() = CPointer.create(this.rawPtr) + +/** + * Returns the corresponding [CPointed]. + * + * @param T must not be abstract + */ +inline val CPointer.pointed: T + get() = interpretPointed(this.rawValue) + +// `null` value of `CPointer?` is mapped to `nativeNullPtr` +val CPointer<*>?.rawValue: NativePtr + get() = this?.rawValue ?: nativeNullPtr + +fun CPointer<*>.reinterpret() = this as CPointer + +/** + * The [CPointed] without any specified interpretation. + */ +interface COpaque : CPointed // TODO: should it correspond to COpaquePointer? + +/** + * The pointer with an opaque type. + */ +typealias COpaquePointer = CPointer // FIXME + +/** + * The variable containing a [COpaquePointer]. + */ +typealias COpaquePointerVar = CPointerVarWithValueMappedTo + +/** + * 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 sizeOf() = Type.of().size + inline fun alignOf() = Type.of().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 CAggregate.memberAt(offset: Long): T { + return interpretPointed(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(override val rawPtr: NativePtr) : CPrimitiveVar() { + companion object : Type(1) +} + +class CInt16VarWithValueMappedTo(override val rawPtr: NativePtr) : CPrimitiveVar() { + companion object : Type(2) +} + +class CInt32VarWithValueMappedTo(override val rawPtr: NativePtr) : CPrimitiveVar() { + companion object : Type(4) +} + +class CInt64VarWithValueMappedTo(override val rawPtr: NativePtr) : CPrimitiveVar() { + companion object : Type(8) +} + +class CFloat32VarWithValueMappedTo(override val rawPtr: NativePtr) : CPrimitiveVar() { + companion object : Type(4) +} + +class CFloat64VarWithValueMappedTo(override val rawPtr: NativePtr) : CPrimitiveVar() { + companion object : Type(8) +} + +typealias CInt8Var = CInt8VarWithValueMappedTo +typealias CInt16Var = CInt16VarWithValueMappedTo +typealias CInt32Var = CInt32VarWithValueMappedTo +typealias CInt64Var = CInt64VarWithValueMappedTo +typealias CFloat32Var = CFloat32VarWithValueMappedTo +typealias CFloat64Var = CFloat64VarWithValueMappedTo + +var CInt8VarWithValueMappedTo.value: T + get() = nativeMemUtils.getByte(this) as T + set(value) = nativeMemUtils.putByte(this, value) + +var CInt16VarWithValueMappedTo.value: T + get() = nativeMemUtils.getShort(this) as T + set(value) = nativeMemUtils.putShort(this, value) + +var CInt32VarWithValueMappedTo.value: T + get() = nativeMemUtils.getInt(this) as T + set(value) = nativeMemUtils.putInt(this, value) + +var CInt64VarWithValueMappedTo.value: T + get() = nativeMemUtils.getLong(this) as T + set(value) = nativeMemUtils.putLong(this, value) + +// TODO: ensure native floats have the appropriate binary representation + +var CFloat32VarWithValueMappedTo.value: T + get() = nativeMemUtils.getFloat(this) as T + set(value) = nativeMemUtils.putFloat(this, value) + +var CFloat64VarWithValueMappedTo.value: T + get() = nativeMemUtils.getDouble(this) as T + set(value) = nativeMemUtils.putDouble(this, value) + + +class CPointerVarWithValueMappedTo>(override val rawPtr: NativePtr) : CVariable { + companion object : CVariable.Type(pointerSize.toLong(), pointerSize) +} + +/** + * The C data variable containing the pointer to `T`. + */ +typealias CPointerVar = CPointerVarWithValueMappedTo> + +/** + * The value of this variable. + */ +inline var > CPointerVarWithValueMappedTo

.value: P? + get() = CPointer.createNullable(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 > CPointerVarWithValueMappedTo

.pointed: T? + get() = this.value?.pointed + set(value) { + this.value = value?.ptr as P? + } + +class CArray(override val rawPtr: NativePtr) : CAggregate + +inline fun CArray.elementOffset(index: Long) = if (index == 0L) { + 0L // optimization for JVM impl which uses reflection for now. +} else { + index * CVariable.sizeOf() +} + +inline operator fun CArray.get(index: Long): T = memberAt(elementOffset(index)) +inline operator fun CArray.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> : 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 T : CAdaptedFunctionType> staticCFunction(body: F): CFunctionPointer { + val type = CAdaptedFunctionType.getInstanceOf() + return interpretPointed>(type.fromStatic(body)).ptr +} + +/** + * The C function. + */ +class CFunction(override val rawPtr: NativePtr) : CPointed + +/** + * The pointer to [CFunction]. + */ +typealias CFunctionPointer = CPointer> + +/** + * The variable containing a [CFunctionPointer]. + */ +typealias CFunctionPointerVar = CPointerVarWithValueMappedTo> \ No newline at end of file diff --git a/Interop/Runtime/src/main/kotlin/kotlin_/cinterop/Utils.kt b/Interop/Runtime/src/main/kotlin/kotlin_/cinterop/Utils.kt new file mode 100644 index 00000000000..f1a255c5e49 --- /dev/null +++ b/Interop/Runtime/src/main/kotlin/kotlin_/cinterop/Utils.kt @@ -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() + + 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 NativePlacement.alloc(): T = + alloc(CVariable.sizeOf(), CVariable.alignOf()).reinterpret() + +/** + * Allocates C array of given elements type and length. + * + * @param T must not be abstract + */ +inline fun NativePlacement.allocArray(length: Long): CArray = + alloc(CVariable.sizeOf() * length, CVariable.alignOf()).reinterpret() + +/** + * Allocates C array of given elements type and length. + * + * @param T must not be abstract + */ +inline fun NativePlacement.allocArray(length: Int): CArray = + 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 NativePlacement.allocArray(length: Long, + initializer: T.(Long)->Unit): CArray { + val res = allocArray(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 NativePlacement.allocArray(length: Int, initializer: T.(Long)->Unit) = + allocArray(length.toLong(), initializer) + + +/** + * Allocates C array of pointers to given elements. + */ +fun NativePlacement.allocArrayOfPointersTo(elements: List): CArray> { + val res = allocArray>(elements.size) + elements.forEachIndexed { index, value -> + res[index].value = value?.ptr + } + return res +} + +/** + * Allocates C array of pointers to given elements. + */ +fun NativePlacement.allocArrayOfPointersTo(vararg elements: T?) = + allocArrayOfPointersTo(elements.toList()) + +/** + * Allocates C array of given values. + */ +inline fun > + NativePlacement.allocArrayOf(vararg elements: T?): CArray> { + + return allocArrayOf(elements.toList()) +} + +/** + * Allocates C array of given values. + */ +inline fun > + NativePlacement.allocArrayOf(elements: List): CArray> { + + val res = allocArray>(elements.size) + elements.forEachIndexed { index, value -> + res[index].value = value + } + + return res +} + +fun NativePlacement.allocArrayOf(elements: ByteArray): CArray { + val res = allocArray(elements.size) + elements.forEachIndexed { i, byte -> + res[i].value = byte + } + return res +} + +fun NativePlacement.allocPointerTo() = alloc>() + +/** + * The zero-terminated string. + */ +class CString private constructor(override val rawPtr: NativePtr) : CPointed { + + companion object { + fun fromArray(array: CArray) = CString(array.rawPtr) + } + + fun length(): Int { + val array = reinterpret>() + + var res = 0 + while (array[res].value != 0.toByte()) { + ++res + } + return res + } + + override fun toString(): String { + val array = reinterpret>() + + val bytes = ByteArray(this.length()) + bytes.forEachIndexed { i, byte -> + bytes[i] = array[i].value + } + return String(bytes) // TODO: encoding + } + + fun asCharPtr() = reinterpret() +} + +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(len + 1) + + bytes.forEachIndexed { i, byte -> + nativeBytes[i].value = byte + } + nativeBytes[len].value = 0 + + return CString.fromArray(nativeBytes) +} + +fun CPointer.asCString() = CString.fromArray(this.reinterpret>().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 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 memScoped(block: MemScope.()->R): R { + @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") // TODO: it is a hack + return MemScope.use(block) +} \ No newline at end of file diff --git a/Interop/Runtime/src/main/kotlin/kotlin_/package-info.java b/Interop/Runtime/src/main/kotlin/kotlin_/package-info.java new file mode 100644 index 00000000000..8729a26605b --- /dev/null +++ b/Interop/Runtime/src/main/kotlin/kotlin_/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO: rename to kotlin + */ +package kotlin_; \ No newline at end of file diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt index 054f54db7d3..97e981b405b 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt @@ -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(${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, ret: NativePtr)") { + block("object $name : CAdaptedFunctionTypeImpl<$kotlinFunctionType>($constructorArgsStr)") { + block("override fun invoke(function: $kotlinFunctionType, args: CArray, 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("")