Support for wchar, minor opts in interop runtime (#641)
This commit is contained in:
@@ -76,6 +76,18 @@ object nativeMemUtils {
|
||||
unsafe.copyMemory(source, baseOffset, null, dest.address, length.toLong())
|
||||
}
|
||||
|
||||
fun getCharArray(source: NativePointed, dest: CharArray, length: Int) {
|
||||
val clazz = CharArray::class.java
|
||||
val baseOffset = unsafe.arrayBaseOffset(clazz).toLong();
|
||||
unsafe.copyMemory(null, source.address, dest, baseOffset, length.toLong() * 2)
|
||||
}
|
||||
|
||||
fun putCharArray(source: CharArray, dest: NativePointed, length: Int) {
|
||||
val clazz = CharArray::class.java
|
||||
val baseOffset = unsafe.arrayBaseOffset(clazz).toLong();
|
||||
unsafe.copyMemory(source, baseOffset, null, dest.address, length.toLong() * 2)
|
||||
}
|
||||
|
||||
fun zeroMemory(dest: NativePointed, length: Int): Unit = unsafe.setMemory(dest.address, length.toLong(), 0)
|
||||
|
||||
internal class NativeAllocated(override val rawPtr: NativePtr) : NativePointed
|
||||
|
||||
@@ -147,10 +147,11 @@ inline fun <reified T : CPointer<*>>
|
||||
NativePlacement.allocArrayOf(elements: List<T?>): CArrayPointer<CPointerVarOf<T>> {
|
||||
|
||||
val res = allocArray<CPointerVarOf<T>>(elements.size)
|
||||
elements.forEachIndexed { index, value ->
|
||||
res[index] = value
|
||||
var index = 0
|
||||
while (index < elements.size) {
|
||||
res[index] = elements[index]
|
||||
++index
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -163,8 +164,9 @@ fun NativePlacement.allocArrayOf(elements: ByteArray): CArrayPointer<ByteVar> {
|
||||
fun NativePlacement.allocArrayOf(vararg elements: Float): CArrayPointer<FloatVar> {
|
||||
val res = allocArray<FloatVar>(elements.size)
|
||||
var index = 0
|
||||
for (element in elements) {
|
||||
res[index++] = element
|
||||
while (index < elements.size) {
|
||||
res[index] = elements[index]
|
||||
++index
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -287,8 +289,6 @@ 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<ByteVar>
|
||||
@@ -307,6 +307,21 @@ val String.cstr: CValues<ByteVar>
|
||||
}
|
||||
}
|
||||
|
||||
val String.wcstr: CValues<ShortVar>
|
||||
get() {
|
||||
val chars = CharArray(this.length, { i -> this.get(i)})
|
||||
return object : CValues<ShortVar>() {
|
||||
override val size get() = chars.size + 1
|
||||
|
||||
override fun getPointer(placement: NativePlacement): CPointer<ShortVar> {
|
||||
val result = placement.allocArray<ShortVar>(chars.size + 1)
|
||||
nativeMemUtils.putCharArray(chars, result.pointed, chars.size)
|
||||
result[chars.size] = 0.toShort()
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: should the name of the function reflect the encoding?
|
||||
*
|
||||
|
||||
@@ -33,24 +33,50 @@ object nativeMemUtils {
|
||||
// TODO: optimize
|
||||
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
|
||||
val sourceArray = source.reinterpret<ByteVar>().ptr
|
||||
for (index in 0 .. length - 1) {
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
dest[index] = sourceArray[index]
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) {
|
||||
val destArray = dest.reinterpret<ByteVar>().ptr
|
||||
for (index in 0 .. length - 1) {
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
destArray[index] = source[index]
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun getCharArray(source: NativePointed, dest: CharArray, length: Int) {
|
||||
val sourceArray = source.reinterpret<ShortVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
dest[index] = sourceArray[index].toChar()
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun putCharArray(source: CharArray, dest: NativePointed, length: Int) {
|
||||
val destArray = dest.reinterpret<ShortVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
destArray[index] = source[index].toShort()
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun zeroMemory(dest: NativePointed, length: Int): Unit {
|
||||
val destArray = dest.reinterpret<ByteVar>().ptr
|
||||
for (index in 0 .. length - 1) {
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
destArray[index] = 0
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
@@ -60,6 +60,8 @@ class StubGenerator(
|
||||
"true", "try", "typealias", "val", "var", "when", "while"
|
||||
)
|
||||
|
||||
val platformWStringTypes = setOf("LPCWSTR")
|
||||
|
||||
/**
|
||||
* For this identifier constructs the string to be parsed by Kotlin as `SimpleName`
|
||||
* defined [here](https://kotlinlang.org/docs/reference/grammar.html#SimpleName).
|
||||
@@ -516,12 +518,24 @@ class StubGenerator(
|
||||
return pointeeType
|
||||
}
|
||||
|
||||
private fun Type.isAliasOf(names: Set<String>): Boolean {
|
||||
var type = this
|
||||
while (type is Typedef) {
|
||||
if (names.contains(type.def.name)) return true
|
||||
type = type.def.aliased
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun representCFunctionParameterAsString(type: Type): Boolean {
|
||||
val unwrappedType = type.unwrapTypedefs()
|
||||
return unwrappedType is PointerType && unwrappedType.pointeeIsConst &&
|
||||
unwrappedType.pointeeType.unwrapTypedefs() == CharType
|
||||
}
|
||||
|
||||
// We take this approach as generic 'const short*' shall not be used as String.
|
||||
fun representCFunctionParameterAsWString(type: Type)= type.isAliasOf(platformWStringTypes)
|
||||
|
||||
fun getCFunctionParamBinding(type: Type): OutValueBinding {
|
||||
if (representCFunctionParameterAsString(type)) {
|
||||
return OutValueBinding(
|
||||
@@ -532,6 +546,15 @@ class StubGenerator(
|
||||
)
|
||||
}
|
||||
|
||||
if (representCFunctionParameterAsWString(type)) {
|
||||
return OutValueBinding(
|
||||
kotlinType = "String?", // TODO: mention the C type (e.g. with annotation).
|
||||
kotlinConv = { name -> "$name?.wcstr?.getPointer(memScope).rawValue" },
|
||||
memScoped = true,
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
)
|
||||
}
|
||||
|
||||
representCFunctionParameterAsValuesRef(type)?.let {
|
||||
val pointeeMirror = mirror(it)
|
||||
return OutValueBinding(
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import win32.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
MessageBoxW(null, "Konan говорит:\nЗДРАВСТВУЙ МИР!\n",
|
||||
"Заголовок окна", MB_YESNOCANCEL or MB_ICONQUESTION)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
setlocal
|
||||
set DIR=.
|
||||
set "PATH=..\..\dist\bin;..\..\bin;%PATH%"
|
||||
if "%TARGET%" == "" set TARGET=mingw
|
||||
|
||||
set "LFLAGS=-Wl,--subsystem,windows"
|
||||
|
||||
call cinterop -def "%DIR%\win32.def" -target "%TARGET%" -o win32 || exit /b
|
||||
call konanc -target "%TARGET%" "%DIR%\MessageBox.kt" -library win32 -linkerOpts "%LFLAGS%" -opt -o MessageBox || exit /b
|
||||
|
||||
copy MessageBox.kexe MessageBox.exe
|
||||
@@ -0,0 +1,3 @@
|
||||
headers = windows.h
|
||||
headerFilter = win**.h
|
||||
compilerOpts = -DUNICODE -Wno-incompatible-pointer-types -Wno-deprecated-declarations
|
||||
Reference in New Issue
Block a user