Interop: improve by using callbacks in Indexer

This commit is contained in:
Svyatoslav Scherbina
2016-10-10 13:44:08 +03:00
parent af09ce085c
commit 373bf3bef9
7 changed files with 155 additions and 148 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>
}
@@ -590,10 +590,15 @@ class StubGenerator(
is IntPtrType, is UIntPtrType, // TODO
is PointerType, is ArrayType -> "Pointer"
is EnumType -> getFfiType(type.def.baseType)
// FIXME: check that alignment is natural
is RecordType -> "Struct(" + type.decl.def!!.fields.map {
getFfiType(it.type)
}.joinToString(", ") + ")"
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())
}
}