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:
Svyatoslav Scherbina
2017-03-16 14:48:17 +07:00
committed by SvyatoslavScherbina
parent 7a876a3ccd
commit c9d8d4d57d
22 changed files with 2072 additions and 1557 deletions
File diff suppressed because it is too large Load Diff
@@ -45,25 +45,23 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
override val macroConstants = mutableListOf<ConstantDef>() override val macroConstants = mutableListOf<ConstantDef>()
private fun getDeclarationId(cursor: CXCursor): DeclarationID = memScoped { private fun getDeclarationId(cursor: CValue<CXCursor>): DeclarationID {
val usr = clang_getCursorUSR(cursor, memScope).convertAndDispose() val usr = clang_getCursorUSR(cursor).convertAndDispose()
DeclarationID(usr) return DeclarationID(usr)
} }
private fun getStructDeclAt(cursor: CXCursor): StructDeclImpl { private fun getStructDeclAt(cursor: CValue<CXCursor>): StructDeclImpl {
val declId = getDeclarationId(cursor) val declId = getDeclarationId(cursor)
return structById.getOrPut(declId) { return structById.getOrPut(declId) {
memScoped { val cursorType = clang_getCursorType(cursor)
val cursorType = clang_getCursorType(cursor, memScope) val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
val typeSpelling = clang_getTypeSpelling(cursorType, memScope).convertAndDispose()
StructDeclImpl(typeSpelling) StructDeclImpl(typeSpelling)
}
} }
} }
private fun getEnumDefAt(cursor: CXCursor): EnumDefImpl { private fun getEnumDefAt(cursor: CValue<CXCursor>): EnumDefImpl {
if (clang_isCursorDefinition(cursor) == 0) { if (clang_isCursorDefinition(cursor) == 0) {
TODO("support enum forward declarations") TODO("support enum forward declarations")
} }
@@ -71,19 +69,17 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
val declId = getDeclarationId(cursor) val declId = getDeclarationId(cursor)
return enumById.getOrPut(declId) { return enumById.getOrPut(declId) {
memScoped { val cursorType = clang_getCursorType(cursor)
val cursorType = clang_getCursorType(cursor, memScope) val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
val typeSpelling = clang_getTypeSpelling(cursorType, memScope).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 { private fun builtinVaListType(type: CValue<CXType>, name: String, underlying: Type): Type {
assert (type.kind.value == CXType_Typedef) assert (type.kind == CXType_Typedef)
val declarationId = DeclarationID("c:@T@$name") val declarationId = DeclarationID("c:@T@$name")
val structDeclaration = structById.getOrPut(declarationId) { val structDeclaration = structById.getOrPut(declarationId) {
@@ -100,11 +96,11 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
return ConstArrayType(RecordType(structDeclaration), 1) return ConstArrayType(RecordType(structDeclaration), 1)
} }
fun getTypedef(type: CXType): Type = memScoped { fun getTypedef(type: CValue<CXType>): Type {
val declCursor = clang_getTypeDeclaration(type, memScope) val declCursor = clang_getTypeDeclaration(type)
val name = getCursorSpelling(declCursor) val name = getCursorSpelling(declCursor)
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor, memScope)) val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor))
if (name == "__builtin_va_list") { if (name == "__builtin_va_list") {
// On some platforms (e.g. macOS) libclang reports `__builtin_va_list` to be defined as array using // 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. * Computes [StructDef.hasNaturalLayout] property.
*/ */
fun structHasNaturalLayout(structDefCursor: CXCursor): Boolean { fun structHasNaturalLayout(structDefCursor: CValue<CXCursor>): Boolean {
val defKind = structDefCursor.kind.value val defKind = structDefCursor.kind
when (defKind) { when (defKind) {
@@ -161,23 +157,22 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
} }
} }
private fun convertCursorType(cursor: CXCursor) = memScoped { private fun convertCursorType(cursor: CValue<CXCursor>) =
convertType(clang_getCursorType(cursor, memScope)) convertType(clang_getCursorType(cursor))
}
fun convertType(type: CXType): Type = memScoped { fun convertType(type: CValue<CXType>): Type {
val primitiveType = convertUnqualifiedPrimitiveType(type) val primitiveType = convertUnqualifiedPrimitiveType(type)
if (primitiveType != UnsupportedType) { if (primitiveType != UnsupportedType) {
return primitiveType return primitiveType
} }
val kind = type.kind.value val kind = type.kind
return when (kind) { return when (kind) {
CXType_Elaborated -> convertType(clang_Type_getNamedType(type, memScope)) CXType_Elaborated -> convertType(clang_Type_getNamedType(type))
CXType_Unexposed -> { CXType_Unexposed -> {
val canonicalType = clang_getCanonicalType(type, memScope) val canonicalType = clang_getCanonicalType(type)
if (canonicalType.kind.value != CXType_Unexposed) { if (canonicalType.kind != CXType_Unexposed) {
convertType(canonicalType) convertType(canonicalType)
} else { } else {
throw NotImplementedError() throw NotImplementedError()
@@ -188,19 +183,27 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
CXType_Typedef -> getTypedef(type) CXType_Typedef -> getTypedef(type)
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type, memScope))) CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type)))
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type, memScope))) 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 -> { CXType_ConstantArray -> {
val elemType = convertType(clang_getArrayElementType(type, memScope)) val elemType = convertType(clang_getArrayElementType(type))
val length = clang_getArraySize(type) val length = clang_getArraySize(type)
ConstArrayType(elemType, length) ConstArrayType(elemType, length)
} }
CXType_IncompleteArray -> { CXType_IncompleteArray -> {
val elemType = convertType(clang_getArrayElementType(type, memScope)) val elemType = convertType(clang_getArrayElementType(type))
IncompleteArrayType(elemType) IncompleteArrayType(elemType)
} }
@@ -208,10 +211,10 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
if (clang_isFunctionTypeVariadic(type) != 0) { if (clang_isFunctionTypeVariadic(type) != 0) {
UnsupportedType UnsupportedType
} else { } else {
val returnType = convertType(clang_getResultType(type, memScope)) val returnType = convertType(clang_getResultType(type))
val numArgs = clang_getNumArgTypes(type) val numArgs = clang_getNumArgTypes(type)
val paramTypes = (0..numArgs - 1).map { val paramTypes = (0..numArgs - 1).map {
convertType(clang_getArgType(type, it, memScope)) convertType(clang_getArgType(type, it))
} }
FunctionType(paramTypes, returnType) FunctionType(paramTypes, returnType)
} }
@@ -221,12 +224,11 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
} }
} }
fun indexDeclaration(info: CXIdxDeclInfo): Unit = memScoped { fun indexDeclaration(info: CXIdxDeclInfo): Unit {
val cursor = info.cursor val cursor = info.cursor.readValue()
val entityInfo = info.entityInfo.pointed!! val entityInfo = info.entityInfo.pointed!!
val entityName = entityInfo.name.value?.asCString()?.toString() val entityName = entityInfo.name.value?.toKString()
val kind = entityInfo.kind.value val kind = entityInfo.kind.value
when (kind) { when (kind) {
CXIdxEntity_Field -> { CXIdxEntity_Field -> {
val name = entityName!! val name = entityName!!
@@ -234,14 +236,14 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
val offset = clang_Cursor_getOffsetOfField(cursor) val offset = clang_Cursor_getOffsetOfField(cursor)
val container = info.semanticContainer.pointed!! 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)) structDef.fields.add(Field(name, type, offset))
} }
CXIdxEntity_Struct, CXIdxEntity_Union -> { CXIdxEntity_Struct, CXIdxEntity_Union -> {
val structDecl = getStructDeclAt(cursor) val structDecl = getStructDeclAt(cursor)
if (clang_isCursorDefinition(cursor) != 0) { if (clang_isCursorDefinition(cursor) != 0) {
val type = clang_getCursorType(cursor, memScope) val type = clang_getCursorType(cursor)
val size = clang_Type_getSizeOf(type) val size = clang_Type_getSizeOf(type)
val align = clang_Type_getAlignOf(type).toInt() val align = clang_Type_getAlignOf(type).toInt()
val hasNaturalLayout = structHasNaturalLayout(cursor) val hasNaturalLayout = structHasNaturalLayout(cursor)
@@ -251,17 +253,17 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
CXIdxEntity_Function -> { CXIdxEntity_Function -> {
val name = entityName!! val name = entityName!!
val returnType = convertType(clang_getCursorResultType(cursor, memScope)) val returnType = convertType(clang_getCursorResultType(cursor))
val argNum = clang_Cursor_getNumArguments(cursor) val argNum = clang_Cursor_getNumArguments(cursor)
val args = (0 .. argNum - 1).map { 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 argName = getCursorSpelling(argCursor)
val type = convertCursorType(argCursor) val type = convertCursorType(argCursor)
Parameter(argName, type) Parameter(argName, type)
} }
val binaryName = when (language) { 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) functionByName[name] = FunctionDecl(name, args, returnType, binaryName)
@@ -274,9 +276,9 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
CXIdxEntity_EnumConstant -> { CXIdxEntity_EnumConstant -> {
val container = info.semanticContainer.pointed!! val container = info.semanticContainer.pointed!!
val name = entityName!! 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 } val existingConstant = constants.find { it.name == name }
if (existingConstant == null) { if (existingConstant == null) {
val constant = EnumConstant(name, value, isExplicitlyDefined = !cursor.isLeaf()) val constant = EnumConstant(name, value, isExplicitlyDefined = !cursor.isLeaf())
@@ -1,7 +1,7 @@
package org.jetbrains.kotlin.native.interop.indexer package org.jetbrains.kotlin.native.interop.indexer
import clang.* import clang.*
import kotlinx.cinterop.memScoped import kotlinx.cinterop.CValue
import java.io.File import java.io.File
/** /**
@@ -16,7 +16,7 @@ internal fun findMacroConstants(library: NativeLibrary, nativeIndex: NativeIndex
nativeIndex.macroConstants.addAll(constants) 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, * 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 longValue: Long? = null
var doubleValue: Double? = null var doubleValue: Double? = null
val visitor: CursorVisitor = { cursor: CXCursor, parent: CXCursor -> val visitor: CursorVisitor = { cursor, parent ->
val kind = cursor.kind.value val kind = cursor.kind
when { when {
state == VisitorState.EXPECT_VARIABLE && kind == CXCursorKind.CXCursor_VarDecl -> { state == VisitorState.EXPECT_VARIABLE && kind == CXCursorKind.CXCursor_VarDecl -> {
state = VisitorState.EXPECT_VARIABLE_VALUE state = VisitorState.EXPECT_VARIABLE_VALUE
@@ -144,10 +144,7 @@ private fun processCodeSnippet(
state == VisitorState.EXPECT_VARIABLE_VALUE && clang_isExpression(kind) != 0 -> { state == VisitorState.EXPECT_VARIABLE_VALUE && clang_isExpression(kind) != 0 -> {
state = VisitorState.EXPECT_ENUM state = VisitorState.EXPECT_ENUM
type = memScoped { type = typeConverter(clang_getCursorType(cursor))
val cursorType = clang_getCursorType(cursor, memScope)
typeConverter(cursorType)
}
CXChildVisitResult.CXChildVisit_Continue CXChildVisitResult.CXChildVisit_Continue
} }
@@ -204,7 +201,7 @@ private fun collectMacroConstantsNames(library: NativeLibrary): List<String> {
val translationUnit = library.parse(index, options).ensureNoCompileErrors() val translationUnit = library.parse(index, options).ensureNoCompileErrors()
try { try {
visitChildren(translationUnit) { cursor, parent -> 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) val spelling = getCursorSpelling(cursor)
result.add(spelling) result.add(spelling)
} }
@@ -221,7 +218,7 @@ private fun collectMacroConstantsNames(library: NativeLibrary): List<String> {
return result.toList() return result.toList()
} }
private fun canMacroBeConstant(cursor: CXCursor): Boolean { private fun canMacroBeConstant(cursor: CValue<CXCursor>): Boolean {
if (clang_Cursor_isMacroFunctionLike(cursor) != 0) { if (clang_Cursor_isMacroFunctionLike(cursor) != 0) {
return false return false
@@ -94,6 +94,8 @@ interface PrimitiveType : Type
object VoidType : Type object VoidType : Type
object CharType : PrimitiveType
object Int8Type : PrimitiveType object Int8Type : PrimitiveType
object UInt8Type : PrimitiveType object UInt8Type : PrimitiveType
@@ -116,7 +118,14 @@ data class RecordType(val decl: StructDecl) : Type
data class EnumType(val def: EnumDef) : 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 data class FunctionType(val parameterTypes: List<Type>, val returnType: Type) : Type
@@ -4,23 +4,28 @@ import clang.*
import kotlinx.cinterop.* import kotlinx.cinterop.*
import java.io.File 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 { try {
return clang_getCString(this)!!.asCString().toString() return clang_getCString(this)!!.toKString()
} finally { } finally {
clang_disposeString(this) clang_disposeString(this)
} }
} }
internal fun getCursorSpelling(cursor: CXCursor) = memScoped { internal fun getCursorSpelling(cursor: CValue<CXCursor>) =
clang_getCursorSpelling(cursor, memScope).convertAndDispose() 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? // TODO: is e.g. CXType_Int guaranteed to be int32_t?
CXTypeKind.CXType_Char_U, CXTypeKind.CXType_UChar -> UInt8Type CXTypeKind.CXType_Char_U, CXTypeKind.CXType_Char_S -> CharType
CXTypeKind.CXType_Char_S, CXTypeKind.CXType_SChar -> Int8Type
CXTypeKind.CXType_UChar -> UInt8Type
CXTypeKind.CXType_SChar -> Int8Type
CXTypeKind.CXType_UShort -> UInt16Type CXTypeKind.CXType_UShort -> UInt16Type
CXTypeKind.CXType_Short -> Int16Type CXTypeKind.CXType_Short -> Int16Type
@@ -71,9 +76,7 @@ internal fun getCompileErrors(translationUnit: CXTranslationUnit): Sequence<Stri
severity == CXDiagnosticSeverity.CXDiagnostic_Error || severity == CXDiagnosticSeverity.CXDiagnostic_Error ||
severity == CXDiagnosticSeverity.CXDiagnostic_Fatal severity == CXDiagnosticSeverity.CXDiagnostic_Fatal
}.map { }.map {
memScoped { clang_formatDiagnostic(it, clang_defaultDiagnosticDisplayOptions()).convertAndDispose()
clang_formatDiagnostic(it, clang_defaultDiagnosticDisplayOptions(), memScope).convertAndDispose()
}
} }
} }
@@ -82,24 +85,21 @@ internal fun CXTranslationUnit.ensureNoCompileErrors(): CXTranslationUnit {
throw Error(firstError) 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 visitorPtr = StableObjPtr.create(visitor)
val clientData = visitorPtr.value val clientData = visitorPtr.value
clang_visitChildren(parent, staticCFunction { cursor, parent, clientData -> clang_visitChildren(parent, staticCFunction { cursor, parent, clientData ->
val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor
visitor(cursor, parent) visitor(cursor.readValue(), parent.readValue())
}, clientData) }, clientData)
} }
internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVisitor) { internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVisitor) =
memScoped { visitChildren(clang_getTranslationUnitCursor(translationUnit), visitor)
visitChildren(clang_getTranslationUnitCursor(translationUnit, memScope), visitor)
}
}
internal fun CXCursor.isLeaf(): Boolean { internal fun CValue<CXCursor>.isLeaf(): Boolean {
var hasChildren = false var hasChildren = false
visitChildren(this) { _, _ -> visitChildren(this) { _, _ ->
@@ -112,7 +112,7 @@ internal fun CXCursor.isLeaf(): Boolean {
internal fun List<String>.toNativeStringArray(placement: NativePlacement): CArray<CPointerVar<CInt8Var>> { internal fun List<String>.toNativeStringArray(placement: NativePlacement): CArray<CPointerVar<CInt8Var>> {
return placement.allocArray(this.size) { index -> 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()) 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 internal class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
fun alloc(size: Long, align: Int): NativePointed { fun alloc(size: Long, align: Int): NativePointed {
@@ -33,10 +33,49 @@ inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpre
*/ */
interface CPointed : NativePointed 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. * 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 { override fun equals(other: Any?): Boolean {
if (this === other) { if (this === other) {
@@ -51,6 +90,8 @@ class CPointer<T : CPointed> internal constructor(val rawValue: NativePtr) {
} }
override fun toString() = this.cPointerToString() 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` // `null` value of `CPointer?` is mapped to `nativeNullPtr`
val CPointer<*>?.rawValue: NativePtr 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)!! 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. * 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? get() = interpretCPointer<CPointed>(nativeMemUtils.getNativePtr(this)) as P?
set(value) = nativeMemUtils.putNativePtr(this, value.rawValue) 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. * Allocates variable of given type.
* *
@@ -138,12 +136,9 @@ inline fun <reified T : CPointer<*>>
} }
fun NativePlacement.allocArrayOf(elements: ByteArray): CArray<CInt8Var> { fun NativePlacement.allocArrayOf(elements: ByteArray): CArray<CInt8Var> {
val res = allocArray<CInt8Var>(elements.size) val result = allocArray<CInt8Var>(elements.size)
var index = 0 nativeMemUtils.putByteArray(elements, result, elements.size)
for (byte in elements) { return result
res[index++].value = byte
}
return res
} }
fun NativePlacement.allocArrayOf(vararg elements: Float): CArray<CFloat32Var> { 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 : 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 { inline fun <reified T : CStructVar> CValue<T>.copy(modify: T.() -> Unit): CValue<T> = useContents {
fun fromArray(array: CArray<CInt8Var>) = array.reinterpret<CString>() this.modify()
} this.readValue()
}
fun length(): Int { inline fun <reified T : CStructVar> cValue(initialize: T.() -> Unit): CValue<T> =
val array = reinterpret<CArray<CInt8Var>>() zeroValue<T>().copy(modify = initialize)
var res = 0 inline fun <reified T : CVariable> createValues(count: Int, initializer: T.(index: Int) -> Unit) = memScoped {
while (array[res].value != 0.toByte()) { val array = allocArray<T>(count, initializer)
++res 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() var length = 0
val bytes = ByteArray(len) while (nativeBytes[length].value != 0.toByte()) {
++length
nativeMemUtils.getByteArray(array[0], bytes, len)
return decodeFromUtf8(bytes) // TODO: encoding
} }
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 { class MemScope : NativePlacement {
private val arena = Arena() private val arena = Arena()
@@ -30,6 +30,7 @@ object nativeMemUtils {
@Intrinsic external fun getNativePtr(mem: NativePointed): NativePtr @Intrinsic external fun getNativePtr(mem: NativePointed): NativePtr
@Intrinsic external fun putNativePtr(mem: NativePointed, value: NativePtr) @Intrinsic external fun putNativePtr(mem: NativePointed, value: NativePtr)
// TODO: optimize
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) { fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
val sourceArray: CArray<CInt8Var> = source.reinterpret() val sourceArray: CArray<CInt8Var> = source.reinterpret()
for (index in 0 .. length - 1) { for (index in 0 .. length - 1) {
@@ -37,6 +38,7 @@ object nativeMemUtils {
} }
} }
// TODO: optimize
fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) { fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) {
val destArray: CArray<CInt8Var> = dest.reinterpret() val destArray: CArray<CInt8Var> = dest.reinterpret()
for (index in 0 .. length - 1) { 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 private class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
fun alloc(size: Long, align: Int): NativePointed { fun alloc(size: Long, align: Int): NativePointed {
@@ -173,7 +173,8 @@ class StubGenerator(
return when (this) { return when (this) {
is VoidType -> "void" is VoidType -> "void"
is Int8Type -> "char" is CharType -> "char"
is Int8Type -> "signed char"
is UInt8Type -> "unsigned char" is UInt8Type -> "unsigned char"
is Int16Type -> "short" is Int16Type -> "short"
is UInt16Type -> "unsigned short" is UInt16Type -> "unsigned short"
@@ -214,7 +215,7 @@ class StubGenerator(
val PrimitiveType.kotlinType: String val PrimitiveType.kotlinType: String
get() = when (this) { get() = when (this) {
is Int8Type, is UInt8Type -> "Byte" is CharType, is Int8Type, is UInt8Type -> "Byte"
is Int16Type, is UInt16Type -> "Short" is Int16Type, is UInt16Type -> "Short"
is Int32Type, is UInt32Type -> "Int" is Int32Type, is UInt32Type -> "Int"
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
@@ -326,7 +327,7 @@ class StubGenerator(
private fun mirror(type: PrimitiveType): TypeMirror.ByValue { private fun mirror(type: PrimitiveType): TypeMirror.ByValue {
val varTypeName = when (type) { val varTypeName = when (type) {
is Int8Type, is UInt8Type -> "CInt8Var" is CharType, is Int8Type, is UInt8Type -> "CInt8Var"
is Int16Type, is UInt16Type -> "CInt16Var" is Int16Type, is UInt16Type -> "CInt16Var"
is Int32Type, is UInt32Type -> "CInt32Var" is Int32Type, is UInt32Type -> "CInt32Var"
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific 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 { fun getCFunctionParamBinding(type: Type): OutValueBinding {
if (treatCFunctionParameterAsString(type)) { if (representCFunctionParameterAsString(type)) {
return OutValueBinding( return OutValueBinding(
kotlinType = "String?", kotlinType = "String?", // TODO: mention the C type (e.g. with annotation).
kotlinConv = { name -> "$name?.toCString(memScope).rawPtr" }, 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, memScoped = true,
kotlinJniBridgeType = "NativePtr" kotlinJniBridgeType = "NativePtr"
) )
@@ -452,8 +494,6 @@ class StubGenerator(
return getOutValueBinding(type) return getOutValueBinding(type)
} }
private fun treatCFunctionParameterAsString(type: Type) = type is PointerType && type.pointeeType is Int8Type
fun getCallbackRetValBinding(type: Type): OutValueBinding { fun getCallbackRetValBinding(type: Type): OutValueBinding {
if (type.unwrapTypedefs() is VoidType) { if (type.unwrapTypedefs() is VoidType) {
return OutValueBinding( return OutValueBinding(
@@ -484,12 +524,21 @@ class StubGenerator(
) )
} }
fun getCFunctionRetValBinding(type: Type): InValueBinding { fun getCFunctionRetValBinding(func: FunctionDecl): InValueBinding {
when (type.unwrapTypedefs()) { when {
is VoidType -> return InValueBinding("Unit") 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 { fun getCallbackParamBinding(type: Type): InValueBinding {
@@ -637,7 +686,10 @@ class StubGenerator(
/** /**
* Constructs [InValueBinding] for return value of Kotlin binding for given C function. * 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. * Constructs [OutValueBinding]s for parameters of Kotlin binding for given C function.
@@ -647,13 +699,10 @@ class StubGenerator(
getCFunctionParamBinding(param.type) getCFunctionParamBinding(param.type)
}.toMutableList() }.toMutableList()
val retValType = func.returnType if (func.returnsRecord()) {
if (retValType.unwrapTypedefs() is RecordType) {
val retValMirror = mirror(retValType)
paramBindings.add(OutValueBinding( paramBindings.add(OutValueBinding(
kotlinType = "NativePlacement", kotlinType = "should not be used",
kotlinConv = { name -> "$name.alloc<${retValMirror.pointedTypeName}>().rawPtr" }, kotlinConv = { throw UnsupportedOperationException() },
kotlinJniBridgeType = "NativePtr" kotlinJniBridgeType = "NativePtr"
)) ))
} }
@@ -674,7 +723,7 @@ class StubGenerator(
} }
}.toMutableList() }.toMutableList()
if (func.returnType.unwrapTypedefs() is RecordType) { if (func.returnsRecord()) {
paramNames.add("retValPlacement") paramNames.add("retValPlacement")
} }
@@ -685,7 +734,14 @@ class StubGenerator(
* Produces to [out] the definition of Kotlin binding for given C function. * Produces to [out] the definition of Kotlin binding for given C function.
*/ */
private fun generateKotlinBindingMethod(func: FunctionDecl) { 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 paramBindings = paramBindings(func)
val retValBinding = retValBinding(func) val retValBinding = retValBinding(func)
@@ -716,7 +772,13 @@ class StubGenerator(
externalParamName externalParamName
}.toMutableList()
if (func.returnsRecord()) {
val retValMirror = mirror(func.returnType)
arguments.add("alloc<${retValMirror.pointedTypeName}>().rawPtr")
} }
val callee = when (platform) { val callee = when (platform) {
KotlinPlatform.JVM -> "externals.${func.name}" KotlinPlatform.JVM -> "externals.${func.name}"
KotlinPlatform.NATIVE -> func.kotlinExternalName KotlinPlatform.NATIVE -> func.kotlinExternalName
@@ -741,7 +803,7 @@ class StubGenerator(
} }
block(header) { block(header) {
val memScoped = paramBindings.any { it.memScoped } val memScoped = paramBindings.any { it.memScoped } || func.returnsRecord()
if (memScoped) { if (memScoped) {
block("return memScoped") { block("return memScoped") {
generateBody(true) generateBody(true)
@@ -782,7 +844,8 @@ class StubGenerator(
private fun getFfiType(type: Type): String { private fun getFfiType(type: Type): String {
return when(type) { return when(type) {
is VoidType -> "Void" is VoidType -> "Void"
is Int8Type -> "SInt8" is CharType, is Int8Type -> "SInt8"
// TODO: libffi has separate representation for char type.
is UInt8Type -> "UInt8" is UInt8Type -> "UInt8"
is Int16Type -> "SInt16" is Int16Type -> "SInt16"
is UInt16Type -> "UInt16" is UInt16Type -> "UInt16"
@@ -829,8 +892,12 @@ class StubGenerator(
return true return true
} }
return this.parameters.any { treatCFunctionParameterAsString(it.type) } || return this.returnsRecord() ||
this.returnType.unwrapTypedefs() is RecordType this.parameters.map { it.type }.any {
it.unwrapTypedefs() is RecordType ||
representCFunctionParameterAsString(it) ||
representCFunctionParameterAsValuesRef(it) != null
}
} }
/** /**
@@ -842,8 +909,8 @@ class StubGenerator(
return true return true
} }
val parameterTypes = this.parameters.map { it.type } return this.returnsRecord() ||
return (parameterTypes + returnType).any { it.unwrapTypedefs() is RecordType } this.parameters.map { it.type }.any { it.unwrapTypedefs() is RecordType }
} }
private fun FunctionType.requiresAdapterOnNative(): Boolean { 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. * Produces to [out] the implementation of JNI function used in Kotlin binding for given C function.
*/ */
private fun generateCJniFunction(func: FunctionDecl) { private fun generateCJniFunction(func: FunctionDecl) {
val funcReturnType = func.returnType.unwrapTypedefs()
val paramNames = paramNames(func) val paramNames = paramNames(func)
val paramBindings = paramBindings(func) val paramBindings = paramBindings(func)
val retValBinding = retValBinding(func) val retValBinding = retValBinding(func)
@@ -1241,8 +1307,8 @@ class StubGenerator(
if (cReturnType == "void") { if (cReturnType == "void") {
out("$callExpr;") out("$callExpr;")
} else if (funcReturnType is RecordType) { } else if (func.returnsRecord()) {
out("*(${funcReturnType.decl.spelling}*)retValPlacement = $callExpr;") out("*(${func.returnType.getStringRepresentation()}*)retValPlacement = $callExpr;")
out("return ($cReturnType) retValPlacement;") out("return ($cReturnType) retValPlacement;")
} else { } else {
out("return ($cReturnType) ($callExpr);") out("return ($cReturnType) ($callExpr);")
+1 -1
View File
@@ -24,7 +24,7 @@ fun main(args: Array<String>) = memScoped {
} }
val error = errorRef.value val error = errorRef.value
if (error != null) { if (error != null) {
println(error.asCString().toString()) println(error.toKString())
return return
} }
@@ -140,7 +140,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
private var cleanupLandingpad: LLVMBasicBlockRef? = null private var cleanupLandingpad: LLVMBasicBlockRef? = null
fun setName(value: LLVMValueRef, name: String) = LLVMSetValueName(value, name) 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 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)!! 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 { fun gep(base: LLVMValueRef, index: LLVMValueRef): LLVMValueRef {
memScoped { return LLVMBuildGEP(builder, base, cValuesOf(index), 1, "")!!
val args = allocArrayOf(index)
return LLVMBuildGEP(builder, base, args[0].ptr, 1, "")!!
}
} }
fun updateReturnRef(value: LLVMValueRef, address: LLVMValueRef) { 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>, private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
lazyLandingpad: () -> LLVMBasicBlockRef?): LLVMValueRef { lazyLandingpad: () -> LLVMBasicBlockRef?): LLVMValueRef {
memScoped {
val rargs = allocArrayOf(args)[0].ptr
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ && val rargs = args.toCValues()
(LLVMGetFunctionAttr(llvmFunction) and LLVMNoUnwindAttribute) != 0) { if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
(LLVMGetFunctionAttr(llvmFunction) and LLVMNoUnwindAttribute) != 0) {
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!! return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
} else { } else {
val landingpad = lazyLandingpad() val landingpad = lazyLandingpad()
if (landingpad == null) { if (landingpad == null) {
// When calling a function that is not marked as nounwind (can throw an exception), // 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. // it is required to specify a landingpad to handle exceptions properly.
// Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`. // Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`.
val functionName = getName(llvmFunction) val functionName = getName(llvmFunction)
val message = "no landingpad specified when calling function $functionName without nounwind attr" val message = "no landingpad specified when calling function $functionName without nounwind attr"
throw IllegalArgumentException(message) throw IllegalArgumentException(message)
}
val success = basicBlock()
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, "")!!
positionAtEnd(success)
return result
} }
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>) { fun addPhiIncoming(phi: LLVMValueRef, vararg incoming: Pair<LLVMBasicBlockRef, LLVMValueRef>) {
memScoped { memScoped {
val incomingValues = allocArrayOf(incoming.map { it.second }) val incomingValues = incoming.map { it.second }.toCValues()
val incomingBlocks = allocArrayOf(incoming.map { it.first }) val incomingBlocks = incoming.map { it.first }.toCValues()
LLVMAddIncoming(phi, incomingValues[0].ptr, incomingBlocks[0].ptr, incoming.size) LLVMAddIncoming(phi, incomingValues, incomingBlocks, incoming.size)
} }
} }
@@ -193,11 +193,9 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
} }
private fun importMemset() : LLVMValueRef { private fun importMemset() : LLVMValueRef {
memScoped { val parameterTypes = cValuesOf(int8TypePtr, int8Type, int32Type, int32Type, int1Type)
val parameterTypes = allocArrayOf(int8TypePtr, int8Type, int32Type, int32Type, int1Type) val functionType = LLVMFunctionType(LLVMVoidType(), parameterTypes, 5, 0)
val functionType = LLVMFunctionType(LLVMVoidType(), parameterTypes[0].ptr, 5, 0) return LLVMAddFunction(llvmModule, "llvm.memset.p0i8.i32", functionType)!!
return LLVMAddFunction(llvmModule, "llvm.memset.p0i8.i32", functionType)!!
}
} }
internal fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef { internal fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef {
@@ -28,7 +28,7 @@ public fun base64Encode(data: ByteArray): String {
val bytes = allocArrayOf(data) val bytes = allocArrayOf(data)
EncodeBase64(bytes.ptr, data.size, result.ptr, resultSize) EncodeBase64(bytes.ptr, data.size, result.ptr, resultSize)
// TODO: any better way to do that without two copies? // TODO: any better way to do that without two copies?
return CString.fromArray(result).toString() return result[0].ptr.toKString()
} }
} }
@@ -276,12 +276,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
// Creates static struct InitNode $nodeName = {$initName, NULL}; // Creates static struct InitNode $nodeName = {$initName, NULL};
fun createInitNode(initFunction: LLVMValueRef, nodeName: String): LLVMValueRef { fun createInitNode(initFunction: LLVMValueRef, nodeName: String): LLVMValueRef {
memScoped { val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL.
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL. val argList = cValuesOf(initFunction, nextInitNode) // Construct array of args.
val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args. val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode.
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".
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 fieldInfo = context.llvmDeclarations.forField(value)
val typePtr = pointerType(fieldInfo.classBodyType) val typePtr = pointerType(fieldInfo.classBodyType)
memScoped { val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, cValuesOf(kImmOne), 1, "")
val args = allocArrayOf(kImmOne) val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!)
val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, args[0].ptr, 1, "") val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, fieldInfo.index, "")
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!) return fieldPtr!!
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* } // Create type { i32, void ()*, i8* }
val kCtorType = memScoped { val kCtorType = LLVMStructType(
val ctorType = LLVMPointerType(kVoidFuncType, 0) cValuesOf(
val typeList = allocArrayOf(LLVMInt32Type(), ctorType, kInt8Ptr)[0].ptr LLVMInt32Type(),
LLVMStructType(typeList, 3, 0) LLVMPointerType(kVoidFuncType, 0),
}!! kInt8Ptr
),
3, 0)!!
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
// Create object { i32, void ()*, i8* } { i32 1, void ()* @ctorFunction, i8* null } // 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 priority = kImmInt32One
val data = kNullInt8Ptr val data = kNullInt8Ptr
val argList = allocArrayOf(priority, ctorFunction, data)[0].ptr val argList = cValuesOf(priority, ctorFunction, data)
val ctorItem = LLVMConstNamedStruct(kCtorType, argList, 3)!! val ctorItem = LLVMConstNamedStruct(kCtorType, argList, 3)!!
constPointer(ctorItem) return constPointer(ctorItem)
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -123,14 +123,12 @@ private fun ContextUtils.getDeclaredFields(classDescriptor: ClassDescriptor): Li
} }
private fun ContextUtils.createClassBodyType(name: String, fields: List<PropertyDescriptor>): LLVMTypeRef { 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)!! val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!!
memScoped { LLVMStructSetBody(classType, fieldTypes.toCValues(), fieldTypes.size, 0)
val fieldTypesNativeArrayPtr = allocArrayOf(*fieldTypes)[0].ptr
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
}
return classType return classType
} }
@@ -31,24 +31,15 @@ internal fun constPointer(value: LLVMValueRef) = object : ConstPointer {
} }
private class ConstGetElementPtr(val pointer: ConstPointer, val index: Int) : ConstPointer { private class ConstGetElementPtr(val pointer: ConstPointer, val index: Int) : ConstPointer {
override val llvm = memScoped { override val llvm = LLVMConstInBoundsGEP(pointer.llvm, cValuesOf(Int32(0).llvm, Int32(index).llvm), 2)!!
// TODO: squash multiple GEPs // 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)!!
}
} }
internal fun ConstPointer.bitcast(toType: LLVMTypeRef) = constPointer(LLVMConstBitCast(this.llvm, toType)!!) internal fun ConstPointer.bitcast(toType: LLVMTypeRef) = constPointer(LLVMConstBitCast(this.llvm, toType)!!)
internal class ConstArray(val elemType: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue { internal class ConstArray(val elemType: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue {
override val llvm = memScoped { override val llvm = LLVMConstArray(elemType, elements.map { it.llvm }.toCValues(), elements.size)!!
val values = elements.map { it.llvm }.toTypedArray()
val valuesNativeArrayPtr = allocArrayOf(*values)[0].ptr
LLVMConstArray(elemType, valuesNativeArrayPtr, values.size)!!
}
} }
internal open class Struct(val type: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue { 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) constructor(vararg elements: ConstValue) : this(structType(elements.map { it.llvmType }), *elements)
override val llvm = memScoped { override val llvm = LLVMConstNamedStruct(type, elements.map { it.llvm }.toCValues(), elements.size)!!
val values = elements.map { it.llvm }.toTypedArray()
val valuesNativeArrayPtr = allocArrayOf(*values)[0].ptr
LLVMConstNamedStruct(type, valuesNativeArrayPtr, values.size)!!
}
} }
internal class Int1(val value: Byte) : ConstValue { 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(vararg types: LLVMTypeRef): LLVMTypeRef = structType(types.toList())
internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef = memScoped { internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef =
LLVMStructType(allocArrayOf(types)[0].ptr, types.size, 0)!! LLVMStructType(types.toCValues(), types.size, 0)!!
}
internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int { internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int {
// Note that type is usually function pointer, so we have to dereference it. // 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) = internal fun functionType(returnType: LLVMTypeRef, isVarArg: Boolean = false, vararg paramTypes: LLVMTypeRef) =
memScoped { LLVMFunctionType(
val paramTypesPtr = allocArrayOf(*paramTypes)[0].ptr returnType,
LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, if (isVarArg) 1 else 0)!! cValuesOf(*paramTypes), paramTypes.size,
} if (isVarArg) 1 else 0
)!!
fun llvm2string(value: LLVMValueRef?): String { fun llvm2string(value: LLVMValueRef?): String {
if (value == null) return "<null>" if (value == null) return "<null>"
return LLVMPrintValueToString(value)!!.asCString().toString() return LLVMPrintValueToString(value)!!.toKString()
} }
fun llvmtype2string(type: LLVMTypeRef?): String { fun llvmtype2string(type: LLVMTypeRef?): String {
if (type == null) return "<null type>" if (type == null) return "<null type>"
return LLVMPrintTypeToString(type)!!.asCString().toString() return LLVMPrintTypeToString(type)!!.toKString()
} }
fun getStructElements(type: LLVMTypeRef): List<LLVMTypeRef> { fun getStructElements(type: LLVMTypeRef): List<LLVMTypeRef> {
@@ -61,7 +61,7 @@ class MetadataReader(file: File) : Closeable {
val errorRef = allocPointerTo<CInt8Var>() val errorRef = allocPointerTo<CInt8Var>()
val res = LLVMCreateMemoryBufferWithContentsOfFile(file.toString(), bufRef.ptr, errorRef.ptr) val res = LLVMCreateMemoryBufferWithContentsOfFile(file.toString(), bufRef.ptr, errorRef.ptr)
if (res != 0) { if (res != 0) {
throw Error(errorRef.value?.asCString()?.toString()) throw Error(errorRef.value?.toKString())
} }
llvmContext = LLVMContextCreate()!! llvmContext = LLVMContextCreate()!!
@@ -80,7 +80,7 @@ class MetadataReader(file: File) : Closeable {
memScoped { memScoped {
val len = alloc<CInt32Var>() val len = alloc<CInt32Var>()
val str1 = LLVMGetMDString(node, len.ptr)!! val str1 = LLVMGetMDString(node, len.ptr)!!
val str = str1.asCString().toString() val str = str1.toKString()
return str return str
} }
@@ -117,10 +117,7 @@ class MetadataReader(file: File) : Closeable {
internal class MetadataGenerator(override val context: Context): ContextUtils { internal class MetadataGenerator(override val context: Context): ContextUtils {
private fun metadataNode(args: List<LLVMValueRef?>): LLVMValueRef { private fun metadataNode(args: List<LLVMValueRef?>): LLVMValueRef {
memScoped { return LLVMMDNode(args.toCValues(), args.size)!!
val references = allocArrayOf(args)
return LLVMMDNode(references[0].ptr, args.size)!!
}
} }
private fun metadataFun(fn: LLVMValueRef, info: String): LLVMValueRef { private fun metadataFun(fn: LLVMValueRef, info: String): LLVMValueRef {
@@ -21,7 +21,7 @@ class Runtime(private val bitcodeFile: String) {
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr) val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr)
if (res != 0) { if (res != 0) {
throw Error(errorRef.value?.asCString()?.toString()) throw Error(errorRef.value?.toKString())
} }
val moduleRef = alloc<LLVMModuleRefVar>() val moduleRef = alloc<LLVMModuleRefVar>()
@@ -45,9 +45,9 @@ class Runtime(private val bitcodeFile: String) {
val objHeaderType = getStructType("ObjHeader") val objHeaderType = getStructType("ObjHeader")
val arrayHeaderType = getStructType("ArrayHeader") 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)!! val targetData = LLVMCreateTargetData(dataLayout)!!
@@ -65,16 +65,9 @@ fun initialize() {
// specify implementation-specific hints // specify implementation-specific hints
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
memScoped { glLightModelfv(GL_LIGHT_MODEL_AMBIENT, cValuesOf(0.1f, 0.1f, 0.1f, 1.0f))
val ambLight: CArray<GLfloatVar> = allocArrayOf(0.1f, 0.1f, 0.1f, 1.0f) glLightfv(GL_LIGHT0, GL_DIFFUSE, cValuesOf(0.6f, 0.6f, 0.6f, 1.0f))
val diffuse: CArray<GLfloatVar> = allocArrayOf(0.6f, 0.6f, 0.6f, 1.0f) glLightfv(GL_LIGHT0, GL_SPECULAR, cValuesOf(0.7f, 0.7f, 0.3f, 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)
}
glEnable(GL_LIGHT0) glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL) 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 boxNativePointed(value: NativePointed?) = if (value != null) NativePointedBox(value) else null
fun unboxNativePointed(box: NativePointedBox?) = box?.value 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 { override fun equals(other: Any?): Boolean {
if (other !is CPointerBox) { if (other !is CPointerBox) {
return false return false
@@ -50,7 +50,9 @@ class CPointerBox(val value: CPointer<*>) {
override fun hashCode() = value.hashCode() override fun hashCode() = value.hashCode()
override fun toString() = value.toString() 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 fun unboxCPointer(box: CPointerBox?) = box?.value
+1 -1
View File
@@ -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 { class SDL_Visualizer(val width: Int, val height: Int): GameFieldVisualizer, UserInput {
private val CELL_SIZE = 20 private val CELL_SIZE = 20