Support importing string literal macros with cinterop
This commit is contained in:
committed by
SvyatoslavScherbina
parent
4335f891b7
commit
e035ac1022
+41
-45
@@ -110,10 +110,7 @@ private fun reparseWithCodeSnippet(library: NativeLibrary,
|
||||
val codeSnippetLines = when (library.language) {
|
||||
// Note: __auto_type is a GNU extension which is supported by clang.
|
||||
Language.C, Language.OBJECTIVE_C -> listOf(
|
||||
"const __auto_type KNI_INDEXER_VARIABLE = $name;",
|
||||
// Clang evaluate API doesn't provide a way to get a `long long` value yet;
|
||||
// so extract such values from the enum declaration:
|
||||
"enum { KNI_INDEXER_ENUM_CONST = (long long)KNI_INDEXER_VARIABLE };"
|
||||
"const __auto_type KNI_INDEXER_VARIABLE = $name;"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -133,53 +130,27 @@ private fun processCodeSnippet(
|
||||
): ConstantDef? {
|
||||
|
||||
var state = VisitorState.EXPECT_VARIABLE
|
||||
var evalResultKind: CXEvalResultKind? = null
|
||||
var type: Type? = null
|
||||
var longValue: Long? = null
|
||||
var doubleValue: Double? = null
|
||||
var evalResultOrNull: CXEvalResult? = null
|
||||
var typeOrNull: Type? = null
|
||||
|
||||
val visitor: CursorVisitor = { cursor, _ ->
|
||||
val kind = cursor.kind
|
||||
when {
|
||||
state == VisitorState.EXPECT_VARIABLE && kind == CXCursorKind.CXCursor_VarDecl -> {
|
||||
|
||||
val evalResult = clang_Cursor_Evaluate(cursor)
|
||||
|
||||
if (evalResult != null) {
|
||||
evalResultOrNull = evalResult
|
||||
state = VisitorState.EXPECT_VARIABLE_VALUE
|
||||
try {
|
||||
evalResultKind = clang_EvalResult_getKind(evalResult)
|
||||
if (evalResultKind == CXEvalResultKind.CXEval_Float) {
|
||||
doubleValue = clang_EvalResult_getAsDouble(evalResult)
|
||||
}
|
||||
} finally {
|
||||
clang_EvalResult_dispose(evalResult)
|
||||
}
|
||||
} else {
|
||||
state = VisitorState.INVALID
|
||||
}
|
||||
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
|
||||
state == VisitorState.EXPECT_VARIABLE_VALUE && clang_isExpression(kind) != 0 -> {
|
||||
state = VisitorState.EXPECT_ENUM
|
||||
type = typeConverter(clang_getCursorType(cursor))
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
state == VisitorState.EXPECT_ENUM && kind == CXCursorKind.CXCursor_EnumDecl -> {
|
||||
state = VisitorState.EXPECT_ENUM_CONST
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
|
||||
state == VisitorState.EXPECT_ENUM_CONST && kind == CXCursorKind.CXCursor_EnumConstantDecl -> {
|
||||
state = VisitorState.EXPECT_ENUM_CONST_VALUE
|
||||
if (evalResultKind == CXEvalResultKind.CXEval_Int) {
|
||||
longValue = clang_getEnumConstantDeclValue(cursor)
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
|
||||
state == VisitorState.EXPECT_ENUM_CONST_VALUE && clang_isExpression(kind) != 0 -> {
|
||||
typeOrNull = typeConverter(clang_getCursorType(cursor))
|
||||
state = VisitorState.EXPECT_END
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
@@ -191,21 +162,46 @@ private fun processCodeSnippet(
|
||||
}
|
||||
}
|
||||
|
||||
visitChildren(translationUnit, visitor)
|
||||
try {
|
||||
visitChildren(translationUnit, visitor)
|
||||
|
||||
if (state != VisitorState.EXPECT_END) {
|
||||
return null
|
||||
}
|
||||
return when (evalResultKind!!) {
|
||||
CXEvalResultKind.CXEval_Int -> IntegerConstantDef(name, type!!, longValue!!)
|
||||
CXEvalResultKind.CXEval_Float -> FloatingConstantDef(name, type!!, doubleValue!!)
|
||||
else -> null
|
||||
if (state != VisitorState.EXPECT_END) {
|
||||
return null
|
||||
}
|
||||
|
||||
val evalResult = evalResultOrNull!!
|
||||
val type = typeOrNull!!
|
||||
val evalResultKind = clang_EvalResult_getKind(evalResult)
|
||||
|
||||
return when (evalResultKind) {
|
||||
CXEvalResultKind.CXEval_Int ->
|
||||
IntegerConstantDef(name, type, clang_EvalResult_getAsLongLong(evalResult))
|
||||
|
||||
CXEvalResultKind.CXEval_Float ->
|
||||
FloatingConstantDef(name, type, clang_EvalResult_getAsDouble(evalResult))
|
||||
|
||||
CXEvalResultKind.CXEval_CFStr,
|
||||
CXEvalResultKind.CXEval_ObjCStrLiteral,
|
||||
CXEvalResultKind.CXEval_StrLiteral ->
|
||||
if (evalResultKind == CXEvalResultKind.CXEval_StrLiteral && !type.canonicalIsPointerToChar()) {
|
||||
// libclang doesn't seem to support wide string literals properly in this API;
|
||||
// thus disable wide literals here:
|
||||
null
|
||||
} else {
|
||||
StringConstantDef(name, type, clang_EvalResult_getAsStr(evalResult)!!.toKString())
|
||||
}
|
||||
|
||||
CXEvalResultKind.CXEval_Other,
|
||||
CXEvalResultKind.CXEval_UnExposed -> null
|
||||
}
|
||||
|
||||
} finally {
|
||||
evalResultOrNull?.let { clang_EvalResult_dispose(it) }
|
||||
}
|
||||
}
|
||||
|
||||
enum class VisitorState {
|
||||
EXPECT_VARIABLE, EXPECT_VARIABLE_VALUE,
|
||||
EXPECT_ENUM, EXPECT_ENUM_CONST, EXPECT_ENUM_CONST_VALUE,
|
||||
EXPECT_END, INVALID
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -192,6 +192,7 @@ class TypedefDef(val aliased: Type, val name: String, override val location: Loc
|
||||
abstract class ConstantDef(val name: String, val type: Type)
|
||||
class IntegerConstantDef(name: String, type: Type, val value: Long) : ConstantDef(name, type)
|
||||
class FloatingConstantDef(name: String, type: Type, val value: Double) : ConstantDef(name, type)
|
||||
class StringConstantDef(name: String, type: Type, val value: String) : ConstantDef(name, type)
|
||||
|
||||
class GlobalDecl(val name: String, val type: Type, val isConst: Boolean)
|
||||
|
||||
|
||||
@@ -599,3 +599,14 @@ fun createVfsOverlayFile(virtualPathToReal: Map<Path, Path>): Path {
|
||||
deleteOnExit()
|
||||
}.toPath()
|
||||
}
|
||||
|
||||
tailrec fun Type.unwrapTypedefs(): Type = if (this is Typedef) {
|
||||
this.def.aliased.unwrapTypedefs()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
fun Type.canonicalIsPointerToChar(): Boolean {
|
||||
val unwrappedType = this.unwrapTypedefs()
|
||||
return unwrappedType is PointerType && unwrappedType.pointeeType.unwrapTypedefs() == CharType
|
||||
}
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.native.interop.gen
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.StubGenerator
|
||||
import org.jetbrains.kotlin.native.interop.indexer.ArrayType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.GlobalDecl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
|
||||
class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : KotlinStub, NativeBacked {
|
||||
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.native.interop.gen
|
||||
import org.jetbrains.kotlin.native.interop.indexer.RecordType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.Type
|
||||
import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
|
||||
/**
|
||||
* The [MappingBridgeGenerator] implementation which uses [SimpleBridgeGenerator] as the backend and
|
||||
|
||||
-6
@@ -60,12 +60,6 @@ fun Type.getStringRepresentation(): String = when (this) {
|
||||
private val ObjCQualifiedPointer.protocolQualifier: String
|
||||
get() = if (this.protocols.isEmpty()) "" else " <${protocols.joinToString { it.name }}>"
|
||||
|
||||
tailrec fun Type.unwrapTypedefs(): Type = if (this is Typedef) {
|
||||
this.def.aliased.unwrapTypedefs()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
fun blockTypeStringRepresentation(type: ObjCBlockPointer): String {
|
||||
return buildString {
|
||||
append(type.returnType.getStringRepresentation())
|
||||
|
||||
+12
-1
@@ -761,13 +761,24 @@ class StubGenerator(
|
||||
val literal = when (constant) {
|
||||
is IntegerConstantDef -> integerLiteral(constant.type, constant.value) ?: return
|
||||
is FloatingConstantDef -> floatingLiteral(constant.type, constant.value) ?: return
|
||||
is StringConstantDef -> constant.value.quoteAsKotlinLiteral()
|
||||
else -> {
|
||||
// Not supported yet, ignore:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinType = mirror(constant.type).argType.render(kotlinFile)
|
||||
val kotlinType = when (constant) {
|
||||
is IntegerConstantDef,
|
||||
is FloatingConstantDef -> mirror(constant.type).argType
|
||||
|
||||
is StringConstantDef -> KotlinTypes.string
|
||||
|
||||
else -> {
|
||||
// Not supported yet, ignore:
|
||||
return
|
||||
}
|
||||
}.render(kotlinFile)
|
||||
|
||||
// TODO: improve value rendering.
|
||||
|
||||
|
||||
@@ -2350,6 +2350,10 @@ kotlinNativeInterop {
|
||||
defFile 'interop/basics/cfunptr.def'
|
||||
}
|
||||
|
||||
cmacros {
|
||||
defFile 'interop/basics/cmacros.def'
|
||||
}
|
||||
|
||||
if (isMac()) {
|
||||
objcSmoke {
|
||||
defFile 'interop/objc/objcSmoke.def'
|
||||
@@ -2425,6 +2429,12 @@ task interop_globals(type: RunInteropKonanTest) {
|
||||
interop = 'cglobals'
|
||||
}
|
||||
|
||||
task interop_macros(type: RunInteropKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
source = "interop/basics/macros.kt"
|
||||
interop = 'cmacros'
|
||||
}
|
||||
|
||||
task interop_echo_server(type: RunInteropKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
if (!isMac()) {
|
||||
@@ -2452,6 +2462,8 @@ if (isMac()) {
|
||||
"true\ntrue\n" +
|
||||
"Global string\nAnother global string\nnull\nglobal object\n" +
|
||||
"5\n" +
|
||||
"String macro\n" +
|
||||
"CFString macro\n" +
|
||||
"Deallocated\nDeallocated\n"
|
||||
|
||||
source = "interop/objc/smoke.kt"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
#define ZERO 0
|
||||
#define ONE 1
|
||||
#define MAX_LONG 9223372036854775807
|
||||
#define FOO_STRING "foo"
|
||||
// This one should be ignored:
|
||||
#define WIDE_FOO_STRING L"foo"
|
||||
#define FOURTY_TWO 42
|
||||
#define SEVENTEEN ((long long) 17)
|
||||
#define ONE_POINT_ZERO 1.0
|
||||
#define ONE_POINT_FIVE 1.5f
|
||||
|
||||
#define BAD1 bar
|
||||
#define BAD2 5;
|
||||
@@ -0,0 +1,19 @@
|
||||
import kotlin.test.*
|
||||
import cmacros.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
assertEquals("foo", FOO_STRING)
|
||||
assertEquals(0, ZERO)
|
||||
assertEquals(1, ONE)
|
||||
assertEquals(Long.MAX_VALUE, MAX_LONG)
|
||||
assertEquals(42, FOURTY_TWO)
|
||||
|
||||
val seventeen: Long = SEVENTEEN
|
||||
assertEquals(17L, seventeen)
|
||||
|
||||
val onePointFive: Float = ONE_POINT_FIVE
|
||||
val onePointZero: Double = ONE_POINT_ZERO
|
||||
|
||||
assertEquals(1.5f, onePointFive)
|
||||
assertEquals(1.0, onePointZero)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#import <objc/NSObject.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
@class Foo;
|
||||
|
||||
@@ -57,3 +58,6 @@ extern NSString* globalString;
|
||||
extern NSObject* globalObject;
|
||||
|
||||
int formatStringLength(NSString* format, ...);
|
||||
|
||||
#define STRING_MACRO @"String macro"
|
||||
#define CFSTRING_MACRO CFSTR("CFString macro")
|
||||
|
||||
@@ -62,6 +62,9 @@ fun run() {
|
||||
println(globalObject)
|
||||
|
||||
println(formatStringLength("%d %d", 42, 17))
|
||||
|
||||
println(STRING_MACRO)
|
||||
println(CFSTRING_MACRO)
|
||||
}
|
||||
|
||||
fun MutablePairProtocol.swap() {
|
||||
|
||||
Reference in New Issue
Block a user