From b4db26f8462d4c0b1457dc62ae69a8cccbbc096c Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 14 Oct 2016 10:49:12 +0300 Subject: [PATCH 1/7] Interop/Runtime: do not reuse JNI local ref globally --- Interop/Runtime/src/callbacks/c/callbacks.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Interop/Runtime/src/callbacks/c/callbacks.c b/Interop/Runtime/src/callbacks/c/callbacks.c index d4a5b30b9f7..622853eb373 100644 --- a/Interop/Runtime/src/callbacks/c/callbacks.c +++ b/Interop/Runtime/src/callbacks/c/callbacks.c @@ -164,7 +164,11 @@ static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) { static jmethodID ffiFunImpl0 = NULL; static jclass cls = NULL; if (ffiFunImpl0 == NULL) { - cls = (*env)->FindClass(env, "kotlin_native/interop/CallbacksKt"); + jclass clsLocal = (*env)->FindClass(env, "kotlin_native/interop/CallbacksKt"); + checkException(env); + assert(clsLocal != NULL); + + cls = (jclass) (*env)->NewGlobalRef(env, clsLocal); checkException(env); assert(cls != NULL); From bd449b2ac3abfd923a0a2ffe9045a6fd1b69039e Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 14 Oct 2016 15:07:52 +0300 Subject: [PATCH 2/7] Interop/Runtime: properly initialize ffi_type for structs --- Interop/Runtime/src/callbacks/c/callbacks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Interop/Runtime/src/callbacks/c/callbacks.c b/Interop/Runtime/src/callbacks/c/callbacks.c index 622853eb373..44f90608f65 100644 --- a/Interop/Runtime/src/callbacks/c/callbacks.c +++ b/Interop/Runtime/src/callbacks/c/callbacks.c @@ -101,6 +101,8 @@ JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypePointer(J JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeStruct0(JNIEnv *env, jclass cls, jlong elements) { ffi_type* res = malloc(sizeof(ffi_type)); if (res != NULL) { + res->size = 0; + res->alignment = 0; res->elements = (ffi_type**) elements; res->type = FFI_TYPE_STRUCT; } From 2d2c79c54cb0c85fc4e54ed72ac6ef496ce7bce0 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 14 Oct 2016 16:05:50 +0300 Subject: [PATCH 3/7] Interop/StubGenerator: do not treat array-typed fields as pointers when passing structs by value through libffi --- .../native/interop/gen/jvm/StubGenerator.kt | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) 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 72bce02dbfb..40f3f140f56 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 @@ -578,6 +578,11 @@ class StubGenerator( out("}") } + private fun getFfiStructType(elementTypes: List) = + "Struct(" + + elementTypes.map { getFfiType(it) }.joinToString(", ") + + ")" + private fun getFfiType(type: Type): String { return when(type) { is VoidType -> "Void" @@ -588,29 +593,38 @@ class StubGenerator( is Int32Type -> "SInt32" is UInt32Type -> "UInt32" is IntPtrType, is UIntPtrType, // TODO - is PointerType, is ArrayType -> "Pointer" + is PointerType -> "Pointer" + is ConstArrayType -> getFfiStructType( + Array(type.length.toInt(), { type.elemType }).toList() + ) is EnumType -> getFfiType(type.def.baseType) is RecordType -> { val def = type.decl.def!! if (!def.hasNaturalLayout) { throw NotImplementedError() // TODO: represent pointer to function as NativePtr instead } - "Struct(" + def.fields.map { - getFfiType(it.type) - }.joinToString(", ") + ")" + getFfiStructType(def.fields.map { it.type }) } else -> throw NotImplementedError(type.toString()) } } + private fun getArgFfiType(type: Type) = when (type) { + is ArrayType -> "Pointer" + else -> getFfiType(type) + } + + private fun getRetValFfiType(type: Type) = getArgFfiType(type) + private fun generateFunctionType(type: FunctionType, name: String) { val kotlinFunctionType = getKotlinFunctionType(type) - val constructorArgs = listOf(type.returnType, *type.parameterTypes.toTypedArray()).map { - getFfiType(it) - }.joinToString(", ") + val constructorArgs = listOf(getRetValFfiType(type.returnType)) + + type.parameterTypes.map { getArgFfiType(it) } - out("object $name : NativeFunctionType<$kotlinFunctionType>($constructorArgs) {") + val constructorArgsStr = constructorArgs.joinToString(", ") + + out("object $name : NativeFunctionType<$kotlinFunctionType>($constructorArgsStr) {") indent { out("override fun invoke(function: $kotlinFunctionType, args: NativeArray, ret: NativePtr) {") indent { From 6d043204712616be232a01d6cb1272a44e69b3b6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 14 Oct 2016 16:03:14 +0300 Subject: [PATCH 4/7] Interop/StubGenerator: fix regressions after refactoring performed to support callbacks --- .../kotlin/native/interop/gen/jvm/StubGenerator.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 40f3f140f56..3939343b015 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 @@ -374,7 +374,7 @@ class StubGenerator( is RecordType -> { val refType = getKotlinTypeForRefTo(type) - InValueBinding( + return InValueBinding( kotlinJniBridgeType = "Long", conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})!!" }, kotlinType = refType.typeName @@ -386,6 +386,16 @@ class StubGenerator( } 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) } From 32c6e62297496996eb35ab44e3b3faa740f806e8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 14 Oct 2016 15:18:51 +0300 Subject: [PATCH 5/7] Interop/Indexer: unions don't have natural layout --- .../kotlin/native/interop/indexer/Indexer.kt | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) 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 ab0ac552182..e1eeadd3333 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 @@ -98,16 +98,27 @@ private class NativeIndexImpl : NativeIndex() { * Computes [StructDef.hasNaturalLayout] property. */ fun structHasNaturalLayout(structDefCursor: CXCursor): Boolean { - val hasAttributes = malloc(Int32Box) - hasAttributes.value = 0 - clang_visitChildren(structDefCursor, { cursor, parent, clientData -> - if (clang_isAttribute(cursor.kind.value) != 0) { - clientData.asRef(Int32Box)!!.value = 1 - } - CXChildVisitResult.CXChildVisit_Continue - }, hasAttributes.ptr) + val defKind = structDefCursor.kind.value - return hasAttributes.value == 0 + when (defKind) { + + CXCursorKind.CXCursor_UnionDecl -> return false + + CXCursorKind.CXCursor_StructDecl -> { + val hasAttributes = malloc(Int32Box) + hasAttributes.value = 0 + clang_visitChildren(structDefCursor, { cursor, parent, clientData -> + if (clang_isAttribute(cursor.kind.value) != 0) { + clientData.asRef(Int32Box)!!.value = 1 + } + CXChildVisitResult.CXChildVisit_Continue + }, hasAttributes.ptr) + + return hasAttributes.value == 0 + } + + else -> throw IllegalArgumentException(defKind.toString()) + } } fun convertType(type: CXType): Type { From bdcb7fb33cfc4cbd8a8a8e9cd374aa7a2d7232ec Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 14 Oct 2016 16:06:06 +0300 Subject: [PATCH 6/7] Interop/Indexer: update prebuilt interop stubs after fixing bugs in stub generation --- .../prebuilt/nativeInteropStubs/kotlin/clang/Clang.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/Clang.kt b/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/Clang.kt index 49cde2d3db9..03d658f40de 100644 --- a/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/Clang.kt +++ b/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/Clang.kt @@ -3570,7 +3570,7 @@ enum class CXIndexOptFlags(val value: Int) { } } -object NativeFunctionType1 : NativeFunctionType<(CXCursor, CXCursor, NativePtr?) -> CXChildVisitResult>(UInt32, Struct(UInt32, SInt32, Pointer), Struct(UInt32, SInt32, Pointer), Pointer) { +object NativeFunctionType1 : NativeFunctionType<(CXCursor, CXCursor, NativePtr?) -> CXChildVisitResult>(UInt32, Struct(UInt32, SInt32, Struct(Pointer, Pointer, Pointer)), Struct(UInt32, SInt32, Struct(Pointer, Pointer, Pointer)), Pointer) { override fun invoke(function: (CXCursor, CXCursor, NativePtr?) -> CXChildVisitResult, args: NativeArray, ret: NativePtr) { val res = function(args[0].value.asRef(CXCursor)!!, args[1].value.asRef(CXCursor)!!, args[2].value.asRef(NativePtrBox)!!.value) CXChildVisitResult.ref.byPtr(ret).value = res @@ -3589,14 +3589,14 @@ object NativeFunctionType3 : NativeFunctionType<(NativePtr?, CXSourceLocation?, } } -object NativeFunctionType4 : NativeFunctionType<(CXCursor, NativePtr?) -> CXVisitorResult>(UInt32, Struct(UInt32, SInt32, Pointer), Pointer) { +object NativeFunctionType4 : NativeFunctionType<(CXCursor, NativePtr?) -> CXVisitorResult>(UInt32, Struct(UInt32, SInt32, Struct(Pointer, Pointer, Pointer)), Pointer) { override fun invoke(function: (CXCursor, NativePtr?) -> CXVisitorResult, args: NativeArray, ret: NativePtr) { val res = function(args[0].value.asRef(CXCursor)!!, args[1].value.asRef(NativePtrBox)!!.value) CXVisitorResult.ref.byPtr(ret).value = res } } -object NativeFunctionType5 : NativeFunctionType<(NativePtr?, CXCursor, CXSourceRange) -> CXVisitorResult>(UInt32, Pointer, Struct(UInt32, SInt32, Pointer), Struct(Pointer, UInt32, UInt32)) { +object NativeFunctionType5 : NativeFunctionType<(NativePtr?, CXCursor, CXSourceRange) -> CXVisitorResult>(UInt32, Pointer, Struct(UInt32, SInt32, Struct(Pointer, Pointer, Pointer)), Struct(Struct(Pointer, Pointer), UInt32, UInt32)) { override fun invoke(function: (NativePtr?, CXCursor, CXSourceRange) -> CXVisitorResult, args: NativeArray, ret: NativePtr) { val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(CXCursor)!!, args[2].value.asRef(CXSourceRange)!!) CXVisitorResult.ref.byPtr(ret).value = res From e6351a3446ee735a39db8d098d94a2bb79df5a0c Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 14 Oct 2016 17:11:46 +0300 Subject: [PATCH 7/7] Interop/Indexer: improve memory management --- .../org/jetbrains/kotlin/native/interop/indexer/Indexer.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 e1eeadd3333..86dcdb37ad4 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 @@ -105,7 +105,7 @@ private class NativeIndexImpl : NativeIndex() { CXCursorKind.CXCursor_UnionDecl -> return false CXCursorKind.CXCursor_StructDecl -> { - val hasAttributes = malloc(Int32Box) + val hasAttributes = arena.alloc(Int32Box) hasAttributes.value = 0 clang_visitChildren(structDefCursor, { cursor, parent, clientData -> if (clang_isAttribute(cursor.kind.value) != 0) { @@ -264,6 +264,7 @@ fun CXString.convertAndDispose(): String { } fun buildNativeIndexImpl(headerFile: File, args: List): NativeIndex { + // TODO: dispose all allocated memory and resources val args1 = args.map { CString.fromString(it)!!.asCharPtr() }.toTypedArray() val index = clang_createIndex(0, 0)