Implement CValuesRef in interop
* Represent pointer parameters of C functions as `CValuesRef`. * Represent struct value parameters as `CValue`. * Implement some helper methods to make the new approach more useful. * Migrate some code to new interop approach for pointers and values. Also do not map to `String?`: - pointers to 8-bit integers; - pointers to non-const chars.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
7a876a3ccd
commit
c9d8d4d57d
File diff suppressed because it is too large
Load Diff
+51
-49
@@ -45,25 +45,23 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
|
||||
override val macroConstants = mutableListOf<ConstantDef>()
|
||||
|
||||
private fun getDeclarationId(cursor: CXCursor): DeclarationID = memScoped {
|
||||
val usr = clang_getCursorUSR(cursor, memScope).convertAndDispose()
|
||||
DeclarationID(usr)
|
||||
private fun getDeclarationId(cursor: CValue<CXCursor>): DeclarationID {
|
||||
val usr = clang_getCursorUSR(cursor).convertAndDispose()
|
||||
return DeclarationID(usr)
|
||||
}
|
||||
|
||||
private fun getStructDeclAt(cursor: CXCursor): StructDeclImpl {
|
||||
private fun getStructDeclAt(cursor: CValue<CXCursor>): StructDeclImpl {
|
||||
val declId = getDeclarationId(cursor)
|
||||
|
||||
return structById.getOrPut(declId) {
|
||||
memScoped {
|
||||
val cursorType = clang_getCursorType(cursor, memScope)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType, memScope).convertAndDispose()
|
||||
val cursorType = clang_getCursorType(cursor)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
|
||||
|
||||
StructDeclImpl(typeSpelling)
|
||||
}
|
||||
StructDeclImpl(typeSpelling)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEnumDefAt(cursor: CXCursor): EnumDefImpl {
|
||||
private fun getEnumDefAt(cursor: CValue<CXCursor>): EnumDefImpl {
|
||||
if (clang_isCursorDefinition(cursor) == 0) {
|
||||
TODO("support enum forward declarations")
|
||||
}
|
||||
@@ -71,19 +69,17 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
val declId = getDeclarationId(cursor)
|
||||
|
||||
return enumById.getOrPut(declId) {
|
||||
memScoped {
|
||||
val cursorType = clang_getCursorType(cursor, memScope)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType, memScope).convertAndDispose()
|
||||
val cursorType = clang_getCursorType(cursor)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
|
||||
|
||||
val baseType = convertType(clang_getEnumDeclIntegerType(cursor, memScope)) as PrimitiveType
|
||||
val baseType = convertType(clang_getEnumDeclIntegerType(cursor)) as PrimitiveType
|
||||
|
||||
EnumDefImpl(typeSpelling, baseType)
|
||||
}
|
||||
EnumDefImpl(typeSpelling, baseType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinVaListType(type: CXType, name: String, underlying: Type): Type {
|
||||
assert (type.kind.value == CXType_Typedef)
|
||||
private fun builtinVaListType(type: CValue<CXType>, name: String, underlying: Type): Type {
|
||||
assert (type.kind == CXType_Typedef)
|
||||
val declarationId = DeclarationID("c:@T@$name")
|
||||
|
||||
val structDeclaration = structById.getOrPut(declarationId) {
|
||||
@@ -100,11 +96,11 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
return ConstArrayType(RecordType(structDeclaration), 1)
|
||||
}
|
||||
|
||||
fun getTypedef(type: CXType): Type = memScoped {
|
||||
val declCursor = clang_getTypeDeclaration(type, memScope)
|
||||
fun getTypedef(type: CValue<CXType>): Type {
|
||||
val declCursor = clang_getTypeDeclaration(type)
|
||||
val name = getCursorSpelling(declCursor)
|
||||
|
||||
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor, memScope))
|
||||
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor))
|
||||
|
||||
if (name == "__builtin_va_list") {
|
||||
// On some platforms (e.g. macOS) libclang reports `__builtin_va_list` to be defined as array using
|
||||
@@ -136,8 +132,8 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
/**
|
||||
* Computes [StructDef.hasNaturalLayout] property.
|
||||
*/
|
||||
fun structHasNaturalLayout(structDefCursor: CXCursor): Boolean {
|
||||
val defKind = structDefCursor.kind.value
|
||||
fun structHasNaturalLayout(structDefCursor: CValue<CXCursor>): Boolean {
|
||||
val defKind = structDefCursor.kind
|
||||
|
||||
when (defKind) {
|
||||
|
||||
@@ -161,23 +157,22 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertCursorType(cursor: CXCursor) = memScoped {
|
||||
convertType(clang_getCursorType(cursor, memScope))
|
||||
}
|
||||
private fun convertCursorType(cursor: CValue<CXCursor>) =
|
||||
convertType(clang_getCursorType(cursor))
|
||||
|
||||
fun convertType(type: CXType): Type = memScoped {
|
||||
fun convertType(type: CValue<CXType>): Type {
|
||||
val primitiveType = convertUnqualifiedPrimitiveType(type)
|
||||
if (primitiveType != UnsupportedType) {
|
||||
return primitiveType
|
||||
}
|
||||
|
||||
val kind = type.kind.value
|
||||
val kind = type.kind
|
||||
return when (kind) {
|
||||
CXType_Elaborated -> convertType(clang_Type_getNamedType(type, memScope))
|
||||
CXType_Elaborated -> convertType(clang_Type_getNamedType(type))
|
||||
|
||||
CXType_Unexposed -> {
|
||||
val canonicalType = clang_getCanonicalType(type, memScope)
|
||||
if (canonicalType.kind.value != CXType_Unexposed) {
|
||||
val canonicalType = clang_getCanonicalType(type)
|
||||
if (canonicalType.kind != CXType_Unexposed) {
|
||||
convertType(canonicalType)
|
||||
} else {
|
||||
throw NotImplementedError()
|
||||
@@ -188,19 +183,27 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
|
||||
CXType_Typedef -> getTypedef(type)
|
||||
|
||||
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type, memScope)))
|
||||
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type, memScope)))
|
||||
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type)))
|
||||
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type)))
|
||||
|
||||
CXType_Pointer -> PointerType(convertType(clang_getPointeeType(type, memScope)))
|
||||
CXType_Pointer -> {
|
||||
val pointeeType = clang_getPointeeType(type)
|
||||
val canonicalPointeeType = clang_getCanonicalType(pointeeType)
|
||||
if (clang_isConstQualifiedType(canonicalPointeeType) != 0) {
|
||||
PointerToConstType(convertType(pointeeType))
|
||||
} else {
|
||||
PointerType(convertType(pointeeType))
|
||||
}
|
||||
}
|
||||
|
||||
CXType_ConstantArray -> {
|
||||
val elemType = convertType(clang_getArrayElementType(type, memScope))
|
||||
val elemType = convertType(clang_getArrayElementType(type))
|
||||
val length = clang_getArraySize(type)
|
||||
ConstArrayType(elemType, length)
|
||||
}
|
||||
|
||||
CXType_IncompleteArray -> {
|
||||
val elemType = convertType(clang_getArrayElementType(type, memScope))
|
||||
val elemType = convertType(clang_getArrayElementType(type))
|
||||
IncompleteArrayType(elemType)
|
||||
}
|
||||
|
||||
@@ -208,10 +211,10 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
if (clang_isFunctionTypeVariadic(type) != 0) {
|
||||
UnsupportedType
|
||||
} else {
|
||||
val returnType = convertType(clang_getResultType(type, memScope))
|
||||
val returnType = convertType(clang_getResultType(type))
|
||||
val numArgs = clang_getNumArgTypes(type)
|
||||
val paramTypes = (0..numArgs - 1).map {
|
||||
convertType(clang_getArgType(type, it, memScope))
|
||||
convertType(clang_getArgType(type, it))
|
||||
}
|
||||
FunctionType(paramTypes, returnType)
|
||||
}
|
||||
@@ -221,12 +224,11 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
}
|
||||
}
|
||||
|
||||
fun indexDeclaration(info: CXIdxDeclInfo): Unit = memScoped {
|
||||
val cursor = info.cursor
|
||||
fun indexDeclaration(info: CXIdxDeclInfo): Unit {
|
||||
val cursor = info.cursor.readValue()
|
||||
val entityInfo = info.entityInfo.pointed!!
|
||||
val entityName = entityInfo.name.value?.asCString()?.toString()
|
||||
val entityName = entityInfo.name.value?.toKString()
|
||||
val kind = entityInfo.kind.value
|
||||
|
||||
when (kind) {
|
||||
CXIdxEntity_Field -> {
|
||||
val name = entityName!!
|
||||
@@ -234,14 +236,14 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
val offset = clang_Cursor_getOffsetOfField(cursor)
|
||||
|
||||
val container = info.semanticContainer.pointed!!
|
||||
val structDef = getStructDeclAt(container.cursor).def!!
|
||||
val structDef = getStructDeclAt(container.cursor.readValue()).def!!
|
||||
structDef.fields.add(Field(name, type, offset))
|
||||
}
|
||||
|
||||
CXIdxEntity_Struct, CXIdxEntity_Union -> {
|
||||
val structDecl = getStructDeclAt(cursor)
|
||||
if (clang_isCursorDefinition(cursor) != 0) {
|
||||
val type = clang_getCursorType(cursor, memScope)
|
||||
val type = clang_getCursorType(cursor)
|
||||
val size = clang_Type_getSizeOf(type)
|
||||
val align = clang_Type_getAlignOf(type).toInt()
|
||||
val hasNaturalLayout = structHasNaturalLayout(cursor)
|
||||
@@ -251,17 +253,17 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
|
||||
CXIdxEntity_Function -> {
|
||||
val name = entityName!!
|
||||
val returnType = convertType(clang_getCursorResultType(cursor, memScope))
|
||||
val returnType = convertType(clang_getCursorResultType(cursor))
|
||||
val argNum = clang_Cursor_getNumArguments(cursor)
|
||||
val args = (0 .. argNum - 1).map {
|
||||
val argCursor = clang_Cursor_getArgument(cursor, it, memScope)
|
||||
val argCursor = clang_Cursor_getArgument(cursor, it)
|
||||
val argName = getCursorSpelling(argCursor)
|
||||
val type = convertCursorType(argCursor)
|
||||
Parameter(argName, type)
|
||||
}
|
||||
|
||||
val binaryName = when (language) {
|
||||
Language.C -> clang_Cursor_getMangling(cursor, memScope).convertAndDispose()
|
||||
Language.C -> clang_Cursor_getMangling(cursor).convertAndDispose()
|
||||
}
|
||||
|
||||
functionByName[name] = FunctionDecl(name, args, returnType, binaryName)
|
||||
@@ -274,9 +276,9 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
|
||||
CXIdxEntity_EnumConstant -> {
|
||||
val container = info.semanticContainer.pointed!!
|
||||
val name = entityName!!
|
||||
val value = clang_getEnumConstantDeclValue(info.cursor)
|
||||
val value = clang_getEnumConstantDeclValue(info.cursor.readValue())
|
||||
|
||||
val constants = getEnumDefAt(container.cursor).constants
|
||||
val constants = getEnumDefAt(container.cursor.readValue()).constants
|
||||
val existingConstant = constants.find { it.name == name }
|
||||
if (existingConstant == null) {
|
||||
val constant = EnumConstant(name, value, isExplicitlyDefined = !cursor.isLeaf())
|
||||
|
||||
+7
-10
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.kotlin.native.interop.indexer
|
||||
|
||||
import clang.*
|
||||
import kotlinx.cinterop.memScoped
|
||||
import kotlinx.cinterop.CValue
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ internal fun findMacroConstants(library: NativeLibrary, nativeIndex: NativeIndex
|
||||
nativeIndex.macroConstants.addAll(constants)
|
||||
}
|
||||
|
||||
private typealias TypeConverter = (CXType) -> Type
|
||||
private typealias TypeConverter = (CValue<CXType>) -> Type
|
||||
|
||||
/**
|
||||
* For each name expands the macro with this name declared in the library,
|
||||
@@ -123,8 +123,8 @@ private fun processCodeSnippet(
|
||||
var longValue: Long? = null
|
||||
var doubleValue: Double? = null
|
||||
|
||||
val visitor: CursorVisitor = { cursor: CXCursor, parent: CXCursor ->
|
||||
val kind = cursor.kind.value
|
||||
val visitor: CursorVisitor = { cursor, parent ->
|
||||
val kind = cursor.kind
|
||||
when {
|
||||
state == VisitorState.EXPECT_VARIABLE && kind == CXCursorKind.CXCursor_VarDecl -> {
|
||||
state = VisitorState.EXPECT_VARIABLE_VALUE
|
||||
@@ -144,10 +144,7 @@ private fun processCodeSnippet(
|
||||
|
||||
state == VisitorState.EXPECT_VARIABLE_VALUE && clang_isExpression(kind) != 0 -> {
|
||||
state = VisitorState.EXPECT_ENUM
|
||||
type = memScoped {
|
||||
val cursorType = clang_getCursorType(cursor, memScope)
|
||||
typeConverter(cursorType)
|
||||
}
|
||||
type = typeConverter(clang_getCursorType(cursor))
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
@@ -204,7 +201,7 @@ private fun collectMacroConstantsNames(library: NativeLibrary): List<String> {
|
||||
val translationUnit = library.parse(index, options).ensureNoCompileErrors()
|
||||
try {
|
||||
visitChildren(translationUnit) { cursor, parent ->
|
||||
if (cursor.kind.value == CXCursorKind.CXCursor_MacroDefinition && canMacroBeConstant(cursor)) {
|
||||
if (cursor.kind == CXCursorKind.CXCursor_MacroDefinition && canMacroBeConstant(cursor)) {
|
||||
val spelling = getCursorSpelling(cursor)
|
||||
result.add(spelling)
|
||||
}
|
||||
@@ -221,7 +218,7 @@ private fun collectMacroConstantsNames(library: NativeLibrary): List<String> {
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
private fun canMacroBeConstant(cursor: CXCursor): Boolean {
|
||||
private fun canMacroBeConstant(cursor: CValue<CXCursor>): Boolean {
|
||||
|
||||
if (clang_Cursor_isMacroFunctionLike(cursor) != 0) {
|
||||
return false
|
||||
|
||||
+10
-1
@@ -94,6 +94,8 @@ interface PrimitiveType : Type
|
||||
|
||||
object VoidType : Type
|
||||
|
||||
object CharType : PrimitiveType
|
||||
|
||||
object Int8Type : PrimitiveType
|
||||
object UInt8Type : PrimitiveType
|
||||
|
||||
@@ -116,7 +118,14 @@ data class RecordType(val decl: StructDecl) : Type
|
||||
|
||||
data class EnumType(val def: EnumDef) : Type
|
||||
|
||||
data class PointerType(val pointeeType : Type) : Type
|
||||
open class PointerType(val pointeeType : Type) : Type
|
||||
|
||||
/**
|
||||
* The type of pointer that can't be used to modify pointed data.
|
||||
*
|
||||
* TODO: refactor type representation and support type modifiers more generally.
|
||||
*/
|
||||
class PointerToConstType(pointeeType: Type) : PointerType(pointeeType)
|
||||
|
||||
data class FunctionType(val parameterTypes: List<Type>, val returnType: Type) : Type
|
||||
|
||||
|
||||
+21
-21
@@ -4,23 +4,28 @@ import clang.*
|
||||
import kotlinx.cinterop.*
|
||||
import java.io.File
|
||||
|
||||
internal fun CXString.convertAndDispose(): String {
|
||||
internal val CValue<CXType>.kind: CXTypeKind get() = this.useContents { kind.value }
|
||||
|
||||
internal val CValue<CXCursor>.kind: CXCursorKind get() = this.useContents { kind.value }
|
||||
|
||||
internal fun CValue<CXString>.convertAndDispose(): String {
|
||||
try {
|
||||
return clang_getCString(this)!!.asCString().toString()
|
||||
return clang_getCString(this)!!.toKString()
|
||||
} finally {
|
||||
clang_disposeString(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getCursorSpelling(cursor: CXCursor) = memScoped {
|
||||
clang_getCursorSpelling(cursor, memScope).convertAndDispose()
|
||||
}
|
||||
internal fun getCursorSpelling(cursor: CValue<CXCursor>) =
|
||||
clang_getCursorSpelling(cursor).convertAndDispose()
|
||||
|
||||
internal fun convertUnqualifiedPrimitiveType(type: CXType): Type = when (type.kind.value) {
|
||||
internal fun convertUnqualifiedPrimitiveType(type: CValue<CXType>): Type = when (type.kind) {
|
||||
// TODO: is e.g. CXType_Int guaranteed to be int32_t?
|
||||
|
||||
CXTypeKind.CXType_Char_U, CXTypeKind.CXType_UChar -> UInt8Type
|
||||
CXTypeKind.CXType_Char_S, CXTypeKind.CXType_SChar -> Int8Type
|
||||
CXTypeKind.CXType_Char_U, CXTypeKind.CXType_Char_S -> CharType
|
||||
|
||||
CXTypeKind.CXType_UChar -> UInt8Type
|
||||
CXTypeKind.CXType_SChar -> Int8Type
|
||||
|
||||
CXTypeKind.CXType_UShort -> UInt16Type
|
||||
CXTypeKind.CXType_Short -> Int16Type
|
||||
@@ -71,9 +76,7 @@ internal fun getCompileErrors(translationUnit: CXTranslationUnit): Sequence<Stri
|
||||
severity == CXDiagnosticSeverity.CXDiagnostic_Error ||
|
||||
severity == CXDiagnosticSeverity.CXDiagnostic_Fatal
|
||||
}.map {
|
||||
memScoped {
|
||||
clang_formatDiagnostic(it, clang_defaultDiagnosticDisplayOptions(), memScope).convertAndDispose()
|
||||
}
|
||||
clang_formatDiagnostic(it, clang_defaultDiagnosticDisplayOptions()).convertAndDispose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,24 +85,21 @@ internal fun CXTranslationUnit.ensureNoCompileErrors(): CXTranslationUnit {
|
||||
throw Error(firstError)
|
||||
}
|
||||
|
||||
internal typealias CursorVisitor = (cursor: CXCursor, parent: CXCursor) -> CXChildVisitResult
|
||||
internal typealias CursorVisitor = (cursor: CValue<CXCursor>, parent: CValue<CXCursor>) -> CXChildVisitResult
|
||||
|
||||
internal fun visitChildren(parent: CXCursor, visitor: CursorVisitor) {
|
||||
internal fun visitChildren(parent: CValue<CXCursor>, visitor: CursorVisitor) {
|
||||
val visitorPtr = StableObjPtr.create(visitor)
|
||||
val clientData = visitorPtr.value
|
||||
clang_visitChildren(parent, staticCFunction { cursor, parent, clientData ->
|
||||
val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor
|
||||
visitor(cursor, parent)
|
||||
visitor(cursor.readValue(), parent.readValue())
|
||||
}, clientData)
|
||||
}
|
||||
|
||||
internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVisitor) {
|
||||
memScoped {
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit, memScope), visitor)
|
||||
}
|
||||
}
|
||||
internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVisitor) =
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit), visitor)
|
||||
|
||||
internal fun CXCursor.isLeaf(): Boolean {
|
||||
internal fun CValue<CXCursor>.isLeaf(): Boolean {
|
||||
var hasChildren = false
|
||||
|
||||
visitChildren(this) { _, _ ->
|
||||
@@ -112,7 +112,7 @@ internal fun CXCursor.isLeaf(): Boolean {
|
||||
|
||||
internal fun List<String>.toNativeStringArray(placement: NativePlacement): CArray<CPointerVar<CInt8Var>> {
|
||||
return placement.allocArray(this.size) { index ->
|
||||
this.value = this@toNativeStringArray[index].toCString(placement)!!.asCharPtr()
|
||||
this.value = this@toNativeStringArray[index].cstr.getPointer(placement)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ object nativeMemUtils {
|
||||
unsafe.copyMemory(source, baseOffset, null, dest.address, length.toLong())
|
||||
}
|
||||
|
||||
fun zeroMemory(dest: NativePointed, length: Int): Unit = unsafe.setMemory(dest.address, length.toLong(), 0)
|
||||
|
||||
internal class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
|
||||
|
||||
fun alloc(size: Long, align: Int): NativePointed {
|
||||
|
||||
@@ -33,10 +33,49 @@ inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpre
|
||||
*/
|
||||
interface CPointed : NativePointed
|
||||
|
||||
/**
|
||||
* Represents a reference to (possibly empty) sequence of C values.
|
||||
* It can be either a stable pointer [CPointer] or a sequence of immutable values [CValues].
|
||||
*
|
||||
* [CValuesRef] is designed to be used as Kotlin representation of pointer-typed parameters of C functions.
|
||||
* When passing [CPointer] as [CValuesRef] to the Kotlin binding method, the C function receives exactly this pointer.
|
||||
* Passing [CValues] has nearly the same semantics as passing by value: the C function receives
|
||||
* the pointer to the temporary copy of these values, and the caller can't observe the modifications to this copy.
|
||||
* The copy is valid until the C function returns.
|
||||
*/
|
||||
abstract class CValuesRef<T : CPointed> {
|
||||
/**
|
||||
* If this reference is [CPointer], returns this pointer.
|
||||
* Otherwise copies the referenced values to [placement] and returns the pointer to the copy.
|
||||
*/
|
||||
abstract fun getPointer(placement: NativePlacement): CPointer<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* The (possibly empty) sequence of immutable C values.
|
||||
* It is self-contained and doesn't depend on native memory.
|
||||
*/
|
||||
abstract class CValues<T : CVariable> : CValuesRef<T>() {
|
||||
/**
|
||||
* Copies the values to [placement] and returns the pointer to the copy.
|
||||
*/
|
||||
override abstract fun getPointer(placement: NativePlacement): CPointer<T>
|
||||
}
|
||||
|
||||
fun <T : CVariable> CValues<T>.placeTo(placement: NativePlacement) = this.getPointer(placement)
|
||||
|
||||
/**
|
||||
* The single immutable C value.
|
||||
* It is self-contained and doesn't depend on native memory.
|
||||
*
|
||||
* TODO: consider providing an adapter instead of subtyping [CValues].
|
||||
*/
|
||||
abstract class CValue<T : CVariable> : CValues<T>()
|
||||
|
||||
/**
|
||||
* C pointer.
|
||||
*/
|
||||
class CPointer<T : CPointed> internal constructor(val rawValue: NativePtr) {
|
||||
class CPointer<T : CPointed> internal constructor(val rawValue: NativePtr) : CValuesRef<T>() {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
@@ -51,6 +90,8 @@ class CPointer<T : CPointed> internal constructor(val rawValue: NativePtr) {
|
||||
}
|
||||
|
||||
override fun toString() = this.cPointerToString()
|
||||
|
||||
override fun getPointer(placement: NativePlacement) = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +110,7 @@ inline val <reified T : CPointed> CPointer<T>.pointed: T
|
||||
|
||||
// `null` value of `CPointer?` is mapped to `nativeNullPtr`
|
||||
val CPointer<*>?.rawValue: NativePtr
|
||||
get() = this?.rawValue ?: nativeNullPtr
|
||||
get() = if (this != null) this.rawValue else nativeNullPtr
|
||||
|
||||
fun <T : CPointed> CPointer<*>.reinterpret(): CPointer<T> = interpretCPointer(this.rawValue)!!
|
||||
|
||||
@@ -217,7 +258,7 @@ typealias CPointerVar<T> = CPointerVarWithValueMappedTo<CPointer<T>>
|
||||
/**
|
||||
* The value of this variable.
|
||||
*/
|
||||
inline var <reified P : CPointer<*>> CPointerVarWithValueMappedTo<P>.value: P?
|
||||
inline var <P : CPointer<*>> CPointerVarWithValueMappedTo<P>.value: P?
|
||||
get() = interpretCPointer<CPointed>(nativeMemUtils.getNativePtr(this)) as P?
|
||||
set(value) = nativeMemUtils.putNativePtr(this, value.rawValue)
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ class Arena(private val parent: NativeFreeablePlacement = nativeHeap) : NativePl
|
||||
|
||||
}
|
||||
|
||||
fun NativePlacement.alloc(size: Int, align: Int) = alloc(size.toLong(), align)
|
||||
|
||||
/**
|
||||
* Allocates variable of given type.
|
||||
*
|
||||
@@ -138,12 +136,9 @@ inline fun <reified T : CPointer<*>>
|
||||
}
|
||||
|
||||
fun NativePlacement.allocArrayOf(elements: ByteArray): CArray<CInt8Var> {
|
||||
val res = allocArray<CInt8Var>(elements.size)
|
||||
var index = 0
|
||||
for (byte in elements) {
|
||||
res[index++].value = byte
|
||||
}
|
||||
return res
|
||||
val result = allocArray<CInt8Var>(elements.size)
|
||||
nativeMemUtils.putByteArray(elements, result, elements.size)
|
||||
return result
|
||||
}
|
||||
|
||||
fun NativePlacement.allocArrayOf(vararg elements: Float): CArray<CFloat32Var> {
|
||||
@@ -157,56 +152,139 @@ fun NativePlacement.allocArrayOf(vararg elements: Float): CArray<CFloat32Var> {
|
||||
|
||||
fun <T : CPointed> NativePlacement.allocPointerTo() = alloc<CPointerVar<T>>()
|
||||
|
||||
fun <T : CVariable> zeroValue(size: Int, align: Int): CValue<T> = object : CValue<T>() {
|
||||
override fun getPointer(placement: NativePlacement): CPointer<T> {
|
||||
val result = placement.alloc(size, align)
|
||||
nativeMemUtils.zeroMemory(result, size)
|
||||
return interpretCPointer(result.rawPtr)!!
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : CVariable> zeroValue(): CValue<T> =
|
||||
zeroValue<T>(sizeOf<T>().toInt(), alignOf<T>())
|
||||
|
||||
private fun <T : CPointed> NativePlacement.placeBytes(bytes: ByteArray, align: Int): CPointer<T> {
|
||||
val result = this.alloc(size = bytes.size, align = align)
|
||||
nativeMemUtils.putByteArray(bytes, result, bytes.size)
|
||||
return interpretCPointer(result.rawPtr)!!
|
||||
}
|
||||
|
||||
fun <T : CVariable> CPointed.readValues(size: Int, align: Int): CValues<T> {
|
||||
val bytes = ByteArray(size)
|
||||
nativeMemUtils.getByteArray(this, bytes, size)
|
||||
|
||||
return object : CValues<T>() {
|
||||
override fun getPointer(placement: NativePlacement): CPointer<T> = placement.placeBytes(bytes, align)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : CVariable> T.readValues(count: Int): CValues<T> =
|
||||
this.readValues<T>(size = count * sizeOf<T>().toInt(), align = alignOf<T>())
|
||||
|
||||
fun <T : CVariable> CPointed.readValue(size: Int, align: Int): CValue<T> {
|
||||
val bytes = ByteArray(size)
|
||||
nativeMemUtils.getByteArray(this, bytes, size)
|
||||
return object : CValue<T>() {
|
||||
override fun getPointer(placement: NativePlacement): CPointer<T> = placement.placeBytes(bytes, align)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: can't be declared as property due to possible clash with a struct field.
|
||||
// TODO: find better name.
|
||||
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(sizeOf<T>().toInt(), alignOf<T>())
|
||||
|
||||
/**
|
||||
* The zero-terminated string.
|
||||
* Calls the [block] with temporary copy if this value as receiver.
|
||||
*/
|
||||
class CString(override val rawPtr: NativePtr) : CPointed {
|
||||
inline fun <reified T : CStructVar, R> CValue<T>.useContents(block: T.() -> R): R = memScoped {
|
||||
this@useContents.placeTo(memScope).pointed.block()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromArray(array: CArray<CInt8Var>) = array.reinterpret<CString>()
|
||||
}
|
||||
inline fun <reified T : CStructVar> CValue<T>.copy(modify: T.() -> Unit): CValue<T> = useContents {
|
||||
this.modify()
|
||||
this.readValue()
|
||||
}
|
||||
|
||||
fun length(): Int {
|
||||
val array = reinterpret<CArray<CInt8Var>>()
|
||||
inline fun <reified T : CStructVar> cValue(initialize: T.() -> Unit): CValue<T> =
|
||||
zeroValue<T>().copy(modify = initialize)
|
||||
|
||||
var res = 0
|
||||
while (array[res].value != 0.toByte()) {
|
||||
++res
|
||||
inline fun <reified T : CVariable> createValues(count: Int, initializer: T.(index: Int) -> Unit) = memScoped {
|
||||
val array = allocArray<T>(count, initializer)
|
||||
array[0].readValues(count)
|
||||
}
|
||||
|
||||
fun cValuesOf(vararg elements: Byte): CValues<CInt8Var> = object : CValues<CInt8Var>() {
|
||||
override fun getPointer(placement: NativePlacement) = placement.allocArrayOf(elements)[0].ptr
|
||||
}
|
||||
|
||||
// TODO: optimize other [cValuesOf] methods:
|
||||
|
||||
fun cValuesOf(vararg elements: Short): CValues<CInt16Var> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun cValuesOf(vararg elements: Int): CValues<CInt32Var> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun cValuesOf(vararg elements: Long): CValues<CInt64Var> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun cValuesOf(vararg elements: Float): CValues<CFloat32Var> = object : CValues<CFloat32Var>() {
|
||||
override fun getPointer(placement: NativePlacement) = placement.allocArrayOf(*elements)[0].ptr
|
||||
}
|
||||
|
||||
fun cValuesOf(vararg elements: Double): CValues<CFloat64Var> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun <T : CPointed> cValuesOf(vararg elements: CPointer<T>?): CValues<CPointerVar<T>> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun ByteArray.toCValues() = cValuesOf(*this)
|
||||
fun ShortArray.toCValues() = cValuesOf(*this)
|
||||
fun IntArray.toCValues() = cValuesOf(*this)
|
||||
fun LongArray.toCValues() = cValuesOf(*this)
|
||||
fun FloatArray.toCValues() = cValuesOf(*this)
|
||||
fun DoubleArray.toCValues() = cValuesOf(*this)
|
||||
fun <T : CPointed> Array<CPointer<T>?>.toCValues() = cValuesOf(*this)
|
||||
|
||||
fun <T : CPointed> List<CPointer<T>?>.toCValues() = this.toTypedArray().toCValues()
|
||||
|
||||
/**
|
||||
* TODO: should the name of the function reflect the encoding?
|
||||
*
|
||||
* @return the value of zero-terminated UTF-8-encoded C string constructed from given [kotlin.String].
|
||||
*/
|
||||
val String.cstr: CValues<CInt8Var>
|
||||
get() {
|
||||
val bytes = encodeToUtf8(this)
|
||||
|
||||
return object : CValues<CInt8Var>() {
|
||||
override fun getPointer(placement: NativePlacement): CPointer<CInt8Var> {
|
||||
val result = placement.allocArray<CInt8Var>(bytes.size + 1)
|
||||
nativeMemUtils.putByteArray(bytes, result, bytes.size)
|
||||
result[bytes.size].value = 0.toByte()
|
||||
return result[0].ptr
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val array = reinterpret<CArray<CInt8Var>>()
|
||||
/**
|
||||
* TODO: should the name of the function reflect the encoding?
|
||||
*
|
||||
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
|
||||
*/
|
||||
fun CPointer<CInt8Var>.toKString(): String {
|
||||
val nativeBytes = this.reinterpret<CArray<CInt8Var>>().pointed
|
||||
|
||||
val len = this.length()
|
||||
val bytes = ByteArray(len)
|
||||
|
||||
nativeMemUtils.getByteArray(array[0], bytes, len)
|
||||
return decodeFromUtf8(bytes) // TODO: encoding
|
||||
var length = 0
|
||||
while (nativeBytes[length].value != 0.toByte()) {
|
||||
++length
|
||||
}
|
||||
|
||||
fun asCharPtr() = reinterpret<CInt8Var>().ptr
|
||||
val bytes = ByteArray(length)
|
||||
nativeMemUtils.getByteArray(nativeBytes, bytes, length)
|
||||
return decodeFromUtf8(bytes)
|
||||
}
|
||||
|
||||
fun CString.Companion.fromString(str: String?, placement: NativePlacement): CString? {
|
||||
if (str == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val bytes = encodeToUtf8(str) // TODO: encoding
|
||||
val len = bytes.size
|
||||
val nativeBytes = placement.allocArray<CInt8Var>(len + 1)
|
||||
|
||||
nativeMemUtils.putByteArray(bytes, nativeBytes[0], len)
|
||||
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 : NativePlacement {
|
||||
|
||||
private val arena = Arena()
|
||||
|
||||
@@ -30,6 +30,7 @@ object nativeMemUtils {
|
||||
@Intrinsic external fun getNativePtr(mem: NativePointed): NativePtr
|
||||
@Intrinsic external fun putNativePtr(mem: NativePointed, value: NativePtr)
|
||||
|
||||
// TODO: optimize
|
||||
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
|
||||
val sourceArray: CArray<CInt8Var> = source.reinterpret()
|
||||
for (index in 0 .. length - 1) {
|
||||
@@ -37,6 +38,7 @@ object nativeMemUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) {
|
||||
val destArray: CArray<CInt8Var> = dest.reinterpret()
|
||||
for (index in 0 .. length - 1) {
|
||||
@@ -44,6 +46,14 @@ object nativeMemUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun zeroMemory(dest: NativePointed, length: Int): Unit {
|
||||
val destArray: CArray<CInt8Var> = dest.reinterpret()
|
||||
for (index in 0 .. length - 1) {
|
||||
destArray[index].value = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
|
||||
|
||||
fun alloc(size: Long, align: Int): NativePointed {
|
||||
|
||||
+96
-30
@@ -173,7 +173,8 @@ class StubGenerator(
|
||||
return when (this) {
|
||||
|
||||
is VoidType -> "void"
|
||||
is Int8Type -> "char"
|
||||
is CharType -> "char"
|
||||
is Int8Type -> "signed char"
|
||||
is UInt8Type -> "unsigned char"
|
||||
is Int16Type -> "short"
|
||||
is UInt16Type -> "unsigned short"
|
||||
@@ -214,7 +215,7 @@ class StubGenerator(
|
||||
val PrimitiveType.kotlinType: String
|
||||
get() = when (this) {
|
||||
|
||||
is Int8Type, is UInt8Type -> "Byte"
|
||||
is CharType, is Int8Type, is UInt8Type -> "Byte"
|
||||
is Int16Type, is UInt16Type -> "Short"
|
||||
is Int32Type, is UInt32Type -> "Int"
|
||||
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
||||
@@ -326,7 +327,7 @@ class StubGenerator(
|
||||
|
||||
private fun mirror(type: PrimitiveType): TypeMirror.ByValue {
|
||||
val varTypeName = when (type) {
|
||||
is Int8Type, is UInt8Type -> "CInt8Var"
|
||||
is CharType, is Int8Type, is UInt8Type -> "CInt8Var"
|
||||
is Int16Type, is UInt16Type -> "CInt16Var"
|
||||
is Int32Type, is UInt32Type -> "CInt32Var"
|
||||
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
||||
@@ -439,11 +440,52 @@ class StubGenerator(
|
||||
)
|
||||
}
|
||||
|
||||
fun representCFunctionParameterAsValuesRef(type: Type): Type? {
|
||||
val pointeeType = when (type) {
|
||||
is PointerType -> type.pointeeType
|
||||
is ArrayType -> type.elemType
|
||||
else -> return null
|
||||
}
|
||||
|
||||
val unwrappedPointeeType = pointeeType.unwrapTypedefs()
|
||||
if (unwrappedPointeeType is VoidType || unwrappedPointeeType is FunctionType) {
|
||||
// Passing `void`s or function by value is not very sane:
|
||||
return null
|
||||
}
|
||||
|
||||
return pointeeType
|
||||
}
|
||||
|
||||
fun representCFunctionParameterAsString(type: Type): Boolean {
|
||||
val unwrappedType = type.unwrapTypedefs()
|
||||
return unwrappedType is PointerToConstType && unwrappedType.pointeeType.unwrapTypedefs() == CharType
|
||||
}
|
||||
|
||||
fun getCFunctionParamBinding(type: Type): OutValueBinding {
|
||||
if (treatCFunctionParameterAsString(type)) {
|
||||
if (representCFunctionParameterAsString(type)) {
|
||||
return OutValueBinding(
|
||||
kotlinType = "String?",
|
||||
kotlinConv = { name -> "$name?.toCString(memScope).rawPtr" },
|
||||
kotlinType = "String?", // TODO: mention the C type (e.g. with annotation).
|
||||
kotlinConv = { name -> "$name?.cstr?.getPointer(memScope).rawValue" },
|
||||
memScoped = true,
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
)
|
||||
}
|
||||
|
||||
representCFunctionParameterAsValuesRef(type)?.let {
|
||||
val pointeeMirror = mirror(it)
|
||||
return OutValueBinding(
|
||||
kotlinType = "CValuesRef<${pointeeMirror.pointedTypeName}>?",
|
||||
kotlinConv = { name -> "$name?.getPointer(memScope).rawValue" },
|
||||
memScoped = true,
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
)
|
||||
}
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
// TODO: this case should probably be handled more generally.
|
||||
val typeMirror = mirror(type)
|
||||
return OutValueBinding(
|
||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>",
|
||||
kotlinConv = { name -> "$name.getPointer(memScope).rawValue" }, // TODO: eliminate this copying
|
||||
memScoped = true,
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
)
|
||||
@@ -452,8 +494,6 @@ class StubGenerator(
|
||||
return getOutValueBinding(type)
|
||||
}
|
||||
|
||||
private fun treatCFunctionParameterAsString(type: Type) = type is PointerType && type.pointeeType is Int8Type
|
||||
|
||||
fun getCallbackRetValBinding(type: Type): OutValueBinding {
|
||||
if (type.unwrapTypedefs() is VoidType) {
|
||||
return OutValueBinding(
|
||||
@@ -484,12 +524,21 @@ class StubGenerator(
|
||||
)
|
||||
}
|
||||
|
||||
fun getCFunctionRetValBinding(type: Type): InValueBinding {
|
||||
when (type.unwrapTypedefs()) {
|
||||
is VoidType -> return InValueBinding("Unit")
|
||||
fun getCFunctionRetValBinding(func: FunctionDecl): InValueBinding {
|
||||
when {
|
||||
func.returnsVoid() -> return InValueBinding("Unit")
|
||||
func.returnsRecord() -> {
|
||||
// TODO: this case should probably be handled more generally.
|
||||
val typeMirror = mirror(func.returnType)
|
||||
return InValueBinding(
|
||||
kotlinJniBridgeType = "NativePtr",
|
||||
conv = { name -> "interpretPointed<${typeMirror.pointedTypeName}>($name).readValue()" },
|
||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return getInValueBinding(type)
|
||||
return getInValueBinding(func.returnType)
|
||||
}
|
||||
|
||||
fun getCallbackParamBinding(type: Type): InValueBinding {
|
||||
@@ -637,7 +686,10 @@ class StubGenerator(
|
||||
/**
|
||||
* Constructs [InValueBinding] for return value of Kotlin binding for given C function.
|
||||
*/
|
||||
private fun retValBinding(func: FunctionDecl) = getCFunctionRetValBinding(func.returnType)
|
||||
private fun retValBinding(func: FunctionDecl) = getCFunctionRetValBinding(func)
|
||||
|
||||
private fun FunctionDecl.returnsRecord(): Boolean = this.returnType.unwrapTypedefs() is RecordType
|
||||
private fun FunctionDecl.returnsVoid(): Boolean = this.returnType.unwrapTypedefs() is VoidType
|
||||
|
||||
/**
|
||||
* Constructs [OutValueBinding]s for parameters of Kotlin binding for given C function.
|
||||
@@ -647,13 +699,10 @@ class StubGenerator(
|
||||
getCFunctionParamBinding(param.type)
|
||||
}.toMutableList()
|
||||
|
||||
val retValType = func.returnType
|
||||
if (retValType.unwrapTypedefs() is RecordType) {
|
||||
val retValMirror = mirror(retValType)
|
||||
|
||||
if (func.returnsRecord()) {
|
||||
paramBindings.add(OutValueBinding(
|
||||
kotlinType = "NativePlacement",
|
||||
kotlinConv = { name -> "$name.alloc<${retValMirror.pointedTypeName}>().rawPtr" },
|
||||
kotlinType = "should not be used",
|
||||
kotlinConv = { throw UnsupportedOperationException() },
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
))
|
||||
}
|
||||
@@ -674,7 +723,7 @@ class StubGenerator(
|
||||
}
|
||||
}.toMutableList()
|
||||
|
||||
if (func.returnType.unwrapTypedefs() is RecordType) {
|
||||
if (func.returnsRecord()) {
|
||||
paramNames.add("retValPlacement")
|
||||
}
|
||||
|
||||
@@ -685,7 +734,14 @@ class StubGenerator(
|
||||
* Produces to [out] the definition of Kotlin binding for given C function.
|
||||
*/
|
||||
private fun generateKotlinBindingMethod(func: FunctionDecl) {
|
||||
val paramNames = paramNames(func)
|
||||
val paramNames = paramNames(func).toList().let {
|
||||
if (func.returnsRecord()) {
|
||||
// The last parameter should be present only in C adapter.
|
||||
it.dropLast(1)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
|
||||
@@ -716,7 +772,13 @@ class StubGenerator(
|
||||
|
||||
externalParamName
|
||||
|
||||
}.toMutableList()
|
||||
|
||||
if (func.returnsRecord()) {
|
||||
val retValMirror = mirror(func.returnType)
|
||||
arguments.add("alloc<${retValMirror.pointedTypeName}>().rawPtr")
|
||||
}
|
||||
|
||||
val callee = when (platform) {
|
||||
KotlinPlatform.JVM -> "externals.${func.name}"
|
||||
KotlinPlatform.NATIVE -> func.kotlinExternalName
|
||||
@@ -741,7 +803,7 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
block(header) {
|
||||
val memScoped = paramBindings.any { it.memScoped }
|
||||
val memScoped = paramBindings.any { it.memScoped } || func.returnsRecord()
|
||||
if (memScoped) {
|
||||
block("return memScoped") {
|
||||
generateBody(true)
|
||||
@@ -782,7 +844,8 @@ class StubGenerator(
|
||||
private fun getFfiType(type: Type): String {
|
||||
return when(type) {
|
||||
is VoidType -> "Void"
|
||||
is Int8Type -> "SInt8"
|
||||
is CharType, is Int8Type -> "SInt8"
|
||||
// TODO: libffi has separate representation for char type.
|
||||
is UInt8Type -> "UInt8"
|
||||
is Int16Type -> "SInt16"
|
||||
is UInt16Type -> "UInt16"
|
||||
@@ -829,8 +892,12 @@ class StubGenerator(
|
||||
return true
|
||||
}
|
||||
|
||||
return this.parameters.any { treatCFunctionParameterAsString(it.type) } ||
|
||||
this.returnType.unwrapTypedefs() is RecordType
|
||||
return this.returnsRecord() ||
|
||||
this.parameters.map { it.type }.any {
|
||||
it.unwrapTypedefs() is RecordType ||
|
||||
representCFunctionParameterAsString(it) ||
|
||||
representCFunctionParameterAsValuesRef(it) != null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -842,8 +909,8 @@ class StubGenerator(
|
||||
return true
|
||||
}
|
||||
|
||||
val parameterTypes = this.parameters.map { it.type }
|
||||
return (parameterTypes + returnType).any { it.unwrapTypedefs() is RecordType }
|
||||
return this.returnsRecord() ||
|
||||
this.parameters.map { it.type }.any { it.unwrapTypedefs() is RecordType }
|
||||
}
|
||||
|
||||
private fun FunctionType.requiresAdapterOnNative(): Boolean {
|
||||
@@ -1187,7 +1254,6 @@ class StubGenerator(
|
||||
* Produces to [out] the implementation of JNI function used in Kotlin binding for given C function.
|
||||
*/
|
||||
private fun generateCJniFunction(func: FunctionDecl) {
|
||||
val funcReturnType = func.returnType.unwrapTypedefs()
|
||||
val paramNames = paramNames(func)
|
||||
val paramBindings = paramBindings(func)
|
||||
val retValBinding = retValBinding(func)
|
||||
@@ -1241,8 +1307,8 @@ class StubGenerator(
|
||||
|
||||
if (cReturnType == "void") {
|
||||
out("$callExpr;")
|
||||
} else if (funcReturnType is RecordType) {
|
||||
out("*(${funcReturnType.decl.spelling}*)retValPlacement = $callExpr;")
|
||||
} else if (func.returnsRecord()) {
|
||||
out("*(${func.returnType.getStringRepresentation()}*)retValPlacement = $callExpr;")
|
||||
out("return ($cReturnType) retValPlacement;")
|
||||
} else {
|
||||
out("return ($cReturnType) ($callExpr);")
|
||||
|
||||
@@ -24,7 +24,7 @@ fun main(args: Array<String>) = memScoped {
|
||||
}
|
||||
val error = errorRef.value
|
||||
if (error != null) {
|
||||
println(error.asCString().toString())
|
||||
println(error.toKString())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+23
-28
@@ -140,7 +140,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
private var cleanupLandingpad: LLVMBasicBlockRef? = null
|
||||
|
||||
fun setName(value: LLVMValueRef, name: String) = LLVMSetValueName(value, name)
|
||||
fun getName(value: LLVMValueRef) = LLVMGetValueName(value)?.asCString().toString()
|
||||
fun getName(value: LLVMValueRef) = LLVMGetValueName(value)?.toKString()
|
||||
|
||||
fun plus (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildAdd (builder, arg0, arg1, name)!!
|
||||
fun mul (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildMul (builder, arg0, arg1, name)!!
|
||||
@@ -221,10 +221,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
|
||||
fun gep(base: LLVMValueRef, index: LLVMValueRef): LLVMValueRef {
|
||||
memScoped {
|
||||
val args = allocArrayOf(index)
|
||||
return LLVMBuildGEP(builder, base, args[0].ptr, 1, "")!!
|
||||
}
|
||||
return LLVMBuildGEP(builder, base, cValuesOf(index), 1, "")!!
|
||||
}
|
||||
|
||||
fun updateReturnRef(value: LLVMValueRef, address: LLVMValueRef) {
|
||||
@@ -277,30 +274,28 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
||||
lazyLandingpad: () -> LLVMBasicBlockRef?): LLVMValueRef {
|
||||
memScoped {
|
||||
val rargs = allocArrayOf(args)[0].ptr
|
||||
|
||||
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
|
||||
(LLVMGetFunctionAttr(llvmFunction) and LLVMNoUnwindAttribute) != 0) {
|
||||
val rargs = args.toCValues()
|
||||
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
|
||||
(LLVMGetFunctionAttr(llvmFunction) and LLVMNoUnwindAttribute) != 0) {
|
||||
|
||||
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
|
||||
} else {
|
||||
val landingpad = lazyLandingpad()
|
||||
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
|
||||
} else {
|
||||
val landingpad = lazyLandingpad()
|
||||
|
||||
if (landingpad == null) {
|
||||
// When calling a function that is not marked as nounwind (can throw an exception),
|
||||
// it is required to specify a landingpad to handle exceptions properly.
|
||||
// Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`.
|
||||
val functionName = getName(llvmFunction)
|
||||
val message = "no landingpad specified when calling function $functionName without nounwind attr"
|
||||
throw IllegalArgumentException(message)
|
||||
}
|
||||
|
||||
val success = basicBlock()
|
||||
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, "")!!
|
||||
positionAtEnd(success)
|
||||
return result
|
||||
if (landingpad == null) {
|
||||
// When calling a function that is not marked as nounwind (can throw an exception),
|
||||
// it is required to specify a landingpad to handle exceptions properly.
|
||||
// Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`.
|
||||
val functionName = getName(llvmFunction)
|
||||
val message = "no landingpad specified when calling function $functionName without nounwind attr"
|
||||
throw IllegalArgumentException(message)
|
||||
}
|
||||
|
||||
val success = basicBlock()
|
||||
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, "")!!
|
||||
positionAtEnd(success)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,10 +307,10 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
fun addPhiIncoming(phi: LLVMValueRef, vararg incoming: Pair<LLVMBasicBlockRef, LLVMValueRef>) {
|
||||
memScoped {
|
||||
val incomingValues = allocArrayOf(incoming.map { it.second })
|
||||
val incomingBlocks = allocArrayOf(incoming.map { it.first })
|
||||
val incomingValues = incoming.map { it.second }.toCValues()
|
||||
val incomingBlocks = incoming.map { it.first }.toCValues()
|
||||
|
||||
LLVMAddIncoming(phi, incomingValues[0].ptr, incomingBlocks[0].ptr, incoming.size)
|
||||
LLVMAddIncoming(phi, incomingValues, incomingBlocks, incoming.size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -193,11 +193,9 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
}
|
||||
|
||||
private fun importMemset() : LLVMValueRef {
|
||||
memScoped {
|
||||
val parameterTypes = allocArrayOf(int8TypePtr, int8Type, int32Type, int32Type, int1Type)
|
||||
val functionType = LLVMFunctionType(LLVMVoidType(), parameterTypes[0].ptr, 5, 0)
|
||||
return LLVMAddFunction(llvmModule, "llvm.memset.p0i8.i32", functionType)!!
|
||||
}
|
||||
val parameterTypes = cValuesOf(int8TypePtr, int8Type, int32Type, int32Type, int1Type)
|
||||
val functionType = LLVMFunctionType(LLVMVoidType(), parameterTypes, 5, 0)
|
||||
return LLVMAddFunction(llvmModule, "llvm.memset.p0i8.i32", functionType)!!
|
||||
}
|
||||
|
||||
internal fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef {
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ public fun base64Encode(data: ByteArray): String {
|
||||
val bytes = allocArrayOf(data)
|
||||
EncodeBase64(bytes.ptr, data.size, result.ptr, resultSize)
|
||||
// TODO: any better way to do that without two copies?
|
||||
return CString.fromArray(result).toString()
|
||||
return result[0].ptr.toKString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-21
@@ -276,12 +276,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
// Creates static struct InitNode $nodeName = {$initName, NULL};
|
||||
|
||||
fun createInitNode(initFunction: LLVMValueRef, nodeName: String): LLVMValueRef {
|
||||
memScoped {
|
||||
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL.
|
||||
val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args.
|
||||
val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode.
|
||||
return context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName".
|
||||
}
|
||||
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL.
|
||||
val argList = cValuesOf(initFunction, nextInitNode) // Construct array of args.
|
||||
val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode.
|
||||
return context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName".
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1334,13 +1332,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val fieldInfo = context.llvmDeclarations.forField(value)
|
||||
|
||||
val typePtr = pointerType(fieldInfo.classBodyType)
|
||||
memScoped {
|
||||
val args = allocArrayOf(kImmOne)
|
||||
val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, args[0].ptr, 1, "")
|
||||
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!)
|
||||
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, fieldInfo.index, "")
|
||||
return fieldPtr!!
|
||||
}
|
||||
val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, cValuesOf(kImmOne), 1, "")
|
||||
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!)
|
||||
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, fieldInfo.index, "")
|
||||
return fieldPtr!!
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1893,21 +1888,23 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
//-------------------------------------------------------------------------//
|
||||
// Create type { i32, void ()*, i8* }
|
||||
|
||||
val kCtorType = memScoped {
|
||||
val ctorType = LLVMPointerType(kVoidFuncType, 0)
|
||||
val typeList = allocArrayOf(LLVMInt32Type(), ctorType, kInt8Ptr)[0].ptr
|
||||
LLVMStructType(typeList, 3, 0)
|
||||
}!!
|
||||
val kCtorType = LLVMStructType(
|
||||
cValuesOf(
|
||||
LLVMInt32Type(),
|
||||
LLVMPointerType(kVoidFuncType, 0),
|
||||
kInt8Ptr
|
||||
),
|
||||
3, 0)!!
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
// Create object { i32, void ()*, i8* } { i32 1, void ()* @ctorFunction, i8* null }
|
||||
|
||||
fun createGlobalCtor(ctorFunction: LLVMValueRef) = memScoped {
|
||||
fun createGlobalCtor(ctorFunction: LLVMValueRef): ConstPointer {
|
||||
val priority = kImmInt32One
|
||||
val data = kNullInt8Ptr
|
||||
val argList = allocArrayOf(priority, ctorFunction, data)[0].ptr
|
||||
val argList = cValuesOf(priority, ctorFunction, data)
|
||||
val ctorItem = LLVMConstNamedStruct(kCtorType, argList, 3)!!
|
||||
constPointer(ctorItem)
|
||||
return constPointer(ctorItem)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
+3
-5
@@ -123,14 +123,12 @@ private fun ContextUtils.getDeclaredFields(classDescriptor: ClassDescriptor): Li
|
||||
}
|
||||
|
||||
private fun ContextUtils.createClassBodyType(name: String, fields: List<PropertyDescriptor>): LLVMTypeRef {
|
||||
val fieldTypes = fields.map { getLLVMType(if (it.isDelegated) context.builtIns.nullableAnyType else it.type) }.toTypedArray()
|
||||
val fieldTypes = fields.map { getLLVMType(if (it.isDelegated) context.builtIns.nullableAnyType else it.type) }
|
||||
|
||||
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!!
|
||||
|
||||
memScoped {
|
||||
val fieldTypesNativeArrayPtr = allocArrayOf(*fieldTypes)[0].ptr
|
||||
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
|
||||
}
|
||||
LLVMStructSetBody(classType, fieldTypes.toCValues(), fieldTypes.size, 0)
|
||||
|
||||
return classType
|
||||
}
|
||||
|
||||
|
||||
+13
-26
@@ -31,24 +31,15 @@ internal fun constPointer(value: LLVMValueRef) = object : ConstPointer {
|
||||
}
|
||||
|
||||
private class ConstGetElementPtr(val pointer: ConstPointer, val index: Int) : ConstPointer {
|
||||
override val llvm = memScoped {
|
||||
// TODO: squash multiple GEPs
|
||||
val indices = intArrayOf(0, index).map { Int32(it).llvm }
|
||||
val indicesArray = allocArrayOf(indices)
|
||||
LLVMConstInBoundsGEP(pointer.llvm, indicesArray[0].ptr, indices.size)!!
|
||||
}
|
||||
override val llvm = LLVMConstInBoundsGEP(pointer.llvm, cValuesOf(Int32(0).llvm, Int32(index).llvm), 2)!!
|
||||
// TODO: squash multiple GEPs
|
||||
}
|
||||
|
||||
internal fun ConstPointer.bitcast(toType: LLVMTypeRef) = constPointer(LLVMConstBitCast(this.llvm, toType)!!)
|
||||
|
||||
internal class ConstArray(val elemType: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue {
|
||||
|
||||
override val llvm = memScoped {
|
||||
val values = elements.map { it.llvm }.toTypedArray()
|
||||
|
||||
val valuesNativeArrayPtr = allocArrayOf(*values)[0].ptr
|
||||
LLVMConstArray(elemType, valuesNativeArrayPtr, values.size)!!
|
||||
}
|
||||
override val llvm = LLVMConstArray(elemType, elements.map { it.llvm }.toCValues(), elements.size)!!
|
||||
}
|
||||
|
||||
internal open class Struct(val type: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue {
|
||||
@@ -57,11 +48,7 @@ internal open class Struct(val type: LLVMTypeRef?, val elements: List<ConstValue
|
||||
|
||||
constructor(vararg elements: ConstValue) : this(structType(elements.map { it.llvmType }), *elements)
|
||||
|
||||
override val llvm = memScoped {
|
||||
val values = elements.map { it.llvm }.toTypedArray()
|
||||
val valuesNativeArrayPtr = allocArrayOf(*values)[0].ptr
|
||||
LLVMConstNamedStruct(type, valuesNativeArrayPtr, values.size)!!
|
||||
}
|
||||
override val llvm = LLVMConstNamedStruct(type, elements.map { it.llvm }.toCValues(), elements.size)!!
|
||||
}
|
||||
|
||||
internal class Int1(val value: Byte) : ConstValue {
|
||||
@@ -143,9 +130,8 @@ internal fun pointerType(pointeeType: LLVMTypeRef) = LLVMPointerType(pointeeType
|
||||
|
||||
internal fun structType(vararg types: LLVMTypeRef): LLVMTypeRef = structType(types.toList())
|
||||
|
||||
internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef = memScoped {
|
||||
LLVMStructType(allocArrayOf(types)[0].ptr, types.size, 0)!!
|
||||
}
|
||||
internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef =
|
||||
LLVMStructType(types.toCValues(), types.size, 0)!!
|
||||
|
||||
internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int {
|
||||
// Note that type is usually function pointer, so we have to dereference it.
|
||||
@@ -192,20 +178,21 @@ internal fun ContextUtils.externalGlobal(name: String, type: LLVMTypeRef): LLVMV
|
||||
}
|
||||
|
||||
internal fun functionType(returnType: LLVMTypeRef, isVarArg: Boolean = false, vararg paramTypes: LLVMTypeRef) =
|
||||
memScoped {
|
||||
val paramTypesPtr = allocArrayOf(*paramTypes)[0].ptr
|
||||
LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, if (isVarArg) 1 else 0)!!
|
||||
}
|
||||
LLVMFunctionType(
|
||||
returnType,
|
||||
cValuesOf(*paramTypes), paramTypes.size,
|
||||
if (isVarArg) 1 else 0
|
||||
)!!
|
||||
|
||||
|
||||
fun llvm2string(value: LLVMValueRef?): String {
|
||||
if (value == null) return "<null>"
|
||||
return LLVMPrintValueToString(value)!!.asCString().toString()
|
||||
return LLVMPrintValueToString(value)!!.toKString()
|
||||
}
|
||||
|
||||
fun llvmtype2string(type: LLVMTypeRef?): String {
|
||||
if (type == null) return "<null type>"
|
||||
return LLVMPrintTypeToString(type)!!.asCString().toString()
|
||||
return LLVMPrintTypeToString(type)!!.toKString()
|
||||
}
|
||||
|
||||
fun getStructElements(type: LLVMTypeRef): List<LLVMTypeRef> {
|
||||
|
||||
+3
-6
@@ -61,7 +61,7 @@ class MetadataReader(file: File) : Closeable {
|
||||
val errorRef = allocPointerTo<CInt8Var>()
|
||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(file.toString(), bufRef.ptr, errorRef.ptr)
|
||||
if (res != 0) {
|
||||
throw Error(errorRef.value?.asCString()?.toString())
|
||||
throw Error(errorRef.value?.toKString())
|
||||
}
|
||||
|
||||
llvmContext = LLVMContextCreate()!!
|
||||
@@ -80,7 +80,7 @@ class MetadataReader(file: File) : Closeable {
|
||||
memScoped {
|
||||
val len = alloc<CInt32Var>()
|
||||
val str1 = LLVMGetMDString(node, len.ptr)!!
|
||||
val str = str1.asCString().toString()
|
||||
val str = str1.toKString()
|
||||
return str
|
||||
|
||||
}
|
||||
@@ -117,10 +117,7 @@ class MetadataReader(file: File) : Closeable {
|
||||
internal class MetadataGenerator(override val context: Context): ContextUtils {
|
||||
|
||||
private fun metadataNode(args: List<LLVMValueRef?>): LLVMValueRef {
|
||||
memScoped {
|
||||
val references = allocArrayOf(args)
|
||||
return LLVMMDNode(references[0].ptr, args.size)!!
|
||||
}
|
||||
return LLVMMDNode(args.toCValues(), args.size)!!
|
||||
}
|
||||
|
||||
private fun metadataFun(fn: LLVMValueRef, info: String): LLVMValueRef {
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ class Runtime(private val bitcodeFile: String) {
|
||||
|
||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr)
|
||||
if (res != 0) {
|
||||
throw Error(errorRef.value?.asCString()?.toString())
|
||||
throw Error(errorRef.value?.toKString())
|
||||
}
|
||||
|
||||
val moduleRef = alloc<LLVMModuleRefVar>()
|
||||
@@ -45,9 +45,9 @@ class Runtime(private val bitcodeFile: String) {
|
||||
val objHeaderType = getStructType("ObjHeader")
|
||||
val arrayHeaderType = getStructType("ArrayHeader")
|
||||
|
||||
val target = LLVMGetTarget(llvmModule)!!.asCString().toString()
|
||||
val target = LLVMGetTarget(llvmModule)!!.toKString()
|
||||
|
||||
val dataLayout = LLVMGetDataLayout(llvmModule)!!.asCString().toString()
|
||||
val dataLayout = LLVMGetDataLayout(llvmModule)!!.toKString()
|
||||
|
||||
val targetData = LLVMCreateTargetData(dataLayout)!!
|
||||
|
||||
|
||||
@@ -65,16 +65,9 @@ fun initialize() {
|
||||
// specify implementation-specific hints
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
|
||||
|
||||
memScoped {
|
||||
val ambLight: CArray<GLfloatVar> = allocArrayOf(0.1f, 0.1f, 0.1f, 1.0f)
|
||||
val diffuse: CArray<GLfloatVar> = allocArrayOf(0.6f, 0.6f, 0.6f, 1.0f)
|
||||
val specular: CArray<GLfloatVar> = allocArrayOf(0.7f, 0.7f, 0.3f, 1.0f)
|
||||
|
||||
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambLight[0].ptr)
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse[0].ptr)
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, specular[0].ptr)
|
||||
}
|
||||
|
||||
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, cValuesOf(0.1f, 0.1f, 0.1f, 1.0f))
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, cValuesOf(0.6f, 0.6f, 0.6f, 1.0f))
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, cValuesOf(0.7f, 0.7f, 0.3f, 1.0f))
|
||||
|
||||
glEnable(GL_LIGHT0)
|
||||
glEnable(GL_COLOR_MATERIAL)
|
||||
|
||||
@@ -38,7 +38,7 @@ class NativePointedBox(val value: NativePointed) {
|
||||
fun boxNativePointed(value: NativePointed?) = if (value != null) NativePointedBox(value) else null
|
||||
fun unboxNativePointed(box: NativePointedBox?) = box?.value
|
||||
|
||||
class CPointerBox(val value: CPointer<*>) {
|
||||
class CPointerBox(val value: CPointer<CPointed>) : CValuesRef<CPointed>() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is CPointerBox) {
|
||||
return false
|
||||
@@ -50,7 +50,9 @@ class CPointerBox(val value: CPointer<*>) {
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun getPointer(placement: NativePlacement) = value.getPointer(placement)
|
||||
}
|
||||
|
||||
fun boxCPointer(value: CPointer<*>?) = if (value != null) CPointerBox(value) else null
|
||||
fun boxCPointer(value: CPointer<CPointed>?) = if (value != null) CPointerBox(value) else null
|
||||
fun unboxCPointer(box: CPointerBox?) = box?.value
|
||||
@@ -491,7 +491,7 @@ class Game(width: Int, height: Int, val visualizer: GameFieldVisualizer, val use
|
||||
|
||||
}
|
||||
|
||||
fun get_SDL_Error() = SDL_GetError()!!.asCString().toString()
|
||||
fun get_SDL_Error() = SDL_GetError()!!.toKString()
|
||||
|
||||
class SDL_Visualizer(val width: Int, val height: Int): GameFieldVisualizer, UserInput {
|
||||
private val CELL_SIZE = 20
|
||||
|
||||
Reference in New Issue
Block a user