[Kotlin/Native][Interop] Provide pure c wrappers over cpp for skia interop
This commit is contained in:
committed by
Alexander Gorshenev
parent
61825e9aec
commit
5f582ad28a
+212
-19
@@ -32,6 +32,8 @@ private class StructDefImpl(
|
||||
size, align, decl
|
||||
) {
|
||||
override val members = mutableListOf<StructMember>()
|
||||
override val methods = mutableListOf<FunctionDecl>()
|
||||
override val staticFields = mutableListOf<GlobalDecl>()
|
||||
}
|
||||
|
||||
private class EnumDefImpl(spelling: String, type: Type, override val location: Location) : EnumDef(spelling, type) {
|
||||
@@ -74,6 +76,30 @@ private class ObjCCategoryImpl(
|
||||
override val properties = mutableListOf<ObjCProperty>()
|
||||
}
|
||||
|
||||
|
||||
private fun getParentName(cursor: CValue<CXCursor>, pkg: List<String> = emptyList()) : String? { // }: List<String>? {
|
||||
// This doesn't work for anonymous C++ struct (such as typedef struct { void foo(); } TypeDefName) as well as anon namespace
|
||||
// In contrast, clang_getTypeSpelling return fully qualified name for struct & class (incl. typedef anon struct),
|
||||
// but does not help for anything elde such as template member, namespace etc
|
||||
// So, TODO Use ultimately clang_getTypeSpelling for CXType_Record (no traversing needed) and traverse up the whole hierarchy for anythiong else
|
||||
// Unfortunately, this won't work too for variable decl with anon type like that: ''struct { void foo(); } x;''
|
||||
// while function is accessible as x.foo()
|
||||
|
||||
// skip this (zero) level:
|
||||
|
||||
val parent = clang_getCursorSemanticParent(cursor)
|
||||
if (clang_isDeclaration(parent.kind) == 0)
|
||||
return if (pkg.isNotEmpty()) pkg.joinToString("::") else null
|
||||
|
||||
val type = clang_getCursorType(parent)
|
||||
if (type.kind == CXTypeKind.CXType_Record)
|
||||
return clang_getTypeSpelling(type).convertAndDispose()
|
||||
|
||||
val nextPkg = if (parent.kind == CXCursorKind.CXCursor_Namespace) listOf(parent.spelling) + pkg else pkg
|
||||
return getParentName(parent, nextPkg)
|
||||
}
|
||||
|
||||
|
||||
internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean = false) : NativeIndex() {
|
||||
|
||||
private sealed class DeclarationID {
|
||||
@@ -136,10 +162,10 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
override val typedefs get() = typedefRegistry.included
|
||||
private val typedefRegistry = TypeDeclarationRegistry<TypedefDef>()
|
||||
|
||||
private val functionById = mutableMapOf<DeclarationID, FunctionDecl>()
|
||||
private val functionById = mutableMapOf<DeclarationID, FunctionDecl?>()
|
||||
|
||||
override val functions: Collection<FunctionDecl>
|
||||
get() = functionById.values
|
||||
get() = functionById.values.filterNotNull()
|
||||
|
||||
override val macroConstants = mutableListOf<ConstantDef>()
|
||||
override val wrappedMacros = mutableListOf<WrappedMacroDef>()
|
||||
@@ -191,6 +217,43 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
return StructDeclImpl(typeSpelling, getLocation(cursor))
|
||||
}
|
||||
|
||||
private fun visitClass(cursor: CValue<CXCursor>, clazz: StructDefImpl) {
|
||||
|
||||
// TODO skip method (function) when encounter UnsupportedType in params or ret value. Otherwise all class methods will be lost due to exception (?)
|
||||
visitChildren(cursor) { cursor, _ ->
|
||||
if (cursor.isPublic) {
|
||||
// TODO If a kotlin class is _conceptually_ derived from its c++ counterpart, then it shall be able to override virtual private and access protected
|
||||
when (cursor.kind) {
|
||||
CXCursorKind.CXCursor_CXXMethod -> {
|
||||
val isOperatorFunction = (clang_getCursorSpelling(cursor).convertAndDispose().take(8) == "operator")
|
||||
// operators are Not Implemented Yet
|
||||
if (!isOperatorFunction) {
|
||||
if (clang_isFunctionTypeVariadic(clang_getCursorType(cursor)) == 0) // FIXME why it doesn't work???
|
||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
||||
}
|
||||
}
|
||||
CXCursorKind.CXCursor_Constructor ->
|
||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
||||
CXCursorKind.CXCursor_Destructor ->
|
||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
||||
|
||||
CXCursorKind.CXCursor_VarDecl -> {
|
||||
clazz.staticFields.add(GlobalDecl(
|
||||
name =getCursorSpelling(cursor),
|
||||
type = convertCursorType(cursor),
|
||||
isConst = clang_isConstQualifiedType(clang_getCursorType(cursor)) != 0,
|
||||
parentName = clazz.decl.spelling)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStructDef(structDecl: StructDeclImpl, cursor: CValue<CXCursor>) {
|
||||
val type = clang_getCursorType(cursor)
|
||||
|
||||
@@ -205,17 +268,19 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
when (cursor.kind) {
|
||||
CXCursorKind.CXCursor_UnionDecl -> StructDef.Kind.UNION
|
||||
CXCursorKind.CXCursor_StructDecl -> StructDef.Kind.STRUCT
|
||||
CXCursorKind.CXCursor_ClassDecl -> StructDef.Kind.CLASS
|
||||
else -> error(cursor.kind)
|
||||
}
|
||||
)
|
||||
|
||||
structDef.members += fields
|
||||
visitClass(cursor, structDef)
|
||||
|
||||
structDecl.def = structDef
|
||||
}
|
||||
|
||||
private fun addDeclaredFields(result: MutableList<StructMember>, structType: CValue<CXType>, containerType: CValue<CXType>) {
|
||||
getFields(containerType).forEach { fieldCursor ->
|
||||
getFields(containerType).filter { it.isPublic }.forEach { fieldCursor ->
|
||||
val name = getCursorSpelling(fieldCursor)
|
||||
if (name.isNotEmpty()) {
|
||||
val fieldType = convertCursorType(fieldCursor)
|
||||
@@ -465,7 +530,7 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
convertType(clang_getCursorType(cursor), clang_getDeclTypeAttributes(cursor))
|
||||
|
||||
private inline fun objCType(supplier: () -> ObjCPointer) = when (library.language) {
|
||||
Language.C -> UnsupportedType
|
||||
Language.C, Language.CPP -> UnsupportedType
|
||||
Language.OBJECTIVE_C -> supplier()
|
||||
}
|
||||
|
||||
@@ -582,7 +647,7 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type)))
|
||||
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type)))
|
||||
|
||||
CXType_Pointer -> {
|
||||
CXType_Pointer, CXType_LValueReference -> {
|
||||
val pointeeType = clang_getPointeeType(type)
|
||||
val pointeeIsConst =
|
||||
(clang_isConstQualifiedType(clang_getCanonicalType(pointeeType)) != 0)
|
||||
@@ -590,7 +655,9 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
val convertedPointeeType = convertType(pointeeType)
|
||||
PointerType(
|
||||
if (convertedPointeeType == UnsupportedType) VoidType else convertedPointeeType,
|
||||
pointeeIsConst = pointeeIsConst
|
||||
pointeeIsConst = pointeeIsConst,
|
||||
isLVReference = (kind == CXType_LValueReference),
|
||||
spelling = type.name
|
||||
)
|
||||
}
|
||||
|
||||
@@ -794,23 +861,43 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
return
|
||||
}
|
||||
|
||||
val namespace: Namespace? =
|
||||
// semantic parent of any namespace member is always namespace itself (no type aliases etc)
|
||||
if (info.semanticContainer!!.pointed.cursor.kind == CXCursorKind.CXCursor_Namespace) {
|
||||
val parent = info.semanticContainer!!.pointed.cursor.readValue()
|
||||
Namespace(getCursorSpelling(parent), getParentName(parent))
|
||||
} else null
|
||||
|
||||
if (!cursor.isRecursivelyPublic()) {
|
||||
// c++ : skip anon namespaces, static functions and variables and private inner classes
|
||||
return
|
||||
}
|
||||
/**
|
||||
* TODO It may be better to look at CXTypeKind instead of CXIdxEntity to distinguish C++ classes from templates
|
||||
* C++ templates are also CXIdxEntity_CXXClass but CXCursor_ClassTemplate,
|
||||
* while C++ class is CXCursor_ClassDecl
|
||||
* The same for CXCursor_FunctionDecl vs CXCursor_FunctionTemplate
|
||||
*/
|
||||
when (kind) {
|
||||
CXIdxEntity_Struct, CXIdxEntity_Union -> {
|
||||
CXIdxEntity_Struct, CXIdxEntity_Union, CXIdxEntity_CXXClass -> {
|
||||
if (entityName == null) {
|
||||
// Skip anonymous struct.
|
||||
// (It gets included anyway if used as a named field type).
|
||||
} else {
|
||||
getStructDeclAt(cursor)
|
||||
if (library.language != Language.CPP) {
|
||||
getStructDeclAt(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CXIdxEntity_Typedef -> {
|
||||
CXIdxEntity_Typedef, CXIdxEntity_CXXTypeAlias -> {
|
||||
val type = clang_getCursorType(cursor)
|
||||
getTypedef(type)
|
||||
}
|
||||
|
||||
CXIdxEntity_Function -> {
|
||||
if (isSuitableFunction(cursor)) {
|
||||
if (isSuitableFunction(cursor)
|
||||
&& library.language != Language.CPP) {
|
||||
functionById.getOrPut(getDeclarationId(cursor)) {
|
||||
getFunction(cursor)
|
||||
}
|
||||
@@ -822,13 +909,15 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
}
|
||||
|
||||
CXIdxEntity_Variable -> {
|
||||
if (info.semanticContainer!!.pointed.cursor.kind == CXCursorKind.CXCursor_TranslationUnit) {
|
||||
// Top-level variable.
|
||||
val parentKind = info.semanticContainer!!.pointed.cursor.kind
|
||||
if (parentKind == CXCursorKind.CXCursor_TranslationUnit || parentKind == CXCursorKind.CXCursor_Namespace) {
|
||||
// Top-level or namespace member. Skip class static members - they are loaded by visitClass
|
||||
globalById.getOrPut(getDeclarationId(cursor)) {
|
||||
GlobalDecl(
|
||||
name = entityName!!,
|
||||
type = convertCursorType(cursor),
|
||||
isConst = clang_isConstQualifiedType(clang_getCursorType(cursor)) != 0
|
||||
isConst = clang_isConstQualifiedType(clang_getCursorType(cursor)) != 0,
|
||||
parentName = getParentName(cursor)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -880,6 +969,49 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
}
|
||||
}
|
||||
|
||||
fun indexDeclaration(cursor: CValue<CXCursor>): Unit {
|
||||
if (!library.includesDeclaration(cursor)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (cursor.isRecursivelyPublic()) {
|
||||
when (cursor.kind) {
|
||||
|
||||
CXCursorKind.CXCursor_ClassDecl, CXCursorKind.CXCursor_StructDecl, CXCursorKind.CXCursor_UnionDecl -> {
|
||||
if (library.language == Language.CPP) {
|
||||
if (cursor.spelling.isEmpty()) {
|
||||
// Skip anonymous struct.
|
||||
// (It gets included anyway if used as a named field type).
|
||||
} else {
|
||||
getStructDeclAt(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CXCursorKind.CXCursor_FunctionDecl -> {
|
||||
if (library.language == Language.CPP) {
|
||||
indexCxxFunction(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun indexCxxFunction(cursor: CValue<CXCursor>) {
|
||||
if (isSuitableFunction(cursor)) {
|
||||
if (getCursorSpelling(cursor).take(8) == "operator") {
|
||||
// not implemented yet
|
||||
} else {
|
||||
functionById.getOrPut(getDeclarationId(cursor)) {
|
||||
getFunction(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun indexObjCClass(cursor: CValue<CXCursor>) {
|
||||
if (isAvailable(cursor)) {
|
||||
getObjCClassAt(cursor)
|
||||
@@ -892,14 +1024,19 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFunction(cursor: CValue<CXCursor>): FunctionDecl {
|
||||
val name = clang_getCursorSpelling(cursor).convertAndDispose()
|
||||
val returnType = convertType(clang_getCursorResultType(cursor), clang_getCursorResultTypeAttributes(cursor))
|
||||
private fun getFunction(cursor: CValue<CXCursor>, receiver: StructDecl? = null): FunctionDecl? {
|
||||
if (!isFuncDeclEligible(cursor)) {
|
||||
log("Skip function ${clang_getCursorSpelling(cursor).convertAndDispose()}")
|
||||
return null
|
||||
}
|
||||
var name = clang_getCursorSpelling(cursor).convertAndDispose()
|
||||
var returnType = convertType(clang_getCursorResultType(cursor), clang_getCursorResultTypeAttributes(cursor))
|
||||
|
||||
val parameters = getFunctionParameters(cursor)
|
||||
val parameters = mutableListOf<Parameter>()
|
||||
parameters += getFunctionParameters(cursor)
|
||||
|
||||
val binaryName = when (library.language) {
|
||||
Language.C, Language.OBJECTIVE_C -> clang_Cursor_getMangling(cursor).convertAndDispose()
|
||||
Language.C, Language.CPP, Language.OBJECTIVE_C -> clang_Cursor_getMangling(cursor).convertAndDispose()
|
||||
}
|
||||
|
||||
val definitionCursor = clang_getCursorDefinition(cursor)
|
||||
@@ -907,7 +1044,40 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
|
||||
val isVararg = clang_Cursor_isVariadic(cursor) != 0
|
||||
|
||||
return FunctionDecl(name, parameters, returnType, binaryName, isDefined, isVararg)
|
||||
// TODO Do the following if clang_getCursorLanguage(cursor) == CXLanguageKind.CXLanguage_CPlusPlus ...
|
||||
val parentName = getParentName(cursor)
|
||||
val cxxMethodInfo = receiver?.let { CxxMethodInfo(
|
||||
PointerType(RecordType(receiver),
|
||||
clang_CXXMethod_isConst(cursor) != 0), // CXCursor_ConversionFunction has constness too
|
||||
when (cursor.kind) {
|
||||
CXCursorKind.CXCursor_Constructor -> {
|
||||
returnType = PointerType(RecordType(receiver))
|
||||
name = "__init__" // It is intended to init preallocated memory with placement new, so it is not "create" factory method. TODO One may want "create" method also.
|
||||
// Parameter type for placement new is void*, but I want to emphasize that memory block ahall have proper size and alignment
|
||||
parameters.add(0, Parameter("self", PointerType(RecordType(receiver)), false))
|
||||
CxxMethodKind.Constructor
|
||||
}
|
||||
CXCursorKind.CXCursor_Destructor -> {
|
||||
name = "__destroy__"
|
||||
parameters.add(0, Parameter("self", PointerType(RecordType(receiver)), false))
|
||||
CxxMethodKind.Destructor
|
||||
}
|
||||
// CXCursorKind.CXCursor_ConversionFunction -> ...
|
||||
CXCursorKind.CXCursor_CXXMethod ->
|
||||
if (clang_CXXMethod_isStatic(cursor) != 0) {
|
||||
CxxMethodKind.StaticMethod
|
||||
} else {
|
||||
parameters.add(0, Parameter("self",
|
||||
PointerType(RecordType(receiver), clang_CXXMethod_isConst(cursor) != 0),
|
||||
false))
|
||||
CxxMethodKind.InstanceMethod
|
||||
}
|
||||
else -> CxxMethodKind.None // Not implemented. Not expected, OK to assert (?)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return FunctionDecl(name, parameters, returnType, binaryName, isDefined, isVararg, parentName, cxxMethodInfo)
|
||||
}
|
||||
|
||||
private fun getObjCMethod(cursor: CValue<CXCursor>): ObjCMethod? {
|
||||
@@ -957,6 +1127,22 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
CXAvailabilityKind.CXAvailability_NotAccessible -> false
|
||||
}
|
||||
|
||||
// Skip functions which parameter or return type is TemplateRef
|
||||
private fun isFuncDeclEligible(cursor: CValue<CXCursor>): Boolean {
|
||||
|
||||
var ret = true
|
||||
visitChildren(cursor) { cursor, _ ->
|
||||
when (cursor.kind) {
|
||||
CXCursorKind.CXCursor_TemplateRef -> {
|
||||
ret = false
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
}
|
||||
else -> CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getFunctionParameters(cursor: CValue<CXCursor>): List<Parameter> {
|
||||
val argNum = clang_Cursor_getNumArguments(cursor)
|
||||
val args = (0..argNum - 1).map {
|
||||
@@ -1024,6 +1210,13 @@ private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH
|
||||
}
|
||||
})
|
||||
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
||||
if (getContainingFile(cursor) in headers) {
|
||||
nativeIndex.indexDeclaration(cursor)
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
||||
val file = getContainingFile(cursor)
|
||||
if (file in headers && nativeIndex.library.includesDeclaration(cursor)) {
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ private fun reparseWithCodeSnippets(library: CompilationWithPCH,
|
||||
|
||||
names.forEach { name ->
|
||||
val codeSnippetLines = when (library.language) {
|
||||
Language.C, Language.OBJECTIVE_C ->
|
||||
Language.C, Language.CPP, Language.OBJECTIVE_C ->
|
||||
listOf("void $CODE_SNIPPET_FUNCTION_NAME_PREFIX$name() {",
|
||||
" __auto_type KNI_INDEXER_VARIABLE_$name = $name;",
|
||||
"}")
|
||||
|
||||
+53
-4
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.native.interop.indexer
|
||||
|
||||
enum class Language(val sourceFileExtension: String) {
|
||||
C("c"),
|
||||
CPP("cpp"),
|
||||
OBJECTIVE_C("m")
|
||||
}
|
||||
|
||||
@@ -156,10 +157,12 @@ abstract class StructDecl(val spelling: String) : TypeDeclaration {
|
||||
abstract class StructDef(val size: Long, val align: Int, val decl: StructDecl) {
|
||||
|
||||
enum class Kind {
|
||||
STRUCT, UNION
|
||||
STRUCT, UNION, CLASS
|
||||
}
|
||||
|
||||
abstract val methods: List<FunctionDecl>
|
||||
abstract val members: List<StructMember>
|
||||
abstract val staticFields: List<GlobalDecl>
|
||||
abstract val kind: Kind
|
||||
|
||||
val fields: List<Field> get() = members.filterIsInstance<Field>()
|
||||
@@ -224,11 +227,53 @@ abstract class ObjCCategory(val name: String, val clazz: ObjCClass) : ObjCContai
|
||||
*/
|
||||
data class Parameter(val name: String?, val type: Type, val nsConsumed: Boolean)
|
||||
|
||||
|
||||
enum class CxxMethodKind {
|
||||
None, // not supported yet?
|
||||
Constructor,
|
||||
Destructor,
|
||||
StaticMethod,
|
||||
InstanceMethod // virtual or non-virtual instance member method (non-static)
|
||||
// do we need operators here?
|
||||
// do we need to distinguish virtual and non-virtual? Static? Final?
|
||||
}
|
||||
|
||||
/**
|
||||
* C++ class method, constructor or destructor details
|
||||
*/
|
||||
class CxxMethodInfo(val receiverType: PointerType, val kind: CxxMethodKind = CxxMethodKind.InstanceMethod)
|
||||
|
||||
fun CxxMethodInfo.isConst() : Boolean = receiverType.pointeeIsConst
|
||||
|
||||
|
||||
/**
|
||||
* C function declaration.
|
||||
*/
|
||||
class FunctionDecl(val name: String, val parameters: List<Parameter>, val returnType: Type, val binaryName: String,
|
||||
val isDefined: Boolean, val isVararg: Boolean)
|
||||
val isDefined: Boolean, val isVararg: Boolean,
|
||||
val parentName: String? = null, val cxxMethod: CxxMethodInfo? = null) {
|
||||
|
||||
val fullName: String = parentName?.let { "$parentName::$name" } ?: name
|
||||
|
||||
// C++ virtual or non-virtual instance member, i.e. has "this" receiver
|
||||
val isCxxInstanceMethod: Boolean = cxxMethod != null && cxxMethod.kind == CxxMethodKind.InstanceMethod
|
||||
|
||||
/**
|
||||
* C++ class or instance member function, i.e. any function in the scope of class/struct: method, static, ctor, dtor, cast operator, etc
|
||||
*/
|
||||
val isCxxMethod: Boolean = cxxMethod != null
|
||||
&& this.cxxMethod.kind != CxxMethodKind.None
|
||||
|
||||
val isCxxConstructor: Boolean = cxxMethod != null && this.cxxMethod.kind == CxxMethodKind.Constructor
|
||||
val isCxxDestructor: Boolean = cxxMethod != null && this.cxxMethod.kind == CxxMethodKind.Destructor
|
||||
val cxxReceiverType: PointerType? = cxxMethod?.receiverType
|
||||
val cxxReceiverClass: StructDecl? = cxxMethod?. let { (this.cxxMethod.receiverType.pointeeType as RecordType).decl }
|
||||
}
|
||||
|
||||
class Namespace(val name: String, val parent: String? = null) {
|
||||
val fullName: String = parent?.let { "$parent::$name" } ?: name
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* C typedef definition.
|
||||
@@ -248,7 +293,9 @@ class StringConstantDef(name: String, type: Type, val value: String) : ConstantD
|
||||
|
||||
class WrappedMacroDef(name: String, val type: Type) : MacroDef(name)
|
||||
|
||||
class GlobalDecl(val name: String, val type: Type, val isConst: Boolean)
|
||||
class GlobalDecl(val name: String, val type: Type, val isConst: Boolean, val parentName: String? = null) {
|
||||
val fullName: String = parentName?.let { "$it::$name" } ?: name
|
||||
}
|
||||
|
||||
/**
|
||||
* C type.
|
||||
@@ -279,7 +326,9 @@ data class RecordType(val decl: StructDecl) : Type
|
||||
|
||||
data class EnumType(val def: EnumDef) : Type
|
||||
|
||||
data class PointerType(val pointeeType: Type, val pointeeIsConst: Boolean = false) : Type
|
||||
// when pointer type is provided by clang we'll use ots correct spelling
|
||||
data class PointerType(val pointeeType: Type, val pointeeIsConst: Boolean = false,
|
||||
val isLVReference: Boolean = false, val spelling: String? = null) : Type
|
||||
// TODO: refactor type representation and support type modifiers more generally.
|
||||
|
||||
data class FunctionType(val parameterTypes: List<Type>, val returnType: Type) : Type
|
||||
|
||||
+40
@@ -36,6 +36,46 @@ internal val CValue<CXType>.name: String get() = clang_getTypeSpelling(this).con
|
||||
internal val CXTypeKind.spelling: String get() = clang_getTypeKindSpelling(this).convertAndDispose()
|
||||
internal val CXCursorKind.spelling: String get() = clang_getCursorKindSpelling(this).convertAndDispose()
|
||||
|
||||
internal val CValue<CXCursor>.isPublic: Boolean get() {
|
||||
val access = clang_getCXXAccessSpecifier(this)
|
||||
return access != CX_CXXAccessSpecifier.CX_CXXProtected && access != CX_CXXAccessSpecifier.CX_CXXPrivate
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO Accessibility needs better support
|
||||
* Currently we provide binding (access) to static vars (= internal linkage)
|
||||
* (i.e. following C policy, as the C header would be included into kotlin impl file
|
||||
* Consistent approach to C++ would be:
|
||||
* - Kotlin class inherits from C++ allowing overriding and protected access
|
||||
* - namespace mapped to package
|
||||
* - anon namespace members mapped to "internal" allowing access from the current translation unit
|
||||
* To make this working we have to derive a complete C++ "proxy" class for each original one and declare C wrappers as friends
|
||||
* BTW Such derived C++ proxy class is the only way to allow Kotlin to override the private virtual C++ methods (which is OK in C++)
|
||||
* Without that C++ style callbacks via overriding would be limited or not supported
|
||||
*/
|
||||
internal fun CValue<CXCursor>.isRecursivelyPublic(): Boolean {
|
||||
when {
|
||||
clang_isDeclaration(kind) == 0 ->
|
||||
return true // got the topmost declaration already
|
||||
!isPublic ->
|
||||
return false
|
||||
kind == CXCursorKind.CXCursor_Namespace && getCursorSpelling(this).isEmpty() ->
|
||||
return false
|
||||
|
||||
/*
|
||||
* TODO FIXME In the current design we allow binding to static vars, but this won't work for anon namespaces and private members
|
||||
* Need better (consistent( decision wrt accessibility.
|
||||
*/
|
||||
// clang_getCursorLinkage(this) == CXLinkageKind.CXLinkage_Internal ->
|
||||
// return false; // check disabled for a while
|
||||
|
||||
else ->
|
||||
return clang_getCursorSemanticParent(this).isRecursivelyPublic()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun CValue<CXString>.convertAndDispose(): String {
|
||||
try {
|
||||
return clang_getCString(this)!!.toKString()
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ typealias KotlinExpression = String
|
||||
fun String.asSimpleName(): String = if (this in kotlinKeywords || this.contains("$")) {
|
||||
"`$this`"
|
||||
} else {
|
||||
this
|
||||
this.replace(':', '_')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+43
-12
@@ -16,10 +16,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
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
/**
|
||||
* The [MappingBridgeGenerator] implementation which uses [SimpleBridgeGenerator] as the backend and
|
||||
@@ -27,7 +24,8 @@ import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
*/
|
||||
class MappingBridgeGeneratorImpl(
|
||||
val declarationMapper: DeclarationMapper,
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator,
|
||||
val language: Language
|
||||
) : MappingBridgeGenerator {
|
||||
|
||||
override fun kotlinToNative(
|
||||
@@ -40,7 +38,12 @@ class MappingBridgeGeneratorImpl(
|
||||
): KotlinExpression {
|
||||
val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>()
|
||||
|
||||
kotlinValues.forEach { (type, value) ->
|
||||
if (nativeBacked is FunctionStub && nativeBacked.isCxxInstanceMember()) {
|
||||
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "rawPtr"))
|
||||
kotlinValues.drop(1)
|
||||
} else {
|
||||
kotlinValues
|
||||
}.forEach { (type, value) ->
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
builder.pushMemScoped()
|
||||
val bridgeArgument = "$value.getPointer(memScope).rawValue"
|
||||
@@ -79,6 +82,25 @@ class MappingBridgeGeneratorImpl(
|
||||
val unwrappedType = type.unwrapTypedefs()
|
||||
if (unwrappedType is RecordType) {
|
||||
nativeValues.add("*(${unwrappedType.decl.spelling}*)${bridgeNativeValues[index]}")
|
||||
} else if (language == Language.CPP) {
|
||||
// C++ is more restrictive wrt type conversion
|
||||
val cppRefTypePrefix = if (unwrappedType is PointerType && unwrappedType.isLVReference) "*" else ""
|
||||
when { /// TODO Move this cludge to mirror()
|
||||
type is Typedef ->
|
||||
nativeValues.add("(${type.def.name})${bridgeNativeValues[index]}")
|
||||
type is PointerType && type.spelling != null ->
|
||||
nativeValues.add("(${type.spelling})$cppRefTypePrefix${bridgeNativeValues[index]}")
|
||||
unwrappedType is EnumType ->
|
||||
nativeValues.add("(${unwrappedType.def.spelling})${bridgeNativeValues[index]}")
|
||||
unwrappedType is RecordType ->
|
||||
nativeValues.add("*(${unwrappedType.decl.spelling}*)${bridgeNativeValues[index]}")
|
||||
else ->
|
||||
nativeValues.add(cppRefTypePrefix +
|
||||
mirror(declarationMapper, type).info.cFromBridged(
|
||||
bridgeNativeValues[index], scope, nativeBacked
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
nativeValues.add(
|
||||
mirror(declarationMapper, type).info.cFromBridged(
|
||||
@@ -90,18 +112,27 @@ class MappingBridgeGeneratorImpl(
|
||||
|
||||
val nativeResult = block(nativeValues)
|
||||
|
||||
when (unwrappedReturnType) {
|
||||
is VoidType -> {
|
||||
when {
|
||||
unwrappedReturnType is VoidType -> {
|
||||
out(nativeResult + ";")
|
||||
""
|
||||
}
|
||||
is RecordType -> {
|
||||
unwrappedReturnType is RecordType -> {
|
||||
val kniStructResult = "kniStructResult"
|
||||
|
||||
out("${unwrappedReturnType.decl.spelling} $kniStructResult = $nativeResult;")
|
||||
out("memcpy(${bridgeNativeValues.last()}, &$kniStructResult, sizeof($kniStructResult));")
|
||||
if (language == Language.CPP) {
|
||||
// use copy/move constructor to create object in place.
|
||||
out("new(${bridgeNativeValues.last()}) ${unwrappedReturnType.decl.spelling}($nativeResult);")
|
||||
} else {
|
||||
out("${unwrappedReturnType.decl.spelling} $kniStructResult = $nativeResult;")
|
||||
out("memcpy(${bridgeNativeValues.last()}, &$kniStructResult, sizeof($kniStructResult));")
|
||||
// The following would be better, but won't work in case of const fields: C99 6.3.2.1p1
|
||||
// out("*(${unwrappedReturnType.decl.spelling}*) ${bridgeNativeValues.last()} = $nativeResult;")
|
||||
}
|
||||
""
|
||||
}
|
||||
unwrappedReturnType is PointerType && unwrappedReturnType.isLVReference ->
|
||||
"&$nativeResult"
|
||||
else -> {
|
||||
nativeResult
|
||||
}
|
||||
@@ -204,4 +235,4 @@ class MappingBridgeGeneratorImpl(
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -120,10 +120,11 @@ class SimpleBridgeGeneratorImpl(
|
||||
"JNIEXPORT $cReturnType JNICALL $functionName ($joinedCParameters)"
|
||||
}
|
||||
KotlinPlatform.NATIVE -> {
|
||||
val externCPrefix = if (libraryForCStubs.language == Language.CPP) "extern \"C\" " else ""
|
||||
val functionName = pkgName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_") + "_$kotlinFunctionName"
|
||||
if (independent) kotlinLines.add("@" + topLevelKotlinScope.reference(KotlinTypes.independent))
|
||||
kotlinLines.add("@SymbolName(${functionName.quoteAsKotlinLiteral()})")
|
||||
"$cReturnType $functionName ($joinedCParameters)"
|
||||
"$externCPrefix$cReturnType $functionName ($joinedCParameters)"
|
||||
}
|
||||
}
|
||||
nativeLines.add(cFunctionHeader + " {")
|
||||
|
||||
+1
@@ -5,6 +5,7 @@ import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
fun tryRenderStructOrUnion(def: StructDef): String? = when (def.kind) {
|
||||
StructDef.Kind.STRUCT -> tryRenderStruct(def)
|
||||
StructDef.Kind.UNION -> tryRenderUnion(def)
|
||||
StructDef.Kind.CLASS -> tryRenderStruct(def)
|
||||
}
|
||||
|
||||
private fun tryRenderStruct(def: StructDef): String? {
|
||||
|
||||
+16
-2
@@ -63,7 +63,7 @@ class StubIrBridgeBuilder(
|
||||
)
|
||||
|
||||
private val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator, context.libraryForCStubs.language)
|
||||
|
||||
private val propertyAccessorBridgeBodies = mutableMapOf<PropertyAccessor, String>()
|
||||
private val functionBridgeBodies = mutableMapOf<FunctionStub, List<String>>()
|
||||
@@ -260,6 +260,9 @@ class StubIrBridgeBuilder(
|
||||
isVararg = isVararg or parameter.isVararg
|
||||
val parameterName = parameter.name.asSimpleName()
|
||||
val bridgeArgument = when {
|
||||
function.isCxxInstanceMember() && index == 0 -> {
|
||||
"rawPtr"
|
||||
}
|
||||
parameter in builderResult.bridgeGenerationComponents.cStringParameters -> {
|
||||
bodyGenerator.pushMemScoped()
|
||||
"$parameterName?.cstr?.getPointer(memScope)"
|
||||
@@ -289,7 +292,18 @@ class StubIrBridgeBuilder(
|
||||
bridgeArguments,
|
||||
independent = false
|
||||
) { nativeValues ->
|
||||
"${origin.function.name}(${nativeValues.joinToString()})"
|
||||
with (origin.function) {
|
||||
when {
|
||||
isCxxInstanceMethod ->
|
||||
"(${nativeValues[0]})->${name}(${nativeValues.drop(1).joinToString()})"
|
||||
isCxxConstructor ->
|
||||
"new(${nativeValues[0]}) ${cxxReceiverClass!!.spelling}(${nativeValues.drop(1).joinToString()})"
|
||||
isCxxDestructor ->
|
||||
"(${nativeValues[0]})->~${cxxReceiverClass!!.spelling?.substringAfterLast(':')}()"
|
||||
else ->
|
||||
"${fullName}(${nativeValues.joinToString()})"
|
||||
}
|
||||
}
|
||||
}
|
||||
bodyGenerator.returnResult(result)
|
||||
functionBridgeBodies[function] = bodyGenerator.build()
|
||||
|
||||
+4
-1
@@ -28,12 +28,15 @@ class StubIrContext(
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
add("jni.h")
|
||||
}
|
||||
if (configuration.library.language == Language.CPP) {
|
||||
add("new")
|
||||
}
|
||||
addAll(configuration.library.includes)
|
||||
},
|
||||
compilerArgs = configuration.library.compilerArgs,
|
||||
additionalPreambleLines = configuration.library.additionalPreambleLines +
|
||||
when (configuration.library.language) {
|
||||
Language.C -> emptyList()
|
||||
Language.C, Language.CPP -> emptyList()
|
||||
Language.OBJECTIVE_C -> listOf("void objc_terminate();")
|
||||
}
|
||||
).precompileHeaders()
|
||||
|
||||
+40
-14
@@ -80,6 +80,17 @@ internal class StructStubBuilder(
|
||||
}
|
||||
val classifier = context.getKotlinClassForPointed(decl)
|
||||
|
||||
var methods: List<FunctionStub> =
|
||||
def.methods
|
||||
.filter { it.isCxxInstanceMethod }
|
||||
.map { func ->
|
||||
try {
|
||||
(FunctionStubBuilder(context, func).build() as List<FunctionStub>).single()
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
}.filterNotNull()
|
||||
|
||||
val fields: List<PropertyStub?> = def.fields.map { field ->
|
||||
try {
|
||||
assert(field.name.isNotEmpty())
|
||||
@@ -179,18 +190,33 @@ internal class StructStubBuilder(
|
||||
val annotation = AnnotationStub.CStruct.VarType(def.size, def.align).takeIf {
|
||||
context.generationMode == GenerationMode.METADATA
|
||||
}
|
||||
val companion = ClassStub.Companion(
|
||||
companionClassifier,
|
||||
superClassInit = companionSuperInit,
|
||||
annotations = listOfNotNull(annotation, AnnotationStub.Deprecated.deprecatedCVariableCompanion)
|
||||
)
|
||||
|
||||
var classMethods: List<FunctionStub> =
|
||||
def.methods
|
||||
.filter { !it.isCxxInstanceMethod }
|
||||
.map { func ->
|
||||
try {
|
||||
(FunctionStubBuilder(context, func).build() as List<FunctionStub>).single()
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
}.filterNotNull()
|
||||
val classFields = def.staticFields
|
||||
.map { field -> (GlobalStubBuilder(context, field).build() as List<PropertyStub>).single() }
|
||||
|
||||
val companion = ClassStub.Companion(
|
||||
companionClassifier,
|
||||
superClassInit = companionSuperInit,
|
||||
annotations = listOfNotNull(annotation, AnnotationStub.Deprecated.deprecatedCVariableCompanion),
|
||||
properties = classFields,
|
||||
methods = classMethods
|
||||
)
|
||||
return listOf(ClassStub.Simple(
|
||||
classifier,
|
||||
origin = origin,
|
||||
properties = fields.filterNotNull() + if (platform == KotlinPlatform.NATIVE) bitFields else emptyList(),
|
||||
constructors = listOf(primaryConstructor),
|
||||
methods = emptyList(),
|
||||
methods = methods,
|
||||
modality = ClassStubModality.NONE,
|
||||
annotations = listOfNotNull(structAnnotation),
|
||||
superClassInit = superClassInit,
|
||||
@@ -628,12 +654,12 @@ internal class GlobalStubBuilder(
|
||||
val getter = when (context.platform) {
|
||||
KotlinPlatform.JVM -> {
|
||||
PropertyAccessor.Getter.SimpleGetter().also {
|
||||
val extra = BridgeGenerationInfo(global.name, mirror.info)
|
||||
val extra = BridgeGenerationInfo(global.fullName, mirror.info)
|
||||
context.bridgeComponentsBuilder.arrayGetterBridgeInfo[it] = extra
|
||||
}
|
||||
}
|
||||
KotlinPlatform.NATIVE -> {
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.name}_getter")
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.fullName}_getter")
|
||||
PropertyAccessor.Getter.ExternalGetter(listOf(cCallAnnotation)).also {
|
||||
context.wrapperComponentsBuilder.getterToWrapperInfo[it] = WrapperGenerationInfo(global)
|
||||
}
|
||||
@@ -647,12 +673,12 @@ internal class GlobalStubBuilder(
|
||||
val getter = when (context.platform) {
|
||||
KotlinPlatform.JVM -> {
|
||||
PropertyAccessor.Getter.SimpleGetter().also {
|
||||
val getterExtra = BridgeGenerationInfo(global.name, mirror.info)
|
||||
val getterExtra = BridgeGenerationInfo(global.fullName, mirror.info)
|
||||
context.bridgeComponentsBuilder.getterToBridgeInfo[it] = getterExtra
|
||||
}
|
||||
}
|
||||
KotlinPlatform.NATIVE -> {
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.name}_getter")
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.fullName}_getter")
|
||||
PropertyAccessor.Getter.ExternalGetter(listOf(cCallAnnotation)).also {
|
||||
context.wrapperComponentsBuilder.getterToWrapperInfo[it] = WrapperGenerationInfo(global)
|
||||
}
|
||||
@@ -664,12 +690,12 @@ internal class GlobalStubBuilder(
|
||||
val setter = when (context.platform) {
|
||||
KotlinPlatform.JVM -> {
|
||||
PropertyAccessor.Setter.SimpleSetter().also {
|
||||
val setterExtra = BridgeGenerationInfo(global.name, mirror.info)
|
||||
val setterExtra = BridgeGenerationInfo(global.fullName, mirror.info)
|
||||
context.bridgeComponentsBuilder.setterToBridgeInfo[it] = setterExtra
|
||||
}
|
||||
}
|
||||
KotlinPlatform.NATIVE -> {
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.name}_setter")
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.fullName}_setter")
|
||||
PropertyAccessor.Setter.ExternalSetter(listOf(cCallAnnotation)).also {
|
||||
context.wrapperComponentsBuilder.setterToWrapperInfo[it] = WrapperGenerationInfo(global)
|
||||
}
|
||||
@@ -682,10 +708,10 @@ internal class GlobalStubBuilder(
|
||||
kotlinType = mirror.pointedType
|
||||
val getter = when (context.generationMode) {
|
||||
GenerationMode.SOURCE_CODE -> {
|
||||
PropertyAccessor.Getter.InterpretPointed(global.name, kotlinType.toStubIrType())
|
||||
PropertyAccessor.Getter.InterpretPointed(global.fullName, kotlinType.toStubIrType())
|
||||
}
|
||||
GenerationMode.METADATA -> {
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.name}_getter")
|
||||
val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.fullName}_getter")
|
||||
PropertyAccessor.Getter.ExternalGetter(listOf(cCallAnnotation)).also {
|
||||
context.wrapperComponentsBuilder.getterToWrapperInfo[it] = WrapperGenerationInfo(global, passViaPointer = true)
|
||||
}
|
||||
|
||||
+12
-1
@@ -12,6 +12,17 @@ private val StubOrigin.ObjCMethod.isOptional: Boolean
|
||||
fun FunctionStub.isOptionalObjCMethod(): Boolean = this.origin is StubOrigin.ObjCMethod &&
|
||||
this.origin.isOptional
|
||||
|
||||
fun FunctionStub.isCxxInstanceMember(): Boolean = this.origin is StubOrigin.Function &&
|
||||
this.origin.function.isCxxInstanceMethod
|
||||
|
||||
fun FunctionStub.qualifiedName(): String =
|
||||
if (this.origin is StubOrigin.Function && !this.origin.function.isCxxMethod) {
|
||||
this.origin.function.fullName
|
||||
} else {
|
||||
name
|
||||
}
|
||||
|
||||
|
||||
val StubContainer.isInterface: Boolean
|
||||
get() = if (this is ClassStub.Simple) {
|
||||
modality == ClassStubModality.INTERFACE
|
||||
@@ -97,4 +108,4 @@ val StubType.underlyingTypeFqName: String
|
||||
is AbbreviatedType -> underlyingType.underlyingTypeFqName
|
||||
is FunctionalType -> classifier.fqName
|
||||
is TypeParameterType -> name
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -184,7 +184,8 @@ class StubIrTextEmitter(
|
||||
if (element in bridgeBuilderResult.excludedStubs) return
|
||||
|
||||
val header = run {
|
||||
val parameters = element.parameters.joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
||||
val parameters = (if (element.isCxxInstanceMember()) element.parameters.drop(1) else element.parameters).
|
||||
joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
||||
val receiver = element.receiver?.let { renderFunctionReceiver(it) + "." } ?: ""
|
||||
val typeParameters = renderTypeParameters(element.typeParameters)
|
||||
val override = if (element.isOverride) "override " else ""
|
||||
@@ -660,4 +661,4 @@ class StubIrTextEmitter(
|
||||
"$name : ${renderStubType(it)}"
|
||||
} ?: name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-8
@@ -121,16 +121,20 @@ private fun Properties.putAndRunOnReplace(key: Any, newValue: Any, beforeReplace
|
||||
this[key] = newValue
|
||||
}
|
||||
|
||||
private fun selectNativeLanguage(config: DefFile.DefFileConfig): Language {
|
||||
private fun selectNativeLanguage(config: DefFile.DefFileConfig, hintIsCPP: Boolean = false): Language {
|
||||
val languages = mapOf(
|
||||
"C" to Language.C,
|
||||
"C++" to Language.CPP,
|
||||
"Objective-C" to Language.OBJECTIVE_C
|
||||
)
|
||||
|
||||
val language = config.language ?: return Language.C
|
||||
val lang = config.language?.let {
|
||||
languages[it]
|
||||
?: error("Unexpected language '${config.language}'. Possible values are: ${languages.keys.joinToString { "'$it'" }}")
|
||||
} ?: Language.C
|
||||
|
||||
return if (lang == Language.C && hintIsCPP) Language.CPP else lang
|
||||
|
||||
return languages[language] ?:
|
||||
error("Unexpected language '$language'. Possible values are: ${languages.keys.joinToString { "'$it'" }}")
|
||||
}
|
||||
|
||||
private fun parseImports(dependencies: List<KotlinLibrary>): ImportsImpl =
|
||||
@@ -220,8 +224,6 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
cinteropArguments.linkerOptions.value.toTypedArray()
|
||||
val verbose = cinteropArguments.verbose
|
||||
|
||||
val language = selectNativeLanguage(def.config)
|
||||
|
||||
val entryPoint = def.config.entryPoints.atMostOne()
|
||||
val linkerOpts =
|
||||
def.config.linkerOpts.toTypedArray() +
|
||||
@@ -294,7 +296,7 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
|
||||
|
||||
File(nativeLibsDir).mkdirs()
|
||||
val outCFile = tempFiles.create(libName, ".${language.sourceFileExtension}")
|
||||
val outCFile = tempFiles.create(libName, ".${library.language.sourceFileExtension}")
|
||||
|
||||
val logger = if (verbose) {
|
||||
{ message: String -> println(message) }
|
||||
@@ -451,6 +453,14 @@ internal fun prepareTool(target: String?, flavor: KotlinPlatform): ToolConfig {
|
||||
return tool
|
||||
}
|
||||
|
||||
private fun isCxxOptions(opts: List<String>) : Boolean {
|
||||
if (opts.size >= 2) opts.reduce args@{ prev, that ->
|
||||
if (prev == "-x" && that == "c++") return@isCxxOptions true
|
||||
return@args that
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
internal fun buildNativeLibrary(
|
||||
tool: ToolConfig,
|
||||
def: DefFile,
|
||||
@@ -462,7 +472,8 @@ internal fun buildNativeLibrary(
|
||||
arguments.compilerOptions + arguments.compilerOption).toTypedArray()
|
||||
|
||||
val headerFiles = def.config.headers + additionalHeaders
|
||||
val language = selectNativeLanguage(def.config)
|
||||
val cppOptions = isCxxOptions(def.config.compilerOpts + additionalCompilerOpts)
|
||||
val language = selectNativeLanguage(def.config, cppOptions)
|
||||
val compilerOpts: List<String> = mutableListOf<String>().apply {
|
||||
addAll(def.config.compilerOpts)
|
||||
addAll(tool.defaultCompilerOpts)
|
||||
@@ -476,6 +487,7 @@ internal fun buildNativeLibrary(
|
||||
addAll(getCompilerFlagsForVfsOverlay(arguments.headerFilterPrefix.toTypedArray(), def))
|
||||
addAll(when (language) {
|
||||
Language.C -> emptyList()
|
||||
Language.CPP -> if (cppOptions) emptyList() else listOf("-x", "c++")
|
||||
Language.OBJECTIVE_C -> {
|
||||
// "Objective-C" within interop means "Objective-C with ARC":
|
||||
listOf("-fobjc-arc")
|
||||
|
||||
Reference in New Issue
Block a user