experiments/Interop: add first prototype
This commit is contained in:
Generated
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime">
|
||||
<CLASSES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-runtime.jar!/" />
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-runtime-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<component name="libraryTable">
|
||||
<library name="com.google.protobuf:protobuf-java:2.6.1" type="repository">
|
||||
<properties maven-id="com.google.protobuf:protobuf-java:2.6.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/protobuf-java-2.6.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
Generated
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Example/Example.iml" filepath="$PROJECT_DIR$/Example/Example.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="Example" type="JetRunConfigurationType" factoryName="Kotlin">
|
||||
<option name="MAIN_CLASS_NAME" value="MainKt" />
|
||||
<option name="VM_PARAMETERS" value="-Djava.library.path=$MODULE_DIR$/nativelib" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<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="Example" />
|
||||
<envs />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,14 @@
|
||||
<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="PROGRAM_PARAMETERS" value="$PROJECT_DIR$/Example/src $PROJECT_DIR$/Example/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>
|
||||
@@ -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,60 @@
|
||||
libName = llvmbridge
|
||||
|
||||
headers = llvm-c/Core.h llvm-c/ExecutionEngine.h llvm-c/Target.h llvm-c/Analysis.h llvm-c/BitWriter.h
|
||||
|
||||
|
||||
compiler = cc
|
||||
|
||||
compilerOpts = -std=c99 -I/usr/local/Cellar/llvm/3.8.1/include -fPIC \
|
||||
-Wall -W -Wno-unused-parameter -Wwrite-strings -Wmissing-field-initializers \
|
||||
-pedantic -Wno-long-long -Wcovered-switch-default -Wdelete-non-virtual-dtor \
|
||||
-DNDEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
|
||||
|
||||
|
||||
linker = c++
|
||||
|
||||
linkerOpts = -stdlib=libc++ -fPIC -fvisibility-inlines-hidden \
|
||||
-Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers \
|
||||
-pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor \
|
||||
-std=c++11 \
|
||||
-DNDEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS \
|
||||
-L/usr/local/Cellar/llvm/3.8.1/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names \
|
||||
-lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter \
|
||||
-lLLVMX86Desc -lLLVMMCDisassembler -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMInterpreter \
|
||||
-lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMInstrumentation -lLLVMProfileData -lLLVMTransformUtils \
|
||||
-lLLVMBitWriter -lLLVMExecutionEngine -lLLVMTarget -lLLVMAnalysis -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser \
|
||||
-lLLVMBitReader -lLLVMMC -lLLVMCore -lLLVMSupport -lcurses -lpthread -lz -lm \
|
||||
-L/usr/local/Cellar/libffi/3.0.13/lib -lffi
|
||||
|
||||
excludedFunctions = \
|
||||
LLVMInitializeAMDGPUAsmParser \
|
||||
LLVMInitializeAMDGPUAsmPrinter \
|
||||
LLVMInitializeAMDGPUTarget \
|
||||
LLVMInitializeAMDGPUTargetInfo \
|
||||
LLVMInitializeAMDGPUTargetMC \
|
||||
LLVMInitializeARMAsmParser \
|
||||
LLVMInitializeARMAsmPrinter \
|
||||
LLVMInitializeARMDisassembler \
|
||||
LLVMInitializeARMTarget \
|
||||
LLVMInitializeARMTargetInfo \
|
||||
LLVMInitializeARMTargetMC \
|
||||
LLVMInitializeNVPTXAsmPrinter \
|
||||
LLVMInitializeNVPTXTarget \
|
||||
LLVMInitializeNVPTXTargetInfo \
|
||||
LLVMInitializeNVPTXTargetMC \
|
||||
LLVMLinkInMCJIT \
|
||||
LLVMInitializeAllAsmParsers \
|
||||
LLVMInitializeAllAsmPrinters \
|
||||
LLVMInitializeAllTargets \
|
||||
LLVMInitializeAllTargetInfos \
|
||||
LLVMInitializeAllTargetMCs \
|
||||
LLVMInitializeAllAsmParsers \
|
||||
LLVMInitializeAllAsmPrinters \
|
||||
LLVMInitializeAllDisassemblers \
|
||||
LLVMInitializeAllTargets \
|
||||
LLVMInitializeAllTargetInfos \
|
||||
LLVMInitializeAllTargetMCs \
|
||||
LLVMInitializeAllAsmPrinters \
|
||||
LLVMInitializeAllTargets \
|
||||
LLVMInitializeAllTargetInfos \
|
||||
LLVMInitializeAllTargetMCs
|
||||
@@ -0,0 +1,48 @@
|
||||
import kotlin_native.interop.*
|
||||
import llvm.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val module = LLVMModuleCreateWithName("module")
|
||||
println("module=" + module.getNativePtr().asLong())
|
||||
|
||||
val paramTypes = mallocNativeArrayOf(LLVMOpaqueType, LLVMInt32Type(), LLVMInt32Type())
|
||||
val retType = LLVMFunctionType(LLVMInt32Type(), paramTypes[0], 2, 0)
|
||||
free(paramTypes)
|
||||
|
||||
val sum = LLVMAddFunction(module, "sum", retType)
|
||||
val entry = LLVMAppendBasicBlock(sum, "entry")
|
||||
val builder = LLVMCreateBuilder()
|
||||
LLVMPositionBuilderAtEnd(builder, entry)
|
||||
val tmp = LLVMBuildAdd(builder, LLVMGetParam(sum, 0), LLVMGetParam(sum, 1), "tmp")
|
||||
LLVMBuildRet(builder, tmp)
|
||||
val engineRef = malloc(Ref to LLVMOpaqueExecutionEngine)
|
||||
val errorRef = malloc(Ref to Int8Box)
|
||||
LLVMInitializeNativeTarget()
|
||||
errorRef.value = null
|
||||
if (LLVMCreateExecutionEngineForModule(engineRef, module, errorRef) != 0) {
|
||||
println("failed to create execution engine")
|
||||
return
|
||||
}
|
||||
val error = errorRef.value
|
||||
if (error != null) {
|
||||
println(CString.fromArray(NativeArray.byRefToFirstElem(error, Int8Box)).toString())
|
||||
return
|
||||
}
|
||||
|
||||
println(LLVMGetTypeKind(LLVMInt32Type()))
|
||||
val x = 5L
|
||||
val y = 6L
|
||||
val args = malloc(array[2](Ref to LLVMOpaqueGenericValue))
|
||||
args[0].value = LLVMCreateGenericValueOfInt(LLVMInt32Type(), x, 0)
|
||||
args[1].value = LLVMCreateGenericValueOfInt(LLVMInt32Type(), y, 0)
|
||||
val runRes = LLVMRunFunction(engineRef.value, sum, 2, args[0])
|
||||
println(LLVMGenericValueToInt(runRes, 0))
|
||||
if (LLVMWriteBitcodeToFile(module, "/tmp/sum.bc") != 0) {
|
||||
println("error writing bitcode to file, skipping")
|
||||
}
|
||||
LLVMDisposeBuilder(builder)
|
||||
LLVMDisposeExecutionEngine(engineRef.value)
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,46 @@
|
||||
package kotlin_native.interop
|
||||
|
||||
import sun.misc.Unsafe
|
||||
|
||||
data class NativePtr private constructor(internal val value: Long) {
|
||||
|
||||
fun displacedBy(offset: Int) = NativePtr(value + offset)
|
||||
|
||||
companion object {
|
||||
fun byValue(value: Long): NativePtr? {
|
||||
return if (value == 0L) {
|
||||
null
|
||||
} else {
|
||||
NativePtr(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun NativePtr?.asLong() = if (this == null) 0L else value
|
||||
|
||||
internal object bridge {
|
||||
|
||||
fun malloc(size: Int): NativePtr = NativePtr.byValue(unsafe.allocateMemory(size.toLong()))!!
|
||||
fun free(ptr: NativePtr) = unsafe.freeMemory(ptr.asLong())
|
||||
|
||||
fun getInt64(ptr: NativePtr): Long = unsafe.getLong(ptr.asLong())
|
||||
fun putInt64(ptr: NativePtr, value: Long) = unsafe.putLong(ptr.asLong(), value)
|
||||
|
||||
fun getPtr(ptr: NativePtr): NativePtr? = NativePtr.byValue(unsafe.getLong(ptr.asLong()))
|
||||
fun putPtr(ptr: NativePtr, value: NativePtr?) = unsafe.putLong(ptr.asLong(), value.asLong())
|
||||
|
||||
fun getInt32(ptr: NativePtr): Int = unsafe.getInt(ptr.asLong())
|
||||
fun putInt32(ptr: NativePtr, value: Int) = unsafe.putInt(ptr.asLong(), value)
|
||||
|
||||
fun getInt16(ptr: NativePtr): Short = unsafe.getShort(ptr.asLong())
|
||||
fun putInt16(ptr: NativePtr, value: Short) = unsafe.putShort(ptr.asLong(), value)
|
||||
|
||||
fun getInt8(ptr: NativePtr): Byte = unsafe.getByte(ptr.asLong())
|
||||
fun putInt8(ptr: NativePtr, value: Byte) = unsafe.putByte(ptr.asLong(), value)
|
||||
|
||||
private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) {
|
||||
isAccessible = true
|
||||
return@with this.get(null) as Unsafe
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package kotlin_native.interop
|
||||
|
||||
// TODO: what about equals/hashCode?
|
||||
|
||||
open class NativeRef(val ptr: NativePtr) {
|
||||
open class Type<T : NativeRef>(val byPtr: (NativePtr) -> T)
|
||||
open class TypeWithSize<T : NativeRef>(val size: Int, byPtr: (NativePtr) -> T) : NativeRef.Type<T>(byPtr)
|
||||
}
|
||||
|
||||
fun <T : NativeRef> NativePtr?.asRef(type: NativeRef.Type<T>) = this?.let { type.byPtr(this) }
|
||||
fun NativeRef?.getNativePtr() = this?.ptr
|
||||
|
||||
class Int8Box(ptr: NativePtr) : NativeRef(ptr) {
|
||||
companion object : NativeRef.TypeWithSize<Int8Box>(1, ::Int8Box)
|
||||
var value: Byte
|
||||
get() = bridge.getInt8(ptr)
|
||||
set(value) = bridge.putInt8(ptr, value)
|
||||
}
|
||||
|
||||
class Int16Box(ptr: NativePtr) : NativeRef(ptr) {
|
||||
companion object : NativeRef.TypeWithSize<Int16Box>(2, ::Int16Box)
|
||||
var value: Short
|
||||
get() = bridge.getInt16(ptr)
|
||||
set(value) = bridge.putInt16(ptr, value)
|
||||
}
|
||||
|
||||
class Int32Box(ptr: NativePtr) : NativeRef(ptr) {
|
||||
companion object : NativeRef.TypeWithSize<Int32Box>(4, ::Int32Box)
|
||||
var value: Int
|
||||
get() = bridge.getInt32(ptr)
|
||||
set(value) = bridge.putInt32(ptr, value)
|
||||
}
|
||||
|
||||
class NativePtrBox(ptr: NativePtr) : NativeRef(ptr) {
|
||||
companion object : NativeRef.TypeWithSize<NativePtrBox>(8, ::NativePtrBox) // TODO: 64-bit specific
|
||||
var value: NativePtr?
|
||||
get() = bridge.getPtr(ptr)
|
||||
set(value) = bridge.putPtr(ptr, value)
|
||||
}
|
||||
|
||||
class Int64Box(ptr: NativePtr) : NativeRef(ptr) {
|
||||
companion object : NativeRef.TypeWithSize<Int64Box>(8, ::Int64Box)
|
||||
var value: Long
|
||||
get() = bridge.getInt64(ptr)
|
||||
set(value) = bridge.putInt64(ptr, value)
|
||||
}
|
||||
|
||||
class RefBox<T : NativeRef>(ptr: NativePtr, val referentType: NativeRef.Type<T>) : NativeRef(ptr) {
|
||||
companion object {
|
||||
infix fun <T : NativeRef> of(type: NativeRef.Type<T>) = NativeRef.TypeWithSize<RefBox<T>>(8, { RefBox(it, type) }) // TODO: 64-bit specific
|
||||
}
|
||||
|
||||
var value: T?
|
||||
get() = bridge.getPtr(ptr).asRef(referentType)
|
||||
set(value) = bridge.putPtr(ptr, value.getNativePtr())
|
||||
}
|
||||
|
||||
val <T : NativeRef> NativeRef.Type<T>.ref: NativeRef.TypeWithSize<RefBox<T>>
|
||||
get() = Ref to this
|
||||
|
||||
object Ref {
|
||||
infix fun <T : NativeRef> to(type: NativeRef.Type<T>) = RefBox.Companion of type
|
||||
infix fun to(type: Byte.Companion) = Int8Box.Companion
|
||||
infix fun to(type: Short.Companion) = Int16Box.Companion
|
||||
infix fun to(type: Int.Companion) = Int32Box.Companion
|
||||
infix fun to(type: Long.Companion) = Int64Box.Companion
|
||||
}
|
||||
|
||||
open class NativeStruct(ptr: NativePtr) : NativeRef(ptr) {
|
||||
open class Type<T : NativeStruct>(size: Int, byPtr: (NativePtr) -> T) : NativeRef.TypeWithSize<T>(size, byPtr)
|
||||
|
||||
companion object {
|
||||
class FieldAt<T : NativeRef>(val type: NativeRef.Type<T>, val offset: Int) {
|
||||
operator fun getValue(thisRef: NativeStruct, property: kotlin.reflect.KProperty<*>): T {
|
||||
return type.byPtr(thisRef.ptr.displacedBy(offset))
|
||||
}
|
||||
}
|
||||
|
||||
infix fun <T : NativeRef> NativeRef.Type<T>.at(offset: Int) = NativeStruct.Companion.FieldAt(this, offset)
|
||||
}
|
||||
}
|
||||
|
||||
class NativeArray<T : NativeRef>(ptr: NativePtr, val elemType: NativeRef.TypeWithSize<T>) : NativeRef(ptr) {
|
||||
operator fun get(index: Int): T {
|
||||
return elemType.byPtr(ptr.displacedBy(index * elemType.size))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
class Type<T : NativeRef> (val elemType: NativeRef.TypeWithSize<T>) :
|
||||
NativeRef.Type<NativeArray<T>>({ ptr -> NativeArray(ptr, elemType) }) {
|
||||
|
||||
infix fun length(length: Int) =
|
||||
NativeRef.TypeWithSize(elemType.size * length, { ptr -> NativeArray(ptr, elemType) })
|
||||
}
|
||||
|
||||
infix fun <T : NativeRef> of(elemType: NativeRef.TypeWithSize<T>) = NativeArray.Companion.Type(elemType)
|
||||
|
||||
fun <T : NativeRef> byRefToFirstElem(ref: T, refType: NativeRef.TypeWithSize<T>) = NativeArray(ref.ptr, refType)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun <T : NativeRef> NativeRef.TypeWithSize<T>.get(length: Int) = NativeArray.Companion of this length length
|
||||
object array {
|
||||
|
||||
// array(type)
|
||||
operator fun <T : NativeRef> invoke(type: NativeRef.TypeWithSize<T>) = NativeArray.Companion of type
|
||||
|
||||
// array[length](type)
|
||||
operator fun get(length: Int) = array.ArrayWithLength(length)
|
||||
|
||||
class ArrayWithLength(val length: Int) {
|
||||
infix fun <T : NativeRef> of(type: NativeRef.TypeWithSize<T>) = NativeArray.Companion of type length length
|
||||
|
||||
operator fun <T : NativeRef> invoke(type: NativeRef.TypeWithSize<T>) = this of type
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class CString private constructor(internal val array: NativeArray<Int8Box>) : NativeRef(array.ptr) {
|
||||
companion object {
|
||||
fun fromArray(array: NativeArray<Int8Box>) = CString(array)
|
||||
}
|
||||
|
||||
fun length(): Int {
|
||||
var res = 0
|
||||
while (array[res].value != 0.toByte()) {
|
||||
++res
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val bytes = ByteArray(this.length())
|
||||
bytes.forEachIndexed { i, byte ->
|
||||
bytes[i] = this.array[i].value
|
||||
}
|
||||
return String(bytes) // TODO: encoding
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package kotlin_native.interop
|
||||
|
||||
fun <T : NativeRef> malloc(type: NativeRef.TypeWithSize<T>) = type.byPtr(bridge.malloc(type.size))
|
||||
|
||||
fun <T : NativeRef, R> malloc(type: NativeRef.TypeWithSize<T>, action: (T) -> R): R {
|
||||
val ref = malloc(type)
|
||||
try {
|
||||
return action(ref)
|
||||
} finally {
|
||||
bridge.free(ref.ptr)
|
||||
}
|
||||
}
|
||||
|
||||
fun free(ptr: NativePtr?) {
|
||||
if (ptr != null) {
|
||||
bridge.free(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
fun free(ref: NativeRef?) = free(ref.getNativePtr())
|
||||
|
||||
fun <T : NativeRef> mallocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?): NativeArray<RefBox<T>> {
|
||||
val res = malloc(array[elements.size](elemType.ref))
|
||||
elements.forEachIndexed { i, element ->
|
||||
res[i].value = element
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fun CString.Companion.fromString(str: String?): CString? {
|
||||
if (str == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val bytes = str.toByteArray() // TODO: encoding
|
||||
val len = bytes.size
|
||||
val nativeBytes = malloc(NativeArray of Int8Box length (len + 1))
|
||||
|
||||
bytes.forEachIndexed { i, byte ->
|
||||
nativeBytes[i].value = byte
|
||||
}
|
||||
nativeBytes[len].value = 0
|
||||
|
||||
return CString.fromArray(nativeBytes)
|
||||
}
|
||||
@@ -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="indexer" />
|
||||
</component>
|
||||
</module>
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
|
||||
import org.jetbrains.kni.indexer.NativeIndex
|
||||
import org.jetbrains.kotlin.native.interop.gen.*
|
||||
|
||||
class StubGenerator(
|
||||
val translationUnit: NativeIndex.TranslationUnit,
|
||||
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 }
|
||||
(functionNames + fieldNames).toSet()
|
||||
}
|
||||
|
||||
val enums = translationUnit.enumList.map { it.name to it }.toMap()
|
||||
|
||||
val functionsToBind = translationUnit.functionList.uniqueBy { it.name }.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)
|
||||
|
||||
private var out: (String) -> Unit = {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
fun <R> withOutput(output: (String) -> Unit, action: () -> R): R {
|
||||
val oldOut = out
|
||||
out = output
|
||||
try {
|
||||
return action()
|
||||
} finally {
|
||||
out = oldOut
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R> transaction(action: () -> R): R {
|
||||
val lines = mutableListOf<String>()
|
||||
val res = withOutput({ lines.add(it) }, action)
|
||||
// if action is completed successfully:
|
||||
lines.forEach(out)
|
||||
return res
|
||||
}
|
||||
|
||||
private fun <R> indent(action: () -> R): R {
|
||||
val oldOut = out
|
||||
return withOutput({ oldOut(" $it") }, action)
|
||||
}
|
||||
|
||||
fun CType.getStringRepresentation(): String {
|
||||
return when (this) {
|
||||
is VoidType -> "void"
|
||||
is Int8Type -> "char"
|
||||
is UInt8Type -> "unsigned char"
|
||||
is Int16Type -> "short"
|
||||
is UInt16Type -> "unsigned short"
|
||||
is Int32Type -> "int"
|
||||
is UInt32Type -> "unsigned int"
|
||||
is Int64Type -> "int64_t"
|
||||
is UInt64Type -> "uint64_t"
|
||||
is PointerType -> pointeeType.getStringRepresentation() + "*"
|
||||
is RecordType -> "struct $name"
|
||||
is FunctionPointerType -> this.returnType.getStringRepresentation() + " (*)(" +
|
||||
this.parameterTypes.map { it.getStringRepresentation() }.joinToString(", ") + ")"
|
||||
is ArrayType -> "void*" // TODO
|
||||
is EnumType -> enums["${this.name}"]!!.spelling
|
||||
else -> throw kotlin.NotImplementedError()
|
||||
}
|
||||
}
|
||||
|
||||
class NativeRefType(val typeName: String, val typeExpr: String = typeName)
|
||||
|
||||
fun getKotlinNativeRefType(type: CType): NativeRefType = when (type) {
|
||||
is Int8Type, is UInt8Type -> NativeRefType("Int8Box")
|
||||
is Int16Type, is UInt16Type -> NativeRefType("Int16Box")
|
||||
is Int32Type, is UInt32Type -> NativeRefType("Int32Box")
|
||||
is Int64Type, is UInt64Type -> NativeRefType("Int64Box")
|
||||
is RecordType -> NativeRefType("${mangleStructName(type.name)}")
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType) {
|
||||
NativeRefType("NativePtrBox")
|
||||
} else {
|
||||
val pointeeRefType = getKotlinNativeRefType(type.pointeeType)
|
||||
NativeRefType("RefBox<${pointeeRefType.typeName}>", "${pointeeRefType.typeExpr}.ref")
|
||||
}
|
||||
}
|
||||
is FunctionPointerType -> getKotlinNativeRefType(PointerType(VoidType))
|
||||
is ConstArrayType -> {
|
||||
val elemRefType = getKotlinNativeRefType(type.elemType)
|
||||
NativeRefType("NativeArray<${elemRefType.typeName}>", "array[${type.length}](${elemRefType.typeExpr})")
|
||||
}
|
||||
is IncompleteArrayType -> {
|
||||
val elemRefType = getKotlinNativeRefType(type.elemType)
|
||||
NativeRefType("NativeArray<${elemRefType.typeName}>", "array(${elemRefType.typeExpr})")
|
||||
}
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
class OutValueBinding(val kotlinType: String,
|
||||
val kotlinConv: ((String) -> String)? = null,
|
||||
val convFree: ((String) -> String)? = null,
|
||||
val kotlinJniBridgeType: String = kotlinType)
|
||||
|
||||
class InValueBinding(val kotlinJniBridgeType: String,
|
||||
val conv: ((String) -> String) = { it },
|
||||
val kotlinType: String = kotlinJniBridgeType)
|
||||
|
||||
fun outValueRefBinding(refType: NativeRefType) = OutValueBinding(
|
||||
kotlinType = refType.typeName + "?",
|
||||
kotlinConv = { "$it.getNativePtr().asLong()" },
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
|
||||
fun inValueRefBinding(refType: NativeRefType) = InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})" },
|
||||
kotlinType = refType.typeName + "?"
|
||||
)
|
||||
|
||||
fun getOutValueBinding(type: CType): OutValueBinding = when (type) {
|
||||
is DirectlyMappedType -> OutValueBinding(type.kotlinType)
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType) {
|
||||
OutValueBinding(
|
||||
kotlinType = "NativePtr?",
|
||||
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(getKotlinNativeRefType(type.pointeeType))
|
||||
}
|
||||
}
|
||||
is EnumType -> OutValueBinding(
|
||||
kotlinType = type.name,
|
||||
kotlinConv = { "$it.value" },
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
is ArrayType -> outValueRefBinding(getKotlinNativeRefType(type))
|
||||
is FunctionPointerType -> getOutValueBinding(PointerType(VoidType))
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun getInValueBinding(type: CType): InValueBinding = when (type) {
|
||||
is VoidType -> InValueBinding("Unit")
|
||||
is DirectlyMappedType -> InValueBinding(type.kotlinType)
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType) {
|
||||
InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { "NativePtr.byValue($it)" },
|
||||
kotlinType = "NativePtr?"
|
||||
)
|
||||
} else {
|
||||
inValueRefBinding(getKotlinNativeRefType(type.pointeeType))
|
||||
}
|
||||
}
|
||||
is EnumType -> InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { "${type.name}.byValue($it)" },
|
||||
kotlinType = type.name
|
||||
)
|
||||
is ArrayType -> inValueRefBinding(getKotlinNativeRefType(type))
|
||||
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(s: NativeIndex.CStruct) {
|
||||
val className = s.mangledName
|
||||
out("class $className(ptr: NativePtr) : NativeStruct(ptr) {")
|
||||
indent {
|
||||
out("")
|
||||
out("companion object : Type<$className>(${s.size}, ::$className)")
|
||||
out("")
|
||||
s.fieldList.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))
|
||||
out("val ${field.name} by ${fieldRefType.typeExpr} at $offset")
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate definition for field $className.${field.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun generateKotlinForwardStruct(s: NativeIndex.CForwardStruct) {
|
||||
val className = s.mangledName
|
||||
out("class $className(ptr: NativePtr) : NativeRef(ptr) {")
|
||||
out(" companion object : Type<$className>(::$className)")
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun generateKotlinEnum(e: NativeIndex.CEnum) {
|
||||
out("enum class ${e.name}(val value: Long) {")
|
||||
indent {
|
||||
e.valueList.forEach {
|
||||
out("${it.name}(${it.value}),")
|
||||
}
|
||||
out(";")
|
||||
out("")
|
||||
out("companion object {")
|
||||
out(" fun byValue(value: Long) = ${e.name}.values().find { it.value == value }!!")
|
||||
out("}")
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun retValBinding(func: NativeIndex.Function) = getInValueBinding(parseType(func.returnType))
|
||||
|
||||
private fun paramBindings(func: NativeIndex.Function): Array<OutValueBinding> {
|
||||
val paramBindings = func.parameterList.map { param ->
|
||||
getOutValueBinding(parseType(param.type))
|
||||
}.toTypedArray()
|
||||
return paramBindings
|
||||
}
|
||||
|
||||
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"
|
||||
}.toTypedArray()
|
||||
return paramNames
|
||||
}
|
||||
|
||||
private fun generateKotlinBindingMethod(func: NativeIndex.Function) {
|
||||
val paramNames = paramNames(func)
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
|
||||
val args = paramNames.mapIndexed { i: Int, name: String ->
|
||||
"$name: " + paramBindings[i].kotlinType
|
||||
}.joinToString(", ")
|
||||
|
||||
out("fun ${func.name}($args): ${retValBinding.kotlinType} {")
|
||||
indent {
|
||||
val externalParamNames = paramNames.mapIndexed { i: Int, name: String ->
|
||||
val binding = paramBindings[i]
|
||||
val externalParamName: String
|
||||
|
||||
if (binding.kotlinConv != null) {
|
||||
externalParamName = "_$name"
|
||||
out("val $externalParamName = " + binding.kotlinConv.invoke(name))
|
||||
} else {
|
||||
externalParamName = name
|
||||
}
|
||||
|
||||
externalParamName
|
||||
|
||||
}
|
||||
out("val res = externals.${func.name}(" + externalParamNames.joinToString(", ") + ")")
|
||||
paramNames.forEachIndexed { i, name ->
|
||||
val binding = paramBindings[i]
|
||||
if (binding.convFree != null) {
|
||||
assert(binding.kotlinConv != null)
|
||||
out(binding.convFree.invoke(externalParamNames[i]))
|
||||
}
|
||||
}
|
||||
out("return " + retValBinding.conv("res"))
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
private fun generateKotlinExternalMethod(func: NativeIndex.Function) {
|
||||
val paramNames = paramNames(func)
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
|
||||
val args = paramNames.mapIndexed { i: Int, name: String ->
|
||||
"$name: " + paramBindings[i].kotlinJniBridgeType
|
||||
}.joinToString(", ")
|
||||
|
||||
out("external fun ${func.name}($args): ${retValBinding.kotlinJniBridgeType}")
|
||||
}
|
||||
|
||||
fun generateKotlinFile() {
|
||||
if (pkgName != "") {
|
||||
out("package $pkgName")
|
||||
out("")
|
||||
}
|
||||
out("import kotlin_native.interop.*")
|
||||
out("")
|
||||
|
||||
functionsToBind.forEach {
|
||||
try {
|
||||
transaction {
|
||||
generateKotlinBindingMethod(it)
|
||||
out("")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate binding definition for function ${it.name}")
|
||||
}
|
||||
}
|
||||
|
||||
val generatedStructs = mutableSetOf<String>()
|
||||
|
||||
translationUnit.structList.uniqueBy { it.name }.forEach { s ->
|
||||
try {
|
||||
transaction {
|
||||
generateKotlinStruct(s)
|
||||
out("")
|
||||
generatedStructs.add(s.name)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate definition for struct ${s.name}")
|
||||
}
|
||||
}
|
||||
|
||||
translationUnit.forwardStructList
|
||||
.uniqueBy { it.name }
|
||||
.filter { it.name !in generatedStructs }
|
||||
.forEach { s ->
|
||||
generateKotlinForwardStruct(s)
|
||||
out("")
|
||||
}
|
||||
|
||||
translationUnit.enumList.uniqueBy { it.name }.forEach { e ->
|
||||
generateKotlinEnum(e)
|
||||
out("")
|
||||
}
|
||||
|
||||
out("object externals {")
|
||||
indent {
|
||||
out("init { System.loadLibrary(\"$libName\") }")
|
||||
functionsToBind.forEach {
|
||||
try {
|
||||
transaction {
|
||||
generateKotlinExternalMethod(it)
|
||||
out("")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate external definition for function ${it.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
fun getCJniBridgeType(kotlinJniBridgeType: String) = when (kotlinJniBridgeType) {
|
||||
"Unit" -> "void"
|
||||
"Byte" -> "jbyte"
|
||||
"Short" -> "jshort"
|
||||
"Int" -> "jint"
|
||||
"Long" -> "jlong"
|
||||
else -> throw NotImplementedError(kotlinJniBridgeType)
|
||||
}
|
||||
|
||||
|
||||
fun generateCFile(headerFiles: List<String>, translationUnit: NativeIndex.TranslationUnit) {
|
||||
out("#include <stdint.h>")
|
||||
out("#include <jni.h>")
|
||||
headerFiles.forEach {
|
||||
out("#include <$it>")
|
||||
}
|
||||
out("")
|
||||
|
||||
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()
|
||||
"($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 {
|
||||
out(" return ($cReturnType) ($callExpr);")
|
||||
}
|
||||
out("}")
|
||||
} catch (e: Throwable) {
|
||||
System.err.println("Warning: cannot generate C JNI function definition ${func.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
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 java.io.File
|
||||
import java.util.*
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val ktSrcRoot = args[0]
|
||||
val nativeLibsDir = args[1]
|
||||
|
||||
val defFiles = File(ktSrcRoot).walk().filter { it.name.endsWith(".def") }
|
||||
|
||||
defFiles.forEach { defFile ->
|
||||
val config = Properties()
|
||||
defFile.bufferedReader().use { reader ->
|
||||
config.load(reader)
|
||||
}
|
||||
|
||||
val headerFiles = config.getProperty("headers").split(' ')
|
||||
val compilerOpts = config.getProperty("compilerOpts").split(' ')
|
||||
val compiler = config.getProperty("compiler")
|
||||
val libName = config.getProperty("libName")
|
||||
val linkerOpts = config.getProperty("linkerOpts").split(' ').toTypedArray()
|
||||
val linker = config.getProperty("linker")
|
||||
val excludedFunctions = config.getProperty("excludedFunctions")?.split(' ')?.toSet() ?: emptySet()
|
||||
|
||||
|
||||
val outKtFile = defFile.absolutePath.substringBeforeLast(".def") + ".kt"
|
||||
val outKtPkg = defFile.parentFile.relativeTo(File(ktSrcRoot)).path.replace(File.separatorChar, '.')
|
||||
|
||||
|
||||
|
||||
val translationUnit = buildNativeIndex(headerFiles, compilerOpts)
|
||||
|
||||
val gen = StubGenerator(translationUnit, outKtPkg, libName, excludedFunctions)
|
||||
|
||||
File(outKtFile).bufferedWriter().use { out ->
|
||||
gen.withOutput({ out.appendln(it) }) {
|
||||
gen.generateKotlinFile()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val outCFile = createTempFile(suffix = ".c")
|
||||
|
||||
outCFile.bufferedWriter().use { out ->
|
||||
gen.withOutput({ out.appendln(it) }) {
|
||||
gen.generateCFile(headerFiles, translationUnit)
|
||||
}
|
||||
}
|
||||
|
||||
val outOFile = createTempFile(suffix = ".o")
|
||||
|
||||
val compilerCmd = arrayOf(compiler, *compilerOpts.toTypedArray(),
|
||||
"-I/System/Library/Frameworks/JavaVM.framework/Headers", // FIXME
|
||||
"-c", outCFile.path, "-o", outOFile.path)
|
||||
|
||||
println(compilerCmd.joinToString(" "))
|
||||
|
||||
val compilerRes = ProcessBuilder(*compilerCmd)
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor()
|
||||
|
||||
if (compilerRes != 0) {
|
||||
exitProcess(compilerRes)
|
||||
}
|
||||
|
||||
File(nativeLibsDir).mkdirs()
|
||||
|
||||
val outLib = nativeLibsDir + "/" + System.mapLibraryName(libName)
|
||||
|
||||
val linkerCmd = arrayOf(linker, *linkerOpts, outOFile.path, "-shared", "-o", outLib)
|
||||
println(linkerCmd.joinToString(" "))
|
||||
|
||||
val linkerRes = ProcessBuilder(*linkerCmd)
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor()
|
||||
|
||||
if (linkerRes != 0) {
|
||||
exitProcess(linkerRes)
|
||||
}
|
||||
|
||||
outCFile.delete()
|
||||
outOFile.delete()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun buildNativeIndex(headerFiles: List<String>, compilerOpts: List<String>): NativeIndex.TranslationUnit {
|
||||
val tempHeaderFile = createTempFile(suffix = ".h")
|
||||
tempHeaderFile.deleteOnExit()
|
||||
tempHeaderFile.writer().buffered().use { reader ->
|
||||
headerFiles.forEach {
|
||||
reader.appendln("#include <$it>")
|
||||
}
|
||||
}
|
||||
|
||||
val indexerOptions = IndexerOptions(language = Language.CPP, args = compilerOpts)
|
||||
return buildNativeIndex(tempHeaderFile, indexerOptions)
|
||||
}
|
||||
Reference in New Issue
Block a user