[Kotlin/Native][Interop] Skia interop plugin for cinterop
This commit is contained in:
@@ -129,7 +129,6 @@ sourceSets {
|
||||
srcDirs("prebuilt/nativeInteropStubs/kotlin")
|
||||
}
|
||||
kotlin{
|
||||
|
||||
target {
|
||||
|
||||
}
|
||||
@@ -137,7 +136,6 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
compile(project(":kotlin-stdlib"))
|
||||
compile(project(":kotlin-native:Interop:Runtime"))
|
||||
|
||||
+116
-106
@@ -76,31 +76,7 @@ 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() {
|
||||
public open class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean = false) : NativeIndex() {
|
||||
|
||||
private sealed class DeclarationID {
|
||||
data class USR(val usr: String) : DeclarationID()
|
||||
@@ -200,13 +176,17 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
return DeclarationID.USR(usr)
|
||||
}
|
||||
|
||||
private fun getStructDeclAt(
|
||||
protected fun getStructDeclAt(
|
||||
cursor: CValue<CXCursor>
|
||||
): StructDeclImpl = structRegistry.getOrPut(cursor, { createStructDecl(cursor) }) { decl ->
|
||||
): StructDecl = structRegistry.getOrPut(cursor, { createStructDecl(cursor) }) { decl ->
|
||||
val definitionCursor = clang_getCursorDefinition(cursor)
|
||||
if (clang_Cursor_isNull(definitionCursor) == 0) {
|
||||
assert(clang_isCursorDefinition(definitionCursor) != 0)
|
||||
createStructDef(decl, cursor)
|
||||
// TODO: is this a bug or this is a wrong thing to do?
|
||||
// Otherwise c++ class definition is created from its forward declaration
|
||||
// and hence is empty.
|
||||
//createStructDef(decl, cursor)
|
||||
createStructDef(decl, definitionCursor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,11 +197,12 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
return StructDeclImpl(typeSpelling, getLocation(cursor))
|
||||
}
|
||||
|
||||
private fun visitClass(cursor: CValue<CXCursor>, clazz: StructDefImpl) {
|
||||
private fun addCxxMembers(classCursor: CValue<CXCursor>, clazz: StructDefImpl) {
|
||||
if (library.language != Language.CPP) return
|
||||
|
||||
// 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) {
|
||||
visitChildren(classCursor) { cursor, _ ->
|
||||
if (cursor.isCxxPublic) {
|
||||
// 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 -> {
|
||||
@@ -232,8 +213,7 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
||||
}
|
||||
}
|
||||
CXCursorKind.CXCursor_Constructor ->
|
||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
||||
CXCursorKind.CXCursor_Constructor,
|
||||
CXCursorKind.CXCursor_Destructor ->
|
||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
||||
|
||||
@@ -274,13 +254,15 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
)
|
||||
|
||||
structDef.members += fields
|
||||
visitClass(cursor, structDef)
|
||||
addCxxMembers(cursor, structDef)
|
||||
|
||||
structDecl.def = structDef
|
||||
}
|
||||
|
||||
private fun addDeclaredFields(result: MutableList<StructMember>, structType: CValue<CXType>, containerType: CValue<CXType>) {
|
||||
getFields(containerType).filter { it.isPublic }.forEach { fieldCursor ->
|
||||
// TODO: We don't exactly preserve C++ layout here, but
|
||||
// we don't allow general case C++ classes by value at the moment.
|
||||
getFields(containerType).filter { library.language != Language.CPP || it.isCxxPublic }.forEach { fieldCursor ->
|
||||
val name = getCursorSpelling(fieldCursor)
|
||||
if (name.isNotEmpty()) {
|
||||
val fieldType = convertCursorType(fieldCursor)
|
||||
@@ -606,7 +588,7 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
else -> UnsupportedType
|
||||
}
|
||||
|
||||
fun convertType(type: CValue<CXType>, typeAttributes: CValue<CXTypeAttributes>? = null): Type {
|
||||
open fun convertType(type: CValue<CXType>, typeAttributes: CValue<CXTypeAttributes>? = null): Type {
|
||||
val primitiveType = convertUnqualifiedPrimitiveType(type)
|
||||
if (primitiveType != UnsupportedType) {
|
||||
return primitiveType
|
||||
@@ -615,7 +597,9 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
val kind = type.kind
|
||||
|
||||
return when (kind) {
|
||||
CXType_Elaborated -> convertType(clang_Type_getNamedType(type))
|
||||
CXType_Elaborated -> {
|
||||
convertType(clang_Type_getNamedType(type))
|
||||
}
|
||||
|
||||
CXType_Unexposed -> {
|
||||
if (clang_getResultType(type).kind != CXTypeKind.CXType_Invalid) {
|
||||
@@ -645,6 +629,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_LValueReference -> {
|
||||
@@ -861,23 +846,11 @@ 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()) {
|
||||
if (library.language == Language.CPP && !cursor.isRecursivelyCxxPublic()) {
|
||||
// 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_CXXClass -> {
|
||||
if (entityName == null) {
|
||||
@@ -969,29 +942,25 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
}
|
||||
}
|
||||
|
||||
fun indexDeclaration(cursor: CValue<CXCursor>): Unit {
|
||||
fun indexCxxDeclaration(cursor: CValue<CXCursor>): Unit {
|
||||
if (library.language != Language.CPP) return
|
||||
if (!library.includesDeclaration(cursor)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (cursor.isRecursivelyPublic()) {
|
||||
if (cursor.isRecursivelyCxxPublic()) {
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
indexCxxFunction(cursor)
|
||||
}
|
||||
|
||||
else -> {
|
||||
@@ -1023,6 +992,32 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
getObjCProtocolAt(cursor)
|
||||
}
|
||||
}
|
||||
protected open fun String.isUnknownTemplate() = false
|
||||
|
||||
private fun getParentName(cursor: CValue<CXCursor>, pkg: List<String> = emptyList()) : String? {
|
||||
|
||||
if (library.language != Language.CPP) return null
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
private fun getFunction(cursor: CValue<CXCursor>, receiver: StructDecl? = null): FunctionDecl? {
|
||||
if (!isFuncDeclEligible(cursor)) {
|
||||
@@ -1030,10 +1025,16 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
return null
|
||||
}
|
||||
var name = clang_getCursorSpelling(cursor).convertAndDispose()
|
||||
var returnType = convertType(clang_getCursorResultType(cursor), clang_getCursorResultTypeAttributes(cursor))
|
||||
|
||||
val cursorReturnType = clang_getCursorResultType(cursor)
|
||||
val cursorReturnTypeSpelling = clang_getTypeSpelling(cursorReturnType).convertAndDispose()
|
||||
|
||||
if (cursorReturnTypeSpelling.isUnknownTemplate()) return null
|
||||
|
||||
var returnType = convertType(cursorReturnType, clang_getCursorResultTypeAttributes(cursor))
|
||||
|
||||
val parameters = mutableListOf<Parameter>()
|
||||
parameters += getFunctionParameters(cursor)
|
||||
parameters += getFunctionParameters(cursor) ?: return null
|
||||
|
||||
val binaryName = when (library.language) {
|
||||
Language.C, Language.CPP, Language.OBJECTIVE_C -> clang_Cursor_getMangling(cursor).convertAndDispose()
|
||||
@@ -1046,35 +1047,36 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
else -> CxxMethodKind.None // Not implemented. Not expected, OK to assert (?)
|
||||
}
|
||||
)
|
||||
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)
|
||||
@@ -1094,7 +1096,7 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
|
||||
val encoding = clang_getDeclObjCTypeEncoding(cursor).convertAndDispose()
|
||||
val returnType = convertType(clang_getCursorResultType(cursor), clang_getCursorResultTypeAttributes(cursor))
|
||||
val parameters = getFunctionParameters(cursor)
|
||||
val parameters = getFunctionParameters(cursor)!!
|
||||
|
||||
if (returnType == UnsupportedType || parameters.any { it.type == UnsupportedType }) {
|
||||
return null // TODO: make a more universal fix.
|
||||
@@ -1128,11 +1130,10 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
}
|
||||
|
||||
// Skip functions which parameter or return type is TemplateRef
|
||||
private fun isFuncDeclEligible(cursor: CValue<CXCursor>): Boolean {
|
||||
|
||||
protected open fun isFuncDeclEligible(cursor: CValue<CXCursor>): Boolean {
|
||||
var ret = true
|
||||
visitChildren(cursor) { cursor, _ ->
|
||||
when (cursor.kind) {
|
||||
visitChildren(cursor) { childCursor, _ ->
|
||||
when (childCursor.kind) {
|
||||
CXCursorKind.CXCursor_TemplateRef -> {
|
||||
ret = false
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
@@ -1143,10 +1144,13 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getFunctionParameters(cursor: CValue<CXCursor>): List<Parameter> {
|
||||
private fun getFunctionParameters(cursor: CValue<CXCursor>): List<Parameter>? {
|
||||
val argNum = clang_Cursor_getNumArguments(cursor)
|
||||
val args = (0..argNum - 1).map {
|
||||
val argCursor = clang_Cursor_getArgument(cursor, it)
|
||||
if (clang_getTypeSpelling(clang_getCursorType(argCursor)).convertAndDispose().isUnknownTemplate()) {
|
||||
return null
|
||||
}
|
||||
val argName = getCursorSpelling(argCursor)
|
||||
val type = convertCursorType(argCursor)
|
||||
Parameter(argName, type,
|
||||
@@ -1175,8 +1179,12 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
|
||||
fun buildNativeIndexImpl(library: NativeLibrary, verbose: Boolean): IndexerResult {
|
||||
val result = NativeIndexImpl(library, verbose)
|
||||
val compilation = indexDeclarations(result)
|
||||
return IndexerResult(result, compilation)
|
||||
return buildNativeIndexImpl(result)
|
||||
}
|
||||
|
||||
fun buildNativeIndexImpl(index: NativeIndexImpl): IndexerResult {
|
||||
val compilation = indexDeclarations(index)
|
||||
return IndexerResult(index, compilation)
|
||||
}
|
||||
|
||||
private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH {
|
||||
@@ -1210,11 +1218,13 @@ private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH
|
||||
}
|
||||
})
|
||||
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
||||
if (getContainingFile(cursor) in headers) {
|
||||
nativeIndex.indexDeclaration(cursor)
|
||||
if (nativeIndex.library.language == Language.CPP) {
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
||||
if (getContainingFile(cursor) in headers) {
|
||||
nativeIndex.indexCxxDeclaration(cursor)
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
||||
|
||||
+10
-14
@@ -62,7 +62,6 @@ data class CompilationWithPCH(
|
||||
override val compilerArgs: List<String>,
|
||||
override val language: Language
|
||||
) : Compilation {
|
||||
|
||||
constructor(compilerArgs: List<String>, precompiledHeader: String, language: Language)
|
||||
: this(compilerArgs + listOf("-include-pch", precompiledHeader), language)
|
||||
|
||||
@@ -256,25 +255,20 @@ class FunctionDecl(val name: String, val parameters: List<Parameter>, val return
|
||||
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
|
||||
val isCxxInstanceMethod: Boolean get() = this.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 isCxxMethod: Boolean get() = this.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 }
|
||||
val isCxxConstructor: Boolean get() = this.cxxMethod?.kind == CxxMethodKind.Constructor
|
||||
val isCxxDestructor: Boolean get() = this.cxxMethod?.kind == CxxMethodKind.Destructor
|
||||
val cxxReceiverType: PointerType? get() = cxxMethod?.receiverType
|
||||
val cxxReceiverClass: StructDecl?
|
||||
get() = 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.
|
||||
*
|
||||
@@ -294,7 +288,7 @@ 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, val parentName: String? = null) {
|
||||
val fullName: String = parentName?.let { "$it::$name" } ?: name
|
||||
val fullName: String get() = parentName?.let { "$it::$name" } ?: name
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,6 +318,8 @@ object VoidType : Type
|
||||
|
||||
data class RecordType(val decl: StructDecl) : Type
|
||||
|
||||
data class ManagedType(val decl: StructDecl) : Type
|
||||
|
||||
data class EnumType(val def: EnumDef) : Type
|
||||
|
||||
// when pointer type is provided by clang we'll use ots correct spelling
|
||||
|
||||
+13
-9
@@ -26,17 +26,17 @@ import java.nio.file.Paths
|
||||
import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
internal val CValue<CXType>.kind: CXTypeKind get() = this.useContents { kind }
|
||||
val CValue<CXType>.kind: CXTypeKind get() = this.useContents { kind }
|
||||
|
||||
internal val CValue<CXCursor>.kind: CXCursorKind get() = this.useContents { kind }
|
||||
val CValue<CXCursor>.kind: CXCursorKind get() = this.useContents { kind }
|
||||
|
||||
internal val CValue<CXCursor>.type: CValue<CXType> get() = clang_getCursorType(this)
|
||||
internal val CValue<CXCursor>.spelling: String get() = clang_getCursorSpelling(this).convertAndDispose()
|
||||
val CValue<CXCursor>.spelling: String get() = clang_getCursorSpelling(this).convertAndDispose()
|
||||
internal val CValue<CXType>.name: String get() = clang_getTypeSpelling(this).convertAndDispose()
|
||||
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() {
|
||||
internal val CValue<CXCursor>.isCxxPublic: Boolean get() {
|
||||
val access = clang_getCXXAccessSpecifier(this)
|
||||
return access != CX_CXXAccessSpecifier.CX_CXXProtected && access != CX_CXXAccessSpecifier.CX_CXXPrivate
|
||||
}
|
||||
@@ -54,11 +54,11 @@ internal val CValue<CXCursor>.isPublic: Boolean get() {
|
||||
* 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 {
|
||||
internal fun CValue<CXCursor>.isRecursivelyCxxPublic(): Boolean {
|
||||
when {
|
||||
clang_isDeclaration(kind) == 0 ->
|
||||
return true // got the topmost declaration already
|
||||
!isPublic ->
|
||||
!isCxxPublic ->
|
||||
return false
|
||||
kind == CXCursorKind.CXCursor_Namespace && getCursorSpelling(this).isEmpty() ->
|
||||
return false
|
||||
@@ -71,7 +71,7 @@ internal fun CValue<CXCursor>.isRecursivelyPublic(): Boolean {
|
||||
// return false; // check disabled for a while
|
||||
|
||||
else ->
|
||||
return clang_getCursorSemanticParent(this).isRecursivelyPublic()
|
||||
return clang_getCursorSemanticParent(this).isRecursivelyCxxPublic()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ internal fun CXTranslationUnit.ensureNoCompileErrors(): CXTranslationUnit {
|
||||
|
||||
internal typealias CursorVisitor = (cursor: CValue<CXCursor>, parent: CValue<CXCursor>) -> CXChildVisitResult
|
||||
|
||||
internal fun visitChildren(parent: CValue<CXCursor>, visitor: CursorVisitor) {
|
||||
fun visitChildren(parent: CValue<CXCursor>, visitor: CursorVisitor) {
|
||||
val visitorStableRef = StableRef.create(visitor)
|
||||
try {
|
||||
val clientData = visitorStableRef.asCPointer()
|
||||
@@ -335,7 +335,11 @@ internal fun Compilation.withPrecompiledHeader(translationUnit: CXTranslationUni
|
||||
val precompiledHeader = Files.createTempFile(null, ".pch").toFile().apply { this.deleteOnExit() }
|
||||
clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0)
|
||||
|
||||
return CompilationWithPCH(this.compilerArgs, precompiledHeader.absolutePath, this.language)
|
||||
return CompilationWithPCH(
|
||||
this.compilerArgs,
|
||||
precompiledHeader.absolutePath,
|
||||
this.language
|
||||
)
|
||||
}
|
||||
|
||||
internal fun NativeLibrary.includesDeclaration(cursor: CValue<CXCursor>): Boolean {
|
||||
|
||||
+9
-1
@@ -23,6 +23,10 @@ annotation class CStruct(val spelling: String) {
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class VarType(val size: Long, val align: Int)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ManagedType
|
||||
}
|
||||
|
||||
@Target(
|
||||
@@ -59,6 +63,10 @@ public annotation class CCall(val id: String) {
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Consumed
|
||||
|
||||
@Target(AnnotationTarget.CONSTRUCTOR)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class CppClassConstructor
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,4 +110,4 @@ public annotation class CEnumEntryAlias(val entryName: String)
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
public annotation class CEnumVarTypeSize(val size: Int)
|
||||
public annotation class CEnumVarTypeSize(val size: Int)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
Skia interop support
|
||||
====================
|
||||
|
||||
This is a plugin for cinterop that allows to interop with `Skia Graphics Library`.
|
||||
|
||||
Primarily targeted to be used by Skiko project.
|
||||
|
||||
Usage:
|
||||
------
|
||||
|
||||
Add to the .def file the following clauses:
|
||||
|
||||
```
|
||||
plugin = org.jetbrains.kotlin.native.cinterop.plugin.skia
|
||||
language = C++
|
||||
```
|
||||
|
||||
Implementation details
|
||||
======================
|
||||
|
||||
Limited C++ interop provided via plain C wrappers and cinterop mechanism.
|
||||
WARNING: this is by no means a general support for C++ by cinterop.
|
||||
|
||||
C++ features
|
||||
------------
|
||||
|
||||
* C++ class:
|
||||
+ Virtual and non-virtual methods
|
||||
* Static methods mapped as companion ones
|
||||
* Fields
|
||||
* Static fields as companion ones
|
||||
* Constructors are mapped to `__init__(args)` companion methods
|
||||
* Destructor is mapped to `__destroy__(this)` companion method
|
||||
* LValueReference parameters and return value internally handled as pointers
|
||||
* Namespaces provided as simple mangled class name. It works but awfully ugly. Shall be fixed by mapping to packages.
|
||||
* Nested C++ classes: same as namespaces, simple mangling. Shall be fixed.
|
||||
* Access modifiers: only public members exposed to Kotlin. Anonymous namespaces are silently ignored.
|
||||
|
||||
Known issues
|
||||
------------
|
||||
|
||||
* C++ object with nontrivial copy constructor may work incorrect when used as by value parameter or return typr.
|
||||
"Nontrivial" in this context relates to objects which bahavior depends on memory location. Most of other non-POD objects may be handled well.
|
||||
In fact, "by value" return type is mapped to CValue which is immutrable movable block. Particularly, non-const methods invoked with CValue receiver
|
||||
via useContents mechanism run on temporary object and therefore does not modify the original copy. TBD.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
* Operators are not supported yet (silently ignored)
|
||||
* LVReference is mapped to CPointer<T>? which is incorrect (should be notNull). This may cause segmentation fault in case of null would be sent as a parameter. TBD
|
||||
* const overload not supported and cause compilation error. That is, two class methods with the same signature (`const` and `non-const`) can't be compiled. The same for function parameters: if two functions differ only in `const*` modifier of parameter, this will cause "conflicting overloads" error. TBD.
|
||||
* C++ lambda type is not supported yet.
|
||||
* Member pointer, member reference, rvalue reference and some other types are not wupported.
|
||||
* Inheritance is not implemented yet. C++-style callbacks (overriding virtual method in Kotlin) may be implemented via plain C bridge (this can be done by hand as a workaround). TBD.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
apply from: "$rootDir/kotlin-native/gradle/kotlinGradlePlugin.gradle"
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
implementation project(":kotlin-native:Interop:Indexer")
|
||||
implementation project(":kotlin-native:Interop:StubGenerator")
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors=true
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.skia
|
||||
|
||||
import clang.*
|
||||
import kotlinx.cinterop.CValue
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
fun buildSkiaNativeIndexImpl(library: NativeLibrary, verbose: Boolean): IndexerResult {
|
||||
val result = SkiaNativeIndexImpl(library, verbose)
|
||||
return buildNativeIndexImpl(result)
|
||||
}
|
||||
|
||||
class SkiaNativeIndexImpl(library: NativeLibrary, verbose: Boolean) : NativeIndexImpl(library, verbose) {
|
||||
override fun convertType(type: CValue<CXType>, typeAttributes: CValue<CXTypeAttributes>?): Type {
|
||||
if (type.kind == CXTypeKind.CXType_Record) {
|
||||
val decl: StructDecl = getStructDeclAt(clang_getTypeDeclaration(type))
|
||||
if (decl.isSkiaSharedPointer) {
|
||||
return ManagedType(decl)
|
||||
}
|
||||
}
|
||||
return super.convertType(type, typeAttributes)
|
||||
}
|
||||
|
||||
// Skip functions which parameter or return type is TemplateRef
|
||||
override fun isFuncDeclEligible(cursor: CValue<CXCursor>): Boolean =
|
||||
cursor.containsOnlySkiaSharedPointerTemplates()
|
||||
|
||||
override fun String.isUnknownTemplate() = // TODO: this is a hack.
|
||||
this.isCppTemplate && !this.isSkiaSharedPointer
|
||||
}
|
||||
|
||||
fun CValue<CXCursor>.containsTemplates(): Boolean {
|
||||
var ret = false
|
||||
visitChildren(this) { childCursor, _ ->
|
||||
when (childCursor.kind) {
|
||||
CXCursorKind.CXCursor_TemplateRef -> {
|
||||
ret = true
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
}
|
||||
else -> CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
fun CValue<CXCursor>.containsOnlySkiaSharedPointerTemplates(): Boolean {
|
||||
var ret = true
|
||||
visitChildren(this) { childCursor, _ ->
|
||||
when (childCursor.kind) {
|
||||
CXCursorKind.CXCursor_TemplateRef ->
|
||||
if (childCursor.spelling == "sk_sp" && !childCursor.containsTemplates()) {
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
} else {
|
||||
ret = false
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
}
|
||||
else -> CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
val StructDecl.isSkiaSharedPointer: Boolean
|
||||
get() = spelling.isSkiaSharedPointer
|
||||
|
||||
val StructDecl.stripSkiaSharedPointer: String
|
||||
get() {
|
||||
assert(this.isSkiaSharedPointer)
|
||||
return this.spelling.drop(6).dropLast(1).let { // TODO: this is a hack.
|
||||
if (it.startsWith("const ")) it.drop(6) else it
|
||||
}
|
||||
}
|
||||
|
||||
private val String.isCppTemplate: Boolean // TODO: this is a hack.
|
||||
get() = this.contains("<") && this.endsWith(">")
|
||||
|
||||
private val String.isSkiaSharedPointer: Boolean // TODO: this is a hack.
|
||||
get() = this.startsWith("sk_sp<") && this.endsWith(">")
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.skia
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.ManagedTypePassing
|
||||
import org.jetbrains.kotlin.native.interop.gen.StubIrContext
|
||||
import org.jetbrains.kotlin.native.interop.gen.getStringRepresentation
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.Plugin
|
||||
import org.jetbrains.kotlin.native.interop.indexer.IndexerResult
|
||||
import org.jetbrains.kotlin.native.interop.indexer.ManagedType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
||||
import org.jetbrains.kotlin.native.interop.indexer.Type
|
||||
|
||||
class SkiaPlugin : Plugin {
|
||||
override val name = "Skia"
|
||||
override fun buildNativeIndex(library: NativeLibrary, verbose: Boolean): IndexerResult =
|
||||
buildSkiaNativeIndexImpl(library, verbose)
|
||||
|
||||
override val managedTypePassing = object : ManagedTypePassing() {
|
||||
override val ManagedType.passValue: String get() = "sk_ref_sp<${this.decl.stripSkiaSharedPointer}>"
|
||||
override val ManagedType.returnValue: String get() = ".release()"
|
||||
}
|
||||
|
||||
override val ManagedType.stringRepresentation: String get() {
|
||||
assert(this.decl.isSkiaSharedPointer)
|
||||
return "${this.decl.stripSkiaSharedPointer}*"
|
||||
}
|
||||
override fun stubsBuildingContext(stubIrContext: StubIrContext) = SkiaStubsBuildingContextImpl(stubIrContext)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.skia
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.Classifier
|
||||
import org.jetbrains.kotlin.native.interop.gen.StubIrContext
|
||||
import org.jetbrains.kotlin.native.interop.gen.StubsBuildingContextImpl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.StructDecl
|
||||
|
||||
class SkiaStubsBuildingContextImpl(stubIrContext: StubIrContext) : StubsBuildingContextImpl(stubIrContext) {
|
||||
override val declarationMapper = SkiaDeclarationMapperImpl()
|
||||
|
||||
inner class SkiaDeclarationMapperImpl : DeclarationMapperImpl() {
|
||||
override fun getKotlinClassForManaged(structDecl: StructDecl): Classifier {
|
||||
assert(structDecl.isSkiaSharedPointer)
|
||||
val struct = structDecl.stripSkiaSharedPointer
|
||||
val structArgument = nativeIndex.structs.singleOrNull {
|
||||
it.spelling == struct && it.def != null
|
||||
} ?: error("Expected to find a single template arg struct by name: ${struct}")
|
||||
return getKotlinClassForPointed(structArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,6 @@ apply plugin: 'application'
|
||||
|
||||
mainClassName = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt"
|
||||
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation project(":kotlin-native:Interop:Indexer")
|
||||
implementation project(":kotlin-native:utilities:basic-utils")
|
||||
@@ -56,4 +54,4 @@ sourceSets{
|
||||
srcDir(VersionGeneratorKt.kotlinNativeVersionSrc(project))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+119
-22
@@ -4,13 +4,15 @@
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.FunctionDecl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.GlobalDecl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
internal data class CCalleeWrapper(val lines: List<String>)
|
||||
|
||||
open class ManagedTypePassing {
|
||||
open val ManagedType.passValue: String get() = error("ManagedType support requires a plugin")
|
||||
open val ManagedType.returnValue: String get() = error("ManagedType support requires a plugin")
|
||||
}
|
||||
|
||||
/**
|
||||
* Some functions don't have an address (e.g. macros-based or builtins).
|
||||
* To solve this problem we generate a wrapper function.
|
||||
@@ -26,10 +28,17 @@ internal class CWrappersGenerator(private val context: StubIrContext) {
|
||||
return "${packageName}_${functionName}_wrapper${currentFunctionWrapperId++}"
|
||||
}
|
||||
|
||||
private fun bindSymbolToFunction(symbol: String, function: String): List<String> = listOf(
|
||||
"const void* $symbol __asm(${symbol.quoteAsKotlinLiteral()});",
|
||||
"const void* $symbol = &$function;"
|
||||
)
|
||||
private fun bindSymbolToFunction(symbol: String, function: String): List<String> {
|
||||
val prefix = if (context.configuration.library.language == Language.CPP)
|
||||
"extern \"C\" "
|
||||
else
|
||||
""
|
||||
|
||||
return listOf(
|
||||
"${prefix}const void* $symbol __asm(${symbol.quoteAsKotlinLiteral()});",
|
||||
"${prefix}const void* $symbol = (const void*)&$function;"
|
||||
)
|
||||
}
|
||||
|
||||
private data class Parameter(val type: String, val name: String)
|
||||
|
||||
@@ -42,34 +51,122 @@ internal class CWrappersGenerator(private val context: StubIrContext) {
|
||||
): List<String> = listOf(
|
||||
"__attribute__((always_inline))",
|
||||
"$returnType $wrapperName(${parameters.joinToString { "${it.type} ${it.name}" }}) {",
|
||||
body,
|
||||
"\t$body",
|
||||
"}",
|
||||
*bindSymbolToFunction(symbolName, wrapperName).toTypedArray()
|
||||
)
|
||||
|
||||
private val Type.stringRepresentation get() = this.getStringRepresentation(context.plugin)
|
||||
|
||||
private fun createCCalleeWrapper(function: FunctionDecl, symbolName: String): List<String> {
|
||||
assert(context.configuration.library.language != Language.CPP)
|
||||
|
||||
val wrapperName = generateFunctionWrapperName(function.name)
|
||||
|
||||
val returnType = function.returnType.stringRepresentation
|
||||
|
||||
val parameters = function.parameters.mapIndexed { index, parameter ->
|
||||
val type = parameter.type.stringRepresentation
|
||||
Parameter(type, "p$index")
|
||||
}
|
||||
|
||||
val callExpression = "${function.name}(${parameters.joinToString { it.name }})"
|
||||
|
||||
val wrapperBody = if (function.returnType.unwrapTypedefs() is VoidType) {
|
||||
"$callExpression;"
|
||||
} else {
|
||||
"return (${returnType})($callExpression);"
|
||||
}
|
||||
return createWrapper(symbolName, wrapperName, returnType, parameters, wrapperBody)
|
||||
}
|
||||
|
||||
private fun createCppCalleeWrapper(function: FunctionDecl, symbolName: String): List<String> {
|
||||
assert(context.configuration.library.language == Language.CPP)
|
||||
|
||||
val wrapperName = generateFunctionWrapperName(function.name)
|
||||
|
||||
val returnType = function.returnType.stringRepresentation
|
||||
val unwrappedReturnType = function.returnType.unwrapTypedefs()
|
||||
val returnTypePrefix =
|
||||
if (unwrappedReturnType is PointerType && unwrappedReturnType.isLVReference) "&" else ""
|
||||
val returnTypePostfix =
|
||||
if (unwrappedReturnType is ManagedType)
|
||||
with(context.plugin.managedTypePassing) { unwrappedReturnType.returnValue }
|
||||
else ""
|
||||
|
||||
val parameters = function.parameters.mapIndexed { index, parameter ->
|
||||
val type = parameter.type.stringRepresentation
|
||||
Parameter(type, "p$index")
|
||||
}
|
||||
val argumentTypes = function.parameters.map { parameter ->
|
||||
val parameterTypeText = parameter.type.stringRepresentation
|
||||
val type = parameter.type
|
||||
val unwrappedType = type.unwrapTypedefs()
|
||||
|
||||
val cppRefTypePrefix =
|
||||
if (unwrappedType is PointerType && unwrappedType.isLVReference) "*" else ""
|
||||
val typeExpression = when {
|
||||
type is Typedef ->
|
||||
"(${type.def.name})"
|
||||
type is PointerType && type.spelling != null ->
|
||||
"(${type.spelling})$cppRefTypePrefix"
|
||||
unwrappedType is EnumType ->
|
||||
"(${unwrappedType.def.spelling})"
|
||||
unwrappedType is RecordType ->
|
||||
"*(${unwrappedType.decl.spelling}*)"
|
||||
unwrappedType is ManagedType -> {
|
||||
with(context.plugin.managedTypePassing) { unwrappedType.passValue }
|
||||
}
|
||||
else ->
|
||||
"$cppRefTypePrefix($parameterTypeText)"
|
||||
}
|
||||
|
||||
typeExpression
|
||||
}
|
||||
|
||||
val callExpression = with (function) {
|
||||
assert(argumentTypes.size == parameters.size)
|
||||
val arguments = argumentTypes.mapIndexed { index, type ->
|
||||
"${type}(${parameters[index].name})"
|
||||
}
|
||||
when {
|
||||
isCxxInstanceMethod -> {
|
||||
val parametersPart = arguments.drop(1).joinToString()
|
||||
"(${parameters[0].name})->${name}($parametersPart)"
|
||||
}
|
||||
isCxxConstructor -> {
|
||||
val parametersPart = arguments.drop(1).joinToString()
|
||||
"new(${parameters[0].name}) ${cxxReceiverClass!!.spelling}($parametersPart)"
|
||||
}
|
||||
isCxxDestructor ->
|
||||
"(${parameters[0].name})->~${cxxReceiverClass!!.spelling.substringAfterLast(':')}()"
|
||||
else -> "${fullName}(${arguments.joinToString()})"
|
||||
}
|
||||
}
|
||||
|
||||
val wrapperBody = if (function.returnType.unwrapTypedefs() is VoidType) {
|
||||
"$callExpression;"
|
||||
} else {
|
||||
"return (${returnType})$returnTypePrefix($callExpression)$returnTypePostfix;"
|
||||
}
|
||||
return createWrapper(symbolName, wrapperName, returnType, parameters, wrapperBody)
|
||||
}
|
||||
|
||||
fun generateCCalleeWrapper(function: FunctionDecl, symbolName: String): CCalleeWrapper =
|
||||
if (function.isVararg) {
|
||||
CCalleeWrapper(bindSymbolToFunction(symbolName, function.name))
|
||||
} else {
|
||||
val wrapperName = generateFunctionWrapperName(function.name)
|
||||
|
||||
val returnType = function.returnType.getStringRepresentation()
|
||||
val parameters = function.parameters.mapIndexed { index, parameter ->
|
||||
Parameter(parameter.type.getStringRepresentation(), "p$index")
|
||||
}
|
||||
val callExpression = "${function.name}(${parameters.joinToString { it.name }});"
|
||||
val wrapperBody = if (function.returnType.unwrapTypedefs() is VoidType) {
|
||||
callExpression
|
||||
val wrapper = if (context.configuration.library.language == Language.CPP) {
|
||||
createCppCalleeWrapper(function, symbolName)
|
||||
} else {
|
||||
"return $callExpression"
|
||||
createCCalleeWrapper(function, symbolName)
|
||||
}
|
||||
val wrapper = createWrapper(symbolName, wrapperName, returnType, parameters, wrapperBody)
|
||||
CCalleeWrapper(wrapper)
|
||||
}
|
||||
|
||||
fun generateCGlobalGetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
||||
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_getter")
|
||||
val returnType = globalDecl.type.getStringRepresentation()
|
||||
val returnType = globalDecl.type.stringRepresentation
|
||||
val wrapperBody = "return ${globalDecl.name};"
|
||||
val wrapper = createWrapper(symbolName, wrapperName, returnType, emptyList(), wrapperBody)
|
||||
return CCalleeWrapper(wrapper)
|
||||
@@ -85,7 +182,7 @@ internal class CWrappersGenerator(private val context: StubIrContext) {
|
||||
|
||||
fun generateCGlobalSetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
||||
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_setter")
|
||||
val globalType = globalDecl.type.getStringRepresentation()
|
||||
val globalType = globalDecl.type.stringRepresentation
|
||||
val parameter = Parameter(globalType, "p1")
|
||||
val wrapperBody = "${globalDecl.name} = ${parameter.name};"
|
||||
val wrapper = createWrapper(symbolName, wrapperName, "void", listOf(parameter), wrapperBody)
|
||||
|
||||
+12
-43
@@ -16,7 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
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
|
||||
@@ -24,8 +27,7 @@ import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
*/
|
||||
class MappingBridgeGeneratorImpl(
|
||||
val declarationMapper: DeclarationMapper,
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator,
|
||||
val language: Language
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator
|
||||
) : MappingBridgeGenerator {
|
||||
|
||||
override fun kotlinToNative(
|
||||
@@ -38,12 +40,7 @@ class MappingBridgeGeneratorImpl(
|
||||
): KotlinExpression {
|
||||
val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>()
|
||||
|
||||
if (nativeBacked is FunctionStub && nativeBacked.isCxxInstanceMember()) {
|
||||
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "rawPtr"))
|
||||
kotlinValues.drop(1)
|
||||
} else {
|
||||
kotlinValues
|
||||
}.forEach { (type, value) ->
|
||||
kotlinValues.forEach { (type, value) ->
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
builder.pushMemScoped()
|
||||
val bridgeArgument = "$value.getPointer(memScope).rawValue"
|
||||
@@ -82,25 +79,6 @@ 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(
|
||||
@@ -112,27 +90,18 @@ class MappingBridgeGeneratorImpl(
|
||||
|
||||
val nativeResult = block(nativeValues)
|
||||
|
||||
when {
|
||||
unwrappedReturnType is VoidType -> {
|
||||
when (unwrappedReturnType) {
|
||||
is VoidType -> {
|
||||
out(nativeResult + ";")
|
||||
""
|
||||
}
|
||||
unwrappedReturnType is RecordType -> {
|
||||
is RecordType -> {
|
||||
val kniStructResult = "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;")
|
||||
}
|
||||
out("${unwrappedReturnType.decl.spelling} $kniStructResult = $nativeResult;")
|
||||
out("memcpy(${bridgeNativeValues.last()}, &$kniStructResult, sizeof($kniStructResult));")
|
||||
""
|
||||
}
|
||||
unwrappedReturnType is PointerType && unwrappedReturnType.isLVReference ->
|
||||
"&$nativeResult"
|
||||
else -> {
|
||||
nativeResult
|
||||
}
|
||||
@@ -235,4 +204,4 @@ class MappingBridgeGeneratorImpl(
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -25,6 +25,8 @@ interface DeclarationMapper {
|
||||
fun getPackageFor(declaration: TypeDeclaration): String
|
||||
|
||||
val useUnsignedTypes: Boolean
|
||||
|
||||
fun getKotlinClassForManaged(structDecl: StructDecl): Classifier
|
||||
}
|
||||
|
||||
fun DeclarationMapper.isMappedToSigned(integerType: IntegerType): Boolean = integerType.isSigned || !useUnsignedTypes
|
||||
@@ -121,6 +123,18 @@ sealed class TypeMirror(val pointedType: KotlinClassifierType, val info: TypeInf
|
||||
class ByRef(pointedType: KotlinClassifierType, info: TypeInfo) : TypeMirror(pointedType, info) {
|
||||
override val argType: KotlinType get() = KotlinTypes.cValue.typeWith(pointedType)
|
||||
}
|
||||
/**
|
||||
* Mirror for C++ Managed type.
|
||||
*/
|
||||
|
||||
class Managed(
|
||||
pointedType: KotlinClassifierType,
|
||||
info: TypeInfo
|
||||
) : TypeMirror(pointedType, info) {
|
||||
|
||||
override val argType: KotlinType
|
||||
get() = pointedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,11 +413,18 @@ private fun byRefTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.ByRe
|
||||
return TypeMirror.ByRef(pointedType, info)
|
||||
}
|
||||
|
||||
private fun managedTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.Managed {
|
||||
val info = TypeInfo.ByRef(pointedType) // These are all errors anyways.
|
||||
return TypeMirror.Managed(pointedType, info)
|
||||
}
|
||||
|
||||
fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) {
|
||||
is PrimitiveType -> mirrorPrimitiveType(type, declarationMapper)
|
||||
|
||||
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type)
|
||||
|
||||
is ManagedType -> managedTypeMirror(declarationMapper.getKotlinClassForManaged(type.decl).type)
|
||||
|
||||
is EnumType -> {
|
||||
val pkg = declarationMapper.getPackageFor(type.def)
|
||||
val kotlinName = declarationMapper.getKotlinNameForValue(type.def)
|
||||
@@ -489,6 +510,11 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
||||
Classifier.topLevel(pkg, name).typeAbbreviation(baseType.pointedType),
|
||||
baseType.info
|
||||
)
|
||||
|
||||
is TypeMirror.Managed -> TypeMirror.Managed(
|
||||
Classifier.topLevel(pkg, name).typeAbbreviation(baseType.pointedType),
|
||||
baseType.info
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +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)
|
||||
StructDef.Kind.CLASS -> null
|
||||
}
|
||||
|
||||
private fun tryRenderStruct(def: StructDef): String? {
|
||||
|
||||
+3
@@ -197,6 +197,7 @@ sealed class AnnotationStub(val classifier: Classifier) {
|
||||
object CString : CCall(cCallClassifier.nested("CString"))
|
||||
object WCString : CCall(cCallClassifier.nested("WCString"))
|
||||
class Symbol(val symbolName: String) : CCall(cCallClassifier)
|
||||
object CppClassConstructor : CCall(cCallClassifier.nested("CppClassConstructor"))
|
||||
}
|
||||
|
||||
class CStruct(val struct: String) : AnnotationStub(cStructClassifier) {
|
||||
@@ -207,6 +208,8 @@ sealed class AnnotationStub(val classifier: Classifier) {
|
||||
class BitField(val offset: Long, val size: Int) : AnnotationStub(cStructClassifier.nested("BitField"))
|
||||
|
||||
class VarType(val size: Long, val align: Int) : AnnotationStub(cStructClassifier.nested("VarType"))
|
||||
|
||||
object ManagedType : AnnotationStub(cStructClassifier.nested("ManagedType"))
|
||||
}
|
||||
|
||||
class CNaturalStruct(val members: List<StructMember>) :
|
||||
|
||||
+2
-16
@@ -63,7 +63,7 @@ class StubIrBridgeBuilder(
|
||||
)
|
||||
|
||||
private val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator, context.libraryForCStubs.language)
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||
|
||||
private val propertyAccessorBridgeBodies = mutableMapOf<PropertyAccessor, String>()
|
||||
private val functionBridgeBodies = mutableMapOf<FunctionStub, List<String>>()
|
||||
@@ -260,9 +260,6 @@ 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)"
|
||||
@@ -292,18 +289,7 @@ class StubIrBridgeBuilder(
|
||||
bridgeArguments,
|
||||
independent = false
|
||||
) { nativeValues ->
|
||||
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()})"
|
||||
}
|
||||
}
|
||||
"${origin.function.name}(${nativeValues.joinToString()})"
|
||||
}
|
||||
bodyGenerator.returnResult(result)
|
||||
functionBridgeBodies[function] = bodyGenerator.build()
|
||||
|
||||
+47
-33
@@ -124,7 +124,7 @@ interface StubsBuildingContext {
|
||||
|
||||
fun getKotlinClassForPointed(structDecl: StructDecl): Classifier
|
||||
|
||||
fun isOverloading(func: FunctionDecl): Boolean
|
||||
fun isOverloading(name: String, types: List<StubType>): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +136,7 @@ internal interface StubElementBuilder {
|
||||
fun build(): List<StubIrElement>
|
||||
}
|
||||
|
||||
class StubsBuildingContextImpl(
|
||||
open class StubsBuildingContextImpl(
|
||||
private val stubIrContext: StubIrContext
|
||||
) : StubsBuildingContext {
|
||||
|
||||
@@ -144,12 +144,20 @@ class StubsBuildingContextImpl(
|
||||
override val platform: KotlinPlatform = stubIrContext.platform
|
||||
override val generationMode: GenerationMode = stubIrContext.generationMode
|
||||
val imports: Imports = stubIrContext.imports
|
||||
private val nativeIndex: NativeIndex = stubIrContext.nativeIndex
|
||||
protected val nativeIndex: NativeIndex = stubIrContext.nativeIndex
|
||||
|
||||
private var theCounter = 0
|
||||
|
||||
private val uniqFunctions = mutableSetOf<String>()
|
||||
override fun isOverloading(func: FunctionDecl) = !uniqFunctions.add(func.name) // TODO: params & return type.
|
||||
|
||||
override fun isOverloading(name: String, types: List<StubType>):Boolean {
|
||||
return if (configuration.library.language == Language.CPP) {
|
||||
val signature = "${name}( ${types.map { it.toString() }.joinToString(", ")} )"
|
||||
!uniqFunctions.add(signature)
|
||||
} else {
|
||||
!uniqFunctions.add(name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateNextUniqueId(prefix: String) =
|
||||
prefix + pkgName.replace('.', '_') + theCounter++
|
||||
@@ -181,34 +189,7 @@ class StubsBuildingContextImpl(
|
||||
|
||||
override val generatedObjCCategoriesMembers = mutableMapOf<ObjCClass, GeneratedObjCCategoriesMembers>()
|
||||
|
||||
override val declarationMapper = object : DeclarationMapper {
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val baseName = structDecl.kotlinName
|
||||
val pkg = when (platform) {
|
||||
KotlinPlatform.JVM -> pkgName
|
||||
KotlinPlatform.NATIVE -> if (structDecl.def == null) {
|
||||
cnamesStructsPackageName // to be imported as forward declaration.
|
||||
} else {
|
||||
getPackageFor(structDecl)
|
||||
}
|
||||
}
|
||||
return Classifier.topLevel(pkg, baseName)
|
||||
}
|
||||
|
||||
override fun isMappedToStrict(enumDef: EnumDef): Boolean = isStrictEnum(enumDef)
|
||||
|
||||
override fun getKotlinNameForValue(enumDef: EnumDef): String = enumDef.kotlinName
|
||||
|
||||
override fun getPackageFor(declaration: TypeDeclaration): String {
|
||||
return imports.getPackage(declaration.location) ?: pkgName
|
||||
}
|
||||
|
||||
override val useUnsignedTypes: Boolean
|
||||
get() = when (platform) {
|
||||
KotlinPlatform.JVM -> false
|
||||
KotlinPlatform.NATIVE -> true
|
||||
}
|
||||
}
|
||||
override val declarationMapper = DeclarationMapperImpl()
|
||||
|
||||
override val macroConstantsByName: Map<String, MacroDef> =
|
||||
(nativeIndex.macroConstants + nativeIndex.wrappedMacros).associateBy { it.name }
|
||||
@@ -264,6 +245,39 @@ class StubsBuildingContextImpl(
|
||||
val classifier = declarationMapper.getKotlinClassForPointed(structDecl)
|
||||
return classifier
|
||||
}
|
||||
|
||||
open inner class DeclarationMapperImpl : DeclarationMapper {
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val baseName = structDecl.kotlinName
|
||||
val pkg = when (platform) {
|
||||
KotlinPlatform.JVM -> pkgName
|
||||
KotlinPlatform.NATIVE -> if (structDecl.def == null) {
|
||||
cnamesStructsPackageName // to be imported as forward declaration.
|
||||
} else {
|
||||
getPackageFor(structDecl)
|
||||
}
|
||||
}
|
||||
return Classifier.topLevel(pkg, baseName)
|
||||
}
|
||||
|
||||
override fun getKotlinClassForManaged(structDecl: StructDecl): Classifier =
|
||||
error("ManagedType requires a plugin")
|
||||
|
||||
override fun isMappedToStrict(enumDef: EnumDef): Boolean = isStrictEnum(enumDef)
|
||||
|
||||
override fun getKotlinNameForValue(enumDef: EnumDef): String = enumDef.kotlinName
|
||||
|
||||
override fun getPackageFor(declaration: TypeDeclaration): String {
|
||||
return imports.getPackage(declaration.location) ?: pkgName
|
||||
}
|
||||
|
||||
override val useUnsignedTypes: Boolean
|
||||
get() = when (platform) {
|
||||
KotlinPlatform.JVM -> false
|
||||
KotlinPlatform.NATIVE -> true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class StubIrBuilderResult(
|
||||
@@ -306,7 +320,7 @@ class StubIrBuilder(private val context: StubIrContext) {
|
||||
private val excludedMacros: Set<String>
|
||||
get() = configuration.excludedMacros
|
||||
|
||||
private val buildingContext = StubsBuildingContextImpl(context)
|
||||
private val buildingContext = context.plugin.stubsBuildingContext(context)
|
||||
|
||||
fun build(): StubIrBuilderResult {
|
||||
nativeIndex.objCProtocols.filter { !it.isForwardDeclaration }.forEach { generateStubsForObjCProtocol(it) }
|
||||
|
||||
+12
-2
@@ -8,6 +8,7 @@ import kotlinx.metadata.klib.KlibModuleMetadata
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.GenerationMode
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.Plugin
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@@ -19,7 +20,8 @@ class StubIrContext(
|
||||
val imports: Imports,
|
||||
val platform: KotlinPlatform,
|
||||
val generationMode: GenerationMode,
|
||||
val libName: String
|
||||
val libName: String,
|
||||
val plugin: Plugin
|
||||
) {
|
||||
val libraryForCStubs = configuration.library.copy(
|
||||
includes = mutableListOf<String>().apply {
|
||||
@@ -109,7 +111,8 @@ class StubIrDriver(
|
||||
val entryPoint: String?,
|
||||
val moduleName: String,
|
||||
val outCFile: File,
|
||||
val outKtFileCreator: () -> File
|
||||
val outKtFileCreator: () -> File,
|
||||
val dumpBridges: Boolean
|
||||
)
|
||||
|
||||
sealed class Result {
|
||||
@@ -128,6 +131,13 @@ class StubIrDriver(
|
||||
emitCFile(context, it, entryPoint, bridgeBuilderResult.nativeBridges)
|
||||
}
|
||||
|
||||
if (options.dumpBridges) {
|
||||
context.log("GENERATED KOTLIN: ${bridgeBuilderResult.nativeBridges.kotlinLines.toList().size}")
|
||||
bridgeBuilderResult.nativeBridges.kotlinLines.forEach { context.log(it) }
|
||||
context.log("GENERATED NATIVE: ${bridgeBuilderResult.nativeBridges.nativeLines.toList().size}")
|
||||
bridgeBuilderResult.nativeBridges.nativeLines.forEach { context.log(it) }
|
||||
}
|
||||
|
||||
return when (context.generationMode) {
|
||||
GenerationMode.SOURCE_CODE -> {
|
||||
emitSourceCode(outKtFile(), builderResult, bridgeBuilderResult)
|
||||
|
||||
+171
-50
@@ -78,14 +78,25 @@ internal class StructStubBuilder(
|
||||
AnnotationStub.CStruct(it)
|
||||
}
|
||||
}
|
||||
val managedAnnotation = if (context.configuration.library.language == Language.CPP
|
||||
&& def.kind == StructDef.Kind.CLASS) {
|
||||
AnnotationStub.CStruct.ManagedType
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val structAnnotations = listOfNotNull(structAnnotation, managedAnnotation)
|
||||
|
||||
val classifier = context.getKotlinClassForPointed(decl)
|
||||
|
||||
var methods: List<FunctionStub> =
|
||||
val methods: List<FunctionStub> =
|
||||
def.methods
|
||||
.filter { it.isCxxInstanceMethod }
|
||||
// TODO: this excludes all similar named methods from all calsses.
|
||||
// Consider using fqnames or something.
|
||||
.filterNot { it.name in context.configuration.excludedFunctions }
|
||||
.map { func ->
|
||||
try {
|
||||
(FunctionStubBuilder(context, func).build() as List<FunctionStub>).single()
|
||||
(FunctionStubBuilder(context, func, skipOverloads = true).build().map { it as FunctionStub }).single()
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
@@ -191,18 +202,52 @@ internal class StructStubBuilder(
|
||||
context.generationMode == GenerationMode.METADATA
|
||||
}
|
||||
|
||||
var classMethods: List<FunctionStub> =
|
||||
val classMethods: List<FunctionStub> =
|
||||
def.methods
|
||||
.filter { !it.isCxxInstanceMethod }
|
||||
.map { func ->
|
||||
try {
|
||||
(FunctionStubBuilder(context, func).build() as List<FunctionStub>).single()
|
||||
FunctionStubBuilder(context, func, skipOverloads = true).build().map { it as FunctionStub }.single()
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
}.filterNotNull()
|
||||
|
||||
// Here's what we have for C++.
|
||||
// Note that we account for constructors twice.
|
||||
// class XXX {
|
||||
// // These go into `methods`
|
||||
// foo()
|
||||
// bar(x, y)
|
||||
//
|
||||
// // These are in the `secondaryConstructors` variable.
|
||||
// // their signatures match the signatures of __init__ modulo `self` parameters.
|
||||
// // The primary constructor will be created for the class the same way as for interop structs.
|
||||
// constructor(z)
|
||||
// constructor(t, u)
|
||||
//
|
||||
// Companion {
|
||||
// // These all go to `classMethods`
|
||||
// __init__(self, z)
|
||||
// __init__(self, t, u)
|
||||
// __destroy__(self)
|
||||
// aStaticMathod()
|
||||
// }
|
||||
// }
|
||||
|
||||
val secondaryConstructors: List<ConstructorStub> =
|
||||
def.methods
|
||||
.filter { it.isCxxConstructor }
|
||||
.map { func ->
|
||||
try {
|
||||
ConstructorStubBuilder(context, func).build().map { it as ConstructorStub }.single()
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
}.filterNotNull()
|
||||
|
||||
val classFields = def.staticFields
|
||||
.map { field -> (GlobalStubBuilder(context, field).build() as List<PropertyStub>).single() }
|
||||
.map { field -> (GlobalStubBuilder(context, field).build().map{ it as PropertyStub }).single() }
|
||||
|
||||
val companion = ClassStub.Companion(
|
||||
companionClassifier,
|
||||
@@ -215,10 +260,10 @@ internal class StructStubBuilder(
|
||||
classifier,
|
||||
origin = origin,
|
||||
properties = fields.filterNotNull() + if (platform == KotlinPlatform.NATIVE) bitFields else emptyList(),
|
||||
constructors = listOf(primaryConstructor),
|
||||
constructors = listOf(primaryConstructor) + secondaryConstructors,
|
||||
methods = methods,
|
||||
modality = ClassStubModality.NONE,
|
||||
annotations = listOfNotNull(structAnnotation),
|
||||
annotations = structAnnotations,
|
||||
superClassInit = superClassInit,
|
||||
companion = companion
|
||||
))
|
||||
@@ -491,18 +536,22 @@ internal class EnumStubBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
internal class FunctionStubBuilder(
|
||||
internal abstract class FunctionalStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val func: FunctionDecl,
|
||||
private val skipOverloads: Boolean = false
|
||||
protected val func: FunctionDecl,
|
||||
protected val skipOverloads: Boolean = false
|
||||
) : StubElementBuilder {
|
||||
|
||||
override fun build(): List<StubIrElement> {
|
||||
val platform = context.platform
|
||||
val parameters = mutableListOf<FunctionParameterStub>()
|
||||
abstract override fun build(): List<StubIrElement>
|
||||
|
||||
fun buildParameters(parameters: MutableList<FunctionParameterStub>, platform: KotlinPlatform): Boolean {
|
||||
var hasStableParameterNames = true
|
||||
func.parameters.forEachIndexed { index, parameter ->
|
||||
val funcParameters = if (func.isCxxInstanceMethod) {
|
||||
func.parameters.drop(1)
|
||||
} else {
|
||||
func.parameters
|
||||
}
|
||||
funcParameters.forEachIndexed { index, parameter ->
|
||||
val parameterName = parameter.name.let {
|
||||
if (it == null || it.isEmpty()) {
|
||||
hasStableParameterNames = false
|
||||
@@ -544,45 +593,13 @@ internal class FunctionStubBuilder(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val returnType = if (func.returnsVoid()) {
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
context.mirror(func.returnType).argType
|
||||
}.toStubIrType()
|
||||
|
||||
if (skipOverloads && context.isOverloading(func))
|
||||
return emptyList()
|
||||
|
||||
val annotations: List<AnnotationStub>
|
||||
val mustBeExternal: Boolean
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
annotations = emptyList()
|
||||
mustBeExternal = false
|
||||
} else {
|
||||
if (func.isVararg) {
|
||||
val type = KotlinTypes.any.makeNullable().toStubIrType()
|
||||
parameters += FunctionParameterStub("variadicArguments", type, isVararg = true)
|
||||
}
|
||||
annotations = listOf(AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${func.name}"))
|
||||
mustBeExternal = true
|
||||
}
|
||||
val functionStub = FunctionStub(
|
||||
func.name,
|
||||
returnType,
|
||||
parameters.toList(),
|
||||
StubOrigin.Function(func),
|
||||
annotations,
|
||||
mustBeExternal,
|
||||
null,
|
||||
MemberStubModality.FINAL,
|
||||
hasStableParameterNames = hasStableParameterNames
|
||||
)
|
||||
return listOf(functionStub)
|
||||
return hasStableParameterNames
|
||||
}
|
||||
|
||||
protected fun buildFunctionAnnotations(func: FunctionDecl, stubName: String = func.name) =
|
||||
listOf(AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${stubName}"))
|
||||
|
||||
private fun FunctionDecl.returnsVoid(): Boolean = this.returnType.unwrapTypedefs() is VoidType
|
||||
protected fun FunctionDecl.returnsVoid(): Boolean = this.returnType.unwrapTypedefs() is VoidType
|
||||
|
||||
private fun representCFunctionParameterAsValuesRef(type: Type): KotlinType? {
|
||||
val pointeeType = when (type) {
|
||||
@@ -638,6 +655,105 @@ internal class FunctionStubBuilder(
|
||||
&& !noStringConversion.contains(function.name)
|
||||
}
|
||||
|
||||
internal class FunctionStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
func: FunctionDecl,
|
||||
skipOverloads: Boolean = false
|
||||
) : FunctionalStubBuilder(context, func, skipOverloads) {
|
||||
|
||||
override fun build(): List<StubIrElement> {
|
||||
val platform = context.platform
|
||||
val parameters = mutableListOf<FunctionParameterStub>()
|
||||
|
||||
val hasStableParameterNames = buildParameters(parameters, platform)
|
||||
|
||||
val returnType = when {
|
||||
func.returnsVoid() -> KotlinTypes.unit
|
||||
else -> context.mirror(func.returnType).argType
|
||||
}.toStubIrType()
|
||||
|
||||
if (skipOverloads && context.isOverloading(func.fullName, parameters.map { it.type }))
|
||||
return emptyList()
|
||||
|
||||
val annotations: List<AnnotationStub>
|
||||
val mustBeExternal: Boolean
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
annotations = emptyList()
|
||||
mustBeExternal = false
|
||||
} else {
|
||||
if (func.isVararg) {
|
||||
val type = KotlinTypes.any.makeNullable().toStubIrType()
|
||||
parameters += FunctionParameterStub("variadicArguments", type, isVararg = true)
|
||||
}
|
||||
annotations = buildFunctionAnnotations(func)
|
||||
mustBeExternal = true
|
||||
}
|
||||
val name = if (context.configuration.library.language == Language.CPP && !func.isCxxMethod) {
|
||||
func.fullName
|
||||
} else {
|
||||
func.name
|
||||
}
|
||||
val functionStub = FunctionStub(
|
||||
name,
|
||||
returnType,
|
||||
parameters.toList(),
|
||||
StubOrigin.Function(func),
|
||||
annotations,
|
||||
mustBeExternal,
|
||||
null,
|
||||
MemberStubModality.FINAL,
|
||||
hasStableParameterNames = hasStableParameterNames
|
||||
)
|
||||
return listOf(functionStub)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ConstructorStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
func: FunctionDecl,
|
||||
skipOverloads: Boolean = false
|
||||
) : FunctionalStubBuilder(context, func, skipOverloads) {
|
||||
|
||||
override fun build(): List<StubIrElement> {
|
||||
if (context.configuration.library.language != Language.CPP) return emptyList() // TODO: Should we assert here?
|
||||
|
||||
val platform = context.platform
|
||||
val parameters = mutableListOf<FunctionParameterStub>()
|
||||
|
||||
val name = func.parentName ?: return emptyList()
|
||||
|
||||
buildParameters(parameters, platform)
|
||||
|
||||
// We build it on the basis of "__init__" member, so drop the "placement" argugment.
|
||||
parameters.removeFirst()
|
||||
|
||||
if (skipOverloads && context.isOverloading(func.fullName, parameters.map { it.type }))
|
||||
return emptyList()
|
||||
|
||||
val annotations =
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
emptyList()
|
||||
} else {
|
||||
if (func.isVararg) {
|
||||
val type = KotlinTypes.any.makeNullable().toStubIrType()
|
||||
parameters += FunctionParameterStub("variadicArguments", type, isVararg = true)
|
||||
}
|
||||
buildFunctionAnnotations(func, name) + AnnotationStub.CCall.CppClassConstructor
|
||||
}
|
||||
|
||||
val result = ConstructorStub(
|
||||
parameters,
|
||||
annotations,
|
||||
isPrimary = false,
|
||||
origin = StubOrigin.Function(func),
|
||||
)
|
||||
|
||||
return listOf(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class GlobalStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val global: GlobalDecl
|
||||
@@ -719,6 +835,7 @@ internal class GlobalStubBuilder(
|
||||
}
|
||||
kind = PropertyStub.Kind.Val(getter)
|
||||
}
|
||||
is TypeMirror.Managed -> error("We don't support managed globals for now")
|
||||
}
|
||||
}
|
||||
return listOf(PropertyStub(global.name, kotlinType.toStubIrType(), kind, origin = origin))
|
||||
@@ -748,6 +865,10 @@ internal class TypedefStubBuilder(
|
||||
val varTypeAliasee = baseMirror.pointedType
|
||||
listOf(TypealiasStub(varType, varTypeAliasee.toStubIrType(), origin))
|
||||
}
|
||||
is TypeMirror.Managed -> {
|
||||
val varTypeAliasee = baseMirror.pointedType
|
||||
listOf(TypealiasStub(varType, varTypeAliasee.toStubIrType(), origin))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-11
@@ -12,17 +12,6 @@ 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
|
||||
|
||||
+2
@@ -413,6 +413,7 @@ private class MappingExtensions(
|
||||
is AnnotationStub.CCall.Symbol -> mapOfNotNull(
|
||||
("id" to symbolName).asAnnotationArgument()
|
||||
)
|
||||
is AnnotationStub.CCall.CppClassConstructor -> emptyMap()
|
||||
is AnnotationStub.CStruct -> mapOfNotNull(
|
||||
("spelling" to struct).asAnnotationArgument()
|
||||
)
|
||||
@@ -446,6 +447,7 @@ private class MappingExtensions(
|
||||
("size" to KmAnnotationArgument.LongValue(size)),
|
||||
("align" to KmAnnotationArgument.IntValue(align))
|
||||
)
|
||||
is AnnotationStub.CStruct.ManagedType -> emptyMap()
|
||||
}
|
||||
return KmAnnotation(classifier.fqNameSerialized, args)
|
||||
}
|
||||
|
||||
+5
-2
@@ -184,8 +184,7 @@ class StubIrTextEmitter(
|
||||
if (element in bridgeBuilderResult.excludedStubs) return
|
||||
|
||||
val header = run {
|
||||
val parameters = (if (element.isCxxInstanceMember()) element.parameters.drop(1) else element.parameters).
|
||||
joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
||||
val parameters = 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 ""
|
||||
@@ -485,10 +484,14 @@ class StubIrTextEmitter(
|
||||
"@CCall.WCString"
|
||||
is AnnotationStub.CCall.Symbol ->
|
||||
"@CCall(${annotationStub.symbolName.quoteAsKotlinLiteral()})"
|
||||
AnnotationStub.CCall.CppClassConstructor ->
|
||||
"@CCall.CppClassConstructor"
|
||||
is AnnotationStub.CStruct ->
|
||||
"@CStruct(${annotationStub.struct.quoteAsKotlinLiteral()})"
|
||||
is AnnotationStub.CNaturalStruct ->
|
||||
"@CNaturalStruct(${annotationStub.members.joinToString { it.name.quoteAsKotlinLiteral() }})"
|
||||
is AnnotationStub.CStruct.ManagedType ->
|
||||
"@CStruct.ManagedType"
|
||||
is AnnotationStub.CLength ->
|
||||
"@CLength(${annotationStub.length})"
|
||||
is AnnotationStub.Deprecated ->
|
||||
|
||||
+7
-3
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.DefaultPlugin
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.Plugin
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
val EnumDef.isAnonymous: Boolean
|
||||
@@ -30,10 +32,10 @@ val StructDecl.isAnonymous: Boolean
|
||||
*
|
||||
* TODO: use libclang to implement?
|
||||
*/
|
||||
fun Type.getStringRepresentation(): String = when (this) {
|
||||
fun Type.getStringRepresentation(plugin: Plugin = DefaultPlugin): String = when (this) {
|
||||
VoidType -> "void"
|
||||
CharType -> "char"
|
||||
CBoolType -> "_Bool"
|
||||
CBoolType -> if (plugin.name == "Skia") "bool" else "_Bool"
|
||||
ObjCBoolType -> "BOOL"
|
||||
is IntegerType -> this.spelling
|
||||
is FloatingType -> this.spelling
|
||||
@@ -50,7 +52,7 @@ fun Type.getStringRepresentation(): String = when (this) {
|
||||
this.def.spelling
|
||||
}
|
||||
|
||||
is Typedef -> this.def.aliased.getStringRepresentation()
|
||||
is Typedef -> this.def.aliased.getStringRepresentation(plugin)
|
||||
|
||||
is ObjCPointer -> when (this) {
|
||||
is ObjCIdType -> "id$protocolQualifier"
|
||||
@@ -60,6 +62,8 @@ fun Type.getStringRepresentation(): String = when (this) {
|
||||
is ObjCBlockPointer -> "id"
|
||||
}
|
||||
|
||||
is ManagedType -> with(plugin) { this@getStringRepresentation.stringRepresentation }
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -31,6 +31,7 @@ const val NOPACK = "nopack"
|
||||
const val COMPILE_SOURCES = "Xcompile-source"
|
||||
const val SHORT_MODULE_NAME = "Xshort-module-name"
|
||||
const val FOREIGN_EXCEPTION_MODE = "Xforeign-exception-mode"
|
||||
const val DUMP_BRIDGES = "Xdump-bridges"
|
||||
|
||||
// TODO: unify camel and snake cases.
|
||||
// Possible solution is to accept both cases
|
||||
@@ -121,6 +122,9 @@ open class CInteropArguments(argParser: ArgParser =
|
||||
|
||||
val foreignExceptionMode by argParser.option(ArgType.String, FOREIGN_EXCEPTION_MODE,
|
||||
description = "Handle native exception in Kotlin: <terminate|objc-wrap>")
|
||||
|
||||
val dumpBridges by argParser.option(ArgType.Boolean, DUMP_BRIDGES,
|
||||
description = "Dump generated bridges")
|
||||
}
|
||||
|
||||
class JSInteropArguments(argParser: ArgParser = ArgParser("jsinterop",
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.*
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
object Plugins {
|
||||
fun plugin(pluginName: String?): Plugin = when (pluginName) {
|
||||
"org.jetbrains.kotlin.native.interop.skia" ->
|
||||
Class.forName("$pluginName.SkiaPlugin").newInstance() as Plugin
|
||||
null -> DefaultPlugin
|
||||
else -> error("Unexected interop plugin: $pluginName")
|
||||
}
|
||||
}
|
||||
|
||||
interface Plugin {
|
||||
val name: String
|
||||
fun buildNativeIndex(library: NativeLibrary, verbose: Boolean): IndexerResult
|
||||
val managedTypePassing: ManagedTypePassing
|
||||
val ManagedType.stringRepresentation: String
|
||||
fun stubsBuildingContext(stubIrContext: StubIrContext): StubsBuildingContext
|
||||
}
|
||||
|
||||
object DefaultPlugin : Plugin {
|
||||
override val name = "Default"
|
||||
override fun buildNativeIndex(library: NativeLibrary, verbose: Boolean): IndexerResult =
|
||||
buildNativeIndexImpl(library, verbose)
|
||||
override val managedTypePassing = ManagedTypePassing()
|
||||
override val ManagedType.stringRepresentation get() = error("ManagedType requires non-default interop plugin")
|
||||
override fun stubsBuildingContext(stubIrContext: StubIrContext) = StubsBuildingContextImpl(stubIrContext)
|
||||
}
|
||||
+20
-19
@@ -102,6 +102,7 @@ private fun List<String>?.isTrue(): Boolean {
|
||||
}
|
||||
|
||||
private fun runCmd(command: Array<String>, verbose: Boolean = false) {
|
||||
if (verbose) println("COMMAND: " + command.joinToString(" "))
|
||||
Command(*command).getOutputLines(true).let { lines ->
|
||||
if (verbose) lines.forEach(::println)
|
||||
}
|
||||
@@ -121,19 +122,22 @@ private fun Properties.putAndRunOnReplace(key: Any, newValue: Any, beforeReplace
|
||||
this[key] = newValue
|
||||
}
|
||||
|
||||
private fun selectNativeLanguage(config: DefFile.DefFileConfig, hintIsCPP: Boolean = false): Language {
|
||||
private fun selectNativeLanguage(config: DefFile.DefFileConfig): Language {
|
||||
val languages = mapOf(
|
||||
"C" to Language.C,
|
||||
"C++" to Language.CPP,
|
||||
"Objective-C" to Language.OBJECTIVE_C
|
||||
)
|
||||
|
||||
// C++ is not publicly supported.
|
||||
val publicLanguages = languages.keys.minus("C++")
|
||||
|
||||
val lang = config.language?.let {
|
||||
languages[it]
|
||||
?: error("Unexpected language '${config.language}'. Possible values are: ${languages.keys.joinToString { "'$it'" }}")
|
||||
?: error("Unexpected language '${config.language}'. Possible values are: ${publicLanguages.joinToString { "'$it'" }}")
|
||||
} ?: Language.C
|
||||
|
||||
return if (lang == Language.C && hintIsCPP) Language.CPP else lang
|
||||
return lang
|
||||
|
||||
}
|
||||
|
||||
@@ -267,7 +271,9 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
|
||||
val library = buildNativeLibrary(tool, def, cinteropArguments, imports)
|
||||
|
||||
val (nativeIndex, compilation) = buildNativeIndex(library, verbose)
|
||||
val plugin = Plugins.plugin(def.config.pluginName)
|
||||
|
||||
val (nativeIndex, compilation) = plugin.buildNativeIndex(library, verbose)
|
||||
|
||||
// Our current approach to arm64_32 support is to compile armv7k version of bitcode
|
||||
// for arm64_32. That's the reason for this substitution.
|
||||
@@ -304,7 +310,7 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
{}
|
||||
}
|
||||
|
||||
val stubIrContext = StubIrContext(logger, configuration, nativeIndex, imports, flavor, mode, libName)
|
||||
val stubIrContext = StubIrContext(logger, configuration, nativeIndex, imports, flavor, mode, libName, plugin)
|
||||
val stubIrOutput = run {
|
||||
val outKtFileCreator = {
|
||||
val outKtFileName = fqParts.last() + ".kt"
|
||||
@@ -313,7 +319,13 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
file.parentFile.mkdirs()
|
||||
file
|
||||
}
|
||||
val driverOptions = StubIrDriver.DriverOptions(entryPoint, moduleName, File(outCFile.absolutePath), outKtFileCreator)
|
||||
val driverOptions = StubIrDriver.DriverOptions(
|
||||
entryPoint,
|
||||
moduleName,
|
||||
File(outCFile.absolutePath),
|
||||
outKtFileCreator,
|
||||
cinteropArguments.dumpBridges ?: false
|
||||
)
|
||||
val stubIrDriver = StubIrDriver(stubIrContext, driverOptions)
|
||||
stubIrDriver.run()
|
||||
}
|
||||
@@ -330,7 +342,6 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
def.manifestAddendProperties["ir_provider"] = KLIB_INTEROP_IR_PROVIDER_IDENTIFIER
|
||||
}
|
||||
stubIrContext.addManifestProperties(def.manifestAddendProperties)
|
||||
|
||||
// cinterop command line option overrides def file property
|
||||
val foreignExceptionMode = cinteropArguments.foreignExceptionMode?: def.config.foreignExceptionMode
|
||||
foreignExceptionMode?.let {
|
||||
@@ -360,7 +371,6 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
val outLib = File(nativeLibsDir, "$libName.bc")
|
||||
val compilerCmd = arrayOf(compiler, *compilerArgs,
|
||||
"-emit-llvm", "-c", outCFile.absolutePath, "-o", outLib.absolutePath)
|
||||
|
||||
runCmd(compilerCmd, verbose)
|
||||
outLib.absolutePath
|
||||
}
|
||||
@@ -453,14 +463,6 @@ 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,
|
||||
@@ -472,8 +474,7 @@ internal fun buildNativeLibrary(
|
||||
arguments.compilerOptions + arguments.compilerOption).toTypedArray()
|
||||
|
||||
val headerFiles = def.config.headers + additionalHeaders
|
||||
val cppOptions = isCxxOptions(def.config.compilerOpts + additionalCompilerOpts)
|
||||
val language = selectNativeLanguage(def.config, cppOptions)
|
||||
val language = selectNativeLanguage(def.config)
|
||||
val compilerOpts: List<String> = mutableListOf<String>().apply {
|
||||
addAll(def.config.compilerOpts)
|
||||
addAll(tool.defaultCompilerOpts)
|
||||
@@ -487,7 +488,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.CPP -> emptyList()
|
||||
Language.OBJECTIVE_C -> {
|
||||
// "Objective-C" within interop means "Objective-C with ARC":
|
||||
listOf("-fobjc-arc")
|
||||
|
||||
Reference in New Issue
Block a user