Interop: implement new indexer in Kotlin
and slightly improve StubGenerator code
This commit is contained in:
Generated
+1
-1
@@ -3,10 +3,10 @@
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Example/Example.iml" filepath="$PROJECT_DIR$/Example/Example.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/Indexer/Indexer.iml" filepath="$PROJECT_DIR$/Indexer/Indexer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/Interop.iml" filepath="$PROJECT_DIR$/Interop.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/Runtime/Runtime.iml" filepath="$PROJECT_DIR$/Runtime/Runtime.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/StubGenerator/StubGenerator.iml" filepath="$PROJECT_DIR$/StubGenerator/StubGenerator.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/../kni/indexer/indexer.iml" filepath="$PROJECT_DIR$/../kni/indexer/indexer.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Gen stubs for indexer" type="JetRunConfigurationType" factoryName="Kotlin">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jetbrains.kotlin.native.interop.gen.jvm.MainKt" />
|
||||
<option name="VM_PARAMETERS" value="-Djava.library.path=$PROJECT_DIR$/Indexer/nativelib" />
|
||||
<option name="PROGRAM_PARAMETERS" value="$PROJECT_DIR$/Indexer/src $PROJECT_DIR$/Indexer/nativelib" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="StubGenerator" />
|
||||
<envs />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,7 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="StubGenerator" type="JetRunConfigurationType" factoryName="Kotlin">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jetbrains.kotlin.native.interop.gen.jvm.MainKt" />
|
||||
<option name="VM_PARAMETERS" value="-Djava.library.path=$PROJECT_DIR$/../kni/dist/build/indexer/native" />
|
||||
<option name="VM_PARAMETERS" value="-Djava.library.path=$PROJECT_DIR$/Indexer/nativelib" />
|
||||
<option name="PROGRAM_PARAMETERS" value="$PROJECT_DIR$/Example/src $PROJECT_DIR$/Example/nativelib" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="Runtime" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
clang -shared -o../nativelib/libcallback.dylib callback.c -I /System/Library/Frameworks/JavaVM.framework/Headers -pthread
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <pthread.h>
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
static JavaVM *vm = NULL;
|
||||
|
||||
static JNIEnv* getCurrentEnv() {
|
||||
// printf("%p\n", pthread_self());
|
||||
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_;
|
||||
getCurrentEnv();
|
||||
return JNI_VERSION_1_1;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
libName = clangbridge
|
||||
|
||||
headers = clang-c/Index.h
|
||||
|
||||
compiler = cc
|
||||
|
||||
compilerOpts = -std=c99 -I/usr/local/Cellar/llvm/3.8.1/include -fPIC
|
||||
|
||||
linker = cc
|
||||
|
||||
linkerOpts = -fPIC \
|
||||
-L/usr/local/Cellar/llvm/3.8.1/lib -lclang
|
||||
|
||||
excludedFunctions =
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package org.jetbrains.kotlin.native.interop.indexer
|
||||
|
||||
import clang.CXIdxDeclInfo
|
||||
import kotlin_native.interop.NativePtr
|
||||
|
||||
internal object indexDeclarationCallback {
|
||||
|
||||
private var function: ((CXIdxDeclInfo)->Unit)? = null
|
||||
|
||||
fun setUp(func: (CXIdxDeclInfo)->Unit): NativePtr {
|
||||
assert (function == null)
|
||||
function = func
|
||||
return nativeCallbackPtr
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
assert (function != null)
|
||||
function = null
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun entryFromNative(info: Long) {
|
||||
function!!(CXIdxDeclInfo(NativePtr.Companion.byValue(info)!!))
|
||||
}
|
||||
|
||||
private val nativeCallbackPtr: NativePtr
|
||||
|
||||
init {
|
||||
System.loadLibrary("callback")
|
||||
nativeCallbackPtr = NativePtr.byValue(nativeCallbackPtr())!!
|
||||
}
|
||||
|
||||
private external fun nativeCallbackPtr(): Long
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
package org.jetbrains.kotlin.native.interop.indexer
|
||||
|
||||
import clang.*
|
||||
import clang.CXIdxEntityKind.*
|
||||
import clang.CXTypeKind.*
|
||||
import kotlin_native.interop.*
|
||||
import java.io.File
|
||||
|
||||
private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
|
||||
override var def: StructDefImpl? = null
|
||||
}
|
||||
|
||||
private class StructDefImpl(size: Long, decl: StructDecl) : StructDef(size, decl) {
|
||||
override val fields = mutableListOf<Field>()
|
||||
}
|
||||
|
||||
private class EnumDefImpl(spelling: String, type: PrimitiveType) : EnumDef(spelling, type) {
|
||||
override val values = mutableListOf<EnumValue>()
|
||||
}
|
||||
|
||||
private class NativeIndexImpl : NativeIndex() {
|
||||
|
||||
val arena = Arena()
|
||||
|
||||
fun clearNativeMem() = arena.clear()
|
||||
|
||||
private data class DeclarationID(val usr: String)
|
||||
|
||||
val structById = mutableMapOf<DeclarationID, StructDeclImpl>()
|
||||
|
||||
override val structs: List<StructDecl>
|
||||
get() = structById.values.toList()
|
||||
|
||||
val enumById = mutableMapOf<DeclarationID, EnumDefImpl>()
|
||||
|
||||
override val enums: List<EnumDef>
|
||||
get() = enumById.values.toList()
|
||||
|
||||
val functionByName = mutableMapOf<String, FunctionDecl>()
|
||||
|
||||
override val functions: List<FunctionDecl>
|
||||
get() = functionByName.values.toList()
|
||||
|
||||
fun getDeclarationId(cursor: CXCursor) = DeclarationID(clang_getCursorUSR(cursor, arena).convertAndDispose())
|
||||
|
||||
fun getStructTypeDecl(type: CXType): StructDeclImpl {
|
||||
assert (type.kind.value == CXType_Record)
|
||||
return getStructDeclAt(clang_getTypeDeclaration(type, arena))
|
||||
}
|
||||
|
||||
fun getStructDeclAt(cursor: CXCursor): StructDeclImpl {
|
||||
val declId = getDeclarationId(cursor)
|
||||
|
||||
val decl = structById[declId]
|
||||
if (decl != null) {
|
||||
return decl
|
||||
}
|
||||
|
||||
val cursorType = clang_getCursorType(cursor, arena)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType, arena).convertAndDispose()
|
||||
|
||||
val res = StructDeclImpl(typeSpelling)
|
||||
structById[declId] = res
|
||||
return res
|
||||
}
|
||||
|
||||
fun getEnumTypeDef(type: CXType): EnumDefImpl {
|
||||
assert (type.kind.value == CXType_Enum)
|
||||
val declCursor = clang_getTypeDeclaration(type, arena)
|
||||
return getEnumDefAt(declCursor)
|
||||
}
|
||||
|
||||
fun getEnumDefAt(cursor: CXCursor): EnumDefImpl {
|
||||
if (clang_isCursorDefinition(cursor) == 0) {
|
||||
TODO("support enum forward declarations")
|
||||
}
|
||||
|
||||
val declId = getDeclarationId(cursor)
|
||||
|
||||
val enum = enumById[declId]
|
||||
if (enum != null) {
|
||||
return enum
|
||||
}
|
||||
|
||||
val cursorType = clang_getCursorType(cursor, arena)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType, arena).convertAndDispose()
|
||||
|
||||
val baseType = convertType(clang_getEnumDeclIntegerType(cursor, arena)) as PrimitiveType
|
||||
|
||||
val res = EnumDefImpl(typeSpelling, baseType)
|
||||
enumById[declId] = res
|
||||
return res
|
||||
}
|
||||
|
||||
fun convertType(type: CXType): Type {
|
||||
val kind = type.kind.value
|
||||
return when (kind) {
|
||||
CXType_Unexposed -> {
|
||||
val canonicalType = clang_getCanonicalType(type, arena)
|
||||
if (canonicalType.kind.value != CXType_Unexposed) {
|
||||
convertType(canonicalType)
|
||||
} else {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
|
||||
CXType_Void -> VoidType
|
||||
|
||||
CXType_Char_U, CXType_UChar -> UInt8Type
|
||||
CXType_Char_S, CXType_SChar -> Int8Type
|
||||
|
||||
CXType_UShort -> UInt16Type
|
||||
CXType_Short -> Int16Type
|
||||
|
||||
CXType_UInt -> UInt32Type
|
||||
CXType_Int -> Int32Type
|
||||
|
||||
CXType_ULong -> UIntPtrType
|
||||
CXType_Long -> IntPtrType
|
||||
|
||||
CXType_ULongLong -> UInt64Type
|
||||
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)
|
||||
}
|
||||
|
||||
CXType_Record -> RecordType(getStructTypeDecl(type))
|
||||
CXType_Enum -> EnumType(getEnumTypeDef(type))
|
||||
|
||||
CXType_Pointer -> PointerType(convertType(clang_getPointeeType(type, arena)))
|
||||
|
||||
CXType_ConstantArray -> {
|
||||
val elemType = convertType(clang_getArrayElementType(type, arena))
|
||||
val length = clang_getArraySize(type)
|
||||
ConstArrayType(elemType, length)
|
||||
}
|
||||
|
||||
CXType_IncompleteArray -> {
|
||||
val elemType = convertType(clang_getArrayElementType(type, arena))
|
||||
IncompleteArrayType(elemType)
|
||||
}
|
||||
|
||||
CXType_FunctionProto -> {
|
||||
if (clang_isFunctionTypeVariadic(type) != 0) {
|
||||
UnsupportedType
|
||||
} else {
|
||||
val returnType = convertType(clang_getResultType(type, arena))
|
||||
val numArgs = clang_getNumArgTypes(type)
|
||||
val paramTypes = (0..numArgs - 1).map {
|
||||
convertType(clang_getArgType(type, it, arena))
|
||||
}
|
||||
FunctionType(paramTypes, returnType)
|
||||
}
|
||||
}
|
||||
|
||||
else -> UnsupportedType
|
||||
}
|
||||
}
|
||||
|
||||
fun indexDeclaration(info: CXIdxDeclInfo) {
|
||||
val cursor = info.cursor
|
||||
val entityInfo = info.entityInfo.value!!
|
||||
val entityName = entityInfo.name.value?.asCString()?.toString()
|
||||
val kind = entityInfo.kind.value
|
||||
|
||||
when (kind) {
|
||||
CXIdxEntity_Field -> {
|
||||
val name = entityName!!
|
||||
val type = convertType(clang_getCursorType(cursor, arena))
|
||||
val offset = clang_Cursor_getOffsetOfField(cursor)
|
||||
|
||||
val container = info.semanticContainer.value!!
|
||||
val structDef = getStructDeclAt(container.cursor).def!!
|
||||
structDef.fields.add(Field(name, type, offset))
|
||||
}
|
||||
|
||||
CXIdxEntity_Struct, CXIdxEntity_Union -> {
|
||||
val structDecl = getStructDeclAt(cursor)
|
||||
if (clang_isCursorDefinition(cursor) != 0) {
|
||||
val size = clang_Type_getSizeOf(clang_getCursorType(cursor, arena))
|
||||
structDecl.def = StructDefImpl(size, structDecl)
|
||||
}
|
||||
}
|
||||
|
||||
CXIdxEntity_Function -> {
|
||||
val name = entityName!!
|
||||
val returnType = convertType(clang_getCursorResultType(cursor, arena))
|
||||
val argNum = clang_Cursor_getNumArguments(cursor)
|
||||
val args = (0 .. argNum - 1).map {
|
||||
val argCursor = clang_Cursor_getArgument(cursor, it, arena)
|
||||
val argName = clang_getCursorSpelling(argCursor, arena).convertAndDispose()
|
||||
val type = convertType(clang_getCursorType(argCursor, arena))
|
||||
Parameter(argName, type)
|
||||
}
|
||||
|
||||
functionByName[name] = FunctionDecl(name, args, returnType)
|
||||
}
|
||||
|
||||
CXIdxEntity_Enum -> {
|
||||
getEnumDefAt(cursor)
|
||||
}
|
||||
|
||||
CXIdxEntity_EnumConstant -> {
|
||||
val container = info.semanticContainer.value!!
|
||||
val name = entityName!!
|
||||
val value = clang_getEnumConstantDeclValue(info.cursor)
|
||||
|
||||
getEnumDefAt(container.cursor).values.add(EnumValue(name, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun CXString.convertAndDispose(): String {
|
||||
try {
|
||||
return clang_getCString(this)!!.asCString().toString()
|
||||
} finally {
|
||||
clang_disposeString(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun buildNativeIndexImpl(headerFile: File, args: List<String>): NativeIndex {
|
||||
val args1 = args.map { CString.fromString(it)!!.asCharPtr() }.toTypedArray()
|
||||
|
||||
val index = clang_createIndex(0, 0)
|
||||
val indexAction = clang_IndexAction_create(index)
|
||||
val callbacks = malloc(IndexerCallbacks.Companion)
|
||||
|
||||
val res = NativeIndexImpl()
|
||||
try {
|
||||
val indexDeclarationNativeCallback = indexDeclarationCallback.setUp({ res.indexDeclaration(it) })
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
clang_indexSourceFile(indexAction, null, callbacks, IndexerCallbacks.Companion.size, 0, headerFile.path,
|
||||
mallocNativeArrayOf(Int8Box.Companion, *args1)[0], args1.size, null, 0, null, 0)
|
||||
|
||||
|
||||
return res
|
||||
|
||||
} finally {
|
||||
indexDeclarationCallback.reset()
|
||||
}
|
||||
} finally {
|
||||
res.clearNativeMem()
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package org.jetbrains.kotlin.native.interop.indexer
|
||||
|
||||
import java.io.File
|
||||
|
||||
fun buildNativeIndex(headerFile: File, args: List<String>): NativeIndex = buildNativeIndexImpl(headerFile, args)
|
||||
|
||||
abstract class NativeIndex {
|
||||
abstract val structs: List<StructDecl>
|
||||
abstract val enums: List<EnumDef>
|
||||
abstract val functions: List<FunctionDecl>
|
||||
}
|
||||
|
||||
|
||||
class Field(val name: String, val type: Type, val offset: Long) {
|
||||
override fun toString(): String = "$type $name at $offset"
|
||||
}
|
||||
|
||||
abstract class StructDecl(val spelling: String) {
|
||||
|
||||
abstract val def: StructDef?
|
||||
|
||||
}
|
||||
|
||||
abstract class StructDef(val size: Long, val decl: StructDecl) {
|
||||
abstract val fields: List<Field>
|
||||
}
|
||||
|
||||
class EnumValue(val name: String, val value: Long)
|
||||
|
||||
abstract class EnumDef(val spelling: String, val baseType: PrimitiveType) {
|
||||
abstract val values: List<EnumValue>
|
||||
}
|
||||
|
||||
class Parameter(val name: String?, val type: Type)
|
||||
|
||||
class FunctionDecl(val name: String, val parameters: List<Parameter>, val returnType: Type)
|
||||
|
||||
|
||||
|
||||
open class Type
|
||||
|
||||
open class PrimitiveType : Type()
|
||||
|
||||
object VoidType : Type()
|
||||
|
||||
object Int8Type : PrimitiveType()
|
||||
object UInt8Type : PrimitiveType()
|
||||
|
||||
object Int16Type : PrimitiveType()
|
||||
object UInt16Type : PrimitiveType()
|
||||
|
||||
object Int32Type : PrimitiveType()
|
||||
object UInt32Type : PrimitiveType()
|
||||
|
||||
object IntPtrType : PrimitiveType()
|
||||
object UIntPtrType : PrimitiveType()
|
||||
|
||||
object Int64Type : PrimitiveType()
|
||||
object UInt64Type : PrimitiveType()
|
||||
|
||||
class RecordType(val decl: StructDecl) : Type()
|
||||
|
||||
class EnumType(val def: EnumDef) : Type()
|
||||
|
||||
class PointerType(val pointeeType : Type) : Type()
|
||||
|
||||
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)
|
||||
|
||||
object UnsupportedType : Type()
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="indexer" />
|
||||
<orderEntry type="module" module-name="Indexer" />
|
||||
</component>
|
||||
</module>
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
open class CType
|
||||
open class DirectlyMappedType(val kotlinType: String) : CType()
|
||||
object VoidType : CType()
|
||||
object Int8Type : DirectlyMappedType("Byte")
|
||||
object UInt8Type : DirectlyMappedType("Byte")
|
||||
object Int16Type : DirectlyMappedType("Short")
|
||||
object UInt16Type : DirectlyMappedType("Short")
|
||||
object Int32Type : DirectlyMappedType("Int")
|
||||
object UInt32Type : DirectlyMappedType("Int")
|
||||
object Int64Type : DirectlyMappedType("Long")
|
||||
object UInt64Type : DirectlyMappedType("Long")
|
||||
class RecordType(val name: String) : CType()
|
||||
class EnumType(val name: String) : CType()
|
||||
class PointerType(val pointeeType : CType) : CType()
|
||||
class FunctionPointerType(val parameterTypes: List<CType>, val returnType: CType) : CType()
|
||||
open class ArrayType(val elemType: CType) : CType()
|
||||
class ConstArrayType(elemType: CType, val length: Int) : ArrayType(elemType)
|
||||
class IncompleteArrayType(elemType: CType) : ArrayType(elemType)
|
||||
|
||||
|
||||
fun parseType(type: String): CType = TypeParser(type).parse()
|
||||
|
||||
class TypeParser(private val type: String) {
|
||||
companion object {
|
||||
private val primitiveType = mapOf(
|
||||
"V" to VoidType,
|
||||
"C" to Int8Type,
|
||||
"UC" to UInt8Type,
|
||||
"UB" to UInt8Type,
|
||||
"S" to Int16Type,
|
||||
"US" to UInt16Type,
|
||||
"I" to Int32Type,
|
||||
"UI" to UInt32Type,
|
||||
"J" to Int64Type,
|
||||
"UJ" to UInt64Type
|
||||
)
|
||||
}
|
||||
|
||||
private var at = 0
|
||||
|
||||
private fun at(s: String): Boolean = type.substring(at).startsWith(s)
|
||||
|
||||
private fun expect(s: String) {
|
||||
if (!advance(s)) error("Expecting <$s> (at=$at)")
|
||||
}
|
||||
|
||||
private fun advance(s: String): Boolean {
|
||||
if (at(s)) {
|
||||
at += s.length
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun error(s: String): Nothing = throw IllegalStateException(s + ": " + type)
|
||||
|
||||
fun parse(): CType {
|
||||
if (at == type.length) error("No type to parse")
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val isConst = advance("c")
|
||||
|
||||
for ((string, type) in primitiveType.entries) {
|
||||
if (advance(string)) return type
|
||||
}
|
||||
|
||||
if (advance("R")) {
|
||||
val semicolon = type.indexOf(';', at)
|
||||
if (semicolon < 0) error("L without a matching semicolon")
|
||||
val recordName = type.substring(at, semicolon)
|
||||
expect(recordName)
|
||||
expect(";")
|
||||
return RecordType(recordName)
|
||||
}
|
||||
|
||||
if (advance("E")) { // TODO: copy-pasted!
|
||||
val semicolon = type.indexOf(';', at)
|
||||
if (semicolon < 0) error("E without a matching semicolon")
|
||||
val enumName = type.substring(at, semicolon)
|
||||
expect(enumName)
|
||||
expect(";")
|
||||
return EnumType(enumName)
|
||||
}
|
||||
|
||||
if (advance("*(")) {
|
||||
val paramTypes = mutableListOf<CType>()
|
||||
while (!advance(")")) {
|
||||
if (advance(".")) {
|
||||
// TODO: support vararg
|
||||
continue
|
||||
}
|
||||
paramTypes.add(parse())
|
||||
}
|
||||
val returnType = parse()
|
||||
expect(";")
|
||||
return FunctionPointerType(paramTypes, returnType)
|
||||
}
|
||||
|
||||
if (advance("*")) {
|
||||
val pointee = parse()
|
||||
expect(";")
|
||||
return PointerType(pointee)
|
||||
}
|
||||
|
||||
if (advance("[:")) {
|
||||
val lengthEndIndex = type.indexOf(':', at)
|
||||
val length = Integer.parseInt(type.substring(at, lengthEndIndex))
|
||||
at = lengthEndIndex + 1
|
||||
val elemType = parse()
|
||||
expect(";")
|
||||
return ConstArrayType(elemType, length)
|
||||
}
|
||||
|
||||
if (advance("[")) {
|
||||
val elemType = parse()
|
||||
expect(";")
|
||||
return IncompleteArrayType(elemType)
|
||||
}
|
||||
|
||||
throw NotImplementedError("Unsupported type (at=$at): $type")
|
||||
}
|
||||
}
|
||||
+188
-138
@@ -1,38 +1,46 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
|
||||
import org.jetbrains.kni.indexer.NativeIndex
|
||||
import org.jetbrains.kotlin.native.interop.gen.*
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
class StubGenerator(
|
||||
val translationUnit: NativeIndex.TranslationUnit,
|
||||
val nativeIndex: NativeIndex,
|
||||
val pkgName: String,
|
||||
val libName: String,
|
||||
val excludedFunctions: Set<String>) {
|
||||
|
||||
val forbiddenStructNames = run {
|
||||
val functionNames = translationUnit.functionList.map { it.name }
|
||||
val fieldNames = translationUnit.structList.flatMap { it.fieldList }.map { it.name }
|
||||
val functionNames = nativeIndex.functions.map { it.name }
|
||||
val fieldNames = nativeIndex.structs.mapNotNull { it.def }.flatMap { it.fields }.map { it.name }
|
||||
(functionNames + fieldNames).toSet()
|
||||
}
|
||||
|
||||
val enums = translationUnit.enumList.map { it.name to it }.toMap()
|
||||
val StructDecl.kotlinName: String
|
||||
get() = if (spelling.startsWith("struct ") || spelling.startsWith("union ")) {
|
||||
spelling.substringAfter(' ')
|
||||
} else {
|
||||
spelling
|
||||
}
|
||||
|
||||
val structs = translationUnit.structList.map { it.name to it }.toMap()
|
||||
val EnumDef.kotlinName: String
|
||||
get() = if (spelling.startsWith("enum ")) {
|
||||
spelling.substringAfter(' ')
|
||||
} else {
|
||||
spelling
|
||||
}
|
||||
|
||||
val RecordType.kotlinName: String
|
||||
get() = decl.kotlinName
|
||||
|
||||
val EnumType.kotlinName: String
|
||||
get() = def.kotlinName
|
||||
|
||||
|
||||
val functionsToBind = translationUnit.functionList.uniqueBy { it.name }.filter { it.name !in excludedFunctions }
|
||||
val functionsToBind = nativeIndex.functions.filter { it.name !in excludedFunctions }
|
||||
|
||||
private fun mangleStructName(name: String) = if (name !in forbiddenStructNames) name else (name + "Struct")
|
||||
|
||||
val NativeIndex.CStruct.mangledName: String
|
||||
get() = mangleStructName(name)
|
||||
|
||||
|
||||
val NativeIndex.CForwardStruct.mangledName: String
|
||||
get() = mangleStructName(name)
|
||||
|
||||
val EnumType.baseType: DirectlyMappedType
|
||||
get() = (parseType(enums[name]!!.type) as DirectlyMappedType)
|
||||
val StructDecl.mangledName: String
|
||||
get() = mangleStructName(kotlinName)
|
||||
|
||||
private var out: (String) -> Unit = {
|
||||
throw IllegalStateException()
|
||||
@@ -61,8 +69,10 @@ class StubGenerator(
|
||||
return withOutput({ oldOut(" $it") }, action)
|
||||
}
|
||||
|
||||
fun CType.getStringRepresentation(): String {
|
||||
// TODO: use libclang to implement:
|
||||
fun Type.getStringRepresentation(): String {
|
||||
return when (this) {
|
||||
|
||||
is VoidType -> "void"
|
||||
is Int8Type -> "char"
|
||||
is UInt8Type -> "unsigned char"
|
||||
@@ -70,44 +80,75 @@ class StubGenerator(
|
||||
is UInt16Type -> "unsigned short"
|
||||
is Int32Type -> "int"
|
||||
is UInt32Type -> "unsigned int"
|
||||
is IntPtrType -> "intptr_t"
|
||||
is UIntPtrType -> "uintptr_t"
|
||||
is Int64Type -> "int64_t"
|
||||
is UInt64Type -> "uint64_t"
|
||||
is PointerType -> pointeeType.getStringRepresentation() + "*"
|
||||
is RecordType -> structs[name]?.spelling ?: "void" // FIXME
|
||||
is FunctionPointerType -> this.returnType.getStringRepresentation() + " (*)(" +
|
||||
this.parameterTypes.map { it.getStringRepresentation() }.joinToString(", ") + ")"
|
||||
|
||||
is PointerType -> {
|
||||
val pointeeType = this.pointeeType
|
||||
if (pointeeType is FunctionType) {
|
||||
pointeeType.returnType.getStringRepresentation() + " (*)(" +
|
||||
pointeeType.parameterTypes.map { it.getStringRepresentation() }.joinToString(", ") + ")"
|
||||
} else {
|
||||
pointeeType.getStringRepresentation() + "*"
|
||||
}
|
||||
}
|
||||
|
||||
is RecordType -> this.decl.spelling
|
||||
|
||||
is ArrayType -> "void*" // TODO
|
||||
is EnumType -> enums["${this.name}"]!!.spelling
|
||||
|
||||
is EnumType -> this.def.spelling
|
||||
|
||||
else -> throw kotlin.NotImplementedError()
|
||||
}
|
||||
}
|
||||
|
||||
val PrimitiveType.kotlinType: String
|
||||
get() = when (this) {
|
||||
|
||||
is Int8Type, is UInt8Type -> "Byte"
|
||||
is Int16Type, is UInt16Type -> "Short"
|
||||
is Int32Type, is UInt32Type -> "Int"
|
||||
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
||||
is Int64Type, is UInt64Type -> "Long"
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
class NativeRefType(val typeName: String, val typeExpr: String = typeName)
|
||||
|
||||
fun getKotlinNativeRefType(type: CType): NativeRefType = when (type) {
|
||||
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")
|
||||
is RecordType -> NativeRefType("${mangleStructName(type.name)}")
|
||||
is EnumType -> NativeRefType("${type.name}.ref")
|
||||
|
||||
is RecordType -> NativeRefType("${mangleStructName(type.kotlinName)}")
|
||||
|
||||
is EnumType -> NativeRefType("${type.kotlinName}.ref")
|
||||
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType) {
|
||||
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
|
||||
NativeRefType("NativePtrBox")
|
||||
} else {
|
||||
val pointeeRefType = getKotlinNativeRefType(type.pointeeType)
|
||||
val pointeeRefType = getKotlinTypeForRefTo(type.pointeeType)
|
||||
NativeRefType("RefBox<${pointeeRefType.typeName}>", "${pointeeRefType.typeExpr}.ref")
|
||||
}
|
||||
}
|
||||
is FunctionPointerType -> getKotlinNativeRefType(PointerType(VoidType))
|
||||
|
||||
is ConstArrayType -> {
|
||||
val elemRefType = getKotlinNativeRefType(type.elemType)
|
||||
val elemRefType = getKotlinTypeForRefTo(type.elemType)
|
||||
NativeRefType("NativeArray<${elemRefType.typeName}>", "array[${type.length}](${elemRefType.typeExpr})")
|
||||
}
|
||||
|
||||
is IncompleteArrayType -> {
|
||||
val elemRefType = getKotlinNativeRefType(type.elemType)
|
||||
val elemRefType = getKotlinTypeForRefTo(type.elemType)
|
||||
NativeRefType("NativeArray<${elemRefType.typeName}>", "array(${elemRefType.typeExpr})")
|
||||
}
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
@@ -132,10 +173,12 @@ class StubGenerator(
|
||||
kotlinType = refType.typeName + "?"
|
||||
)
|
||||
|
||||
fun getOutValueBinding(type: CType): OutValueBinding = when (type) {
|
||||
is DirectlyMappedType -> OutValueBinding(type.kotlinType)
|
||||
fun getOutValueBinding(type: Type): OutValueBinding = when (type) {
|
||||
|
||||
is PrimitiveType -> OutValueBinding(type.kotlinType)
|
||||
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType) {
|
||||
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
|
||||
OutValueBinding(
|
||||
kotlinType = "NativePtr?",
|
||||
kotlinConv = { "$it.asLong()" },
|
||||
@@ -149,18 +192,20 @@ class StubGenerator(
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
} else {
|
||||
outValueRefBinding(getKotlinNativeRefType(type.pointeeType))
|
||||
outValueRefBinding(getKotlinTypeForRefTo(type.pointeeType))
|
||||
}
|
||||
}
|
||||
|
||||
is EnumType -> OutValueBinding(
|
||||
kotlinType = type.name,
|
||||
kotlinType = type.kotlinName,
|
||||
kotlinConv = { "$it.value" },
|
||||
kotlinJniBridgeType = type.baseType.kotlinType
|
||||
kotlinJniBridgeType = type.def.baseType.kotlinType
|
||||
)
|
||||
is ArrayType -> outValueRefBinding(getKotlinNativeRefType(type))
|
||||
is FunctionPointerType -> getOutValueBinding(PointerType(VoidType))
|
||||
|
||||
is ArrayType -> outValueRefBinding(getKotlinTypeForRefTo(type))
|
||||
|
||||
is RecordType -> {
|
||||
val refType = getKotlinNativeRefType(type)
|
||||
val refType = getKotlinTypeForRefTo(type)
|
||||
// pointer will be converted to value in C code
|
||||
OutValueBinding(
|
||||
kotlinType = refType.typeName,
|
||||
@@ -168,61 +213,69 @@ class StubGenerator(
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
}
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun getInValueBinding(type: CType): InValueBinding = when (type) {
|
||||
fun getInValueBinding(type: Type): InValueBinding = when (type) {
|
||||
|
||||
is PrimitiveType -> InValueBinding(type.kotlinType)
|
||||
|
||||
is VoidType -> InValueBinding("Unit")
|
||||
is DirectlyMappedType -> InValueBinding(type.kotlinType)
|
||||
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType) {
|
||||
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
|
||||
InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { "NativePtr.byValue($it)" },
|
||||
kotlinType = "NativePtr?"
|
||||
)
|
||||
} else {
|
||||
inValueRefBinding(getKotlinNativeRefType(type.pointeeType))
|
||||
inValueRefBinding(getKotlinTypeForRefTo(type.pointeeType))
|
||||
}
|
||||
}
|
||||
|
||||
is EnumType -> InValueBinding(
|
||||
kotlinJniBridgeType = type.baseType.kotlinType,
|
||||
conv = { "${type.name}.byValue($it)" },
|
||||
kotlinType = type.name
|
||||
kotlinJniBridgeType = type.def.baseType.kotlinType,
|
||||
conv = { "${type.kotlinName}.byValue($it)" },
|
||||
kotlinType = type.kotlinName
|
||||
)
|
||||
is ArrayType -> inValueRefBinding(getKotlinNativeRefType(type))
|
||||
|
||||
is ArrayType -> inValueRefBinding(getKotlinTypeForRefTo(type))
|
||||
|
||||
is RecordType -> {
|
||||
// FIXME
|
||||
val refType = getKotlinNativeRefType(type)
|
||||
// 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()
|
||||
}
|
||||
|
||||
|
||||
private fun <T> Iterable<T>.uniqueBy(key: (T) -> Any): List<T> {
|
||||
val found = mutableSetOf<Any>()
|
||||
return this.filter { found.add(key(it)) }
|
||||
}
|
||||
private fun generateKotlinStruct(decl: StructDecl) {
|
||||
val def = decl.def
|
||||
if (def == null) {
|
||||
generateKotlinForwardStruct(decl)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
private fun generateKotlinStruct(s: NativeIndex.CStruct) {
|
||||
val className = s.mangledName
|
||||
val className = decl.mangledName
|
||||
out("class $className(ptr: NativePtr) : NativeStruct(ptr) {")
|
||||
indent {
|
||||
out("")
|
||||
out("companion object : Type<$className>(${s.size}, ::$className)")
|
||||
out("companion object : Type<$className>(${def.size}, ::$className)")
|
||||
out("")
|
||||
s.fieldList.forEach { field ->
|
||||
def.fields.forEach { field ->
|
||||
try {
|
||||
if (field.offset < 0) throw NotImplementedError();
|
||||
assert(field.offset % 8 == 0L)
|
||||
val offset = field.offset / 8
|
||||
val fieldRefType = getKotlinNativeRefType(parseType(field.type))
|
||||
val fieldRefType = getKotlinTypeForRefTo(field.type)
|
||||
out("val ${field.name} by ${fieldRefType.typeExpr} at $offset")
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate definition for field $className.${field.name}")
|
||||
@@ -232,31 +285,30 @@ class StubGenerator(
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun generateKotlinForwardStruct(s: NativeIndex.CForwardStruct) {
|
||||
private fun generateKotlinForwardStruct(s: StructDecl) {
|
||||
val className = s.mangledName
|
||||
out("class $className(ptr: NativePtr) : NativeRef(ptr) {")
|
||||
out(" companion object : Type<$className>(::$className)")
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun generateKotlinEnum(e: NativeIndex.CEnum) {
|
||||
val baseType = (parseType(e.type) as DirectlyMappedType)
|
||||
val baseRefType = getKotlinNativeRefType(baseType)
|
||||
private fun generateKotlinEnum(e: EnumDef) {
|
||||
val baseRefType = getKotlinTypeForRefTo(e.baseType)
|
||||
|
||||
out("enum class ${e.name}(val value: ${baseType.kotlinType}) {")
|
||||
out("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType}) {")
|
||||
indent {
|
||||
e.valueList.forEach {
|
||||
e.values.forEach {
|
||||
out("${it.name}(${it.value}),")
|
||||
}
|
||||
out(";")
|
||||
out("")
|
||||
out("companion object {")
|
||||
out(" fun byValue(value: ${baseType.kotlinType}) = ${e.name}.values().find { it.value == value }!!")
|
||||
out(" fun byValue(value: ${e.baseType.kotlinType}) = ${e.kotlinName}.values().find { it.value == value }!!")
|
||||
out("}")
|
||||
out("")
|
||||
out("class ref(ptr: NativePtr) : NativeRef(ptr) {")
|
||||
out(" companion object : TypeWithSize<ref>(${baseRefType.typeExpr}.size, ::ref)")
|
||||
out(" var value: ${e.name}")
|
||||
out(" var value: ${e.kotlinName}")
|
||||
out(" get() = byValue(${baseRefType.typeExpr}.byPtr(ptr).value)")
|
||||
out(" set(value) { ${baseRefType.typeExpr}.byPtr(ptr).value = value.value }")
|
||||
out("}")
|
||||
@@ -265,16 +317,16 @@ class StubGenerator(
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun retValBinding(func: NativeIndex.Function) = getInValueBinding(parseType(func.returnType))
|
||||
private fun retValBinding(func: FunctionDecl) = getInValueBinding(func.returnType)
|
||||
|
||||
private fun paramBindings(func: NativeIndex.Function): Array<OutValueBinding> {
|
||||
val paramBindings = func.parameterList.map { param ->
|
||||
getOutValueBinding(parseType(param.type))
|
||||
private fun paramBindings(func: FunctionDecl): Array<OutValueBinding> {
|
||||
val paramBindings = func.parameters.map { param ->
|
||||
getOutValueBinding(param.type)
|
||||
}.toMutableList()
|
||||
|
||||
val retValType = parseType(func.returnType)
|
||||
val retValType = func.returnType
|
||||
if (retValType is RecordType) {
|
||||
val retValRefType = getKotlinNativeRefType(retValType)
|
||||
val retValRefType = getKotlinTypeForRefTo(retValType)
|
||||
val typeExpr = retValRefType.typeExpr
|
||||
|
||||
paramBindings.add(OutValueBinding(
|
||||
@@ -287,19 +339,24 @@ class StubGenerator(
|
||||
return paramBindings.toTypedArray()
|
||||
}
|
||||
|
||||
private fun paramNames(func: NativeIndex.Function): Array<String> {
|
||||
val paramNames = func.parameterList.mapIndexed { i: Int, parameter: NativeIndex.Function.Parameter ->
|
||||
if (parameter.name != "") parameter.name else "arg$i"
|
||||
private fun paramNames(func: FunctionDecl): Array<String> {
|
||||
val paramNames = func.parameters.mapIndexed { i: Int, parameter: Parameter ->
|
||||
val name = parameter.name
|
||||
if (name != null && name != "") {
|
||||
name
|
||||
} else {
|
||||
"arg$i"
|
||||
}
|
||||
}.toMutableList()
|
||||
|
||||
if (parseType(func.returnType) is RecordType) {
|
||||
if (func.returnType is RecordType) {
|
||||
paramNames.add("retValPlacement")
|
||||
}
|
||||
|
||||
return paramNames.toTypedArray()
|
||||
}
|
||||
|
||||
private fun generateKotlinBindingMethod(func: NativeIndex.Function) {
|
||||
private fun generateKotlinBindingMethod(func: FunctionDecl) {
|
||||
val paramNames = paramNames(func)
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
@@ -337,7 +394,7 @@ class StubGenerator(
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun generateKotlinExternalMethod(func: NativeIndex.Function) {
|
||||
private fun generateKotlinExternalMethod(func: FunctionDecl) {
|
||||
val paramNames = paramNames(func)
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
@@ -368,29 +425,18 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
val generatedStructs = mutableSetOf<String>()
|
||||
|
||||
translationUnit.structList.uniqueBy { it.name }.forEach { s ->
|
||||
nativeIndex.structs.forEach { s ->
|
||||
try {
|
||||
transaction {
|
||||
generateKotlinStruct(s)
|
||||
out("")
|
||||
generatedStructs.add(s.name)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate definition for struct ${s.name}")
|
||||
println("Warning: cannot generate definition for struct ${s.kotlinName}")
|
||||
}
|
||||
}
|
||||
|
||||
translationUnit.forwardStructList
|
||||
.uniqueBy { it.name }
|
||||
.filter { it.name !in generatedStructs }
|
||||
.forEach { s ->
|
||||
generateKotlinForwardStruct(s)
|
||||
out("")
|
||||
}
|
||||
|
||||
translationUnit.enumList.uniqueBy { it.name }.forEach { e ->
|
||||
nativeIndex.enums.forEach { e ->
|
||||
generateKotlinEnum(e)
|
||||
out("")
|
||||
}
|
||||
@@ -421,8 +467,7 @@ class StubGenerator(
|
||||
else -> throw NotImplementedError(kotlinJniBridgeType)
|
||||
}
|
||||
|
||||
|
||||
fun generateCFile(headerFiles: List<String>, translationUnit: NativeIndex.TranslationUnit) {
|
||||
fun generateCFile(headerFiles: List<String>) {
|
||||
out("#include <stdint.h>")
|
||||
out("#include <jni.h>")
|
||||
headerFiles.forEach {
|
||||
@@ -432,51 +477,56 @@ class StubGenerator(
|
||||
|
||||
functionsToBind.forEach { func ->
|
||||
try {
|
||||
val paramNames = paramNames(func)
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
|
||||
val args =
|
||||
if (paramBindings.isEmpty())
|
||||
""
|
||||
else paramBindings
|
||||
.map { getCJniBridgeType(it.kotlinJniBridgeType) }
|
||||
.mapIndexed { i, type -> "$type ${paramNames[i]}" }
|
||||
.joinToString(separator = ", ", prefix = ", ")
|
||||
|
||||
val cReturnType = getCJniBridgeType(retValBinding.kotlinJniBridgeType)
|
||||
|
||||
val params = func.parameterList.mapIndexed { i, parameter ->
|
||||
val cType = parseType(parameter.type).getStringRepresentation()
|
||||
if (parseType(parameter.type) is RecordType) {
|
||||
"*($cType*)${paramNames[i]}" // FIXME
|
||||
} else {
|
||||
"($cType)${paramNames[i]}"
|
||||
}
|
||||
}.joinToString(", ")
|
||||
|
||||
val callExpr = "${func.name}($params)"
|
||||
val funcFullName = if (pkgName.isEmpty()) {
|
||||
"externals.${func.name}"
|
||||
} else {
|
||||
"$pkgName.externals.${func.name}"
|
||||
}
|
||||
val jniFuncName = "Java_" + funcFullName.replace("_", "_1").replace('.', '_').replace("$", "_00024")
|
||||
|
||||
out("JNIEXPORT $cReturnType JNICALL $jniFuncName (JNIEnv *env, jobject obj$args) {")
|
||||
|
||||
if (cReturnType == "void") {
|
||||
out(" $callExpr;")
|
||||
} else if (retValBinding.kotlinType in structs) { // FIXME
|
||||
out(" *(${structs[retValBinding.kotlinType]!!.spelling}*)retValPlacement = $callExpr;")
|
||||
out(" return ($cReturnType) retValPlacement;")
|
||||
} else {
|
||||
out(" return ($cReturnType) ($callExpr);")
|
||||
}
|
||||
out("}")
|
||||
generateCJniFunction(func)
|
||||
} catch (e: Throwable) {
|
||||
System.err.println("Warning: cannot generate C JNI function definition ${func.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateCJniFunction(func: FunctionDecl) {
|
||||
val funcReturnType = func.returnType
|
||||
val paramNames = paramNames(func)
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
|
||||
val args =
|
||||
if (paramBindings.isEmpty())
|
||||
""
|
||||
else paramBindings
|
||||
.map { getCJniBridgeType(it.kotlinJniBridgeType) }
|
||||
.mapIndexed { i, type -> "$type ${paramNames[i]}" }
|
||||
.joinToString(separator = ", ", prefix = ", ")
|
||||
|
||||
val cReturnType = getCJniBridgeType(retValBinding.kotlinJniBridgeType)
|
||||
|
||||
val params = func.parameters.mapIndexed { i, parameter ->
|
||||
val cType = parameter.type.getStringRepresentation()
|
||||
if (parameter.type is RecordType) {
|
||||
"*($cType*)${paramNames[i]}"
|
||||
} else {
|
||||
"($cType)${paramNames[i]}"
|
||||
}
|
||||
}.joinToString(", ")
|
||||
|
||||
val callExpr = "${func.name}($params)"
|
||||
val funcFullName = if (pkgName.isEmpty()) {
|
||||
"externals.${func.name}"
|
||||
} else {
|
||||
"$pkgName.externals.${func.name}"
|
||||
}
|
||||
val jniFuncName = "Java_" + funcFullName.replace("_", "_1").replace('.', '_').replace("$", "_00024")
|
||||
|
||||
out("JNIEXPORT $cReturnType JNICALL $jniFuncName (JNIEnv *env, jobject obj$args) {")
|
||||
|
||||
if (cReturnType == "void") {
|
||||
out(" $callExpr;")
|
||||
} else if (funcReturnType is RecordType) {
|
||||
out(" *(${funcReturnType.decl.spelling}*)retValPlacement = $callExpr;")
|
||||
out(" return ($cReturnType) retValPlacement;")
|
||||
} else {
|
||||
out(" return ($cReturnType) ($callExpr);")
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
}
|
||||
+9
-13
@@ -1,10 +1,7 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
|
||||
import org.jetbrains.kni.indexer.IndexerOptions
|
||||
import org.jetbrains.kni.indexer.Language
|
||||
import org.jetbrains.kni.indexer.NativeIndex
|
||||
import org.jetbrains.kni.indexer.buildNativeIndex
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.StubGenerator
|
||||
import org.jetbrains.kotlin.native.interop.indexer.NativeIndex
|
||||
import org.jetbrains.kotlin.native.interop.indexer.buildNativeIndex
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.system.exitProcess
|
||||
@@ -35,9 +32,9 @@ fun main(args: Array<String>) {
|
||||
|
||||
|
||||
|
||||
val translationUnit = buildNativeIndex(headerFiles, compilerOpts)
|
||||
val nativeIndex = buildNativeIndex(headerFiles, compilerOpts)
|
||||
|
||||
val gen = StubGenerator(translationUnit, outKtPkg, libName, excludedFunctions)
|
||||
val gen = StubGenerator(nativeIndex, outKtPkg, libName, excludedFunctions)
|
||||
|
||||
File(outKtFile).bufferedWriter().use { out ->
|
||||
gen.withOutput({ out.appendln(it) }) {
|
||||
@@ -50,7 +47,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
outCFile.bufferedWriter().use { out ->
|
||||
gen.withOutput({ out.appendln(it) }) {
|
||||
gen.generateCFile(headerFiles, translationUnit)
|
||||
gen.generateCFile(headerFiles)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,12 +88,10 @@ fun main(args: Array<String>) {
|
||||
|
||||
outCFile.delete()
|
||||
outOFile.delete()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun buildNativeIndex(headerFiles: List<String>, compilerOpts: List<String>): NativeIndex.TranslationUnit {
|
||||
private fun buildNativeIndex(headerFiles: List<String>, compilerOpts: List<String>): NativeIndex {
|
||||
val tempHeaderFile = createTempFile(suffix = ".h")
|
||||
tempHeaderFile.deleteOnExit()
|
||||
tempHeaderFile.writer().buffered().use { reader ->
|
||||
@@ -105,6 +100,7 @@ private fun buildNativeIndex(headerFiles: List<String>, compilerOpts: List<Strin
|
||||
}
|
||||
}
|
||||
|
||||
val indexerOptions = IndexerOptions(language = Language.CPP, args = compilerOpts)
|
||||
return buildNativeIndex(tempHeaderFile, indexerOptions)
|
||||
val res = buildNativeIndex(tempHeaderFile, compilerOpts)
|
||||
println(res)
|
||||
return res
|
||||
}
|
||||
Reference in New Issue
Block a user