Merge pull request #2 from JetBrains/interop-callbacks-1

Support callbacks in interop
This commit is contained in:
SvyatoslavScherbina
2016-10-11 17:18:49 +03:00
committed by GitHub
11 changed files with 885 additions and 197 deletions
+1 -8
View File
@@ -17,12 +17,6 @@ def compilerArgsForJniIncludes = ["", "linux", "darwin"].collect { "-I$javaHome/
model {
components {
callback(NativeLibrarySpec) {
binaries.all {
cCompiler.args compilerArgsForJniIncludes
}
}
clangbridge(NativeLibrarySpec) {
sources {
c.source.srcDir 'prebuilt/nativeInteropStubs/c'
@@ -59,9 +53,8 @@ dependencies {
}
task nativelibs(type: Copy) {
dependsOn 'callbackSharedLibrary', 'clangbridgeSharedLibrary'
dependsOn 'clangbridgeSharedLibrary'
from "$buildDir/libs/callback/shared/"
from "$buildDir/libs/clangbridge/shared/"
into "$buildDir/nativelibs/"
}
@@ -1256,9 +1256,9 @@ fun clang_getIBOutletCollectionType(arg0: CXCursor, retValPlacement: Placement):
return NativePtr.byValue(res).asRef(CXType)!!
}
fun clang_visitChildren(parent: CXCursor, visitor: NativePtr?, client_data: NativePtr?): Int {
fun clang_visitChildren(parent: CXCursor, visitor: ((CXCursor, CXCursor, NativePtr?) -> CXChildVisitResult)?, client_data: NativePtr?): Int {
val _parent = parent.getNativePtr().asLong()
val _visitor = visitor.asLong()
val _visitor = visitor?.staticAsNative(NativeFunctionType1).asLong()
val _client_data = client_data.asLong()
val res = externals.clang_visitChildren(_parent, _visitor, _client_data)
return res
@@ -1636,8 +1636,8 @@ fun clang_enableStackTraces(): Unit {
return res
}
fun clang_executeOnThread(fn: NativePtr?, user_data: NativePtr?, stack_size: Int): Unit {
val _fn = fn.asLong()
fun clang_executeOnThread(fn: ((NativePtr?) -> Unit)?, user_data: NativePtr?, stack_size: Int): Unit {
val _fn = fn?.staticAsNative(NativeFunctionType2).asLong()
val _user_data = user_data.asLong()
val res = externals.clang_executeOnThread(_fn, _user_data, stack_size)
return res
@@ -1790,9 +1790,9 @@ fun clang_toggleCrashRecovery(isEnabled: Int): Unit {
return res
}
fun clang_getInclusions(tu: CXTranslationUnitImpl?, visitor: NativePtr?, client_data: NativePtr?): Unit {
fun clang_getInclusions(tu: CXTranslationUnitImpl?, visitor: ((NativePtr?, CXSourceLocation?, Int, NativePtr?) -> Unit)?, client_data: NativePtr?): Unit {
val _tu = tu.getNativePtr().asLong()
val _visitor = visitor.asLong()
val _visitor = visitor?.staticAsNative(NativeFunctionType3).asLong()
val _client_data = client_data.asLong()
val res = externals.clang_getInclusions(_tu, _visitor, _client_data)
return res
@@ -1986,9 +1986,9 @@ fun clang_indexLoc_getCXSourceLocation(loc: CXIdxLoc, retValPlacement: Placement
return NativePtr.byValue(res).asRef(CXSourceLocation)!!
}
fun clang_Type_visitFields(T: CXType, visitor: NativePtr?, client_data: NativePtr?): Int {
fun clang_Type_visitFields(T: CXType, visitor: ((CXCursor, NativePtr?) -> CXVisitorResult)?, client_data: NativePtr?): Int {
val _T = T.getNativePtr().asLong()
val _visitor = visitor.asLong()
val _visitor = visitor?.staticAsNative(NativeFunctionType4).asLong()
val _client_data = client_data.asLong()
val res = externals.clang_Type_visitFields(_T, _visitor, _client_data)
return res
@@ -2006,7 +2006,7 @@ class __darwin_pthread_handler_rec(ptr: NativePtr) : NativeStruct(ptr) {
companion object : Type<__darwin_pthread_handler_rec>(24, ::__darwin_pthread_handler_rec)
val __routine by NativePtrBox at 0
val __routine by NativeFunctionType2.ref at 0
val __arg by NativePtrBox at 8
val __next by __darwin_pthread_handler_rec.ref at 16
}
@@ -2266,7 +2266,7 @@ class CXCursorAndRangeVisitor(ptr: NativePtr) : NativeStruct(ptr) {
companion object : Type<CXCursorAndRangeVisitor>(16, ::CXCursorAndRangeVisitor)
val context by NativePtrBox at 0
val visit by NativePtrBox at 8
val visit by NativeFunctionType5.ref at 8
}
class CXIdxLoc(ptr: NativePtr) : NativeStruct(ptr) {
@@ -2446,14 +2446,14 @@ class IndexerCallbacks(ptr: NativePtr) : NativeStruct(ptr) {
companion object : Type<IndexerCallbacks>(64, ::IndexerCallbacks)
val abortQuery by NativePtrBox at 0
val diagnostic by NativePtrBox at 8
val enteredMainFile by NativePtrBox at 16
val ppIncludedFile by NativePtrBox at 24
val importedASTFile by NativePtrBox at 32
val startedTranslationUnit by NativePtrBox at 40
val indexDeclaration by NativePtrBox at 48
val indexEntityReference by NativePtrBox at 56
val abortQuery by NativeFunctionType6.ref at 0
val diagnostic by NativeFunctionType7.ref at 8
val enteredMainFile by NativeFunctionType8.ref at 16
val ppIncludedFile by NativeFunctionType9.ref at 24
val importedASTFile by NativeFunctionType10.ref at 32
val startedTranslationUnit by NativeFunctionType11.ref at 40
val indexDeclaration by NativeFunctionType12.ref at 48
val indexEntityReference by NativeFunctionType13.ref at 56
}
enum class CXErrorCode(val value: Int) {
@@ -3570,6 +3570,92 @@ enum class CXIndexOptFlags(val value: Int) {
}
}
object NativeFunctionType1 : NativeFunctionType<(CXCursor, CXCursor, NativePtr?) -> CXChildVisitResult>(UInt32, Struct(UInt32, SInt32, Pointer), Struct(UInt32, SInt32, Pointer), Pointer) {
override fun invoke(function: (CXCursor, CXCursor, NativePtr?) -> CXChildVisitResult, args: NativeArray<NativePtrBox>, 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
}
}
object NativeFunctionType2 : NativeFunctionType<(NativePtr?) -> Unit>(Void, Pointer) {
override fun invoke(function: (NativePtr?) -> Unit, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value)
}
}
object NativeFunctionType3 : NativeFunctionType<(NativePtr?, CXSourceLocation?, Int, NativePtr?) -> Unit>(Void, Pointer, Pointer, UInt32, Pointer) {
override fun invoke(function: (NativePtr?, CXSourceLocation?, Int, NativePtr?) -> Unit, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(CXSourceLocation.ref)!!.value, args[2].value.asRef(Int32Box)!!.value, args[3].value.asRef(NativePtrBox)!!.value)
}
}
object NativeFunctionType4 : NativeFunctionType<(CXCursor, NativePtr?) -> CXVisitorResult>(UInt32, Struct(UInt32, SInt32, Pointer), Pointer) {
override fun invoke(function: (CXCursor, NativePtr?) -> CXVisitorResult, args: NativeArray<NativePtrBox>, 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)) {
override fun invoke(function: (NativePtr?, CXCursor, CXSourceRange) -> CXVisitorResult, args: NativeArray<NativePtrBox>, 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
}
}
object NativeFunctionType6 : NativeFunctionType<(NativePtr?, NativePtr?) -> Int>(SInt32, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, NativePtr?) -> Int, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(NativePtrBox)!!.value)
Int32Box.byPtr(ret).value = res
}
}
object NativeFunctionType7 : NativeFunctionType<(NativePtr?, NativePtr?, NativePtr?) -> Unit>(Void, Pointer, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, NativePtr?, NativePtr?) -> Unit, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(NativePtrBox)!!.value, args[2].value.asRef(NativePtrBox)!!.value)
}
}
object NativeFunctionType8 : NativeFunctionType<(NativePtr?, NativePtr?, NativePtr?) -> NativePtr?>(Pointer, Pointer, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, NativePtr?, NativePtr?) -> NativePtr?, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(NativePtrBox)!!.value, args[2].value.asRef(NativePtrBox)!!.value)
NativePtrBox.byPtr(ret).value = res
}
}
object NativeFunctionType9 : NativeFunctionType<(NativePtr?, CXIdxIncludedFileInfo?) -> NativePtr?>(Pointer, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, CXIdxIncludedFileInfo?) -> NativePtr?, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(CXIdxIncludedFileInfo.ref)!!.value)
NativePtrBox.byPtr(ret).value = res
}
}
object NativeFunctionType10 : NativeFunctionType<(NativePtr?, CXIdxImportedASTFileInfo?) -> NativePtr?>(Pointer, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, CXIdxImportedASTFileInfo?) -> NativePtr?, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(CXIdxImportedASTFileInfo.ref)!!.value)
NativePtrBox.byPtr(ret).value = res
}
}
object NativeFunctionType11 : NativeFunctionType<(NativePtr?, NativePtr?) -> NativePtr?>(Pointer, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, NativePtr?) -> NativePtr?, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(NativePtrBox)!!.value)
NativePtrBox.byPtr(ret).value = res
}
}
object NativeFunctionType12 : NativeFunctionType<(NativePtr?, CXIdxDeclInfo?) -> Unit>(Void, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, CXIdxDeclInfo?) -> Unit, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(CXIdxDeclInfo.ref)!!.value)
}
}
object NativeFunctionType13 : NativeFunctionType<(NativePtr?, CXIdxEntityRefInfo?) -> Unit>(Void, Pointer, Pointer) {
override fun invoke(function: (NativePtr?, CXIdxEntityRefInfo?) -> Unit, args: NativeArray<NativePtrBox>, ret: NativePtr) {
val res = function(args[0].value.asRef(NativePtrBox)!!.value, args[1].value.asRef(CXIdxEntityRefInfo.ref)!!.value)
}
}
object externals {
init { System.loadLibrary("clangbridge") }
external fun asctime(arg0: Long): Long
-49
View File
@@ -1,49 +0,0 @@
#include <jni.h>
#include <stdlib.h>
#include <assert.h>
static JavaVM *vm = NULL;
// Returns the JNI env which can be used by the caller.
// If current thread is not attached to JVM, then it gets attached as daemon.
static JNIEnv* getCurrentEnv() {
JNIEnv* env;
assert (vm != NULL);
jint res = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_1);
if (res != JNI_OK) {
assert (res == JNI_EDETACHED);
res = (*vm)->AttachCurrentThreadAsDaemon(vm, (void**)&env, NULL);
assert (res == JNI_OK);
}
return env;
}
jint JNI_OnLoad(JavaVM *vm_, void *reserved) {
vm = vm_;
return JNI_VERSION_1_1;
}
// The implementation of native callback.
void indexDeclarationNativeCallback(void* arg1, void* arg2) {
JNIEnv* env = getCurrentEnv();
static jclass cls = NULL;
static jmethodID entryFromNative = NULL;
if (cls == NULL) {
cls = (*env)->FindClass(env, "org/jetbrains/kotlin/native/interop/indexer/indexDeclarationCallback");
assert(cls != NULL);
entryFromNative = (*env)->GetStaticMethodID(env, cls, "entryFromNative", "(J)V");
assert(entryFromNative != NULL);
}
(*env)->CallStaticVoidMethod(env, cls, entryFromNative, (jlong) arg2);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
assert(0);
}
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_kotlin_native_interop_indexer_indexDeclarationCallback_nativeCallbackPtr(JNIEnv *env, jobject obj) {
return (jlong) indexDeclarationNativeCallback;
}
@@ -1,55 +0,0 @@
package org.jetbrains.kotlin.native.interop.indexer
import clang.CXIdxDeclInfo
import kotlin_native.interop.NativePtr
/**
* This object helps to create native callbacks with signature `void (CXClientData, const CXIdxDeclInfo *)`.
* At any time only one instance of native callback can exist.
*/
internal object indexDeclarationCallback {
/**
* The Kotlin function to be called when native callback is called.
*/
private var function: ((CXIdxDeclInfo)->Unit)? = null
/**
* Creates instance of native callback with given Kotlin implementation.
*
* To create another one, this instance must be destroyed using [reset].
*/
fun setUp(func: (CXIdxDeclInfo)->Unit): NativePtr {
assert (function == null)
function = func
return nativeCallbackPtr
}
/**
* Destroys the (only) instance of native callback.
*/
fun reset() {
assert (function != null)
function = null
}
/**
* This function is called from native callback implementation.
*/
@JvmStatic
private fun entryFromNative(info: Long) {
function!!(CXIdxDeclInfo(NativePtr.Companion.byValue(info)!!))
}
private val nativeCallbackPtr: NativePtr
init {
System.loadLibrary("callback")
nativeCallbackPtr = NativePtr.byValue(nativeCallbackPtr())!!
}
/**
* Returns the pointer to native callback.
*/
private external fun nativeCallbackPtr(): Long
}
@@ -10,7 +10,9 @@ private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
override var def: StructDefImpl? = null
}
private class StructDefImpl(size: Long, decl: StructDecl) : StructDef(size, decl) {
private class StructDefImpl(size: Long, decl: StructDecl, hasNaturalLayout: Boolean) :
StructDef(size, decl, hasNaturalLayout) {
override val fields = mutableListOf<Field>()
}
@@ -92,6 +94,22 @@ private class NativeIndexImpl : NativeIndex() {
return res
}
/**
* 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)
return hasAttributes.value == 0
}
fun convertType(type: CXType): Type {
val kind = type.kind.value
return when (kind) {
@@ -104,6 +122,7 @@ private class NativeIndexImpl : NativeIndex() {
}
}
// TODO: is e.g. CXType_Int guaranteed to be int32_t?
CXType_Void -> VoidType
CXType_Char_U, CXType_UChar -> UInt8Type
@@ -182,7 +201,8 @@ private class NativeIndexImpl : NativeIndex() {
val structDecl = getStructDeclAt(cursor)
if (clang_isCursorDefinition(cursor) != 0) {
val size = clang_Type_getSizeOf(clang_getCursorType(cursor, arena))
structDecl.def = StructDefImpl(size, structDecl)
val hasNaturalLayout = structHasNaturalLayout(cursor)
structDecl.def = StructDefImpl(size, structDecl, hasNaturalLayout)
}
}
@@ -241,28 +261,32 @@ fun buildNativeIndexImpl(headerFile: File, args: List<String>): NativeIndex {
val res = NativeIndexImpl()
try {
val indexDeclarationNativeCallback = indexDeclarationCallback.setUp({ res.indexDeclaration(it) })
val nativeIndexPtr = StableObjPtr.create(res)
val clientData = nativeIndexPtr.value
try {
with(callbacks) {
abortQuery.value = null
diagnostic.value = null
enteredMainFile.value = null
ppIncludedFile.value = null
importedASTFile.value = null
startedTranslationUnit.value = null
indexDeclaration.value = indexDeclarationNativeCallback
indexEntityReference.value = null
abortQuery.setStatic(null)
diagnostic.setStatic(null)
enteredMainFile.setStatic(null)
ppIncludedFile.setStatic(null)
importedASTFile.setStatic(null)
startedTranslationUnit.setStatic(null)
indexDeclaration.setStatic { clientData, info ->
val index = StableObjPtr.fromValue(clientData!!).get() as NativeIndexImpl
index.indexDeclaration(info!!)
}
indexEntityReference.setStatic(null)
}
clang_indexSourceFile(indexAction, null, callbacks, IndexerCallbacks.Companion.size, 0, headerFile.path,
clang_indexSourceFile(indexAction, clientData, callbacks, IndexerCallbacks.size, 0, headerFile.path,
mallocNativeArrayOf(Int8Box.Companion, *args1)[0], args1.size, null, 0, null, 0)
return res
} finally {
indexDeclarationCallback.reset()
nativeIndexPtr.dispose()
}
} finally {
res.clearNativeMem()
@@ -31,8 +31,11 @@ abstract class StructDecl(val spelling: String) {
/**
* C struct definition.
*
* @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) {
abstract class StructDef(val size: Long, val decl: StructDecl, val hasNaturalLayout: Boolean) {
abstract val fields: List<Field>
}
@@ -64,38 +67,40 @@ class FunctionDecl(val name: String, val parameters: List<Parameter>, val return
/**
* C type.
*/
open class Type
interface Type
open class PrimitiveType : Type()
interface PrimitiveType : Type
object VoidType : Type()
object VoidType : Type
object Int8Type : PrimitiveType()
object UInt8Type : PrimitiveType()
object Int8Type : PrimitiveType
object UInt8Type : PrimitiveType
object Int16Type : PrimitiveType()
object UInt16Type : PrimitiveType()
object Int16Type : PrimitiveType
object UInt16Type : PrimitiveType
object Int32Type : PrimitiveType()
object UInt32Type : PrimitiveType()
object Int32Type : PrimitiveType
object UInt32Type : PrimitiveType
object IntPtrType : PrimitiveType()
object UIntPtrType : PrimitiveType()
object IntPtrType : PrimitiveType
object UIntPtrType : PrimitiveType
object Int64Type : PrimitiveType()
object UInt64Type : PrimitiveType()
object Int64Type : PrimitiveType
object UInt64Type : PrimitiveType
class RecordType(val decl: StructDecl) : Type()
data class RecordType(val decl: StructDecl) : Type
class EnumType(val def: EnumDef) : Type()
data class EnumType(val def: EnumDef) : Type
class PointerType(val pointeeType : Type) : Type()
data class PointerType(val pointeeType : Type) : Type
class FunctionType(val parameterTypes: List<Type>, val returnType: Type) : Type()
data class FunctionType(val parameterTypes: List<Type>, val returnType: Type) : Type
open class ArrayType(val elemType: Type) : Type()
class ConstArrayType(elemType: Type, val length: Long) : ArrayType(elemType)
class IncompleteArrayType(elemType: Type) : ArrayType(elemType)
interface ArrayType : Type {
val elemType: Type
}
object UnsupportedType : Type()
data class ConstArrayType(override val elemType: Type, val length: Long) : ArrayType
data class IncompleteArrayType(override val elemType: Type) : ArrayType
object UnsupportedType : Type
+33
View File
@@ -9,6 +9,29 @@ buildscript {
}
apply plugin: 'kotlin'
apply plugin: 'c'
def javaHome = System.getProperty('java.home')
def compilerArgsForJniIncludes = ["", "linux", "darwin"].collect { "-I$javaHome/../include/$it" } as String[]
model {
components {
callbacks(NativeLibrarySpec) {
sources.c.source {
srcDir 'src/callbacks/c'
include '**/*.c'
}
binaries.all {
cCompiler.args compilerArgsForJniIncludes
cCompiler.args "-I$ffiIncludePath"
String libffiPicPath = "$ffiLibPath/libffi_pic.a"
linker.args file(libffiPicPath).exists() ? libffiPicPath : "$ffiLibPath/libffi.a"
}
}
}
}
repositories {
mavenCentral()
@@ -17,3 +40,13 @@ repositories {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
task nativelibs(type: Copy) {
dependsOn 'callbacksSharedLibrary'
from "$buildDir/libs/callbacks/shared/"
into "$buildDir/nativelibs/"
}
classes.dependsOn nativelibs
+233
View File
@@ -0,0 +1,233 @@
#include <stdlib.h>
#include <assert.h>
#include <jni.h>
#include <ffi.h>
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeVoid
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeVoid(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_void;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeUInt8
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt8(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_uint8;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeSInt8
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt8(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_sint8;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeUInt16
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt16(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_uint16;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeSInt16
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt16(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_sint16;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeUInt32
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt32(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_uint32;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeSInt32
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt32(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_sint32;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeUInt64
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt64(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_uint64;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeSInt64
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt64(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_sint64;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypePointer
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypePointer(JNIEnv *env, jclass cls) {
return (jlong) &ffi_type_pointer;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiTypeStruct0
* Signature: (J)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->elements = (ffi_type**) elements;
res->type = FFI_TYPE_STRUCT;
}
return (jlong) res;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiCreateCif0
* Signature: (IJJ)J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiCreateCif0(JNIEnv *env, jclass cls, jint nArgs, jlong rType, jlong argTypes) {
ffi_cif* res = malloc(sizeof(ffi_cif));
if (res != NULL) {
ffi_status status = ffi_prep_cif(res, FFI_DEFAULT_ABI, nArgs, (ffi_type*)rType, (ffi_type**)argTypes);
if (status != FFI_OK) {
if (status == FFI_BAD_TYPEDEF) {
return -(jlong)1;
} else if (status == FFI_BAD_ABI) {
return -(jlong)2;
} else {
return -(jlong)3;
}
}
}
return (jlong) res;
}
static JavaVM *vm = NULL;
// Returns the JNI env which can be used by the caller.
// If current thread is not attached to JVM, then it gets attached as daemon.
static JNIEnv* getCurrentEnv() {
JNIEnv* env;
assert(vm != NULL);
jint res = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_1);
if (res != JNI_OK) {
assert(res == JNI_EDETACHED);
res = (*vm)->AttachCurrentThreadAsDaemon(vm, (void**)&env, NULL);
assert(res == JNI_OK);
}
return env;
}
jint JNI_OnLoad(JavaVM *vm_, void *reserved) {
vm = vm_;
return JNI_VERSION_1_1;
}
// Checks for pending exception. If there is one, describes it and terminates the process.
static void checkException(JNIEnv *env) {
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
abort();
}
}
static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
JNIEnv* env = getCurrentEnv();
static jmethodID ffiFunImpl0 = NULL;
static jclass cls = NULL;
if (ffiFunImpl0 == NULL) {
cls = (*env)->FindClass(env, "kotlin_native/interop/CallbacksKt");
checkException(env);
assert(cls != NULL);
ffiFunImpl0 = (*env)->GetStaticMethodID(env, cls, "ffiFunImpl0", "(JJJLjava/lang/Object;)V");
checkException(env);
assert(ffiFunImpl0 != NULL);
}
(*env)->CallStaticVoidMethod(env, cls, ffiFunImpl0, (jlong) cif, (jlong) ret, (jlong) args, (jobject) user_data);
checkException(env);
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: ffiCreateClosure0
* Signature: (JLjava/lang/Object;)J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiCreateClosure0(JNIEnv *env, jclass cls, jlong ffiCif, jobject userData) {
jobject userDataGlobalRef = (*env)->NewGlobalRef(env, userData);
if (userDataGlobalRef == NULL) {
return (jlong)0;
}
assert(sizeof(jobject) == sizeof(void*)); // TODO: check statically
void* userDataPtr = (void*) userDataGlobalRef;
void* res;
ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), &res);
if (closure == NULL) {
return (jlong)0;
}
ffi_status status = ffi_prep_closure_loc(closure, (ffi_cif*)ffiCif, ffi_fun, userDataPtr, res);
if (status != FFI_OK) {
return -(jlong)1;
}
return (jlong) res;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: newGlobalRef
* Signature: (Ljava/lang/Object;)J
*/
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_newGlobalRef(JNIEnv *env, jclass cls, jobject obj) {
jobject res = (*env)->NewGlobalRef(env, obj);
return (jlong) res;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: derefGlobalRef
* Signature: (J)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_kotlin_1native_interop_CallbacksKt_derefGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
return (jobject) ref;
}
/*
* Class: kotlin_native_interop_CallbacksKt
* Method: deleteGlobalRef
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_kotlin_1native_interop_CallbacksKt_deleteGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
(*env)->DeleteGlobalRef(env, (jobject) ref);
}
@@ -0,0 +1,287 @@
package kotlin_native.interop
/**
* 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: NativePtr) {
companion object {
/**
* Creates a handle for given object.
*/
fun create(any: Any) = StableObjPtr(NativePtr.byValue(newGlobalRef(any))!!)
/**
* Creates [StableObjPtr] from given raw value.
*
* @param value must be a [value] of some [StableObjPtr]
*/
fun fromValue(value: NativePtr) = StableObjPtr(value)
init {
loadCallbacksLibrary()
}
}
/**
* Disposes the handle. It must not be [used][get] after that.
*/
fun dispose() {
deleteGlobalRef(value.value)
}
/**
* Returns the object this handle was [created][create] for.
*/
fun get(): Any = derefGlobalRef(value.value)
}
/**
* Describes the type of native function.
*
* The instances of this class are supposed to be Kotlin objects (singletons),
* 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 NativeFunctionType<F : Function<*>> protected constructor(returnType: CType, vararg paramTypes: CType) {
/**
* Returns a native function of this type, which calls given Kotlin *static* function.
*
* Given 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 {
// 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(NativePtr.byValue(ffiTypePtr).asRef(ffi_type)!!)
}
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: NativeArray<NativePtrBox>, ret: NativePtr)
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 = { ret: NativePtr, args: NativeArray<NativePtrBox> ->
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>()
}
/**
* @see NativeFunctionType.fromStatic
*/
fun <F : Function<*>> F.staticAsNative(type: NativeFunctionType<F>) = type.fromStatic(this)
/**
* Describes a "struct" with native function pointer field.
*/
class NativeFunctionBox<F : Function<*>>(ptr: NativePtr, private val type: NativeFunctionType<F>) : NativeRef(ptr) {
/**
* Sets the function pointer field to null or native function calling given Kotlin function.
*/
fun setStatic(function: F?) {
val nativeFunPtr = function?.staticAsNative(type)
bridge.putPtr(ptr, nativeFunPtr)
}
}
val <F : Function<*>> NativeFunctionType<F>.ref: NativeRef.TypeWithSize<NativeFunctionBox<F>>
get() = NativeRef.TypeWithSize(8, { NativeFunctionBox(it, this) }) // TODO: 64-bit specific
private fun loadCallbacksLibrary() {
System.loadLibrary("callbacks")
}
/**
* Reference to `ffi_type` struct instance.
*/
internal class ffi_type (ptr: NativePtr) : NativeRef(ptr) {
companion object : Type<ffi_type>(::ffi_type)
}
/**
* Reference to `ffi_cif` struct instance.
*/
internal class ffi_cif (ptr: NativePtr) : NativeRef(ptr) {
companion object : Type<ffi_cif>(::ffi_cif)
}
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 = mallocNativeArrayOf(ffi_type, *elementTypes.toTypedArray(), null).ptr
val res = ffiTypeStruct0(elements.value)
if (res == 0L) {
throw OutOfMemoryError()
}
return NativePtr.byValue(res).asRef(ffi_type)!!
}
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 rType = returnType.ptr
val argTypes = mallocNativeArrayOf(ffi_type, *paramTypes.toTypedArray(), null).ptr
val res = ffiCreateCif0(nArgs, rType.value, argTypes.value)
when (res) {
0L -> throw OutOfMemoryError()
-1L -> throw Error("FFI_BAD_TYPEDEF")
-2L -> throw Error("FFI_BAD_ABI")
-3L -> throw Error("libffi error occurred")
}
return NativePtr.byValue(res).asRef(ffi_cif)!!
}
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
ffiFunImpl(NativePtr.byValue(ffiCif).asRef(ffi_cif)!!,
NativePtr.byValue(ret)!!,
NativePtr.byValue(args).asRef(array(NativePtrBox))!!,
userData as (ret: NativePtr, args: NativeArray<NativePtrBox>) -> Unit)
}
/**
* 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: NativePtr, args: NativeArray<NativePtrBox>,
userData: (NativePtr, NativeArray<NativePtrBox>) -> Unit) {
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: (NativePtr, NativeArray<NativePtrBox>) -> Unit): NativePtr {
val res = ffiCreateClosure0(ffiCif.ptr.value, userData)
when (res) {
0L -> throw OutOfMemoryError()
-1L -> throw Error("libffi error occurred")
}
return NativePtr.byValue(res)!!
}
private external fun newGlobalRef(any: Any): Long
private external fun derefGlobalRef(ref: Long): Any
private external fun deleteGlobalRef(ref: Long)
@@ -66,6 +66,15 @@ class StubGenerator(
val functionsToBind = nativeIndex.functions.filter { it.name !in excludedFunctions }
private val usedFunctionTypes = mutableMapOf<FunctionType, String>()
val FunctionType.kotlinName: String
get() {
return usedFunctionTypes.getOrPut(this, {
"NativeFunctionType" + (usedFunctionTypes.size + 1)
})
}
/**
* The output currently used by the generator.
* Should append line separator after any usage.
@@ -181,10 +190,13 @@ class StubGenerator(
}
is PointerType -> {
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
val pointeeType = type.pointeeType
if (pointeeType is VoidType) {
NativeRefType("NativePtrBox")
} else if (pointeeType is FunctionType) {
NativeRefType("NativeFunctionBox<${getKotlinFunctionType(pointeeType)}>", "${pointeeType.kotlinName}.ref")
} else {
val pointeeRefType = getKotlinTypeForRefTo(type.pointeeType)
val pointeeRefType = getKotlinTypeForRefTo(pointeeType)
NativeRefType("RefBox<${pointeeRefType.typeName}>", "${pointeeRefType.typeExpr}.ref")
}
}
@@ -258,13 +270,6 @@ class StubGenerator(
kotlinConv = { "$it.asLong()" },
kotlinJniBridgeType = "Long"
)
} else if (type.pointeeType is Int8Type) {
OutValueBinding(
kotlinType = "String?",
kotlinConv = { name -> "CString.fromString($name).getNativePtr().asLong()" },
convFree = { name -> "free(NativePtr.byValue($name))" },
kotlinJniBridgeType = "Long"
)
} else {
outValueRefBinding(getKotlinTypeForRefTo(type.pointeeType))
}
@@ -282,17 +287,51 @@ class StubGenerator(
is ArrayType -> outValueRefBinding(getKotlinTypeForRefTo(type))
is RecordType -> {
val refType = getKotlinTypeForRefTo(type)
// pointer will be converted to value in C code
OutValueBinding(
kotlinType = refType.typeName,
kotlinConv = { "$it.getNativePtr().asLong()" },
kotlinJniBridgeType = "Long"
else -> throw NotImplementedError()
}
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"
)
is Int8Type -> return OutValueBinding(
kotlinType = "String?",
kotlinConv = { name -> "CString.fromString($name).getNativePtr().asLong()" },
convFree = { name -> "free(NativePtr.byValue($name))" },
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)
}
fun getCallbackRetValBinding(type: Type): OutValueBinding {
if (type is VoidType) {
return OutValueBinding(
kotlinType = "Unit",
kotlinConv = { throw UnsupportedOperationException() },
kotlinJniBridgeType = "void"
)
}
else -> throw NotImplementedError()
return getOutValueBinding(type)
}
/**
@@ -302,8 +341,6 @@ class StubGenerator(
is PrimitiveType -> InValueBinding(type.kotlinType)
is VoidType -> InValueBinding("Unit")
is PointerType -> {
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
InValueBinding(
@@ -328,17 +365,35 @@ class StubGenerator(
is ArrayType -> inValueRefBinding(getKotlinTypeForRefTo(type))
is RecordType -> {
// TODO: valid only for return values
val refType = getKotlinTypeForRefTo(type)
InValueBinding(
kotlinJniBridgeType = "Long",
conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})!!" },
kotlinType = refType.typeName
)
else -> throw NotImplementedError()
}
fun getCFunctionRetValBinding(type: Type): InValueBinding {
when (type) {
is VoidType -> return InValueBinding("Unit")
is RecordType -> {
val refType = getKotlinTypeForRefTo(type)
InValueBinding(
kotlinJniBridgeType = "Long",
conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})!!" },
kotlinType = refType.typeName
)
}
}
else -> throw NotImplementedError()
return getInValueBinding(type)
}
fun getCallbackParamBinding(type: Type): InValueBinding {
return getInValueBinding(type)
}
private fun getKotlinFunctionType(type: FunctionType): String {
return "(" +
type.parameterTypes.map { getCallbackParamBinding(it).kotlinType }.joinToString(", ") +
") -> " +
getCallbackRetValBinding(type.returnType).kotlinType
}
/**
@@ -437,14 +492,14 @@ class StubGenerator(
/**
* Constructs [InValueBinding] for return value of Kotlin binding for given C function.
*/
private fun retValBinding(func: FunctionDecl) = getInValueBinding(func.returnType)
private fun retValBinding(func: FunctionDecl) = getCFunctionRetValBinding(func.returnType)
/**
* Constructs [OutValueBinding]s for parameters of Kotlin binding for given C function.
*/
private fun paramBindings(func: FunctionDecl): Array<OutValueBinding> {
val paramBindings = func.parameters.map { param ->
getOutValueBinding(param.type)
getCFunctionParamBinding(param.type)
}.toMutableList()
val retValType = func.returnType
@@ -523,6 +578,68 @@ class StubGenerator(
out("}")
}
private fun getFfiType(type: Type): String {
return when(type) {
is VoidType -> "Void"
is Int8Type -> "SInt8"
is UInt8Type -> "UInt8"
is Int16Type -> "SInt16"
is UInt16Type -> "UInt16"
is Int32Type -> "SInt32"
is UInt32Type -> "UInt32"
is IntPtrType, is UIntPtrType, // TODO
is PointerType, is ArrayType -> "Pointer"
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(", ") + ")"
}
else -> throw NotImplementedError(type.toString())
}
}
private fun generateFunctionType(type: FunctionType, name: String) {
val kotlinFunctionType = getKotlinFunctionType(type)
val constructorArgs = listOf(type.returnType, *type.parameterTypes.toTypedArray()).map {
getFfiType(it)
}.joinToString(", ")
out("object $name : NativeFunctionType<$kotlinFunctionType>($constructorArgs) {")
indent {
out("override fun invoke(function: $kotlinFunctionType, args: NativeArray<NativePtrBox>, ret: NativePtr) {")
indent {
val args = type.parameterTypes.mapIndexed { i, paramType ->
val refType = getKotlinTypeForRefTo(paramType)
val ref = "args[$i].value.asRef(${refType.typeExpr})!!"
when (paramType) {
is RecordType -> "$ref"
else -> "$ref.value"
}
}.joinToString(", ")
out("val res = function($args)")
when (type.returnType) {
is RecordType -> throw NotImplementedError()
is VoidType -> {} // nothing to do
else -> {
val retRefType = getKotlinTypeForRefTo(type.returnType)
out("${retRefType.typeExpr}.byPtr(ret).value = res")
}
}
}
out("}")
}
out("}")
}
/**
* Produces to [out] the definition of Kotlin JNI function used in binding for given C function.
*/
@@ -576,6 +693,11 @@ class StubGenerator(
out("")
}
usedFunctionTypes.entries.forEach {
generateFunctionType(it.key, it.value)
out("")
}
out("object externals {")
indent {
out("init { System.loadLibrary(\"$libName\") }")
@@ -8,6 +8,8 @@ class NativeInteropPlugin implements Plugin<Project> {
@Override
void apply(Project prj) {
def runtimeNativeLibsDir = new File(prj.findProject(':Interop:Runtime').buildDir, 'nativelibs')
def nativeLibsDir = new File(prj.buildDir, "nativelibs")
prj.configurations {
@@ -30,7 +32,11 @@ class NativeInteropPlugin implements Plugin<Project> {
main = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt"
jvmArgs '-ea'
systemProperties "java.library.path" : new File(prj.findProject(":Interop:Indexer").buildDir, "nativelibs")
systemProperties "java.library.path" : prj.files(
new File(prj.findProject(":Interop:Indexer").buildDir, "nativelibs"),
runtimeNativeLibsDir
).asPath
systemProperties "llvmInstallPath" : prj.llvmInstallPath
environment "LIBCLANG_DISABLE_CRASH_RECOVERY": "1"
environment "DYLD_LIBRARY_PATH": "${prj.llvmInstallPath}/lib"
@@ -60,7 +66,10 @@ class NativeInteropPlugin implements Plugin<Project> {
// FIXME: choose tasks more wisely
prj.tasks.withType(JavaExec) {
if (!name.endsWith("InteropStubs")) {
systemProperties "java.library.path": nativeLibsDir
systemProperties "java.library.path": prj.files(
nativeLibsDir,
runtimeNativeLibsDir
).asPath
}
}
}