KT-28102 fixes (#2325)
* Fix support for C functions returning structs with const fields #KT-28065 Fixed * Improve support for const C globals Workaround the case when libclang improperly reports types of const variables as non-const Partially fixes KT-28102 * Improve support for C functions overloaded by macros Don't erase pointers to void* in some cases when generating C stubs Partially fixes KT-28102
This commit is contained in:
committed by
Nikolay Igotti
parent
77be16c834
commit
471771e8b8
+10
-10
@@ -32,6 +32,9 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
|
|||||||
"&${global.name}"
|
"&${global.name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val setterStub = object : NativeBacked {}
|
||||||
|
|
||||||
val header: String
|
val header: String
|
||||||
val getter: KotlinExpression
|
val getter: KotlinExpression
|
||||||
val setter: KotlinExpression?
|
val setter: KotlinExpression?
|
||||||
@@ -66,14 +69,14 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
|
|||||||
val bridgedValue = BridgeTypedKotlinValue(mirror.info.bridgedType, mirror.info.argToBridged("value"))
|
val bridgedValue = BridgeTypedKotlinValue(mirror.info.bridgedType, mirror.info.argToBridged("value"))
|
||||||
|
|
||||||
stubGenerator.simpleBridgeGenerator.kotlinToNative(
|
stubGenerator.simpleBridgeGenerator.kotlinToNative(
|
||||||
nativeBacked = this,
|
nativeBacked = setterStub,
|
||||||
returnType = BridgedType.VOID,
|
returnType = BridgedType.VOID,
|
||||||
kotlinValues = listOf(bridgedValue)
|
kotlinValues = listOf(bridgedValue)
|
||||||
) { nativeValues ->
|
) { nativeValues ->
|
||||||
out("${global.name} = ${mirror.info.cFromBridged(
|
out("${global.name} = ${mirror.info.cFromBridged(
|
||||||
nativeValues.single(),
|
nativeValues.single(),
|
||||||
scope,
|
scope,
|
||||||
nativeBacked = this@GlobalVariableStub
|
nativeBacked = setterStub
|
||||||
)};")
|
)};")
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
@@ -90,8 +93,6 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
|
|||||||
}
|
}
|
||||||
|
|
||||||
header = buildString {
|
header = buildString {
|
||||||
append(if (setter != null) "var" else "val")
|
|
||||||
append(" ")
|
|
||||||
append(getDeclarationName(kotlinScope, global.name))
|
append(getDeclarationName(kotlinScope, global.name))
|
||||||
append(": ")
|
append(": ")
|
||||||
append(kotlinType.render(kotlinScope))
|
append(kotlinType.render(kotlinScope))
|
||||||
@@ -105,18 +106,17 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
|
|||||||
override fun generate(context: StubGenerationContext): Sequence<String> {
|
override fun generate(context: StubGenerationContext): Sequence<String> {
|
||||||
val lines = mutableListOf<String>()
|
val lines = mutableListOf<String>()
|
||||||
if (context.nativeBridges.isSupported(this)) {
|
if (context.nativeBridges.isSupported(this)) {
|
||||||
lines.add(header)
|
val mutable = setter != null && context.nativeBridges.isSupported(setterStub)
|
||||||
|
val kind = if (mutable) "var" else "val"
|
||||||
|
lines.add("$kind $header")
|
||||||
lines.add(" get() = $getter")
|
lines.add(" get() = $getter")
|
||||||
if (setter != null) {
|
if (mutable) {
|
||||||
lines.add(" set(value) { $setter }")
|
lines.add(" set(value) { $setter }")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lines.add(annotationForUnableToImport)
|
lines.add(annotationForUnableToImport)
|
||||||
lines.add(header)
|
lines.add("val $header")
|
||||||
lines.add(" get() = TODO()")
|
lines.add(" get() = TODO()")
|
||||||
if (setter != null) {
|
|
||||||
lines.add(" set(value) = TODO()")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.asSequence()
|
return lines.asSequence()
|
||||||
|
|||||||
+4
-1
@@ -94,7 +94,10 @@ class MappingBridgeGeneratorImpl(
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
is RecordType -> {
|
is RecordType -> {
|
||||||
out("*(${unwrappedReturnType.decl.spelling}*)${bridgeNativeValues.last()} = $nativeResult;")
|
val kniStructResult = "kniStructResult"
|
||||||
|
|
||||||
|
out("${unwrappedReturnType.decl.spelling} $kniStructResult = $nativeResult;")
|
||||||
|
out("memcpy(${bridgeNativeValues.last()}, &$kniStructResult, sizeof($kniStructResult));")
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
+5
-5
@@ -188,7 +188,7 @@ sealed class TypeInfo {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Pointer(val pointee: KotlinType) : TypeInfo() {
|
class Pointer(val pointee: KotlinType, val cPointee: Type) : TypeInfo() {
|
||||||
override fun argToBridged(expr: String) = "$expr.rawValue"
|
override fun argToBridged(expr: String) = "$expr.rawValue"
|
||||||
|
|
||||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||||
@@ -198,7 +198,7 @@ sealed class TypeInfo {
|
|||||||
get() = BridgedType.NATIVE_PTR
|
get() = BridgedType.NATIVE_PTR
|
||||||
|
|
||||||
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
||||||
"(void*)$expr" // Note: required for JVM
|
"(${getPointerTypeStringRepresentation(cPointee)})$expr"
|
||||||
|
|
||||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.cPointerVarOf.typeWith(valueType)
|
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.cPointerVarOf.typeWith(valueType)
|
||||||
}
|
}
|
||||||
@@ -423,13 +423,13 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
|||||||
val pointeeType = type.pointeeType
|
val pointeeType = type.pointeeType
|
||||||
val unwrappedPointeeType = pointeeType.unwrapTypedefs()
|
val unwrappedPointeeType = pointeeType.unwrapTypedefs()
|
||||||
if (unwrappedPointeeType is VoidType) {
|
if (unwrappedPointeeType is VoidType) {
|
||||||
val info = TypeInfo.Pointer(KotlinTypes.cOpaque)
|
val info = TypeInfo.Pointer(KotlinTypes.cOpaque, pointeeType)
|
||||||
TypeMirror.ByValue(KotlinTypes.cOpaquePointerVar, info, KotlinTypes.cOpaquePointer)
|
TypeMirror.ByValue(KotlinTypes.cOpaquePointerVar, info, KotlinTypes.cOpaquePointer)
|
||||||
} else if (unwrappedPointeeType is ArrayType) {
|
} else if (unwrappedPointeeType is ArrayType) {
|
||||||
mirror(declarationMapper, pointeeType)
|
mirror(declarationMapper, pointeeType)
|
||||||
} else {
|
} else {
|
||||||
val pointeeMirror = mirror(declarationMapper, pointeeType)
|
val pointeeMirror = mirror(declarationMapper, pointeeType)
|
||||||
val info = TypeInfo.Pointer(pointeeMirror.pointedType)
|
val info = TypeInfo.Pointer(pointeeMirror.pointedType, pointeeType)
|
||||||
TypeMirror.ByValue(
|
TypeMirror.ByValue(
|
||||||
KotlinTypes.cPointerVar.typeWith(pointeeMirror.pointedType),
|
KotlinTypes.cPointerVar.typeWith(pointeeMirror.pointedType),
|
||||||
info,
|
info,
|
||||||
@@ -444,7 +444,7 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
|||||||
if (type.elemType.unwrapTypedefs() is ArrayType) {
|
if (type.elemType.unwrapTypedefs() is ArrayType) {
|
||||||
elemTypeMirror
|
elemTypeMirror
|
||||||
} else {
|
} else {
|
||||||
val info = TypeInfo.Pointer(elemTypeMirror.pointedType)
|
val info = TypeInfo.Pointer(elemTypeMirror.pointedType, type.elemType)
|
||||||
TypeMirror.ByValue(
|
TypeMirror.ByValue(
|
||||||
KotlinTypes.cArrayPointerVar.typeWith(elemTypeMirror.pointedType),
|
KotlinTypes.cArrayPointerVar.typeWith(elemTypeMirror.pointedType),
|
||||||
info,
|
info,
|
||||||
|
|||||||
+21
@@ -21,6 +21,9 @@ import org.jetbrains.kotlin.native.interop.indexer.*
|
|||||||
val EnumDef.isAnonymous: Boolean
|
val EnumDef.isAnonymous: Boolean
|
||||||
get() = spelling.contains("(anonymous ") // TODO: it is a hack
|
get() = spelling.contains("(anonymous ") // TODO: it is a hack
|
||||||
|
|
||||||
|
val StructDecl.isAnonymous: Boolean
|
||||||
|
get() = spelling.contains("(anonymous ") // TODO: it is a hack
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the expression which could be used for this type in C code.
|
* Returns the expression which could be used for this type in C code.
|
||||||
* Note: the resulting string doesn't exactly represent this type, but it is enough for current purposes.
|
* Note: the resulting string doesn't exactly represent this type, but it is enough for current purposes.
|
||||||
@@ -57,6 +60,24 @@ fun Type.getStringRepresentation(): String = when (this) {
|
|||||||
else -> throw kotlin.NotImplementedError()
|
else -> throw kotlin.NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPointerTypeStringRepresentation(pointee: Type): String =
|
||||||
|
(getStringRepresentationOfPointee(pointee) ?: "void") + "*"
|
||||||
|
|
||||||
|
private fun getStringRepresentationOfPointee(type: Type): String? {
|
||||||
|
val unwrapped = type.unwrapTypedefs()
|
||||||
|
|
||||||
|
return when (unwrapped) {
|
||||||
|
is PrimitiveType -> unwrapped.getStringRepresentation()
|
||||||
|
is PointerType -> getStringRepresentationOfPointee(unwrapped.pointeeType)?.plus("*")
|
||||||
|
is RecordType -> if (unwrapped.decl.isAnonymous || unwrapped.decl.spelling == "struct __va_list_tag") {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
unwrapped.decl.spelling
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val ObjCQualifiedPointer.protocolQualifier: String
|
private val ObjCQualifiedPointer.protocolQualifier: String
|
||||||
get() = if (this.protocols.isEmpty()) "" else " <${protocols.joinToString { it.name }}>"
|
get() = if (this.protocols.isEmpty()) "" else " <${protocols.joinToString { it.name }}>"
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -74,9 +74,6 @@ class StubGenerator(
|
|||||||
typedefNames.toSet()
|
typedefNames.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
val StructDecl.isAnonymous: Boolean
|
|
||||||
get() = spelling.contains("(anonymous ") // TODO: it is a hack
|
|
||||||
|
|
||||||
val anonymousStructKotlinNames = mutableMapOf<StructDecl, String>()
|
val anonymousStructKotlinNames = mutableMapOf<StructDecl, String>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -981,6 +978,7 @@ class StubGenerator(
|
|||||||
val libraryForCStubs = configuration.library.copy(
|
val libraryForCStubs = configuration.library.copy(
|
||||||
includes = mutableListOf<String>().apply {
|
includes = mutableListOf<String>().apply {
|
||||||
add("stdint.h")
|
add("stdint.h")
|
||||||
|
add("string.h")
|
||||||
if (platform == KotlinPlatform.JVM) {
|
if (platform == KotlinPlatform.JVM) {
|
||||||
add("jni.h")
|
add("jni.h")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2775,6 +2775,10 @@ kotlinNativeInterop {
|
|||||||
defFile 'interop/basics/cunsupported.def'
|
defFile 'interop/basics/cunsupported.def'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cstructs {
|
||||||
|
defFile 'interop/basics/cstructs.def'
|
||||||
|
}
|
||||||
|
|
||||||
if (isMac()) {
|
if (isMac()) {
|
||||||
objcSmoke {
|
objcSmoke {
|
||||||
defFile 'interop/objc/objcSmoke.def'
|
defFile 'interop/objc/objcSmoke.def'
|
||||||
@@ -2868,6 +2872,12 @@ task interop_unsupported(type: RunInteropKonanTest) {
|
|||||||
interop = 'cunsupported'
|
interop = 'cunsupported'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task interop_structs(type: RunInteropKonanTest) {
|
||||||
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
|
source = "interop/basics/structs.kt"
|
||||||
|
interop = 'cstructs'
|
||||||
|
}
|
||||||
|
|
||||||
task interop_echo_server(type: RunInteropKonanTest) {
|
task interop_echo_server(type: RunInteropKonanTest) {
|
||||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
if (!isMac()) {
|
if (!isMac()) {
|
||||||
|
|||||||
@@ -26,3 +26,8 @@ MyInt g7;
|
|||||||
// Test property name mangling:
|
// Test property name mangling:
|
||||||
struct g1 {};
|
struct g1 {};
|
||||||
struct g1_ {};
|
struct g1_ {};
|
||||||
|
|
||||||
|
typedef void* voidptr;
|
||||||
|
_Pragma("clang assume_nonnull begin")
|
||||||
|
const voidptr g8 = 0x1, g9 = 0x2;
|
||||||
|
_Pragma("clang assume_nonnull end")
|
||||||
@@ -58,3 +58,6 @@ int global_var = 5;
|
|||||||
#define BAD1 bar
|
#define BAD1 bar
|
||||||
#define BAD2 5;
|
#define BAD2 5;
|
||||||
#define BAD3 { foo(); }
|
#define BAD3 { foo(); }
|
||||||
|
|
||||||
|
void increment(int* counter);
|
||||||
|
#define increment(counter) { (*(counter))++; }
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
// KT-28065
|
||||||
|
struct StructWithConstFields {
|
||||||
|
int x;
|
||||||
|
const int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StructWithConstFields getStructWithConstFields() {
|
||||||
|
struct StructWithConstFields result = { 111, 222 };
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -27,4 +27,7 @@ fun main(args: Array<String>) {
|
|||||||
assert(g5[0] == 16)
|
assert(g5[0] == 16)
|
||||||
|
|
||||||
assert(g6 == g3.ptr)
|
assert(g6 == g3.ptr)
|
||||||
|
|
||||||
|
assert(g8.toLong() == 0x1L)
|
||||||
|
assert(g9.toLong() == 0x2L)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,4 +38,11 @@ fun main(args: Array<String>) {
|
|||||||
assertEquals(42, INT_CALL)
|
assertEquals(42, INT_CALL)
|
||||||
assertEquals(84, CALL_SUM)
|
assertEquals(84, CALL_SUM)
|
||||||
assertEquals(5, GLOBAL_VAR)
|
assertEquals(5, GLOBAL_VAR)
|
||||||
|
|
||||||
|
memScoped {
|
||||||
|
val counter = alloc<IntVar>()
|
||||||
|
counter.value = 42
|
||||||
|
increment(counter.ptr)
|
||||||
|
assertEquals(43, counter.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import cstructs.*
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
getStructWithConstFields().useContents {
|
||||||
|
assertEquals(111, x)
|
||||||
|
assertEquals(222, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user