Merge pull request #82 from JetBrains/interop-ng
The new interop (again)
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
@@ -43,10 +46,6 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
compile project(':Interop:Runtime')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+59
-31
@@ -3,15 +3,15 @@ package org.jetbrains.kotlin.native.interop.indexer
|
||||
import clang.*
|
||||
import clang.CXIdxEntityKind.*
|
||||
import clang.CXTypeKind.*
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import java.io.File
|
||||
|
||||
private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
|
||||
override var def: StructDefImpl? = null
|
||||
}
|
||||
|
||||
private class StructDefImpl(size: Long, decl: StructDecl, hasNaturalLayout: Boolean) :
|
||||
StructDef(size, decl, hasNaturalLayout) {
|
||||
private class StructDefImpl(size: Long, align: Int, decl: StructDecl, hasNaturalLayout: Boolean) :
|
||||
StructDef(size, align, decl, hasNaturalLayout) {
|
||||
|
||||
override val fields = mutableListOf<Field>()
|
||||
}
|
||||
@@ -38,6 +38,11 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
override val enums: List<EnumDef>
|
||||
get() = enumById.values.toList()
|
||||
|
||||
val typedefById = mutableMapOf<DeclarationID, TypedefDef>()
|
||||
|
||||
override val typedefs: List<TypedefDef>
|
||||
get() = typedefById.values.toList()
|
||||
|
||||
val functionByName = mutableMapOf<String, FunctionDecl>()
|
||||
|
||||
override val functions: List<FunctionDecl>
|
||||
@@ -94,6 +99,31 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
return res
|
||||
}
|
||||
|
||||
fun getTypedef(type: CXType): Type {
|
||||
assert (type.kind.value == CXType_Typedef)
|
||||
val declCursor = clang_getTypeDeclaration(type, arena)
|
||||
val name = clang_getCursorSpelling(declCursor, arena).convertAndDispose()
|
||||
|
||||
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor, arena))
|
||||
if ((underlying is RecordType && underlying.decl.spelling.split(' ').last() == name) ||
|
||||
(underlying is EnumType && underlying.def.spelling.split(' ').last() == name)) {
|
||||
|
||||
// special handling for:
|
||||
// typedef struct { ... } name;
|
||||
// typedef enum { ... } name;
|
||||
// FIXME: implement better solution
|
||||
return underlying
|
||||
}
|
||||
|
||||
val declId = getDeclarationId(declCursor)
|
||||
val typedefDef = typedefById.getOrPut(declId) {
|
||||
|
||||
TypedefDef(underlying, name)
|
||||
}
|
||||
|
||||
return Typedef(typedefDef)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes [StructDef.hasNaturalLayout] property.
|
||||
*/
|
||||
@@ -105,11 +135,12 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
CXCursorKind.CXCursor_UnionDecl -> return false
|
||||
|
||||
CXCursorKind.CXCursor_StructDecl -> {
|
||||
val hasAttributes = arena.alloc(Int32Box)
|
||||
val hasAttributes = arena.alloc<CInt32Var>()
|
||||
hasAttributes.value = 0
|
||||
clang_visitChildren(structDefCursor, { cursor, parent, clientData ->
|
||||
clang_visitChildren(structDefCursor, staticCFunction { cursor, parent, clientData ->
|
||||
if (clang_isAttribute(cursor.kind.value) != 0) {
|
||||
clientData.asRef(Int32Box)!!.value = 1
|
||||
val hasAttributes = clientData!!.reinterpret<CInt32Var>().pointed
|
||||
hasAttributes.value = 1
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}, hasAttributes.ptr)
|
||||
@@ -151,12 +182,7 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
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_Typedef -> getTypedef(type)
|
||||
|
||||
CXType_Record -> RecordType(getStructTypeDecl(type))
|
||||
CXType_Enum -> EnumType(getEnumTypeDef(type))
|
||||
@@ -193,7 +219,7 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
|
||||
fun indexDeclaration(info: CXIdxDeclInfo) {
|
||||
val cursor = info.cursor
|
||||
val entityInfo = info.entityInfo.value!!
|
||||
val entityInfo = info.entityInfo.pointed!!
|
||||
val entityName = entityInfo.name.value?.asCString()?.toString()
|
||||
val kind = entityInfo.kind.value
|
||||
|
||||
@@ -203,7 +229,7 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
val type = convertType(clang_getCursorType(cursor, arena))
|
||||
val offset = clang_Cursor_getOffsetOfField(cursor)
|
||||
|
||||
val container = info.semanticContainer.value!!
|
||||
val container = info.semanticContainer.pointed!!
|
||||
val structDef = getStructDeclAt(container.cursor).def!!
|
||||
structDef.fields.add(Field(name, type, offset))
|
||||
}
|
||||
@@ -211,9 +237,11 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
CXIdxEntity_Struct, CXIdxEntity_Union -> {
|
||||
val structDecl = getStructDeclAt(cursor)
|
||||
if (clang_isCursorDefinition(cursor) != 0) {
|
||||
val size = clang_Type_getSizeOf(clang_getCursorType(cursor, arena))
|
||||
val type = clang_getCursorType(cursor, arena)
|
||||
val size = clang_Type_getSizeOf(type)
|
||||
val align = clang_Type_getAlignOf(clang_getCursorType(cursor, arena)).toInt()
|
||||
val hasNaturalLayout = structHasNaturalLayout(cursor)
|
||||
structDecl.def = StructDefImpl(size, structDecl, hasNaturalLayout)
|
||||
structDecl.def = StructDefImpl(size, align, structDecl, hasNaturalLayout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +264,7 @@ private class NativeIndexImpl : NativeIndex() {
|
||||
}
|
||||
|
||||
CXIdxEntity_EnumConstant -> {
|
||||
val container = info.semanticContainer.value!!
|
||||
val container = info.semanticContainer.pointed!!
|
||||
val name = entityName!!
|
||||
val value = clang_getEnumConstantDeclValue(info.cursor)
|
||||
|
||||
@@ -265,11 +293,11 @@ fun CXString.convertAndDispose(): String {
|
||||
|
||||
fun buildNativeIndexImpl(headerFile: File, args: List<String>): NativeIndex {
|
||||
// TODO: dispose all allocated memory and resources
|
||||
val args1 = args.map { CString.fromString(it)!!.asCharPtr() }.toTypedArray()
|
||||
val args1 = args.map { CString.fromString(it, nativeHeap)!!.asCharPtr() }.toTypedArray()
|
||||
|
||||
val index = clang_createIndex(0, 0)
|
||||
val indexAction = clang_IndexAction_create(index)
|
||||
val callbacks = malloc(IndexerCallbacks.Companion)
|
||||
val callbacks = nativeHeap.alloc<IndexerCallbacks>()
|
||||
|
||||
val res = NativeIndexImpl()
|
||||
try {
|
||||
@@ -278,23 +306,23 @@ fun buildNativeIndexImpl(headerFile: File, args: List<String>): NativeIndex {
|
||||
|
||||
try {
|
||||
with(callbacks) {
|
||||
abortQuery.setStatic(null)
|
||||
diagnostic.setStatic(null)
|
||||
enteredMainFile.setStatic(null)
|
||||
ppIncludedFile.setStatic(null)
|
||||
importedASTFile.setStatic(null)
|
||||
startedTranslationUnit.setStatic(null)
|
||||
indexDeclaration.setStatic { clientData, info ->
|
||||
abortQuery.value = null
|
||||
diagnostic.value = null
|
||||
enteredMainFile.value = null
|
||||
ppIncludedFile.value = null
|
||||
importedASTFile.value = null
|
||||
startedTranslationUnit.value = null
|
||||
indexDeclaration.value = staticCFunction { clientData, info ->
|
||||
val index = StableObjPtr.fromValue(clientData!!).get() as NativeIndexImpl
|
||||
index.indexDeclaration(info!!)
|
||||
index.indexDeclaration(info!!.pointed)
|
||||
}
|
||||
indexEntityReference.setStatic(null)
|
||||
indexEntityReference.value = null
|
||||
}
|
||||
|
||||
val commandLineArgs = if (args1.size != 0) mallocNativeArrayOf(Int8Box.Companion, *args1)[0] else null
|
||||
val commandLineArgs = if (args1.size != 0) nativeHeap.allocArrayOfPointersTo(*args1)[0].ptr else null
|
||||
|
||||
clang_indexSourceFile(indexAction, clientData, callbacks, IndexerCallbacks.size, 0, headerFile.path,
|
||||
commandLineArgs, args1.size, null, 0, null, 0)
|
||||
clang_indexSourceFile(indexAction, clientData, callbacks.ptr, IndexerCallbacks.size.toInt(),
|
||||
0, headerFile.path, commandLineArgs, args1.size, null, 0, null, 0)
|
||||
|
||||
|
||||
return res
|
||||
|
||||
+15
-1
@@ -13,6 +13,7 @@ fun buildNativeIndex(headerFile: File, args: List<String>): NativeIndex = buildN
|
||||
abstract class NativeIndex {
|
||||
abstract val structs: List<StructDecl>
|
||||
abstract val enums: List<EnumDef>
|
||||
abstract val typedefs: List<TypedefDef>
|
||||
abstract val functions: List<FunctionDecl>
|
||||
}
|
||||
|
||||
@@ -35,7 +36,9 @@ abstract class StructDecl(val spelling: String) {
|
||||
* @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, val hasNaturalLayout: Boolean) {
|
||||
abstract class StructDef(val size: Long, val align: Int,
|
||||
val decl: StructDecl,
|
||||
val hasNaturalLayout: Boolean) {
|
||||
|
||||
abstract val fields: List<Field>
|
||||
}
|
||||
@@ -63,6 +66,15 @@ class Parameter(val name: String?, val type: Type)
|
||||
*/
|
||||
class FunctionDecl(val name: String, val parameters: List<Parameter>, val returnType: Type)
|
||||
|
||||
/**
|
||||
* C typedef definition.
|
||||
*
|
||||
* ```
|
||||
* typedef $aliased $name;
|
||||
* ```
|
||||
*/
|
||||
class TypedefDef(val aliased: Type, val name: String)
|
||||
|
||||
|
||||
/**
|
||||
* C type.
|
||||
@@ -103,4 +115,6 @@ interface ArrayType : Type {
|
||||
data class ConstArrayType(override val elemType: Type, val length: Long) : ArrayType
|
||||
data class IncompleteArrayType(override val elemType: Type) : ArrayType
|
||||
|
||||
data class Typedef(val def: TypedefDef) : Type
|
||||
|
||||
object UnsupportedType : Type
|
||||
@@ -1,7 +1,10 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
@@ -32,14 +35,13 @@ model {
|
||||
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
}
|
||||
|
||||
sourceSets.main.kotlin.srcDirs += "src/jvm/kotlin"
|
||||
|
||||
|
||||
task nativelibs(type: Copy) {
|
||||
dependsOn 'callbacksSharedLibrary'
|
||||
|
||||
@@ -4,101 +4,101 @@
|
||||
#include <ffi.h>
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeVoid
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeVoid(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeVoid(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_void;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt8
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt8(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt8(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt8
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt8(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt8(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt16
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt16(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt16(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint16;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt16
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt16(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt16(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint16;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt32
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt32(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt32(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt32
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt32(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt32(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt64
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeUInt64(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt64(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt64
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeSInt64(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt64(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypePointer
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypePointer(JNIEnv *env, jclass cls) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypePointer(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeStruct0
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeStruct0(JNIEnv *env, jclass cls, jlong elements) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeStruct0(JNIEnv *env, jclass cls, jlong elements) {
|
||||
ffi_type* res = malloc(sizeof(ffi_type));
|
||||
if (res != NULL) {
|
||||
res->size = 0;
|
||||
@@ -110,11 +110,11 @@ JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiTypeStruct0(J
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiCreateCif0
|
||||
* Signature: (IJJ)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiCreateCif0(JNIEnv *env, jclass cls, jint nArgs, jlong rType, jlong argTypes) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateCif0(JNIEnv *env, jclass cls, jint nArgs, jlong rType, jlong argTypes) {
|
||||
ffi_cif* res = malloc(sizeof(ffi_cif));
|
||||
if (res != NULL) {
|
||||
ffi_status status = ffi_prep_cif(res, FFI_DEFAULT_ABI, nArgs, (ffi_type*)rType, (ffi_type**)argTypes);
|
||||
@@ -166,7 +166,7 @@ static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
||||
static jmethodID ffiFunImpl0 = NULL;
|
||||
static jclass cls = NULL;
|
||||
if (ffiFunImpl0 == NULL) {
|
||||
jclass clsLocal = (*env)->FindClass(env, "kotlin_native/interop/CallbacksKt");
|
||||
jclass clsLocal = (*env)->FindClass(env, "kotlinx/cinterop/JvmCallbacksKt");
|
||||
checkException(env);
|
||||
assert(clsLocal != NULL);
|
||||
|
||||
@@ -184,11 +184,11 @@ static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiCreateClosure0
|
||||
* Signature: (JLjava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiCreateClosure0(JNIEnv *env, jclass cls, jlong ffiCif, jobject userData) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateClosure0(JNIEnv *env, jclass cls, jlong ffiCif, jobject userData) {
|
||||
jobject userDataGlobalRef = (*env)->NewGlobalRef(env, userData);
|
||||
if (userDataGlobalRef == NULL) {
|
||||
return (jlong)0;
|
||||
@@ -211,29 +211,29 @@ JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_ffiCreateClosure
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: newGlobalRef
|
||||
* Signature: (Ljava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlin_1native_interop_CallbacksKt_newGlobalRef(JNIEnv *env, jclass cls, jobject obj) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_newGlobalRef(JNIEnv *env, jclass cls, jobject obj) {
|
||||
jobject res = (*env)->NewGlobalRef(env, obj);
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: derefGlobalRef
|
||||
* Signature: (J)Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_kotlin_1native_interop_CallbacksKt_derefGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
|
||||
JNIEXPORT jobject JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_derefGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
|
||||
return (jobject) ref;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlin_native_interop_CallbacksKt
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: deleteGlobalRef
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_kotlin_1native_interop_CallbacksKt_deleteGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
|
||||
JNIEXPORT void JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_deleteGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
|
||||
(*env)->DeleteGlobalRef(env, (jobject) ref);
|
||||
}
|
||||
|
||||
+38
-63
@@ -1,4 +1,4 @@
|
||||
package kotlin_native.interop
|
||||
package kotlinx.cinterop
|
||||
|
||||
/**
|
||||
* This class provides a way to create a stable handle to any Kotlin object.
|
||||
@@ -6,21 +6,23 @@ package kotlin_native.interop
|
||||
*
|
||||
* Any [StableObjPtr] should be manually [disposed][dispose]
|
||||
*/
|
||||
data class StableObjPtr private constructor(val value: NativePtr) {
|
||||
data class StableObjPtr private constructor(val value: COpaquePointer) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates a handle for given object.
|
||||
*/
|
||||
fun create(any: Any) = StableObjPtr(NativePtr.byValue(newGlobalRef(any))!!)
|
||||
fun create(any: Any) = fromValue(newGlobalRef(any))
|
||||
|
||||
private fun fromValue(value: NativePtr) = fromValue(CPointer.create(value))
|
||||
|
||||
/**
|
||||
* Creates [StableObjPtr] from given raw value.
|
||||
*
|
||||
* @param value must be a [value] of some [StableObjPtr]
|
||||
*/
|
||||
fun fromValue(value: NativePtr) = StableObjPtr(value)
|
||||
fun fromValue(value: COpaquePointer) = StableObjPtr(value)
|
||||
|
||||
init {
|
||||
loadCallbacksLibrary()
|
||||
@@ -31,20 +33,21 @@ data class StableObjPtr private constructor(val value: NativePtr) {
|
||||
* Disposes the handle. It must not be [used][get] after that.
|
||||
*/
|
||||
fun dispose() {
|
||||
deleteGlobalRef(value.value)
|
||||
deleteGlobalRef(value.rawValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object this handle was [created][create] for.
|
||||
*/
|
||||
fun get(): Any = derefGlobalRef(value.value)
|
||||
fun get(): Any = derefGlobalRef(value.rawValue)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the type of native function.
|
||||
* Describes the type of C function with adapter for Kotlin functions.
|
||||
*
|
||||
* The instances of this class are supposed to be Kotlin objects (singletons),
|
||||
* The instances of this class are supposed to be Kotlin object declarations (singletons),
|
||||
* because it is required by [CAdaptedFunctionType] and
|
||||
* because creating the instance implies allocating some amount of non-freeable memory for the instance itself
|
||||
* and for any unique Kotlin function "converted" to this type.
|
||||
*
|
||||
@@ -53,16 +56,11 @@ data class StableObjPtr private constructor(val value: NativePtr) {
|
||||
* - Implementation of [invoke] method which describes how to convert between these types and Kotlin types used in [F]
|
||||
*
|
||||
* @param F Kotlin function type corresponding to given native function type
|
||||
*/
|
||||
abstract class NativeFunctionType<F : Function<*>> protected constructor(returnType: CType, vararg paramTypes: CType) {
|
||||
*/
|
||||
abstract class CAdaptedFunctionTypeImpl<F : Function<*>>
|
||||
protected constructor(returnType: CType, vararg paramTypes: CType) : CAdaptedFunctionType<F> {
|
||||
|
||||
/**
|
||||
* Returns a native function of this type, which calls given Kotlin *static* function.
|
||||
*
|
||||
* Given function must be *static*, i.e. an (unbound) reference to a Kotlin function or
|
||||
* a closure which doesn't capture any variable
|
||||
*/
|
||||
fun fromStatic(function: F): NativePtr {
|
||||
override fun fromStatic(function: F): NativePtr {
|
||||
// TODO: optimize synchronization
|
||||
synchronized(cache) {
|
||||
return cache.getOrPut(function, { createFromStatic(function) })
|
||||
@@ -76,7 +74,7 @@ abstract class NativeFunctionType<F : Function<*>> protected constructor(returnT
|
||||
* This description omits the details that are irrelevant for the ABI.
|
||||
*/
|
||||
protected open class CType internal constructor(val ffiType: ffi_type) {
|
||||
internal constructor(ffiTypePtr: Long) : this(NativePtr.byValue(ffiTypePtr).asRef(ffi_type)!!)
|
||||
internal constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
||||
}
|
||||
|
||||
protected object Void : CType(ffiTypeVoid())
|
||||
@@ -102,7 +100,7 @@ abstract class NativeFunctionType<F : Function<*>> protected constructor(returnT
|
||||
* @param args array of pointers to arguments to be passed to [function]
|
||||
* @param ret pointer to memory to be filled with return value of [function]
|
||||
*/
|
||||
protected abstract fun invoke(function: F, args: NativeArray<NativePtrBox>, ret: NativePtr)
|
||||
protected abstract fun invoke(function: F, args: CArray<COpaquePointerVar>, ret: COpaquePointer)
|
||||
|
||||
companion object {
|
||||
init {
|
||||
@@ -120,7 +118,7 @@ abstract class NativeFunctionType<F : Function<*>> protected constructor(returnT
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
val impl = { ret: NativePtr, args: NativeArray<NativePtrBox> ->
|
||||
val impl: UserData = { ret: COpaquePointer, args: CArray<COpaquePointerVar> ->
|
||||
invoke(function, args, ret)
|
||||
}
|
||||
|
||||
@@ -150,28 +148,10 @@ abstract class NativeFunctionType<F : Function<*>> protected constructor(returnT
|
||||
private val cache = mutableMapOf<F, NativePtr>()
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NativeFunctionType.fromStatic
|
||||
*/
|
||||
fun <F : Function<*>> F.staticAsNative(type: NativeFunctionType<F>) = type.fromStatic(this)
|
||||
|
||||
/**
|
||||
* Describes a "struct" with native function pointer field.
|
||||
*/
|
||||
class NativeFunctionBox<F : Function<*>>(ptr: NativePtr, private val type: NativeFunctionType<F>) : NativeRef(ptr) {
|
||||
|
||||
/**
|
||||
* Sets the function pointer field to null or native function calling given Kotlin function.
|
||||
*/
|
||||
fun setStatic(function: F?) {
|
||||
val nativeFunPtr = function?.staticAsNative(type)
|
||||
bridge.putPtr(ptr, nativeFunPtr)
|
||||
}
|
||||
}
|
||||
|
||||
val <F : Function<*>> NativeFunctionType<F>.ref: NativeRef.TypeWithSize<NativeFunctionBox<F>>
|
||||
get() = NativeRef.TypeWithSize(8, { NativeFunctionBox(it, this) }) // TODO: 64-bit specific
|
||||
private typealias UserData = (ret: COpaquePointer, args: CArray<COpaquePointerVar>)->Unit
|
||||
|
||||
inline fun <reified T : CAdaptedFunctionTypeImpl<*>> CAdaptedFunctionTypeImpl.Companion.of(): T =
|
||||
T::class.objectInstance!!
|
||||
|
||||
private fun loadCallbacksLibrary() {
|
||||
System.loadLibrary("callbacks")
|
||||
@@ -181,16 +161,12 @@ private fun loadCallbacksLibrary() {
|
||||
/**
|
||||
* Reference to `ffi_type` struct instance.
|
||||
*/
|
||||
internal class ffi_type (ptr: NativePtr) : NativeRef(ptr) {
|
||||
companion object : Type<ffi_type>(::ffi_type)
|
||||
}
|
||||
internal class ffi_type(override val rawPtr: NativePtr) : COpaque
|
||||
|
||||
/**
|
||||
* Reference to `ffi_cif` struct instance.
|
||||
*/
|
||||
internal class ffi_cif (ptr: NativePtr) : NativeRef(ptr) {
|
||||
companion object : Type<ffi_cif>(::ffi_cif)
|
||||
}
|
||||
internal class ffi_cif(override val rawPtr: NativePtr) : COpaque
|
||||
|
||||
private external fun ffiTypeVoid(): Long
|
||||
private external fun ffiTypeUInt8(): Long
|
||||
@@ -211,12 +187,12 @@ private external fun ffiTypeStruct0(elements: Long): Long
|
||||
* @param elements types of the struct elements
|
||||
*/
|
||||
private fun ffiTypeStruct(elementTypes: List<ffi_type>): ffi_type {
|
||||
val elements = mallocNativeArrayOf(ffi_type, *elementTypes.toTypedArray(), null).ptr
|
||||
val res = ffiTypeStruct0(elements.value)
|
||||
val elements = nativeHeap.allocArrayOfPointersTo(*elementTypes.toTypedArray(), null)
|
||||
val res = ffiTypeStruct0(elements.rawPtr)
|
||||
if (res == 0L) {
|
||||
throw OutOfMemoryError()
|
||||
}
|
||||
return NativePtr.byValue(res).asRef(ffi_type)!!
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
private external fun ffiCreateCif0(nArgs: Int, rType: Long, argTypes: Long): Long
|
||||
@@ -231,9 +207,8 @@ private external fun ffiCreateCif0(nArgs: Int, rType: Long, argTypes: Long): Lon
|
||||
*/
|
||||
private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_cif {
|
||||
val nArgs = paramTypes.size
|
||||
val rType = returnType.ptr
|
||||
val argTypes = mallocNativeArrayOf(ffi_type, *paramTypes.toTypedArray(), null).ptr
|
||||
val res = ffiCreateCif0(nArgs, rType.value, argTypes.value)
|
||||
val argTypes = nativeHeap.allocArrayOfPointersTo(*paramTypes.toTypedArray(), null)
|
||||
val res = ffiCreateCif0(nArgs, returnType.rawPtr, argTypes.rawPtr)
|
||||
|
||||
when (res) {
|
||||
0L -> throw OutOfMemoryError()
|
||||
@@ -242,14 +217,14 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_
|
||||
-3L -> throw Error("libffi error occurred")
|
||||
}
|
||||
|
||||
return NativePtr.byValue(res).asRef(ffi_cif)!!
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
|
||||
ffiFunImpl(NativePtr.byValue(ffiCif).asRef(ffi_cif)!!,
|
||||
NativePtr.byValue(ret)!!,
|
||||
NativePtr.byValue(args).asRef(array(NativePtrBox))!!,
|
||||
userData as (ret: NativePtr, args: NativeArray<NativePtrBox>) -> Unit)
|
||||
ffiFunImpl(interpretPointed(ffiCif),
|
||||
CPointer.create(ret),
|
||||
interpretPointed(args),
|
||||
userData as UserData)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,8 +233,8 @@ private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
|
||||
* @param ret pointer to memory to be filled with return value of the invoked native function
|
||||
* @param args pointer to array of pointers to arguments passed to the invoked native function
|
||||
*/
|
||||
private fun ffiFunImpl(ffiCif: ffi_cif, ret: NativePtr, args: NativeArray<NativePtrBox>,
|
||||
userData: (NativePtr, NativeArray<NativePtrBox>) -> Unit) {
|
||||
private fun ffiFunImpl(ffiCif: ffi_cif, ret: COpaquePointer, args: CArray<COpaquePointerVar>,
|
||||
userData: UserData) {
|
||||
|
||||
userData.invoke(ret, args)
|
||||
}
|
||||
@@ -271,15 +246,15 @@ private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
|
||||
*
|
||||
* @param ffiCif describes the type of the function to create
|
||||
*/
|
||||
private fun ffiCreateClosure(ffiCif: ffi_cif, userData: (NativePtr, NativeArray<NativePtrBox>) -> Unit): NativePtr {
|
||||
val res = ffiCreateClosure0(ffiCif.ptr.value, userData)
|
||||
private fun ffiCreateClosure(ffiCif: ffi_cif, userData: UserData): NativePtr {
|
||||
val res = ffiCreateClosure0(ffiCif.rawPtr, userData)
|
||||
|
||||
when (res) {
|
||||
0L -> throw OutOfMemoryError()
|
||||
-1L -> throw Error("libffi error occurred")
|
||||
}
|
||||
|
||||
return NativePtr.byValue(res)!!
|
||||
return res
|
||||
}
|
||||
|
||||
private external fun newGlobalRef(any: Any): Long
|
||||
@@ -0,0 +1,67 @@
|
||||
package kotlinx.cinterop
|
||||
|
||||
import sun.misc.Unsafe
|
||||
|
||||
private val NativePointed.address: Long
|
||||
get() = this.rawPtr
|
||||
|
||||
private enum class DataModel(val pointerSize: Long) {
|
||||
_32BIT(4),
|
||||
_64BIT(8)
|
||||
}
|
||||
|
||||
private val dataModel: DataModel = when (System.getProperty("sun.arch.data.model")) {
|
||||
null -> TODO()
|
||||
"32" -> DataModel._32BIT
|
||||
"64" -> DataModel._64BIT
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
internal val pointerSize: Int = dataModel.pointerSize.toInt()
|
||||
|
||||
object nativeMemUtils {
|
||||
fun getByte(mem: NativePointed) = unsafe.getByte(mem.address)
|
||||
fun putByte(mem: NativePointed, value: Byte) = unsafe.putByte(mem.address, value)
|
||||
|
||||
fun getShort(mem: NativePointed) = unsafe.getShort(mem.address)
|
||||
fun putShort(mem: NativePointed, value: Short) = unsafe.putShort(mem.address, value)
|
||||
|
||||
fun getInt(mem: NativePointed) = unsafe.getInt(mem.address)
|
||||
fun putInt(mem: NativePointed, value: Int) = unsafe.putInt(mem.address, value)
|
||||
|
||||
fun getLong(mem: NativePointed) = unsafe.getLong(mem.address)
|
||||
fun putLong(mem: NativePointed, value: Long) = unsafe.putLong(mem.address, value)
|
||||
|
||||
fun getFloat(mem: NativePointed) = unsafe.getFloat(mem.address)
|
||||
fun putFloat(mem: NativePointed, value: Float) = unsafe.putFloat(mem.address, value)
|
||||
|
||||
fun getDouble(mem: NativePointed) = unsafe.getDouble(mem.address)
|
||||
fun putDouble(mem: NativePointed, value: Double) = unsafe.putDouble(mem.address, value)
|
||||
|
||||
fun getPtr(mem: NativePointed): NativePtr = when (dataModel) {
|
||||
DataModel._32BIT -> getInt(mem).toLong()
|
||||
DataModel._64BIT -> getLong(mem)
|
||||
}
|
||||
|
||||
fun putPtr(mem: NativePointed, value: NativePtr) = when (dataModel) {
|
||||
DataModel._32BIT -> putInt(mem, value.toInt())
|
||||
DataModel._64BIT -> putLong(mem, value)
|
||||
}
|
||||
|
||||
internal class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
|
||||
|
||||
fun alloc(size: Long, align: Int): NativePointed {
|
||||
val address = unsafe.allocateMemory(size)
|
||||
if (address % align != 0L) TODO(align.toString())
|
||||
return interpretPointed<NativeAllocated>(address)
|
||||
}
|
||||
|
||||
fun free(mem: NativePointed) {
|
||||
unsafe.freeMemory(mem.rawPtr)
|
||||
}
|
||||
|
||||
private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) {
|
||||
isAccessible = true
|
||||
return@with this.get(null) as Unsafe
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package kotlinx.cinterop
|
||||
|
||||
import kotlin.reflect.companionObjectInstance
|
||||
import kotlin.reflect.primaryConstructor
|
||||
|
||||
typealias NativePtr = Long
|
||||
val nativeNullPtr: NativePtr = 0L
|
||||
|
||||
// TODO: the functions below should eventually be intrinsified
|
||||
|
||||
inline fun <reified T : CVariable> CVariable.Type.Companion.of() = T::class.companionObjectInstance as CVariable.Type
|
||||
|
||||
/**
|
||||
* Returns interpretation of entity with given pointer.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : NativePointed> interpretPointed(ptr: NativePtr): T {
|
||||
return ensuringNotNull(ptr) {
|
||||
val kClass = T::class
|
||||
val primaryConstructor = kClass.primaryConstructor
|
||||
if (primaryConstructor == null) {
|
||||
throw IllegalArgumentException("${kClass.simpleName} doesn't have a constructor")
|
||||
}
|
||||
(primaryConstructor as (NativePtr) -> T)(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
|
||||
T::class.objectInstance!!
|
||||
@@ -1,46 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
package kotlin_native.interop
|
||||
|
||||
import java.io.Closeable
|
||||
|
||||
// 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)
|
||||
|
||||
final override fun equals(other: Any?): Boolean{
|
||||
if (this === other) return true
|
||||
if (other !is NativeRef) return false
|
||||
|
||||
return ptr == other.ptr
|
||||
}
|
||||
|
||||
final override fun hashCode(): Int{
|
||||
return ptr.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun asCharPtr() = array[0]
|
||||
}
|
||||
|
||||
class __va_list_tag(ptr: NativePtr) : NativeRef(ptr) // FIXME
|
||||
|
||||
interface Placement {
|
||||
fun alloc(size: Int): NativePtr
|
||||
}
|
||||
|
||||
object heap : Placement {
|
||||
override fun alloc(size: Int) = bridge.malloc(size)
|
||||
|
||||
fun free(ptr: NativePtr) = bridge.free(ptr)
|
||||
|
||||
fun free(ref: NativeRef) = free(ref.ptr)
|
||||
}
|
||||
|
||||
// TODO: implement optimally
|
||||
class Arena : Placement, Closeable {
|
||||
|
||||
private val allocatedChunks = mutableListOf<NativePtr>()
|
||||
|
||||
override fun alloc(size: Int): NativePtr {
|
||||
val res = heap.alloc(size)
|
||||
try {
|
||||
allocatedChunks.add(res)
|
||||
return res
|
||||
} catch (e: Throwable) {
|
||||
heap.free(res)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
allocatedChunks.forEach {
|
||||
heap.free(it)
|
||||
}
|
||||
|
||||
allocatedChunks.clear()
|
||||
}
|
||||
|
||||
override fun close() = clear()
|
||||
|
||||
}
|
||||
|
||||
fun <T : NativeRef> Placement.alloc(type: NativeRef.TypeWithSize<T>): T {
|
||||
return type.byPtr(this.alloc(type.size))
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package kotlin_native.interop
|
||||
|
||||
fun <T : NativeRef> malloc(type: NativeRef.TypeWithSize<T>) = type.byPtr(bridge.malloc(type.size))
|
||||
|
||||
fun free(ptr: NativePtr?) {
|
||||
if (ptr != null) {
|
||||
bridge.free(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
fun free(ref: NativeRef?) = free(ref.getNativePtr())
|
||||
|
||||
fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, elements: List<T?>): NativeArray<RefBox<T>> {
|
||||
val res = this.alloc(array[elements.size](elemType.ref))
|
||||
elements.forEachIndexed { i, element ->
|
||||
res[i].value = element
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fun <T : NativeRef> Placement.allocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) =
|
||||
allocNativeArrayOf(elemType, elements.toList())
|
||||
|
||||
fun <T : NativeRef> mallocNativeArrayOf(elemType: NativeRef.Type<T>, vararg elements: T?) = heap.allocNativeArrayOf(elemType, *elements)
|
||||
|
||||
fun Placement.allocNativeArrayOf(elements: ByteArray): NativeArray<Int8Box> {
|
||||
val res = this.alloc(array[elements.size](Int8Box))
|
||||
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)
|
||||
}
|
||||
|
||||
fun NativeArray<Int8Box>.asCString() = CString.fromArray(this)
|
||||
fun Int8Box.asCString() = CString.fromArray(NativeArray.byRefToFirstElem(this, Int8Box))
|
||||
fun String.toCString() = CString.fromString(this)
|
||||
|
||||
class MemScope private constructor(private val arena: Arena) : Placement by arena {
|
||||
val memScope: Placement
|
||||
get() = this
|
||||
|
||||
companion object {
|
||||
internal inline fun <R> use(block: MemScope.()->R): R {
|
||||
val memScope = MemScope(Arena())
|
||||
try {
|
||||
return memScope.block()
|
||||
} finally {
|
||||
memScope.arena.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs given [block] providing allocation of memory
|
||||
* which will be automatically disposed at the end of this scope.
|
||||
*/
|
||||
inline fun <R> memScoped(block: MemScope.()->R): R {
|
||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") // TODO: it is a hack
|
||||
return MemScope.use(block)
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
package kotlinx.cinterop
|
||||
|
||||
/**
|
||||
* The entity which has an associated native pointer.
|
||||
* Subtypes are supposed to represent interpretations of the pointed data or code.
|
||||
*
|
||||
* This interface is likely to be handled by compiler magic and shouldn't be subtyped by arbitrary classes.
|
||||
*/
|
||||
interface NativePointed {
|
||||
val rawPtr: NativePtr
|
||||
}
|
||||
|
||||
// `null` value of `NativePointed?` is mapped to `nativeNullPtr`.
|
||||
val NativePointed?.rawPtr: NativePtr
|
||||
get() = this?.rawPtr ?: nativeNullPtr
|
||||
|
||||
/**
|
||||
* Returns interpretation of entity with given pointer, or `null` if it is null.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : NativePointed> interpretNullablePointed(ptr: NativePtr): T? {
|
||||
return ifNotNull(ptr) {
|
||||
interpretPointed<T>(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the function to the pointer if it is not null, otherwise returns `null`.
|
||||
*/
|
||||
inline fun <T> ifNotNull(ptr: NativePtr, function: (NativePtr)->T): T? {
|
||||
return if (ptr == nativeNullPtr) {
|
||||
null
|
||||
} else {
|
||||
function(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the function to the pointer ensuring that it is not null.
|
||||
*/
|
||||
inline fun <T> ensuringNotNull(ptr: NativePtr, function: (NativePtr)->T): T {
|
||||
if (ptr == nativeNullPtr) {
|
||||
throw IllegalArgumentException()
|
||||
} else {
|
||||
return function(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the interpretation of the pointed data or code.
|
||||
*/
|
||||
inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpretPointed(this.rawPtr)
|
||||
|
||||
/**
|
||||
* C data or code.
|
||||
*/
|
||||
interface CPointed : NativePointed
|
||||
|
||||
/**
|
||||
* C pointer.
|
||||
*/
|
||||
class CPointer<T : CPointed> private constructor(val rawValue: NativePtr) {
|
||||
companion object {
|
||||
fun <T : CPointed> create(rawValue: NativePtr) = ensuringNotNull(rawValue) {
|
||||
CPointer<T>(rawValue)
|
||||
}
|
||||
|
||||
fun <T : CPointed> createNullable(rawValue: NativePtr) = ifNotNull(rawValue) {
|
||||
CPointer<T>(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
return true // fast path
|
||||
}
|
||||
|
||||
return (other is CPointer<*>) && (rawValue == other.rawValue)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return rawValue.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "CPointer(raw=0x%x)".format(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pointer to this data or code.
|
||||
*/
|
||||
val <T : CPointed> T.ptr: CPointer<T>
|
||||
get() = CPointer.create(this.rawPtr)
|
||||
|
||||
/**
|
||||
* Returns the corresponding [CPointed].
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline val <reified T : CPointed> CPointer<T>.pointed: T
|
||||
get() = interpretPointed<T>(this.rawValue)
|
||||
|
||||
// `null` value of `CPointer?` is mapped to `nativeNullPtr`
|
||||
val CPointer<*>?.rawValue: NativePtr
|
||||
get() = this?.rawValue ?: nativeNullPtr
|
||||
|
||||
fun <T : CPointed> CPointer<*>.reinterpret(): CPointer<T> = CPointer.create(this.rawValue)
|
||||
|
||||
/**
|
||||
* The [CPointed] without any specified interpretation.
|
||||
*/
|
||||
interface COpaque : CPointed // TODO: should it correspond to COpaquePointer?
|
||||
|
||||
/**
|
||||
* The pointer with an opaque type.
|
||||
*/
|
||||
typealias COpaquePointer = CPointer<out CPointed> // FIXME
|
||||
|
||||
/**
|
||||
* The variable containing a [COpaquePointer].
|
||||
*/
|
||||
typealias COpaquePointerVar = CPointerVarWithValueMappedTo<COpaquePointer>
|
||||
|
||||
/**
|
||||
* The C data variable located in memory.
|
||||
*
|
||||
* The non-abstract subclasses should represent the (complete) C data type and thus specify size and alignment.
|
||||
* Each such subclass must have a companion object which is a [Type].
|
||||
*/
|
||||
interface CVariable : CPointed {
|
||||
|
||||
/**
|
||||
* The (complete) C data type.
|
||||
*
|
||||
* @param size the size in bytes of data of this type
|
||||
* @param align the alignments in bytes that is enough for this data type.
|
||||
* It may be greater than actually required for simplicity.
|
||||
*/
|
||||
open class Type(val size: Long, val align: Int) {
|
||||
|
||||
init {
|
||||
assert (size % align == 0L)
|
||||
}
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline fun <reified T : CVariable> sizeOf() = Type.of<T>().size
|
||||
inline fun <reified T : CVariable> alignOf() = Type.of<T>().align
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The C data which is composed from several members.
|
||||
*/
|
||||
interface CAggregate : CPointed
|
||||
|
||||
/**
|
||||
* Returns the member of this [CAggregate] which is located by given offset in bytes.
|
||||
*/
|
||||
inline fun <reified T : CPointed> CAggregate.memberAt(offset: Long): T {
|
||||
return interpretPointed<T>(this.rawPtr + offset)
|
||||
}
|
||||
|
||||
/**
|
||||
* The C struct-typed variable located in memory.
|
||||
*/
|
||||
abstract class CStructVar : CVariable, CAggregate {
|
||||
open class Type(size: Long, align: Int) : CVariable.Type(size, align)
|
||||
}
|
||||
|
||||
/**
|
||||
* The C primitive-typed variable located in memory.
|
||||
*/
|
||||
sealed class CPrimitiveVar : CVariable {
|
||||
// aligning by size is obviously enough
|
||||
open class Type(size: Int, align: Int = size) : CVariable.Type(size.toLong(), align)
|
||||
}
|
||||
|
||||
abstract class CEnumVar : CPrimitiveVar()
|
||||
|
||||
// generics below are used for typedef support
|
||||
// these classes are not supposed to be used directly, instead the typealiases are provided.
|
||||
|
||||
class CInt8VarWithValueMappedTo<T : Byte>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||
companion object : Type(1)
|
||||
}
|
||||
|
||||
class CInt16VarWithValueMappedTo<T : Short>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||
companion object : Type(2)
|
||||
}
|
||||
|
||||
class CInt32VarWithValueMappedTo<T : Int>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||
companion object : Type(4)
|
||||
}
|
||||
|
||||
class CInt64VarWithValueMappedTo<T : Long>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||
companion object : Type(8)
|
||||
}
|
||||
|
||||
class CFloat32VarWithValueMappedTo<T : Float>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||
companion object : Type(4)
|
||||
}
|
||||
|
||||
class CFloat64VarWithValueMappedTo<T : Double>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||
companion object : Type(8)
|
||||
}
|
||||
|
||||
typealias CInt8Var = CInt8VarWithValueMappedTo<Byte>
|
||||
typealias CInt16Var = CInt16VarWithValueMappedTo<Short>
|
||||
typealias CInt32Var = CInt32VarWithValueMappedTo<Int>
|
||||
typealias CInt64Var = CInt64VarWithValueMappedTo<Long>
|
||||
typealias CFloat32Var = CFloat32VarWithValueMappedTo<Float>
|
||||
typealias CFloat64Var = CFloat64VarWithValueMappedTo<Double>
|
||||
|
||||
var <T : Byte> CInt8VarWithValueMappedTo<T>.value: T
|
||||
get() = nativeMemUtils.getByte(this) as T
|
||||
set(value) = nativeMemUtils.putByte(this, value)
|
||||
|
||||
var <T : Short> CInt16VarWithValueMappedTo<T>.value: T
|
||||
get() = nativeMemUtils.getShort(this) as T
|
||||
set(value) = nativeMemUtils.putShort(this, value)
|
||||
|
||||
var <T : Int> CInt32VarWithValueMappedTo<T>.value: T
|
||||
get() = nativeMemUtils.getInt(this) as T
|
||||
set(value) = nativeMemUtils.putInt(this, value)
|
||||
|
||||
var <T : Long> CInt64VarWithValueMappedTo<T>.value: T
|
||||
get() = nativeMemUtils.getLong(this) as T
|
||||
set(value) = nativeMemUtils.putLong(this, value)
|
||||
|
||||
// TODO: ensure native floats have the appropriate binary representation
|
||||
|
||||
var <T : Float> CFloat32VarWithValueMappedTo<T>.value: T
|
||||
get() = nativeMemUtils.getFloat(this) as T
|
||||
set(value) = nativeMemUtils.putFloat(this, value)
|
||||
|
||||
var <T : Double> CFloat64VarWithValueMappedTo<T>.value: T
|
||||
get() = nativeMemUtils.getDouble(this) as T
|
||||
set(value) = nativeMemUtils.putDouble(this, value)
|
||||
|
||||
|
||||
class CPointerVarWithValueMappedTo<T : CPointer<*>>(override val rawPtr: NativePtr) : CVariable {
|
||||
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* The C data variable containing the pointer to `T`.
|
||||
*/
|
||||
typealias CPointerVar<T> = CPointerVarWithValueMappedTo<CPointer<T>>
|
||||
|
||||
/**
|
||||
* The value of this variable.
|
||||
*/
|
||||
inline var <reified P : CPointer<*>> CPointerVarWithValueMappedTo<P>.value: P?
|
||||
get() = CPointer.createNullable<CPointed>(nativeMemUtils.getPtr(this)) as P?
|
||||
set(value) = nativeMemUtils.putPtr(this, value.rawValue)
|
||||
|
||||
/**
|
||||
* The code or data pointed by the value of this variable.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline var <reified T : CPointed, reified P : CPointer<T>> CPointerVarWithValueMappedTo<P>.pointed: T?
|
||||
get() = this.value?.pointed
|
||||
set(value) {
|
||||
this.value = value?.ptr as P?
|
||||
}
|
||||
|
||||
class CArray<T : CVariable>(override val rawPtr: NativePtr) : CAggregate
|
||||
|
||||
inline fun <reified T : CVariable> CArray<T>.elementOffset(index: Long) = if (index == 0L) {
|
||||
0L // optimization for JVM impl which uses reflection for now.
|
||||
} else {
|
||||
index * CVariable.sizeOf<T>()
|
||||
}
|
||||
|
||||
inline operator fun <reified T : CVariable> CArray<T>.get(index: Long): T = memberAt(elementOffset(index))
|
||||
inline operator fun <reified T : CVariable> CArray<T>.get(index: Int) = this.get(index.toLong())
|
||||
|
||||
/**
|
||||
* The type of C function.
|
||||
*/
|
||||
interface CFunctionType
|
||||
|
||||
/**
|
||||
* The type of C function constructed from some Kotlin function, possibly using an adapter.
|
||||
* The (non-abstract) implementation classes are supposed to be object declarations.
|
||||
*/
|
||||
interface CAdaptedFunctionType<F : Function<*>> : CFunctionType {
|
||||
|
||||
/**
|
||||
* Returns a raw pointer to C function of this type, which calls given Kotlin *static* function.
|
||||
*
|
||||
* This inconvenient method should not be used directly; use [staticCFunction] instead.
|
||||
*
|
||||
* @param function must be *static*, i.e. an (unbound) reference to a Kotlin function or
|
||||
* a closure which doesn't capture any variable
|
||||
*/
|
||||
fun fromStatic(function: F): NativePtr
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to `T`-typed C function which calls given Kotlin *static* function.
|
||||
* @see CAdaptedFunctionType.fromStatic
|
||||
*/
|
||||
inline fun <reified F : Function<*>, reified T : CAdaptedFunctionType<F>> staticCFunction(body: F): CFunctionPointer<T> {
|
||||
val type = CAdaptedFunctionType.getInstanceOf<T>()
|
||||
return interpretPointed<CFunction<T>>(type.fromStatic(body)).ptr
|
||||
}
|
||||
|
||||
/**
|
||||
* The C function.
|
||||
*/
|
||||
class CFunction<T : CFunctionType>(override val rawPtr: NativePtr) : CPointed
|
||||
|
||||
/**
|
||||
* The pointer to [CFunction].
|
||||
*/
|
||||
typealias CFunctionPointer<T> = CPointer<CFunction<T>>
|
||||
|
||||
/**
|
||||
* The variable containing a [CFunctionPointer].
|
||||
*/
|
||||
typealias CFunctionPointerVar<T> = CPointerVarWithValueMappedTo<CFunctionPointer<T>>
|
||||
@@ -0,0 +1,225 @@
|
||||
package kotlinx.cinterop
|
||||
|
||||
interface NativePlacement {
|
||||
|
||||
fun alloc(size: Long, align: Int): NativePointed
|
||||
|
||||
fun alloc(size: Int, align: Int) = alloc(size.toLong(), align)
|
||||
}
|
||||
|
||||
interface NativeFreeablePlacement : NativePlacement {
|
||||
fun free(mem: NativePointed)
|
||||
}
|
||||
|
||||
object nativeHeap : NativeFreeablePlacement {
|
||||
override fun alloc(size: Long, align: Int) = nativeMemUtils.alloc(size, align)
|
||||
|
||||
override fun free(mem: NativePointed) = nativeMemUtils.free(mem)
|
||||
}
|
||||
|
||||
// TODO: implement optimally
|
||||
class Arena(private val parent: NativeFreeablePlacement = nativeHeap) : NativePlacement {
|
||||
|
||||
private val allocatedChunks = mutableListOf<NativePointed>()
|
||||
|
||||
override fun alloc(size: Long, align: Int): NativePointed {
|
||||
val res = nativeHeap.alloc(size, align)
|
||||
try {
|
||||
allocatedChunks.add(res)
|
||||
return res
|
||||
} catch (e: Throwable) {
|
||||
nativeHeap.free(res)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
allocatedChunks.forEach {
|
||||
nativeHeap.free(it)
|
||||
}
|
||||
|
||||
allocatedChunks.clear()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun NativePlacement.alloc(size: Int, align: Int) = alloc(size.toLong(), align)
|
||||
|
||||
/**
|
||||
* Allocates variable of given type.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : CVariable> NativePlacement.alloc(): T =
|
||||
alloc(CVariable.sizeOf<T>(), CVariable.alignOf<T>()).reinterpret()
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long): CArray<T> =
|
||||
alloc(CVariable.sizeOf<T>() * length, CVariable.alignOf<T>()).reinterpret()
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int): CArray<T> =
|
||||
allocArray(length.toLong())
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length, and initializes its elements applying given block.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long,
|
||||
initializer: T.(index: Long)->Unit): CArray<T> {
|
||||
val res = allocArray<T>(length)
|
||||
|
||||
(0 until length).forEach { index ->
|
||||
res[index].initializer(index)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length, and initializes its elements applying given block.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int,
|
||||
initializer: T.(index: Int)->Unit): CArray<T> =
|
||||
allocArray(length.toLong()) { index ->
|
||||
this.initializer(index.toInt())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates C array of pointers to given elements.
|
||||
*/
|
||||
fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(elements: List<T?>): CArray<CPointerVar<T>> {
|
||||
val res = allocArray<CPointerVar<T>>(elements.size)
|
||||
elements.forEachIndexed { index, value ->
|
||||
res[index].value = value?.ptr
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates C array of pointers to given elements.
|
||||
*/
|
||||
fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(vararg elements: T?) =
|
||||
allocArrayOfPointersTo(elements.toList())
|
||||
|
||||
/**
|
||||
* Allocates C array of given values.
|
||||
*/
|
||||
inline fun <reified T : CPointer<*>>
|
||||
NativePlacement.allocArrayOf(vararg elements: T?): CArray<CPointerVarWithValueMappedTo<T>> {
|
||||
|
||||
return allocArrayOf(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates C array of given values.
|
||||
*/
|
||||
inline fun <reified T : CPointer<*>>
|
||||
NativePlacement.allocArrayOf(elements: List<T?>): CArray<CPointerVarWithValueMappedTo<T>> {
|
||||
|
||||
val res = allocArray<CPointerVarWithValueMappedTo<T>>(elements.size)
|
||||
elements.forEachIndexed { index, value ->
|
||||
res[index].value = value
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fun NativePlacement.allocArrayOf(elements: ByteArray): CArray<CInt8Var> {
|
||||
val res = allocArray<CInt8Var>(elements.size)
|
||||
elements.forEachIndexed { i, byte ->
|
||||
res[i].value = byte
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fun <T : CPointed> NativePlacement.allocPointerTo() = alloc<CPointerVar<T>>()
|
||||
|
||||
/**
|
||||
* The zero-terminated string.
|
||||
*/
|
||||
class CString private constructor(override val rawPtr: NativePtr) : CPointed {
|
||||
|
||||
companion object {
|
||||
fun fromArray(array: CArray<CInt8Var>) = CString(array.rawPtr)
|
||||
}
|
||||
|
||||
fun length(): Int {
|
||||
val array = reinterpret<CArray<CInt8Var>>()
|
||||
|
||||
var res = 0
|
||||
while (array[res].value != 0.toByte()) {
|
||||
++res
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val array = reinterpret<CArray<CInt8Var>>()
|
||||
|
||||
val bytes = ByteArray(this.length())
|
||||
bytes.forEachIndexed { i, byte ->
|
||||
bytes[i] = array[i].value
|
||||
}
|
||||
return String(bytes) // TODO: encoding
|
||||
}
|
||||
|
||||
fun asCharPtr() = reinterpret<CInt8Var>()
|
||||
}
|
||||
|
||||
fun CString.Companion.fromString(str: String?, placement: NativePlacement): CString? {
|
||||
if (str == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val bytes = str.toByteArray() // TODO: encoding
|
||||
val len = bytes.size
|
||||
val nativeBytes = nativeHeap.allocArray<CInt8Var>(len + 1)
|
||||
|
||||
bytes.forEachIndexed { i, byte ->
|
||||
nativeBytes[i].value = byte
|
||||
}
|
||||
nativeBytes[len].value = 0
|
||||
|
||||
return CString.fromArray(nativeBytes)
|
||||
}
|
||||
|
||||
fun CPointer<CInt8Var>.asCString() = CString.fromArray(this.reinterpret<CArray<CInt8Var>>().pointed)
|
||||
fun String.toCString(placement: NativePlacement) = CString.fromString(this, placement)
|
||||
|
||||
class MemScope private constructor(private val arena: Arena) : NativePlacement by arena {
|
||||
val memScope: NativePlacement
|
||||
get() = this
|
||||
|
||||
companion object {
|
||||
internal inline fun <R> use(block: MemScope.()->R): R {
|
||||
val memScope = MemScope(Arena())
|
||||
try {
|
||||
return memScope.block()
|
||||
} finally {
|
||||
memScope.arena.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs given [block] providing allocation of memory
|
||||
* which will be automatically disposed at the end of this scope.
|
||||
*/
|
||||
inline fun <R> memScoped(block: MemScope.()->R): R {
|
||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") // TODO: it is a hack
|
||||
return MemScope.use(block)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* This package contains API and runtime support for calling C code from Kotlin (aka Kotlin C interop).
|
||||
*
|
||||
* TODO: decide about package location.
|
||||
*/
|
||||
package kotlinx.cinterop;
|
||||
@@ -1,7 +1,10 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
@@ -13,10 +16,6 @@ apply plugin: 'application'
|
||||
|
||||
mainClassName = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
compile project(':Interop:Indexer')
|
||||
|
||||
+259
-199
@@ -72,7 +72,7 @@ class StubGenerator(
|
||||
val FunctionType.kotlinName: String
|
||||
get() {
|
||||
return usedFunctionTypes.getOrPut(this, {
|
||||
"NativeFunctionType" + (usedFunctionTypes.size + 1)
|
||||
"CFunctionType" + (usedFunctionTypes.size + 1)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -107,6 +107,15 @@ class StubGenerator(
|
||||
return withOutput({ oldOut(" $it") }, action)
|
||||
}
|
||||
|
||||
private fun <R> block(header: String, body: () -> R): R {
|
||||
out("$header {")
|
||||
val res = indent {
|
||||
body()
|
||||
}
|
||||
out("}")
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expression which could be used for this type in C code.
|
||||
*
|
||||
@@ -147,6 +156,8 @@ class StubGenerator(
|
||||
this.def.spelling
|
||||
}
|
||||
|
||||
is Typedef -> this.def.name
|
||||
|
||||
else -> throw kotlin.NotImplementedError()
|
||||
}
|
||||
}
|
||||
@@ -164,55 +175,169 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the Kotlin native reference type
|
||||
*
|
||||
* @param typeName the name of the Kotlin native reference type
|
||||
* @param typeExpr such Kotlin expression that `typeExpr(ptr)` constructs the reference of this type
|
||||
* to the value located at `ptr`
|
||||
* Describes the Kotlin types used to represent some C type.
|
||||
*/
|
||||
class NativeRefType(val typeName: String, val typeExpr: String = typeName)
|
||||
private sealed class TypeMirror(val pointedTypeName: String, val info: TypeInfo) {
|
||||
/**
|
||||
* Type to be used in bindings for argument or return value.
|
||||
*/
|
||||
abstract val argType: String
|
||||
|
||||
/**
|
||||
* Mirror for C type to be represented in Kotlin as by-value type.
|
||||
*/
|
||||
class ByValue(pointedTypeName: String, info: TypeInfo, val valueTypeName: String) :
|
||||
TypeMirror(pointedTypeName, info) {
|
||||
|
||||
override val argType: String
|
||||
get() = valueTypeName + if (info is TypeInfo.Pointer) "?" else ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror for C type to be represented in Kotlin as by-ref type.
|
||||
*/
|
||||
class ByRef(pointedTypeName: String, info: TypeInfo) : TypeMirror(pointedTypeName, info) {
|
||||
override val argType: String
|
||||
get() = pointedTypeName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Kotlin type which describes the reference to value of given (C) type.
|
||||
* Describes various type conversions for [TypeMirror].
|
||||
*/
|
||||
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")
|
||||
private sealed class TypeInfo {
|
||||
/**
|
||||
* The conversion from [TypeMirror.argType] to [jniType].
|
||||
*/
|
||||
abstract fun argToJni(name: String): String
|
||||
|
||||
is RecordType -> NativeRefType("${type.kotlinName}")
|
||||
/**
|
||||
* The conversion from [jniType] to [TypeMirror.argType].
|
||||
*/
|
||||
abstract fun argFromJni(name: String): String
|
||||
|
||||
/**
|
||||
* The type to be used for passing [TypeMirror.argType] through JNI.
|
||||
*/
|
||||
abstract val jniType: String
|
||||
|
||||
/**
|
||||
* If this info is for [TypeMirror.ByValue], then this method describes how to
|
||||
* construct pointed-type from value type.
|
||||
*/
|
||||
abstract fun constructPointedType(valueType: String): String
|
||||
|
||||
class Primitive(override val jniType: String, val varTypeName: String) : TypeInfo() {
|
||||
|
||||
override fun argToJni(name: String) = name
|
||||
override fun argFromJni(name: String) = name
|
||||
|
||||
override fun constructPointedType(valueType: String) = "${varTypeName}WithValueMappedTo<$valueType>"
|
||||
}
|
||||
|
||||
class Enum(val className: String, val baseType: String) : TypeInfo() {
|
||||
override fun argToJni(name: String) = "$name.value"
|
||||
|
||||
override fun argFromJni(name: String) = "$className.byValue($name)"
|
||||
|
||||
override val jniType: String
|
||||
get() = baseType
|
||||
|
||||
override fun constructPointedType(valueType: String) = "$className.Var" // TODO: improve
|
||||
|
||||
}
|
||||
|
||||
class Pointer(val pointee: String) : TypeInfo() {
|
||||
override fun argToJni(name: String) = "$name.rawValue"
|
||||
|
||||
override fun argFromJni(name: String) = "CPointer.createNullable<$pointee>($name)"
|
||||
|
||||
override val jniType: String
|
||||
get() = "Long"
|
||||
|
||||
override fun constructPointedType(valueType: String) = "CPointerVarWithValueMappedTo<$valueType>"
|
||||
}
|
||||
|
||||
class ByRef(val pointed: String) : TypeInfo() {
|
||||
override fun argToJni(name: String) = "$name.rawPtr"
|
||||
|
||||
override fun argFromJni(name: String) = "interpretPointed<$pointed>($name)"
|
||||
|
||||
override val jniType: String
|
||||
get() = "Long"
|
||||
|
||||
override fun constructPointedType(valueType: String): String {
|
||||
// TODO: this method must not exist
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mirror(type: PrimitiveType): TypeMirror {
|
||||
val varTypeName = when (type) {
|
||||
is Int8Type, is UInt8Type -> "CInt8Var"
|
||||
is Int16Type, is UInt16Type -> "CInt16Var"
|
||||
is Int32Type, is UInt32Type -> "CInt32Var"
|
||||
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
||||
is Int64Type, is UInt64Type -> "CInt64Var"
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
val info = TypeInfo.Primitive(type.kotlinType, varTypeName)
|
||||
return TypeMirror.ByValue(varTypeName, info, type.kotlinType)
|
||||
}
|
||||
|
||||
private fun byRefTypeMirror(pointedTypeName: String) : TypeMirror.ByRef {
|
||||
val info = TypeInfo.ByRef(pointedTypeName)
|
||||
return TypeMirror.ByRef(pointedTypeName, info)
|
||||
}
|
||||
|
||||
private fun mirror(type: Type): TypeMirror = when (type) {
|
||||
is PrimitiveType -> mirror(type)
|
||||
|
||||
is RecordType -> byRefTypeMirror(type.kotlinName)
|
||||
|
||||
is EnumType -> if (type.isKotlinEnum) {
|
||||
NativeRefType("${type.kotlinName}.ref")
|
||||
val className = type.kotlinName
|
||||
val info = TypeInfo.Enum(className, type.def.baseType.kotlinType)
|
||||
TypeMirror.ByValue("$className.Var", info, className)
|
||||
} else {
|
||||
getKotlinTypeForRefTo(type.def.baseType)
|
||||
mirror(type.def.baseType)
|
||||
}
|
||||
|
||||
is PointerType -> {
|
||||
val pointeeType = type.pointeeType
|
||||
if (pointeeType is VoidType) {
|
||||
NativeRefType("NativePtrBox")
|
||||
val info = TypeInfo.Pointer("COpaque")
|
||||
TypeMirror.ByValue("COpaquePointerVar", info, "COpaquePointer")
|
||||
} else if (pointeeType is FunctionType) {
|
||||
NativeRefType("NativeFunctionBox<${getKotlinFunctionType(pointeeType)}>", "${pointeeType.kotlinName}.ref")
|
||||
val kotlinName = pointeeType.kotlinName
|
||||
val info = TypeInfo.Pointer("CFunction<$kotlinName>")
|
||||
TypeMirror.ByValue("CFunctionPointerVar<$kotlinName>", info, "CFunctionPointer<$kotlinName>")
|
||||
} else {
|
||||
val pointeeRefType = getKotlinTypeForRefTo(pointeeType)
|
||||
NativeRefType("RefBox<${pointeeRefType.typeName}>", "${pointeeRefType.typeExpr}.ref")
|
||||
val pointeeMirror = mirror(pointeeType)
|
||||
val info = TypeInfo.Pointer(pointeeMirror.pointedTypeName)
|
||||
TypeMirror.ByValue("CPointerVar<${pointeeMirror.pointedTypeName}>", info,
|
||||
"CPointer<${pointeeMirror.pointedTypeName}>")
|
||||
}
|
||||
}
|
||||
|
||||
is ConstArrayType -> {
|
||||
val elemRefType = getKotlinTypeForRefTo(type.elemType)
|
||||
NativeRefType("NativeArray<${elemRefType.typeName}>", "array[${type.length}](${elemRefType.typeExpr})")
|
||||
is ArrayType -> {
|
||||
val elemMirror = mirror(type.elemType)
|
||||
byRefTypeMirror("CArray<${elemMirror.pointedTypeName}>")
|
||||
}
|
||||
|
||||
is IncompleteArrayType -> {
|
||||
val elemRefType = getKotlinTypeForRefTo(type.elemType)
|
||||
NativeRefType("NativeArray<${elemRefType.typeName}>", "array(${elemRefType.typeExpr})")
|
||||
is Typedef -> {
|
||||
val baseType = mirror(type.def.aliased)
|
||||
val name = type.def.name
|
||||
when (baseType) {
|
||||
is TypeMirror.ByValue -> TypeMirror.ByValue("${name}Var", baseType.info, name)
|
||||
is TypeMirror.ByRef -> TypeMirror.ByRef(name, baseType.info)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,12 +345,12 @@ class StubGenerator(
|
||||
*
|
||||
* @param kotlinType the name of Kotlin type to be used for this value in Kotlin.
|
||||
* @param kotlinConv the function such that `kotlinConv(name)` converts variable `name` to pass it into JNI stub
|
||||
* @param convFree the function such that `convFree(name)` is the statement that frees the result of conversion
|
||||
* @param memScoped the conversion allocates memory and should be placed into `memScoped {}` block
|
||||
* @param kotlinJniBridgeType the name of Kotlin type to be used for this value in JNI stub
|
||||
*/
|
||||
class OutValueBinding(val kotlinType: String,
|
||||
val kotlinConv: ((String) -> String)? = null,
|
||||
val convFree: ((String) -> String)? = null,
|
||||
val memScoped: Boolean = false,
|
||||
val kotlinJniBridgeType: String = kotlinType)
|
||||
|
||||
/**
|
||||
@@ -239,85 +364,36 @@ class StubGenerator(
|
||||
val conv: ((String) -> String) = { it },
|
||||
val kotlinType: String = kotlinJniBridgeType)
|
||||
|
||||
/**
|
||||
* Constructs [OutValueBinding] for the value represented by given native reference type in Kotlin.
|
||||
*/
|
||||
fun outValueRefBinding(refType: NativeRefType) = OutValueBinding(
|
||||
kotlinType = refType.typeName + "?",
|
||||
kotlinConv = { "$it.getNativePtr().asLong()" },
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [InValueBinding] for the value represented by given native reference type in Kotlin.
|
||||
*/
|
||||
fun inValueRefBinding(refType: NativeRefType) = InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})" },
|
||||
kotlinType = refType.typeName + "?"
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [OutValueBinding] for the value of given C type.
|
||||
*/
|
||||
fun getOutValueBinding(type: Type): OutValueBinding = when (type) {
|
||||
|
||||
is PrimitiveType -> OutValueBinding(type.kotlinType)
|
||||
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
|
||||
OutValueBinding(
|
||||
kotlinType = "NativePtr?",
|
||||
kotlinConv = { "$it.asLong()" },
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
} else {
|
||||
outValueRefBinding(getKotlinTypeForRefTo(type.pointeeType))
|
||||
}
|
||||
fun getOutValueBinding(type: Type): OutValueBinding {
|
||||
if (type is ArrayType) {
|
||||
// array-typed C function argument or return value is actually a pointer to array.
|
||||
return getOutValueBinding(PointerType(type))
|
||||
}
|
||||
|
||||
is EnumType -> if (type.isKotlinEnum) {
|
||||
OutValueBinding(
|
||||
kotlinType = type.kotlinName,
|
||||
kotlinConv = { "$it.value" },
|
||||
kotlinJniBridgeType = type.def.baseType.kotlinType
|
||||
)
|
||||
} else {
|
||||
getOutValueBinding(type.def.baseType)
|
||||
}
|
||||
val mirror = mirror(type)
|
||||
|
||||
is ArrayType -> outValueRefBinding(getKotlinTypeForRefTo(type))
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
return OutValueBinding(
|
||||
kotlinType = mirror.argType,
|
||||
kotlinConv = mirror.info::argToJni,
|
||||
kotlinJniBridgeType = mirror.info.jniType
|
||||
)
|
||||
}
|
||||
|
||||
fun getCFunctionParamBinding(type: Type): OutValueBinding {
|
||||
when (type) {
|
||||
is PointerType -> {
|
||||
val pointeeType = type.pointeeType
|
||||
when (pointeeType) {
|
||||
is FunctionType -> return OutValueBinding(
|
||||
kotlinType = "(" + getKotlinFunctionType(pointeeType) + ")?",
|
||||
kotlinConv = { "$it?.staticAsNative(${pointeeType.kotlinName}).asLong()" },
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
when (type.pointeeType) {
|
||||
is Int8Type -> return OutValueBinding(
|
||||
kotlinType = "String?",
|
||||
kotlinConv = { name -> "CString.fromString($name).getNativePtr().asLong()" },
|
||||
convFree = { name -> "free(NativePtr.byValue($name))" },
|
||||
kotlinConv = { name -> "$name?.toCString(memScope).rawPtr" },
|
||||
memScoped = true,
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
}
|
||||
}
|
||||
is RecordType -> {
|
||||
val refType = getKotlinTypeForRefTo(type)
|
||||
// pointer will be converted to value in C code
|
||||
return OutValueBinding(
|
||||
kotlinType = refType.typeName,
|
||||
kotlinConv = { "$it.getNativePtr().asLong()" },
|
||||
kotlinJniBridgeType = "Long"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return getOutValueBinding(type)
|
||||
@@ -338,65 +414,30 @@ class StubGenerator(
|
||||
/**
|
||||
* Constructs [InValueBinding] for the value of given C type.
|
||||
*/
|
||||
fun getInValueBinding(type: Type): InValueBinding = when (type) {
|
||||
|
||||
is PrimitiveType -> InValueBinding(type.kotlinType)
|
||||
|
||||
is PointerType -> {
|
||||
if (type.pointeeType is VoidType || type.pointeeType is FunctionType) {
|
||||
InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { "NativePtr.byValue($it)" },
|
||||
kotlinType = "NativePtr?"
|
||||
)
|
||||
} else {
|
||||
inValueRefBinding(getKotlinTypeForRefTo(type.pointeeType))
|
||||
}
|
||||
fun getInValueBinding(type: Type): InValueBinding {
|
||||
if (type is ArrayType) {
|
||||
// array-typed C function argument or return value is actually a pointer to array.
|
||||
return getInValueBinding(PointerType(type))
|
||||
}
|
||||
|
||||
is EnumType -> if (type.isKotlinEnum) {
|
||||
InValueBinding(
|
||||
kotlinJniBridgeType = type.def.baseType.kotlinType,
|
||||
conv = { "${type.kotlinName}.byValue($it)" },
|
||||
kotlinType = type.kotlinName
|
||||
)
|
||||
} else {
|
||||
getInValueBinding(type.def.baseType)
|
||||
}
|
||||
val mirror = mirror(type)
|
||||
|
||||
is ArrayType -> inValueRefBinding(getKotlinTypeForRefTo(type))
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
return InValueBinding(
|
||||
kotlinJniBridgeType = mirror.info.jniType,
|
||||
conv = mirror.info::argFromJni,
|
||||
kotlinType = mirror.argType
|
||||
)
|
||||
}
|
||||
|
||||
fun getCFunctionRetValBinding(type: Type): InValueBinding {
|
||||
when (type) {
|
||||
is VoidType -> return InValueBinding("Unit")
|
||||
|
||||
is RecordType -> {
|
||||
val refType = getKotlinTypeForRefTo(type)
|
||||
return InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { "NativePtr.byValue($it).asRef(${refType.typeExpr})!!" },
|
||||
kotlinType = refType.typeName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return getInValueBinding(type)
|
||||
}
|
||||
|
||||
fun getCallbackParamBinding(type: Type): InValueBinding {
|
||||
when (type) {
|
||||
is RecordType -> {
|
||||
val refType = getKotlinTypeForRefTo(type)
|
||||
return InValueBinding(
|
||||
kotlinJniBridgeType = "Long",
|
||||
conv = { throw UnsupportedOperationException() },
|
||||
kotlinType = refType.typeName
|
||||
)
|
||||
}
|
||||
}
|
||||
return getInValueBinding(type)
|
||||
}
|
||||
|
||||
@@ -418,24 +459,24 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
val className = decl.kotlinName
|
||||
out("class $className(ptr: NativePtr) : NativeStruct(ptr) {")
|
||||
indent {
|
||||
block("class $className(override val rawPtr: NativePtr) : CStructVar()") {
|
||||
out("")
|
||||
out("companion object : Type<$className>(${def.size}, ::$className)")
|
||||
out("companion object : Type(${def.size}, ${def.align})") // FIXME: align
|
||||
out("")
|
||||
def.fields.forEach { field ->
|
||||
try {
|
||||
if (field.offset < 0) throw NotImplementedError();
|
||||
assert(field.offset % 8 == 0L)
|
||||
val offset = field.offset / 8
|
||||
val fieldRefType = getKotlinTypeForRefTo(field.type)
|
||||
out("val ${field.name} by ${fieldRefType.typeExpr} at $offset")
|
||||
val fieldRefType = mirror(field.type)
|
||||
out("val ${field.name}: ${fieldRefType.pointedTypeName}")
|
||||
out(" get() = memberAt($offset)")
|
||||
out("")
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate definition for field $className.${field.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,9 +484,7 @@ class StubGenerator(
|
||||
*/
|
||||
private fun generateForwardStruct(s: StructDecl) {
|
||||
val className = s.kotlinName
|
||||
out("class $className(ptr: NativePtr) : NativeRef(ptr) {")
|
||||
out(" companion object : Type<$className>(::$className)")
|
||||
out("}")
|
||||
out("class $className(override val rawPtr: NativePtr) : COpaque")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -457,28 +496,25 @@ class StubGenerator(
|
||||
return
|
||||
}
|
||||
|
||||
val baseRefType = getKotlinTypeForRefTo(e.baseType)
|
||||
val baseTypeMirror = mirror(e.baseType)
|
||||
|
||||
out("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType}) {")
|
||||
indent {
|
||||
block("enum class ${e.kotlinName}(val value: ${e.baseType.kotlinType})") {
|
||||
e.values.forEach {
|
||||
out("${it.name}(${it.value}),")
|
||||
}
|
||||
out(";")
|
||||
out("")
|
||||
out("companion object {")
|
||||
out(" fun byValue(value: ${e.baseType.kotlinType}) = ${e.kotlinName}.values().find { it.value == value }!!")
|
||||
out("}")
|
||||
block("companion object") {
|
||||
out("fun byValue(value: ${e.baseType.kotlinType}) = ${e.kotlinName}.values().find { it.value == value }!!")
|
||||
}
|
||||
out("")
|
||||
out("class ref(ptr: NativePtr) : NativeRef(ptr) {")
|
||||
out(" companion object : TypeWithSize<ref>(${baseRefType.typeExpr}.size, ::ref)")
|
||||
out(" var value: ${e.kotlinName}")
|
||||
out(" get() = byValue(${baseRefType.typeExpr}.byPtr(ptr).value)")
|
||||
out(" set(value) { ${baseRefType.typeExpr}.byPtr(ptr).value = value.value }")
|
||||
out("}")
|
||||
|
||||
block("class Var(override val rawPtr: NativePtr) : CEnumVar()") {
|
||||
out("companion object : Type(${baseTypeMirror.pointedTypeName}.size.toInt())")
|
||||
out("var value: ${e.kotlinName}")
|
||||
out(" get() = byValue(this.reinterpret<${baseTypeMirror.pointedTypeName}>().value)")
|
||||
out(" set(value) { this.reinterpret<${baseTypeMirror.pointedTypeName}>().value = value.value }")
|
||||
}
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,6 +536,23 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateTypedef(def: TypedefDef) {
|
||||
val mirror = mirror(Typedef(def))
|
||||
val baseMirror = mirror(def.aliased)
|
||||
|
||||
when (baseMirror) {
|
||||
is TypeMirror.ByValue -> {
|
||||
val name = def.name
|
||||
val varTypeName = mirror.info.constructPointedType(name)
|
||||
out("typealias ${mirror.pointedTypeName} = $varTypeName")
|
||||
out("typealias ${(mirror as TypeMirror.ByValue).valueTypeName} = ${baseMirror.valueTypeName}")
|
||||
}
|
||||
is TypeMirror.ByRef -> {
|
||||
out("typealias ${mirror.pointedTypeName} = ${baseMirror.pointedTypeName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs [InValueBinding] for return value of Kotlin binding for given C function.
|
||||
*/
|
||||
@@ -515,12 +568,11 @@ class StubGenerator(
|
||||
|
||||
val retValType = func.returnType
|
||||
if (retValType is RecordType) {
|
||||
val retValRefType = getKotlinTypeForRefTo(retValType)
|
||||
val typeExpr = retValRefType.typeExpr
|
||||
val retValMirror = mirror(retValType)
|
||||
|
||||
paramBindings.add(OutValueBinding(
|
||||
kotlinType = "Placement",
|
||||
kotlinConv = { name -> "$name.alloc($typeExpr.size).asLong()" },
|
||||
kotlinType = "NativePlacement",
|
||||
kotlinConv = { name -> "$name.alloc<${retValMirror.pointedTypeName}>().rawPtr" },
|
||||
kotlinJniBridgeType = "Long"
|
||||
))
|
||||
}
|
||||
@@ -560,8 +612,9 @@ class StubGenerator(
|
||||
"$name: " + paramBindings[i].kotlinType
|
||||
}.joinToString(", ")
|
||||
|
||||
out("fun ${func.name}($args): ${retValBinding.kotlinType} {")
|
||||
indent {
|
||||
val header = "fun ${func.name}($args): ${retValBinding.kotlinType}"
|
||||
|
||||
fun generateBody() {
|
||||
val externalParamNames = paramNames.mapIndexed { i: Int, name: String ->
|
||||
val binding = paramBindings[i]
|
||||
val externalParamName: String
|
||||
@@ -577,13 +630,6 @@ class StubGenerator(
|
||||
|
||||
}
|
||||
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]))
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpShims) {
|
||||
val returnValueRepresentation = retValBinding.conv("res")
|
||||
@@ -595,9 +641,18 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
out("return " + retValBinding.conv("res"))
|
||||
|
||||
}
|
||||
out("}")
|
||||
|
||||
block(header) {
|
||||
val memScoped = paramBindings.any { it.memScoped }
|
||||
if (memScoped) {
|
||||
block("memScoped") {
|
||||
generateBody()
|
||||
}
|
||||
} else {
|
||||
generateBody()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFfiStructType(elementTypes: List<Type>) =
|
||||
@@ -646,15 +701,13 @@ class StubGenerator(
|
||||
|
||||
val constructorArgsStr = constructorArgs.joinToString(", ")
|
||||
|
||||
out("object $name : NativeFunctionType<$kotlinFunctionType>($constructorArgsStr) {")
|
||||
indent {
|
||||
out("override fun invoke(function: $kotlinFunctionType, args: NativeArray<NativePtrBox>, ret: NativePtr) {")
|
||||
indent {
|
||||
block("object $name : CAdaptedFunctionTypeImpl<$kotlinFunctionType>($constructorArgsStr)") {
|
||||
block("override fun invoke(function: $kotlinFunctionType, args: CArray<COpaquePointerVar>, ret: COpaquePointer)") {
|
||||
val args = type.parameterTypes.mapIndexed { i, paramType ->
|
||||
val refType = getKotlinTypeForRefTo(paramType)
|
||||
val ref = "args[$i].value.asRef(${refType.typeExpr})!!"
|
||||
val pointedTypeName = mirror(paramType).pointedTypeName
|
||||
val ref = "args[$i].value!!.reinterpret<$pointedTypeName>().pointed"
|
||||
when (paramType) {
|
||||
is RecordType -> "$ref"
|
||||
is RecordType -> ref
|
||||
else -> "$ref.value"
|
||||
}
|
||||
}.joinToString(", ")
|
||||
@@ -665,15 +718,13 @@ class StubGenerator(
|
||||
is RecordType -> throw NotImplementedError()
|
||||
is VoidType -> {} // nothing to do
|
||||
else -> {
|
||||
val retRefType = getKotlinTypeForRefTo(type.returnType)
|
||||
out("${retRefType.typeExpr}.byPtr(ret).value = res")
|
||||
val pointedTypeName = mirror(type.returnType).pointedTypeName
|
||||
out("ret.reinterpret<$pointedTypeName>().pointed.value = res")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -699,7 +750,7 @@ class StubGenerator(
|
||||
out("package $pkgName")
|
||||
out("")
|
||||
}
|
||||
out("import kotlin_native.interop.*")
|
||||
out("import kotlinx.cinterop.*")
|
||||
out("")
|
||||
|
||||
functionsToBind.forEach {
|
||||
@@ -729,13 +780,23 @@ class StubGenerator(
|
||||
out("")
|
||||
}
|
||||
|
||||
nativeIndex.typedefs.forEach { t ->
|
||||
try {
|
||||
transaction {
|
||||
generateTypedef(t)
|
||||
out("")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
println("Warning: cannot generate typedef ${t.name}")
|
||||
}
|
||||
}
|
||||
|
||||
usedFunctionTypes.entries.forEach {
|
||||
generateFunctionType(it.key, it.value)
|
||||
out("")
|
||||
}
|
||||
|
||||
out("object externals {")
|
||||
indent {
|
||||
block("object externals") {
|
||||
out("init { System.loadLibrary(\"$libName\") }")
|
||||
functionsToBind.forEach {
|
||||
try {
|
||||
@@ -748,7 +809,6 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
}
|
||||
out("}")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -819,16 +879,16 @@ class StubGenerator(
|
||||
}
|
||||
val jniFuncName = "Java_" + funcFullName.replace("_", "_1").replace('.', '_').replace("$", "_00024")
|
||||
|
||||
out("JNIEXPORT $cReturnType JNICALL $jniFuncName (JNIEnv *env, jobject obj$args) {")
|
||||
block("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);")
|
||||
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("}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -12,10 +15,6 @@ apply plugin: 'kotlin'
|
||||
apply plugin: org.jetbrains.kotlin.NativeInteropPlugin
|
||||
apply plugin: 'application'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlinNativeInterop {
|
||||
llvm {
|
||||
defFile '../backend.native/llvm.def'
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
fun main(args: Array<String>) = memScoped {
|
||||
val module = LLVMModuleCreateWithName("module")
|
||||
println("module=" + module.getNativePtr().asLong())
|
||||
println("module=" + module.rawValue)
|
||||
|
||||
val paramTypes = mallocNativeArrayOf(LLVMOpaqueType, LLVMInt32Type(), LLVMInt32Type())
|
||||
val retType = LLVMFunctionType(LLVMInt32Type(), paramTypes[0], 2, 0)
|
||||
free(paramTypes)
|
||||
val paramTypes = allocArrayOf(LLVMInt32Type(), LLVMInt32Type())
|
||||
val retType = LLVMFunctionType(LLVMInt32Type(), paramTypes[0].ptr, 2, 0)
|
||||
|
||||
val sum = LLVMAddFunction(module, "sum", retType)
|
||||
val entry = LLVMAppendBasicBlock(sum, "entry")
|
||||
@@ -15,34 +14,32 @@ fun main(args: Array<String>) {
|
||||
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)
|
||||
val engineRef = alloc<LLVMExecutionEngineRefVar>()
|
||||
val errorRef = allocPointerTo<CInt8Var>()
|
||||
LLVMInitializeNativeTarget()
|
||||
errorRef.value = null
|
||||
if (LLVMCreateExecutionEngineForModule(engineRef, module, errorRef) != 0) {
|
||||
if (LLVMCreateExecutionEngineForModule(engineRef.ptr, module, errorRef.ptr) != 0) {
|
||||
println("failed to create execution engine")
|
||||
return
|
||||
}
|
||||
val error = errorRef.value
|
||||
if (error != null) {
|
||||
println(CString.fromArray(NativeArray.byRefToFirstElem(error, Int8Box)).toString())
|
||||
println(error.asCString().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])
|
||||
val args = allocArrayOf(
|
||||
LLVMCreateGenericValueOfInt(LLVMInt32Type(), x, 0),
|
||||
LLVMCreateGenericValueOfInt(LLVMInt32Type(), y, 0))
|
||||
|
||||
val runRes = LLVMRunFunction(engineRef.value, sum, 2, args[0].ptr)
|
||||
println(LLVMGenericValueToInt(runRes, 0))
|
||||
if (LLVMWriteBitcodeToFile(module, "/tmp/sum.bc") != 0) {
|
||||
println("error writing bitcode to file, skipping")
|
||||
}
|
||||
LLVMDisposeBuilder(builder)
|
||||
LLVMDisposeExecutionEngine(engineRef.value)
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -21,7 +24,6 @@ String kotlinCompilerModule = 'org.jetbrains.kotlin:kotlin-compiler:1.1-20161121
|
||||
// (gets applied to this project and all its subprojects)
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'http://oss.sonatype.org/content/repositories/snapshots'
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@@ -118,9 +118,8 @@ internal class CodeGenerator(override val context:Context) : ContextUtils {
|
||||
fun call(llvmFunction: LLVMOpaqueValue?, args: List<LLVMOpaqueValue?>, result: String?): LLVMOpaqueValue? {
|
||||
if (args.size == 0) return LLVMBuildCall(context.llvmBuilder, llvmFunction, null, 0, result)
|
||||
memScoped {
|
||||
val rargs = alloc(array[args.size](Ref to LLVMOpaqueValue))
|
||||
args.forEachIndexed { i, llvmOpaqueValue -> rargs[i].value = args[i] }
|
||||
return LLVMBuildCall(context.llvmBuilder, llvmFunction, rargs[0], args.size, result)
|
||||
val rargs = allocArrayOf(args)
|
||||
return LLVMBuildCall(context.llvmBuilder, llvmFunction, rargs[0].ptr, args.size, result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -4,12 +4,12 @@ import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.ModuleIndex
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
|
||||
internal class Context(val irModule: IrModuleFragment, val runtime: Runtime, val llvmModule: LLVMOpaqueModule) {
|
||||
internal class Context(val irModule: IrModuleFragment, val runtime: Runtime, val llvmModule: LLVMModuleRef) {
|
||||
val moduleIndex = ModuleIndex(irModule)
|
||||
|
||||
val llvmBuilder = LLVMCreateBuilder()
|
||||
|
||||
private fun importFunction(name: String, otherModule: LLVMOpaqueModule): LLVMOpaqueValue {
|
||||
private fun importFunction(name: String, otherModule: LLVMModuleRef): LLVMValueRef {
|
||||
if (LLVMGetNamedFunction(llvmModule, name) != null) {
|
||||
throw IllegalArgumentException("function $name already exists")
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.hash.GlobalHash
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@@ -22,7 +22,7 @@ internal interface ContextUtils {
|
||||
val runtime: Runtime
|
||||
get() = context.runtime
|
||||
|
||||
val llvmTargetData: LLVMOpaqueTargetData
|
||||
val llvmTargetData: LLVMTargetDataRef
|
||||
get() = runtime.targetData
|
||||
|
||||
val staticData: StaticData
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import llvm.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
internal fun getLLVMType(type: KotlinType): LLVMOpaqueType {
|
||||
internal fun getLLVMType(type: KotlinType): LLVMTypeRef {
|
||||
return when {
|
||||
KotlinBuiltIns.isBoolean(type) -> LLVMInt1Type()
|
||||
KotlinBuiltIns.isByte(type) -> LLVMInt8Type()
|
||||
|
||||
+11
-11
@@ -1,22 +1,22 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import org.jetbrains.kotlin.backend.konan.hash.*
|
||||
|
||||
internal fun localHash(data: ByteArray): Long {
|
||||
memScoped {
|
||||
val hashBox = alloc(Int64Box)
|
||||
val bytes = allocNativeArrayOf(data)
|
||||
MakeLocalHash(bytes.ptr, data.size, hashBox)
|
||||
return hashBox.value
|
||||
val res = alloc<LocalHashVar>()
|
||||
val bytes = allocArrayOf(data)
|
||||
MakeLocalHash(bytes[0].ptr, data.size, res.ptr)
|
||||
return res.value
|
||||
}
|
||||
}
|
||||
|
||||
internal fun globalHash(data: ByteArray, retValPlacement: Placement): GlobalHash {
|
||||
val res = retValPlacement.alloc(GlobalHash)
|
||||
internal fun globalHash(data: ByteArray, retValPlacement: NativePlacement): GlobalHash {
|
||||
val res = retValPlacement.alloc<GlobalHash>()
|
||||
memScoped {
|
||||
val bytes = allocNativeArrayOf(data)
|
||||
MakeGlobalHash(bytes.ptr, data.size, res)
|
||||
val bytes = allocArrayOf(data)
|
||||
MakeGlobalHash(bytes[0].ptr, data.size, res.ptr)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -24,8 +24,8 @@ internal fun globalHash(data: ByteArray, retValPlacement: Placement): GlobalHash
|
||||
internal fun base64Encode(data: ByteArray): String {
|
||||
memScoped {
|
||||
val resultSize = 4 * data.size / 3 + 3 + 1
|
||||
val result = alloc(NativeArray of Int8Box length resultSize)
|
||||
val bytes = allocNativeArrayOf(data)
|
||||
val result = allocArray<CInt8Var>(resultSize)
|
||||
val bytes = allocArrayOf(data)
|
||||
Base64Encode(bytes.ptr, data.size, result.ptr, resultSize)
|
||||
// TODO: any better way to do that without two copies?
|
||||
return CString.fromArray(result).toString()
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
@@ -37,10 +37,10 @@ fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) {
|
||||
println("\n--- Generate bitcode ------------------------------------------------------\n")
|
||||
module.acceptVoid(CodeGeneratorVisitor(context))
|
||||
memScoped {
|
||||
val errorRef = alloc(Int8Box.ref)
|
||||
val errorRef = allocPointerTo<CInt8Var>()
|
||||
// TODO: use LLVMDisposeMessage() on errorRef, once possible in interop.
|
||||
if (LLVMVerifyModule(
|
||||
llvmModule, LLVMVerifierFailureAction.LLVMPrintMessageAction, errorRef) == 1) {
|
||||
llvmModule, LLVMVerifierFailureAction.LLVMPrintMessageAction, errorRef.ptr) == 1) {
|
||||
LLVMDumpModule(llvmModule)
|
||||
throw Error("Invalid module");
|
||||
}
|
||||
@@ -561,8 +561,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val objHeaderPtr = codegen.bitcast(kObjHeaderPtr, thisPtr, codegen.newVar())
|
||||
val typePtr = pointerType(codegen.classType(value.containingDeclaration as ClassDescriptor))
|
||||
memScoped {
|
||||
val args = allocNativeArrayOf(LLVMOpaqueValue, kImmOne)
|
||||
val objectPtr = LLVMBuildGEP(codegen.context.llvmBuilder, objHeaderPtr, args[0], 1, codegen.newVar())
|
||||
val args = allocArrayOf(kImmOne)
|
||||
val objectPtr = LLVMBuildGEP(codegen.context.llvmBuilder, objHeaderPtr, args[0].ptr, 1, codegen.newVar())
|
||||
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!, codegen.newVar())
|
||||
val fieldPtr = LLVMBuildStructGEP(codegen.context.llvmBuilder, typedObjPtr, codegen.indexInClass(value), codegen.newVar())
|
||||
return fieldPtr
|
||||
|
||||
+27
-27
@@ -1,6 +1,6 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
@@ -11,9 +11,9 @@ import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
*/
|
||||
internal interface ConstValue {
|
||||
|
||||
fun getLlvmValue(): LLVMOpaqueValue?
|
||||
fun getLlvmValue(): LLVMValueRef?
|
||||
|
||||
fun getLlvmType(): LLVMOpaqueType {
|
||||
fun getLlvmType(): LLVMTypeRef {
|
||||
return LLVMTypeOf(getLlvmValue())!!
|
||||
}
|
||||
|
||||
@@ -23,45 +23,45 @@ internal interface ConstPointer : ConstValue {
|
||||
fun getElementPtr(index: Int): ConstPointer = ConstGetElementPtr(this, index)
|
||||
}
|
||||
|
||||
internal fun constPointer(value: LLVMOpaqueValue?) = object : ConstPointer {
|
||||
internal fun constPointer(value: LLVMValueRef?) = object : ConstPointer {
|
||||
override fun getLlvmValue() = value
|
||||
}
|
||||
|
||||
private class ConstGetElementPtr(val pointer: ConstPointer, val index: Int) : ConstPointer {
|
||||
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||
override fun getLlvmValue(): LLVMValueRef? {
|
||||
// TODO: probably it should be computed once when initialized?
|
||||
// TODO: squash multiple GEPs
|
||||
val indices = intArrayOf(0, index).map { Int32(it).getLlvmValue() }
|
||||
memScoped {
|
||||
val indicesArray = allocNativeArrayOf(LLVMOpaqueValue, indices)
|
||||
return LLVMConstInBoundsGEP(pointer.getLlvmValue(), indicesArray[0], indices.size)
|
||||
val indicesArray = allocArrayOf(indices)
|
||||
return LLVMConstInBoundsGEP(pointer.getLlvmValue(), indicesArray[0].ptr, indices.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ConstPointer.bitcast(toType: LLVMOpaqueType) = constPointer(LLVMConstBitCast(this.getLlvmValue(), toType))
|
||||
internal fun ConstPointer.bitcast(toType: LLVMTypeRef) = constPointer(LLVMConstBitCast(this.getLlvmValue(), toType))
|
||||
|
||||
internal class ConstArray(val elemType: LLVMOpaqueType?, val elements: List<ConstValue>) : ConstValue {
|
||||
internal class ConstArray(val elemType: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue {
|
||||
|
||||
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||
override fun getLlvmValue(): LLVMValueRef? {
|
||||
val values = elements.map { it.getLlvmValue() }.toTypedArray()
|
||||
|
||||
memScoped {
|
||||
val valuesNativeArrayPtr = allocNativeArrayOf(LLVMOpaqueValue, *values)[0]
|
||||
val valuesNativeArrayPtr = allocArrayOf(*values)[0].ptr
|
||||
|
||||
return LLVMConstArray(elemType, valuesNativeArrayPtr, values.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal open class Struct(val type: LLVMOpaqueType?, val elements: List<ConstValue>) : ConstValue {
|
||||
internal open class Struct(val type: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue {
|
||||
|
||||
constructor(type: LLVMOpaqueType?, vararg elements: ConstValue) : this(type, elements.toList())
|
||||
constructor(type: LLVMTypeRef?, vararg elements: ConstValue) : this(type, elements.toList())
|
||||
|
||||
override fun getLlvmValue(): LLVMOpaqueValue? {
|
||||
override fun getLlvmValue(): LLVMValueRef? {
|
||||
val values = elements.map { it.getLlvmValue() }.toTypedArray()
|
||||
memScoped {
|
||||
val valuesNativeArrayPtr = allocNativeArrayOf(LLVMOpaqueValue, *values)[0]
|
||||
val valuesNativeArrayPtr = allocArrayOf(*values)[0].ptr
|
||||
return LLVMConstNamedStruct(type, valuesNativeArrayPtr, values.size)
|
||||
}
|
||||
}
|
||||
@@ -79,43 +79,43 @@ internal class Int64(val value: Long) : ConstValue {
|
||||
override fun getLlvmValue() = LLVMConstInt(LLVMInt64Type(), value, 1)
|
||||
}
|
||||
|
||||
internal class Zero(val type: LLVMOpaqueType?) : ConstValue {
|
||||
internal class Zero(val type: LLVMTypeRef?) : ConstValue {
|
||||
override fun getLlvmValue() = LLVMConstNull(type)
|
||||
}
|
||||
|
||||
internal class NullPointer(val pointeeType: LLVMOpaqueType?): ConstPointer {
|
||||
internal class NullPointer(val pointeeType: LLVMTypeRef?): ConstPointer {
|
||||
override fun getLlvmValue() = LLVMConstNull(pointerType(pointeeType))
|
||||
}
|
||||
|
||||
internal fun constValue(value: LLVMOpaqueValue?) = object : ConstValue {
|
||||
internal fun constValue(value: LLVMValueRef?) = object : ConstValue {
|
||||
override fun getLlvmValue() = value
|
||||
}
|
||||
|
||||
internal val int8Type = LLVMInt8Type()
|
||||
internal val int32Type = LLVMInt32Type()
|
||||
|
||||
internal fun pointerType(pointeeType: LLVMOpaqueType?) = LLVMPointerType(pointeeType, 0)
|
||||
internal fun pointerType(pointeeType: LLVMTypeRef?) = LLVMPointerType(pointeeType, 0)
|
||||
|
||||
internal fun structType(vararg types: LLVMOpaqueType?): LLVMOpaqueType = memScoped {
|
||||
LLVMStructType(allocNativeArrayOf(LLVMOpaqueType, *types)[0], types.size, 0)!!
|
||||
internal fun structType(vararg types: LLVMTypeRef?): LLVMTypeRef = memScoped {
|
||||
LLVMStructType(allocArrayOf(*types)[0].ptr, types.size, 0)!!
|
||||
}
|
||||
|
||||
internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType? {
|
||||
internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef? {
|
||||
val returnType = getLLVMType(function.returnType!!)
|
||||
val params = function.dispatchReceiverParameter.singletonOrEmptyList() +
|
||||
function.extensionReceiverParameter.singletonOrEmptyList() +
|
||||
function.valueParameters
|
||||
|
||||
var extraParam = listOf<LLVMOpaqueType?>()
|
||||
var extraParam = listOf<LLVMTypeRef?>()
|
||||
if (function is ClassConstructorDescriptor) {
|
||||
extraParam += pointerType(LLVMInt8Type())
|
||||
}
|
||||
val paramTypes:List<LLVMOpaqueType?> = params.map { getLLVMType(it.type) }
|
||||
val paramTypes:List<LLVMTypeRef?> = params.map { getLLVMType(it.type) }
|
||||
extraParam += paramTypes
|
||||
|
||||
if (extraParam.size == 0) return LLVMFunctionType(returnType, null, 0, 0)
|
||||
memScoped {
|
||||
val paramTypesPtr = allocNativeArrayOf(LLVMOpaqueType, *extraParam.toTypedArray())[0] // TODO: dispose
|
||||
val paramTypesPtr = allocArrayOf(extraParam)[0].ptr
|
||||
return LLVMFunctionType(returnType, paramTypesPtr, extraParam.size, 0)
|
||||
}
|
||||
}
|
||||
@@ -123,10 +123,10 @@ internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType?
|
||||
/**
|
||||
* Reads [size] bytes contained in this array.
|
||||
*/
|
||||
internal fun NativeArray<Int8Box>.getBytes(size: Int) =
|
||||
internal fun CArray<CInt8Var>.getBytes(size: Long) =
|
||||
(0 .. size-1).map { this[it].value }.toByteArray()
|
||||
|
||||
internal fun getFunctionType(ptrToFunction: LLVMOpaqueValue?): LLVMOpaqueType {
|
||||
internal fun getFunctionType(ptrToFunction: LLVMValueRef?): LLVMTypeRef {
|
||||
val typeOfPtrToFunction = LLVMTypeOf(ptrToFunction)
|
||||
return LLVMGetElementType(typeOfPtrToFunction)!!
|
||||
}
|
||||
|
||||
+2
-6
@@ -1,11 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlin_native.interop.asCString
|
||||
import llvm.LLVMPrintModuleToString
|
||||
import llvm.LLVMPrintValueToString
|
||||
import llvm.LLVMOpaqueValue
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Context
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.ContextUtils
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
|
||||
import java.io.StringWriter
|
||||
|
||||
+16
-17
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
@@ -33,21 +33,21 @@ fun readModuleMetadata(file: File): String {
|
||||
|
||||
class MetadataReader(file: File) {
|
||||
|
||||
lateinit var llvmModule: LLVMOpaqueModule
|
||||
lateinit var llvmContext: LLVMOpaqueContext
|
||||
lateinit var llvmModule: LLVMModuleRef
|
||||
lateinit var llvmContext: LLVMContextRef
|
||||
|
||||
init {
|
||||
memScoped {
|
||||
val bufRef = alloc(LLVMOpaqueMemoryBuffer.ref)
|
||||
val errorRef = alloc(Int8Box.ref)
|
||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(file.toString(), bufRef, errorRef)
|
||||
val bufRef = alloc<LLVMMemoryBufferRefVar>()
|
||||
val errorRef = allocPointerTo<CInt8Var>()
|
||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(file.toString(), bufRef.ptr, errorRef.ptr)
|
||||
if (res != 0) {
|
||||
throw Error(errorRef.value?.asCString()?.toString())
|
||||
}
|
||||
|
||||
llvmContext = LLVMContextCreate()!!
|
||||
val moduleRef = alloc(LLVMOpaqueModule.ref)
|
||||
val parseResult = LLVMParseBitcodeInContext2(llvmContext, bufRef.value, moduleRef)
|
||||
val moduleRef = alloc<LLVMModuleRefVar>()
|
||||
val parseResult = LLVMParseBitcodeInContext2(llvmContext, bufRef.value, moduleRef.ptr)
|
||||
if (parseResult != 0) {
|
||||
throw Error(parseResult.toString())
|
||||
}
|
||||
@@ -59,8 +59,8 @@ class MetadataReader(file: File) {
|
||||
|
||||
fun string(node: LLVMOpaqueValue): String {
|
||||
memScoped {
|
||||
val len = alloc(Int32Box)
|
||||
val str1 = LLVMGetMDString(node, len)!!
|
||||
val len = alloc<CInt32Var>()
|
||||
val str1 = LLVMGetMDString(node, len.ptr)!!
|
||||
val str = str1.asCString().toString()
|
||||
return str
|
||||
|
||||
@@ -70,9 +70,9 @@ class MetadataReader(file: File) {
|
||||
fun namedMetadataNode(name: String, index: Int): LLVMOpaqueValue {
|
||||
memScoped {
|
||||
val nodeCount = LLVMGetNamedMetadataNumOperands(llvmModule, "kmetadata")!!
|
||||
val nodeArray = alloc(array[nodeCount](Ref to LLVMOpaqueValue))
|
||||
val nodeArray = allocArray<LLVMValueRefVar>(nodeCount)
|
||||
|
||||
LLVMGetNamedMetadataOperands(llvmModule, "kmetadata", nodeArray[0])!!
|
||||
LLVMGetNamedMetadataOperands(llvmModule, "kmetadata", nodeArray[0].ptr)!!
|
||||
|
||||
return nodeArray[0].value!!
|
||||
}
|
||||
@@ -81,9 +81,9 @@ class MetadataReader(file: File) {
|
||||
fun metadataOperand(metadataNode: LLVMOpaqueValue, index: Int): LLVMOpaqueValue {
|
||||
memScoped {
|
||||
val operandCount = LLVMGetMDNodeNumOperands(metadataNode)!!
|
||||
val operandArray = alloc(array[operandCount](Ref to LLVMOpaqueValue))
|
||||
val operandArray = allocArray<LLVMValueRefVar>(operandCount)
|
||||
|
||||
LLVMGetMDNodeOperands(metadataNode, operandArray[0])!!
|
||||
LLVMGetMDNodeOperands(metadataNode, operandArray[0].ptr)!!
|
||||
|
||||
return operandArray[0].value!!
|
||||
}
|
||||
@@ -104,9 +104,8 @@ internal class MetadataGenerator(override val context: Context): ContextUtils {
|
||||
|
||||
private fun metadataNode(args: List<LLVMOpaqueValue?>): LLVMOpaqueValue {
|
||||
memScoped {
|
||||
val references = alloc(array[args.size](Ref to LLVMOpaqueValue))
|
||||
args.forEachIndexed { i, llvmOpaqueValue -> references[i].value = args[i]}
|
||||
return LLVMMDNode(references[0], args.size)!!
|
||||
val references = allocArrayOf(args)
|
||||
return LLVMMDNode(references[0].ptr, args.size)!!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -1,8 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
|
||||
import kotlin_native.interop.allocNativeArrayOf
|
||||
import kotlin_native.interop.memScoped
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.implementation
|
||||
import org.jetbrains.kotlin.backend.konan.implementedInterfaces
|
||||
@@ -66,7 +65,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
memScoped {
|
||||
val fieldTypesNativeArrayPtr = if (fieldTypes.size > 0) {
|
||||
allocNativeArrayOf(LLVMOpaqueType, *fieldTypes)[0]
|
||||
allocArrayOf(*fieldTypes)[0].ptr
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
+9
-13
@@ -1,32 +1,28 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlin_native.interop.*
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
|
||||
class Runtime(private val bitcodeFile: String) {
|
||||
val llvmModule: LLVMOpaqueModule
|
||||
val llvmModule: LLVMModuleRef
|
||||
|
||||
init {
|
||||
val arena = Arena()
|
||||
try {
|
||||
llvmModule = memScoped {
|
||||
|
||||
val bufRef = arena.alloc(LLVMOpaqueMemoryBuffer.ref)
|
||||
val errorRef = arena.alloc(Int8Box.ref)
|
||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef, errorRef)
|
||||
val bufRef = allocPointerTo<LLVMOpaqueMemoryBuffer>()
|
||||
val errorRef = allocPointerTo<CInt8Var>()
|
||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr)
|
||||
if (res != 0) {
|
||||
throw Error(errorRef.value?.asCString()?.toString())
|
||||
}
|
||||
|
||||
val moduleRef = arena.alloc(LLVMOpaqueModule.ref)
|
||||
val parseRes = LLVMParseBitcode2(bufRef.value, moduleRef)
|
||||
val moduleRef = alloc<LLVMModuleRefVar>()
|
||||
val parseRes = LLVMParseBitcode2(bufRef.value, moduleRef.ptr)
|
||||
if (parseRes != 0) {
|
||||
throw Error(parseRes.toString())
|
||||
}
|
||||
|
||||
llvmModule = moduleRef.value!!
|
||||
|
||||
} finally {
|
||||
arena.clear()
|
||||
moduleRef.value!!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlinx.cinterop.CPointer
|
||||
import kotlinx.cinterop.CPointerVarWithValueMappedTo
|
||||
|
||||
// TODO: the definitions below are required to perform smooth migration to new interop;
|
||||
// remove after replacing with LLVM*Ref typedefs
|
||||
|
||||
typealias LLVMOpaqueValue = CPointer<llvm.LLVMOpaqueValue>
|
||||
typealias LLVMOpaqueType = CPointer<llvm.LLVMOpaqueType>
|
||||
typealias LLVMOpaqueBasicBlock = CPointer<llvm.LLVMOpaqueBasicBlock>
|
||||
@@ -13,6 +13,13 @@ allprojects {
|
||||
evaluationDependsOn(":dependencies")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
|
||||
}
|
||||
}
|
||||
|
||||
ext.clangPath = ["$llvmDir/bin"]
|
||||
ext.clangArgs = []
|
||||
final String binDir
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
kotlin_version=1.0.4
|
||||
kotlin_version=1.1-M02
|
||||
|
||||
Reference in New Issue
Block a user