[Kotlin/Native][Interop] Skia interop plugin for cinterop
This commit is contained in:
@@ -129,7 +129,6 @@ sourceSets {
|
|||||||
srcDirs("prebuilt/nativeInteropStubs/kotlin")
|
srcDirs("prebuilt/nativeInteropStubs/kotlin")
|
||||||
}
|
}
|
||||||
kotlin{
|
kotlin{
|
||||||
|
|
||||||
target {
|
target {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -137,7 +136,6 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile(project(":kotlin-stdlib"))
|
compile(project(":kotlin-stdlib"))
|
||||||
compile(project(":kotlin-native:Interop:Runtime"))
|
compile(project(":kotlin-native:Interop:Runtime"))
|
||||||
|
|||||||
+116
-106
@@ -76,31 +76,7 @@ private class ObjCCategoryImpl(
|
|||||||
override val properties = mutableListOf<ObjCProperty>()
|
override val properties = mutableListOf<ObjCProperty>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public open class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean = false) : NativeIndex() {
|
||||||
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 {
|
private sealed class DeclarationID {
|
||||||
data class USR(val usr: String) : 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)
|
return DeclarationID.USR(usr)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStructDeclAt(
|
protected fun getStructDeclAt(
|
||||||
cursor: CValue<CXCursor>
|
cursor: CValue<CXCursor>
|
||||||
): StructDeclImpl = structRegistry.getOrPut(cursor, { createStructDecl(cursor) }) { decl ->
|
): StructDecl = structRegistry.getOrPut(cursor, { createStructDecl(cursor) }) { decl ->
|
||||||
val definitionCursor = clang_getCursorDefinition(cursor)
|
val definitionCursor = clang_getCursorDefinition(cursor)
|
||||||
if (clang_Cursor_isNull(definitionCursor) == 0) {
|
if (clang_Cursor_isNull(definitionCursor) == 0) {
|
||||||
assert(clang_isCursorDefinition(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))
|
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 (?)
|
// 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, _ ->
|
visitChildren(classCursor) { cursor, _ ->
|
||||||
if (cursor.isPublic) {
|
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
|
// 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) {
|
when (cursor.kind) {
|
||||||
CXCursorKind.CXCursor_CXXMethod -> {
|
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) }
|
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CXCursorKind.CXCursor_Constructor ->
|
CXCursorKind.CXCursor_Constructor,
|
||||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
|
||||||
CXCursorKind.CXCursor_Destructor ->
|
CXCursorKind.CXCursor_Destructor ->
|
||||||
getFunction(cursor, clazz.decl)?.let { clazz.methods.add(it) }
|
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
|
structDef.members += fields
|
||||||
visitClass(cursor, structDef)
|
addCxxMembers(cursor, structDef)
|
||||||
|
|
||||||
structDecl.def = structDef
|
structDecl.def = structDef
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addDeclaredFields(result: MutableList<StructMember>, structType: CValue<CXType>, containerType: CValue<CXType>) {
|
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)
|
val name = getCursorSpelling(fieldCursor)
|
||||||
if (name.isNotEmpty()) {
|
if (name.isNotEmpty()) {
|
||||||
val fieldType = convertCursorType(fieldCursor)
|
val fieldType = convertCursorType(fieldCursor)
|
||||||
@@ -606,7 +588,7 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
else -> UnsupportedType
|
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)
|
val primitiveType = convertUnqualifiedPrimitiveType(type)
|
||||||
if (primitiveType != UnsupportedType) {
|
if (primitiveType != UnsupportedType) {
|
||||||
return primitiveType
|
return primitiveType
|
||||||
@@ -615,7 +597,9 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
val kind = type.kind
|
val kind = type.kind
|
||||||
|
|
||||||
return when (kind) {
|
return when (kind) {
|
||||||
CXType_Elaborated -> convertType(clang_Type_getNamedType(type))
|
CXType_Elaborated -> {
|
||||||
|
convertType(clang_Type_getNamedType(type))
|
||||||
|
}
|
||||||
|
|
||||||
CXType_Unexposed -> {
|
CXType_Unexposed -> {
|
||||||
if (clang_getResultType(type).kind != CXTypeKind.CXType_Invalid) {
|
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_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type)))
|
||||||
|
|
||||||
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type)))
|
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type)))
|
||||||
|
|
||||||
CXType_Pointer, CXType_LValueReference -> {
|
CXType_Pointer, CXType_LValueReference -> {
|
||||||
@@ -861,23 +846,11 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val namespace: Namespace? =
|
if (library.language == Language.CPP && !cursor.isRecursivelyCxxPublic()) {
|
||||||
// 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
|
// c++ : skip anon namespaces, static functions and variables and private inner classes
|
||||||
return
|
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) {
|
when (kind) {
|
||||||
CXIdxEntity_Struct, CXIdxEntity_Union, CXIdxEntity_CXXClass -> {
|
CXIdxEntity_Struct, CXIdxEntity_Union, CXIdxEntity_CXXClass -> {
|
||||||
if (entityName == null) {
|
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)) {
|
if (!library.includesDeclaration(cursor)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cursor.isRecursivelyPublic()) {
|
if (cursor.isRecursivelyCxxPublic()) {
|
||||||
when (cursor.kind) {
|
when (cursor.kind) {
|
||||||
|
|
||||||
CXCursorKind.CXCursor_ClassDecl, CXCursorKind.CXCursor_StructDecl, CXCursorKind.CXCursor_UnionDecl -> {
|
CXCursorKind.CXCursor_ClassDecl, CXCursorKind.CXCursor_StructDecl, CXCursorKind.CXCursor_UnionDecl -> {
|
||||||
if (library.language == Language.CPP) {
|
if (cursor.spelling.isEmpty()) {
|
||||||
if (cursor.spelling.isEmpty()) {
|
// Skip anonymous struct.
|
||||||
// Skip anonymous struct.
|
// (It gets included anyway if used as a named field type).
|
||||||
// (It gets included anyway if used as a named field type).
|
} else {
|
||||||
} else {
|
getStructDeclAt(cursor)
|
||||||
getStructDeclAt(cursor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CXCursorKind.CXCursor_FunctionDecl -> {
|
CXCursorKind.CXCursor_FunctionDecl -> {
|
||||||
if (library.language == Language.CPP) {
|
indexCxxFunction(cursor)
|
||||||
indexCxxFunction(cursor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
@@ -1023,6 +992,32 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
getObjCProtocolAt(cursor)
|
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? {
|
private fun getFunction(cursor: CValue<CXCursor>, receiver: StructDecl? = null): FunctionDecl? {
|
||||||
if (!isFuncDeclEligible(cursor)) {
|
if (!isFuncDeclEligible(cursor)) {
|
||||||
@@ -1030,10 +1025,16 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
var name = clang_getCursorSpelling(cursor).convertAndDispose()
|
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>()
|
val parameters = mutableListOf<Parameter>()
|
||||||
parameters += getFunctionParameters(cursor)
|
parameters += getFunctionParameters(cursor) ?: return null
|
||||||
|
|
||||||
val binaryName = when (library.language) {
|
val binaryName = when (library.language) {
|
||||||
Language.C, Language.CPP, Language.OBJECTIVE_C -> clang_Cursor_getMangling(cursor).convertAndDispose()
|
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 ...
|
// TODO Do the following if clang_getCursorLanguage(cursor) == CXLanguageKind.CXLanguage_CPlusPlus ...
|
||||||
val parentName = getParentName(cursor)
|
val parentName = getParentName(cursor)
|
||||||
val cxxMethodInfo = receiver?.let { CxxMethodInfo(
|
val cxxMethodInfo = receiver?.let {
|
||||||
PointerType(RecordType(receiver),
|
CxxMethodInfo(
|
||||||
clang_CXXMethod_isConst(cursor) != 0), // CXCursor_ConversionFunction has constness too
|
PointerType(RecordType(receiver),
|
||||||
when (cursor.kind) {
|
clang_CXXMethod_isConst(cursor) != 0), // CXCursor_ConversionFunction has constness too
|
||||||
CXCursorKind.CXCursor_Constructor -> {
|
when (cursor.kind) {
|
||||||
returnType = PointerType(RecordType(receiver))
|
CXCursorKind.CXCursor_Constructor -> {
|
||||||
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.
|
returnType = PointerType(RecordType(receiver))
|
||||||
// Parameter type for placement new is void*, but I want to emphasize that memory block ahall have proper size and alignment
|
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.
|
||||||
parameters.add(0, Parameter("self", PointerType(RecordType(receiver)), false))
|
// Parameter type for placement new is void*, but I want to emphasize that memory block ahall have proper size and alignment
|
||||||
CxxMethodKind.Constructor
|
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 (?)
|
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)
|
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 encoding = clang_getDeclObjCTypeEncoding(cursor).convertAndDispose()
|
||||||
val returnType = convertType(clang_getCursorResultType(cursor), clang_getCursorResultTypeAttributes(cursor))
|
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 }) {
|
if (returnType == UnsupportedType || parameters.any { it.type == UnsupportedType }) {
|
||||||
return null // TODO: make a more universal fix.
|
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
|
// 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
|
var ret = true
|
||||||
visitChildren(cursor) { cursor, _ ->
|
visitChildren(cursor) { childCursor, _ ->
|
||||||
when (cursor.kind) {
|
when (childCursor.kind) {
|
||||||
CXCursorKind.CXCursor_TemplateRef -> {
|
CXCursorKind.CXCursor_TemplateRef -> {
|
||||||
ret = false
|
ret = false
|
||||||
CXChildVisitResult.CXChildVisit_Break
|
CXChildVisitResult.CXChildVisit_Break
|
||||||
@@ -1143,10 +1144,13 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
return ret
|
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 argNum = clang_Cursor_getNumArguments(cursor)
|
||||||
val args = (0..argNum - 1).map {
|
val args = (0..argNum - 1).map {
|
||||||
val argCursor = clang_Cursor_getArgument(cursor, it)
|
val argCursor = clang_Cursor_getArgument(cursor, it)
|
||||||
|
if (clang_getTypeSpelling(clang_getCursorType(argCursor)).convertAndDispose().isUnknownTemplate()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
val argName = getCursorSpelling(argCursor)
|
val argName = getCursorSpelling(argCursor)
|
||||||
val type = convertCursorType(argCursor)
|
val type = convertCursorType(argCursor)
|
||||||
Parameter(argName, type,
|
Parameter(argName, type,
|
||||||
@@ -1175,8 +1179,12 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
|
|
||||||
fun buildNativeIndexImpl(library: NativeLibrary, verbose: Boolean): IndexerResult {
|
fun buildNativeIndexImpl(library: NativeLibrary, verbose: Boolean): IndexerResult {
|
||||||
val result = NativeIndexImpl(library, verbose)
|
val result = NativeIndexImpl(library, verbose)
|
||||||
val compilation = indexDeclarations(result)
|
return buildNativeIndexImpl(result)
|
||||||
return IndexerResult(result, compilation)
|
}
|
||||||
|
|
||||||
|
fun buildNativeIndexImpl(index: NativeIndexImpl): IndexerResult {
|
||||||
|
val compilation = indexDeclarations(index)
|
||||||
|
return IndexerResult(index, compilation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH {
|
private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH {
|
||||||
@@ -1210,11 +1218,13 @@ private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
if (nativeIndex.library.language == Language.CPP) {
|
||||||
if (getContainingFile(cursor) in headers) {
|
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
||||||
nativeIndex.indexDeclaration(cursor)
|
if (getContainingFile(cursor) in headers) {
|
||||||
|
nativeIndex.indexCxxDeclaration(cursor)
|
||||||
|
}
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
CXChildVisitResult.CXChildVisit_Recurse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
visitChildren(clang_getTranslationUnitCursor(translationUnit)) { cursor, _ ->
|
||||||
|
|||||||
+10
-14
@@ -62,7 +62,6 @@ data class CompilationWithPCH(
|
|||||||
override val compilerArgs: List<String>,
|
override val compilerArgs: List<String>,
|
||||||
override val language: Language
|
override val language: Language
|
||||||
) : Compilation {
|
) : Compilation {
|
||||||
|
|
||||||
constructor(compilerArgs: List<String>, precompiledHeader: String, language: Language)
|
constructor(compilerArgs: List<String>, precompiledHeader: String, language: Language)
|
||||||
: this(compilerArgs + listOf("-include-pch", precompiledHeader), 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
|
val fullName: String = parentName?.let { "$parentName::$name" } ?: name
|
||||||
|
|
||||||
// C++ virtual or non-virtual instance member, i.e. has "this" receiver
|
// 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
|
* 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
|
val isCxxMethod: Boolean get() = this.cxxMethod != null && this.cxxMethod.kind != CxxMethodKind.None
|
||||||
&& this.cxxMethod.kind != CxxMethodKind.None
|
|
||||||
|
|
||||||
val isCxxConstructor: Boolean = cxxMethod != null && this.cxxMethod.kind == CxxMethodKind.Constructor
|
val isCxxConstructor: Boolean get() = this.cxxMethod?.kind == CxxMethodKind.Constructor
|
||||||
val isCxxDestructor: Boolean = cxxMethod != null && this.cxxMethod.kind == CxxMethodKind.Destructor
|
val isCxxDestructor: Boolean get() = this.cxxMethod?.kind == CxxMethodKind.Destructor
|
||||||
val cxxReceiverType: PointerType? = cxxMethod?.receiverType
|
val cxxReceiverType: PointerType? get() = cxxMethod?.receiverType
|
||||||
val cxxReceiverClass: StructDecl? = cxxMethod?. let { (this.cxxMethod.receiverType.pointeeType as RecordType).decl }
|
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.
|
* 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 WrappedMacroDef(name: String, val type: Type) : MacroDef(name)
|
||||||
|
|
||||||
class GlobalDecl(val name: String, val type: Type, val isConst: Boolean, val parentName: String? = null) {
|
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 RecordType(val decl: StructDecl) : Type
|
||||||
|
|
||||||
|
data class ManagedType(val decl: StructDecl) : Type
|
||||||
|
|
||||||
data class EnumType(val def: EnumDef) : Type
|
data class EnumType(val def: EnumDef) : Type
|
||||||
|
|
||||||
// when pointer type is provided by clang we'll use ots correct spelling
|
// 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.DigestInputStream
|
||||||
import java.security.MessageDigest
|
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>.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 CValue<CXType>.name: String get() = clang_getTypeSpelling(this).convertAndDispose()
|
||||||
internal val CXTypeKind.spelling: String get() = clang_getTypeKindSpelling(this).convertAndDispose()
|
internal val CXTypeKind.spelling: String get() = clang_getTypeKindSpelling(this).convertAndDispose()
|
||||||
internal val CXCursorKind.spelling: String get() = clang_getCursorKindSpelling(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)
|
val access = clang_getCXXAccessSpecifier(this)
|
||||||
return access != CX_CXXAccessSpecifier.CX_CXXProtected && access != CX_CXXAccessSpecifier.CX_CXXPrivate
|
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++)
|
* 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
|
* 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 {
|
when {
|
||||||
clang_isDeclaration(kind) == 0 ->
|
clang_isDeclaration(kind) == 0 ->
|
||||||
return true // got the topmost declaration already
|
return true // got the topmost declaration already
|
||||||
!isPublic ->
|
!isCxxPublic ->
|
||||||
return false
|
return false
|
||||||
kind == CXCursorKind.CXCursor_Namespace && getCursorSpelling(this).isEmpty() ->
|
kind == CXCursorKind.CXCursor_Namespace && getCursorSpelling(this).isEmpty() ->
|
||||||
return false
|
return false
|
||||||
@@ -71,7 +71,7 @@ internal fun CValue<CXCursor>.isRecursivelyPublic(): Boolean {
|
|||||||
// return false; // check disabled for a while
|
// return false; // check disabled for a while
|
||||||
|
|
||||||
else ->
|
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 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)
|
val visitorStableRef = StableRef.create(visitor)
|
||||||
try {
|
try {
|
||||||
val clientData = visitorStableRef.asCPointer()
|
val clientData = visitorStableRef.asCPointer()
|
||||||
@@ -335,7 +335,11 @@ internal fun Compilation.withPrecompiledHeader(translationUnit: CXTranslationUni
|
|||||||
val precompiledHeader = Files.createTempFile(null, ".pch").toFile().apply { this.deleteOnExit() }
|
val precompiledHeader = Files.createTempFile(null, ".pch").toFile().apply { this.deleteOnExit() }
|
||||||
clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0)
|
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 {
|
internal fun NativeLibrary.includesDeclaration(cursor: CValue<CXCursor>): Boolean {
|
||||||
|
|||||||
+9
-1
@@ -23,6 +23,10 @@ annotation class CStruct(val spelling: String) {
|
|||||||
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
annotation class VarType(val size: Long, val align: Int)
|
annotation class VarType(val size: Long, val align: Int)
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class ManagedType
|
||||||
}
|
}
|
||||||
|
|
||||||
@Target(
|
@Target(
|
||||||
@@ -59,6 +63,10 @@ public annotation class CCall(val id: String) {
|
|||||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
annotation class Consumed
|
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)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
public annotation class CEnumVarTypeSize(val size: Int)
|
public annotation class CEnumVarTypeSize(val size: Int)
|
||||||
|
|||||||
@@ -1,12 +1,25 @@
|
|||||||
Current status
|
Skia interop support
|
||||||
==============
|
====================
|
||||||
|
|
||||||
Limited C++ interop provided via plain C wrappers and cinterop mechanism.
|
This is a plugin for cinterop that allows to interop with `Skia Graphics Library`.
|
||||||
|
|
||||||
|
Primarily targeted to be used by Skiko project.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
------
|
------
|
||||||
|
|
||||||
Add `Language = C++` to .def file or `compilerOpts = -x c++` to command line or def file.
|
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++ features
|
||||||
------------
|
------------
|
||||||
@@ -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"
|
mainClassName = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":kotlin-native:Interop:Indexer")
|
implementation project(":kotlin-native:Interop:Indexer")
|
||||||
implementation project(":kotlin-native:utilities:basic-utils")
|
implementation project(":kotlin-native:utilities:basic-utils")
|
||||||
@@ -56,4 +54,4 @@ sourceSets{
|
|||||||
srcDir(VersionGeneratorKt.kotlinNativeVersionSrc(project))
|
srcDir(VersionGeneratorKt.kotlinNativeVersionSrc(project))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+119
-22
@@ -4,13 +4,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.jetbrains.kotlin.native.interop.gen
|
package org.jetbrains.kotlin.native.interop.gen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.FunctionDecl
|
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.GlobalDecl
|
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
|
||||||
|
|
||||||
internal data class CCalleeWrapper(val lines: List<String>)
|
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).
|
* Some functions don't have an address (e.g. macros-based or builtins).
|
||||||
* To solve this problem we generate a wrapper function.
|
* 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++}"
|
return "${packageName}_${functionName}_wrapper${currentFunctionWrapperId++}"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindSymbolToFunction(symbol: String, function: String): List<String> = listOf(
|
private fun bindSymbolToFunction(symbol: String, function: String): List<String> {
|
||||||
"const void* $symbol __asm(${symbol.quoteAsKotlinLiteral()});",
|
val prefix = if (context.configuration.library.language == Language.CPP)
|
||||||
"const void* $symbol = &$function;"
|
"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)
|
private data class Parameter(val type: String, val name: String)
|
||||||
|
|
||||||
@@ -42,34 +51,122 @@ internal class CWrappersGenerator(private val context: StubIrContext) {
|
|||||||
): List<String> = listOf(
|
): List<String> = listOf(
|
||||||
"__attribute__((always_inline))",
|
"__attribute__((always_inline))",
|
||||||
"$returnType $wrapperName(${parameters.joinToString { "${it.type} ${it.name}" }}) {",
|
"$returnType $wrapperName(${parameters.joinToString { "${it.type} ${it.name}" }}) {",
|
||||||
body,
|
"\t$body",
|
||||||
"}",
|
"}",
|
||||||
*bindSymbolToFunction(symbolName, wrapperName).toTypedArray()
|
*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 =
|
fun generateCCalleeWrapper(function: FunctionDecl, symbolName: String): CCalleeWrapper =
|
||||||
if (function.isVararg) {
|
if (function.isVararg) {
|
||||||
CCalleeWrapper(bindSymbolToFunction(symbolName, function.name))
|
CCalleeWrapper(bindSymbolToFunction(symbolName, function.name))
|
||||||
} else {
|
} else {
|
||||||
val wrapperName = generateFunctionWrapperName(function.name)
|
val wrapper = if (context.configuration.library.language == Language.CPP) {
|
||||||
|
createCppCalleeWrapper(function, symbolName)
|
||||||
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
|
|
||||||
} else {
|
} else {
|
||||||
"return $callExpression"
|
createCCalleeWrapper(function, symbolName)
|
||||||
}
|
}
|
||||||
val wrapper = createWrapper(symbolName, wrapperName, returnType, parameters, wrapperBody)
|
|
||||||
CCalleeWrapper(wrapper)
|
CCalleeWrapper(wrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateCGlobalGetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
fun generateCGlobalGetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
||||||
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_getter")
|
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_getter")
|
||||||
val returnType = globalDecl.type.getStringRepresentation()
|
val returnType = globalDecl.type.stringRepresentation
|
||||||
val wrapperBody = "return ${globalDecl.name};"
|
val wrapperBody = "return ${globalDecl.name};"
|
||||||
val wrapper = createWrapper(symbolName, wrapperName, returnType, emptyList(), wrapperBody)
|
val wrapper = createWrapper(symbolName, wrapperName, returnType, emptyList(), wrapperBody)
|
||||||
return CCalleeWrapper(wrapper)
|
return CCalleeWrapper(wrapper)
|
||||||
@@ -85,7 +182,7 @@ internal class CWrappersGenerator(private val context: StubIrContext) {
|
|||||||
|
|
||||||
fun generateCGlobalSetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
fun generateCGlobalSetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
||||||
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_setter")
|
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_setter")
|
||||||
val globalType = globalDecl.type.getStringRepresentation()
|
val globalType = globalDecl.type.stringRepresentation
|
||||||
val parameter = Parameter(globalType, "p1")
|
val parameter = Parameter(globalType, "p1")
|
||||||
val wrapperBody = "${globalDecl.name} = ${parameter.name};"
|
val wrapperBody = "${globalDecl.name} = ${parameter.name};"
|
||||||
val wrapper = createWrapper(symbolName, wrapperName, "void", listOf(parameter), wrapperBody)
|
val wrapper = createWrapper(symbolName, wrapperName, "void", listOf(parameter), wrapperBody)
|
||||||
|
|||||||
+12
-43
@@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.gen
|
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
|
* The [MappingBridgeGenerator] implementation which uses [SimpleBridgeGenerator] as the backend and
|
||||||
@@ -24,8 +27,7 @@ import org.jetbrains.kotlin.native.interop.indexer.*
|
|||||||
*/
|
*/
|
||||||
class MappingBridgeGeneratorImpl(
|
class MappingBridgeGeneratorImpl(
|
||||||
val declarationMapper: DeclarationMapper,
|
val declarationMapper: DeclarationMapper,
|
||||||
val simpleBridgeGenerator: SimpleBridgeGenerator,
|
val simpleBridgeGenerator: SimpleBridgeGenerator
|
||||||
val language: Language
|
|
||||||
) : MappingBridgeGenerator {
|
) : MappingBridgeGenerator {
|
||||||
|
|
||||||
override fun kotlinToNative(
|
override fun kotlinToNative(
|
||||||
@@ -38,12 +40,7 @@ class MappingBridgeGeneratorImpl(
|
|||||||
): KotlinExpression {
|
): KotlinExpression {
|
||||||
val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>()
|
val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>()
|
||||||
|
|
||||||
if (nativeBacked is FunctionStub && nativeBacked.isCxxInstanceMember()) {
|
kotlinValues.forEach { (type, value) ->
|
||||||
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "rawPtr"))
|
|
||||||
kotlinValues.drop(1)
|
|
||||||
} else {
|
|
||||||
kotlinValues
|
|
||||||
}.forEach { (type, value) ->
|
|
||||||
if (type.unwrapTypedefs() is RecordType) {
|
if (type.unwrapTypedefs() is RecordType) {
|
||||||
builder.pushMemScoped()
|
builder.pushMemScoped()
|
||||||
val bridgeArgument = "$value.getPointer(memScope).rawValue"
|
val bridgeArgument = "$value.getPointer(memScope).rawValue"
|
||||||
@@ -82,25 +79,6 @@ class MappingBridgeGeneratorImpl(
|
|||||||
val unwrappedType = type.unwrapTypedefs()
|
val unwrappedType = type.unwrapTypedefs()
|
||||||
if (unwrappedType is RecordType) {
|
if (unwrappedType is RecordType) {
|
||||||
nativeValues.add("*(${unwrappedType.decl.spelling}*)${bridgeNativeValues[index]}")
|
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 {
|
} else {
|
||||||
nativeValues.add(
|
nativeValues.add(
|
||||||
mirror(declarationMapper, type).info.cFromBridged(
|
mirror(declarationMapper, type).info.cFromBridged(
|
||||||
@@ -112,27 +90,18 @@ class MappingBridgeGeneratorImpl(
|
|||||||
|
|
||||||
val nativeResult = block(nativeValues)
|
val nativeResult = block(nativeValues)
|
||||||
|
|
||||||
when {
|
when (unwrappedReturnType) {
|
||||||
unwrappedReturnType is VoidType -> {
|
is VoidType -> {
|
||||||
out(nativeResult + ";")
|
out(nativeResult + ";")
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
unwrappedReturnType is RecordType -> {
|
is RecordType -> {
|
||||||
val kniStructResult = "kniStructResult"
|
val kniStructResult = "kniStructResult"
|
||||||
|
|
||||||
if (language == Language.CPP) {
|
out("${unwrappedReturnType.decl.spelling} $kniStructResult = $nativeResult;")
|
||||||
// use copy/move constructor to create object in place.
|
out("memcpy(${bridgeNativeValues.last()}, &$kniStructResult, sizeof($kniStructResult));")
|
||||||
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 -> {
|
else -> {
|
||||||
nativeResult
|
nativeResult
|
||||||
}
|
}
|
||||||
@@ -235,4 +204,4 @@ class MappingBridgeGeneratorImpl(
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+26
@@ -25,6 +25,8 @@ interface DeclarationMapper {
|
|||||||
fun getPackageFor(declaration: TypeDeclaration): String
|
fun getPackageFor(declaration: TypeDeclaration): String
|
||||||
|
|
||||||
val useUnsignedTypes: Boolean
|
val useUnsignedTypes: Boolean
|
||||||
|
|
||||||
|
fun getKotlinClassForManaged(structDecl: StructDecl): Classifier
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DeclarationMapper.isMappedToSigned(integerType: IntegerType): Boolean = integerType.isSigned || !useUnsignedTypes
|
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) {
|
class ByRef(pointedType: KotlinClassifierType, info: TypeInfo) : TypeMirror(pointedType, info) {
|
||||||
override val argType: KotlinType get() = KotlinTypes.cValue.typeWith(pointedType)
|
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)
|
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) {
|
fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) {
|
||||||
is PrimitiveType -> mirrorPrimitiveType(type, declarationMapper)
|
is PrimitiveType -> mirrorPrimitiveType(type, declarationMapper)
|
||||||
|
|
||||||
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type)
|
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type)
|
||||||
|
|
||||||
|
is ManagedType -> managedTypeMirror(declarationMapper.getKotlinClassForManaged(type.decl).type)
|
||||||
|
|
||||||
is EnumType -> {
|
is EnumType -> {
|
||||||
val pkg = declarationMapper.getPackageFor(type.def)
|
val pkg = declarationMapper.getPackageFor(type.def)
|
||||||
val kotlinName = declarationMapper.getKotlinNameForValue(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),
|
Classifier.topLevel(pkg, name).typeAbbreviation(baseType.pointedType),
|
||||||
baseType.info
|
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) {
|
fun tryRenderStructOrUnion(def: StructDef): String? = when (def.kind) {
|
||||||
StructDef.Kind.STRUCT -> tryRenderStruct(def)
|
StructDef.Kind.STRUCT -> tryRenderStruct(def)
|
||||||
StructDef.Kind.UNION -> tryRenderUnion(def)
|
StructDef.Kind.UNION -> tryRenderUnion(def)
|
||||||
StructDef.Kind.CLASS -> tryRenderStruct(def)
|
StructDef.Kind.CLASS -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryRenderStruct(def: StructDef): String? {
|
private fun tryRenderStruct(def: StructDef): String? {
|
||||||
|
|||||||
+3
@@ -197,6 +197,7 @@ sealed class AnnotationStub(val classifier: Classifier) {
|
|||||||
object CString : CCall(cCallClassifier.nested("CString"))
|
object CString : CCall(cCallClassifier.nested("CString"))
|
||||||
object WCString : CCall(cCallClassifier.nested("WCString"))
|
object WCString : CCall(cCallClassifier.nested("WCString"))
|
||||||
class Symbol(val symbolName: String) : CCall(cCallClassifier)
|
class Symbol(val symbolName: String) : CCall(cCallClassifier)
|
||||||
|
object CppClassConstructor : CCall(cCallClassifier.nested("CppClassConstructor"))
|
||||||
}
|
}
|
||||||
|
|
||||||
class CStruct(val struct: String) : AnnotationStub(cStructClassifier) {
|
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 BitField(val offset: Long, val size: Int) : AnnotationStub(cStructClassifier.nested("BitField"))
|
||||||
|
|
||||||
class VarType(val size: Long, val align: Int) : AnnotationStub(cStructClassifier.nested("VarType"))
|
class VarType(val size: Long, val align: Int) : AnnotationStub(cStructClassifier.nested("VarType"))
|
||||||
|
|
||||||
|
object ManagedType : AnnotationStub(cStructClassifier.nested("ManagedType"))
|
||||||
}
|
}
|
||||||
|
|
||||||
class CNaturalStruct(val members: List<StructMember>) :
|
class CNaturalStruct(val members: List<StructMember>) :
|
||||||
|
|||||||
+2
-16
@@ -63,7 +63,7 @@ class StubIrBridgeBuilder(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val mappingBridgeGenerator: MappingBridgeGenerator =
|
private val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator, context.libraryForCStubs.language)
|
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||||
|
|
||||||
private val propertyAccessorBridgeBodies = mutableMapOf<PropertyAccessor, String>()
|
private val propertyAccessorBridgeBodies = mutableMapOf<PropertyAccessor, String>()
|
||||||
private val functionBridgeBodies = mutableMapOf<FunctionStub, List<String>>()
|
private val functionBridgeBodies = mutableMapOf<FunctionStub, List<String>>()
|
||||||
@@ -260,9 +260,6 @@ class StubIrBridgeBuilder(
|
|||||||
isVararg = isVararg or parameter.isVararg
|
isVararg = isVararg or parameter.isVararg
|
||||||
val parameterName = parameter.name.asSimpleName()
|
val parameterName = parameter.name.asSimpleName()
|
||||||
val bridgeArgument = when {
|
val bridgeArgument = when {
|
||||||
function.isCxxInstanceMember() && index == 0 -> {
|
|
||||||
"rawPtr"
|
|
||||||
}
|
|
||||||
parameter in builderResult.bridgeGenerationComponents.cStringParameters -> {
|
parameter in builderResult.bridgeGenerationComponents.cStringParameters -> {
|
||||||
bodyGenerator.pushMemScoped()
|
bodyGenerator.pushMemScoped()
|
||||||
"$parameterName?.cstr?.getPointer(memScope)"
|
"$parameterName?.cstr?.getPointer(memScope)"
|
||||||
@@ -292,18 +289,7 @@ class StubIrBridgeBuilder(
|
|||||||
bridgeArguments,
|
bridgeArguments,
|
||||||
independent = false
|
independent = false
|
||||||
) { nativeValues ->
|
) { nativeValues ->
|
||||||
with (origin.function) {
|
"${origin.function.name}(${nativeValues.joinToString()})"
|
||||||
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)
|
bodyGenerator.returnResult(result)
|
||||||
functionBridgeBodies[function] = bodyGenerator.build()
|
functionBridgeBodies[function] = bodyGenerator.build()
|
||||||
|
|||||||
+47
-33
@@ -124,7 +124,7 @@ interface StubsBuildingContext {
|
|||||||
|
|
||||||
fun getKotlinClassForPointed(structDecl: StructDecl): Classifier
|
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>
|
fun build(): List<StubIrElement>
|
||||||
}
|
}
|
||||||
|
|
||||||
class StubsBuildingContextImpl(
|
open class StubsBuildingContextImpl(
|
||||||
private val stubIrContext: StubIrContext
|
private val stubIrContext: StubIrContext
|
||||||
) : StubsBuildingContext {
|
) : StubsBuildingContext {
|
||||||
|
|
||||||
@@ -144,12 +144,20 @@ class StubsBuildingContextImpl(
|
|||||||
override val platform: KotlinPlatform = stubIrContext.platform
|
override val platform: KotlinPlatform = stubIrContext.platform
|
||||||
override val generationMode: GenerationMode = stubIrContext.generationMode
|
override val generationMode: GenerationMode = stubIrContext.generationMode
|
||||||
val imports: Imports = stubIrContext.imports
|
val imports: Imports = stubIrContext.imports
|
||||||
private val nativeIndex: NativeIndex = stubIrContext.nativeIndex
|
protected val nativeIndex: NativeIndex = stubIrContext.nativeIndex
|
||||||
|
|
||||||
private var theCounter = 0
|
private var theCounter = 0
|
||||||
|
|
||||||
private val uniqFunctions = mutableSetOf<String>()
|
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) =
|
override fun generateNextUniqueId(prefix: String) =
|
||||||
prefix + pkgName.replace('.', '_') + theCounter++
|
prefix + pkgName.replace('.', '_') + theCounter++
|
||||||
@@ -181,34 +189,7 @@ class StubsBuildingContextImpl(
|
|||||||
|
|
||||||
override val generatedObjCCategoriesMembers = mutableMapOf<ObjCClass, GeneratedObjCCategoriesMembers>()
|
override val generatedObjCCategoriesMembers = mutableMapOf<ObjCClass, GeneratedObjCCategoriesMembers>()
|
||||||
|
|
||||||
override val declarationMapper = object : DeclarationMapper {
|
override val declarationMapper = DeclarationMapperImpl()
|
||||||
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 macroConstantsByName: Map<String, MacroDef> =
|
override val macroConstantsByName: Map<String, MacroDef> =
|
||||||
(nativeIndex.macroConstants + nativeIndex.wrappedMacros).associateBy { it.name }
|
(nativeIndex.macroConstants + nativeIndex.wrappedMacros).associateBy { it.name }
|
||||||
@@ -264,6 +245,39 @@ class StubsBuildingContextImpl(
|
|||||||
val classifier = declarationMapper.getKotlinClassForPointed(structDecl)
|
val classifier = declarationMapper.getKotlinClassForPointed(structDecl)
|
||||||
return classifier
|
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(
|
data class StubIrBuilderResult(
|
||||||
@@ -306,7 +320,7 @@ class StubIrBuilder(private val context: StubIrContext) {
|
|||||||
private val excludedMacros: Set<String>
|
private val excludedMacros: Set<String>
|
||||||
get() = configuration.excludedMacros
|
get() = configuration.excludedMacros
|
||||||
|
|
||||||
private val buildingContext = StubsBuildingContextImpl(context)
|
private val buildingContext = context.plugin.stubsBuildingContext(context)
|
||||||
|
|
||||||
fun build(): StubIrBuilderResult {
|
fun build(): StubIrBuilderResult {
|
||||||
nativeIndex.objCProtocols.filter { !it.isForwardDeclaration }.forEach { generateStubsForObjCProtocol(it) }
|
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.GenerationMode
|
||||||
import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration
|
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.KotlinPlatform
|
||||||
|
import org.jetbrains.kotlin.native.interop.gen.jvm.Plugin
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -19,7 +20,8 @@ class StubIrContext(
|
|||||||
val imports: Imports,
|
val imports: Imports,
|
||||||
val platform: KotlinPlatform,
|
val platform: KotlinPlatform,
|
||||||
val generationMode: GenerationMode,
|
val generationMode: GenerationMode,
|
||||||
val libName: String
|
val libName: String,
|
||||||
|
val plugin: Plugin
|
||||||
) {
|
) {
|
||||||
val libraryForCStubs = configuration.library.copy(
|
val libraryForCStubs = configuration.library.copy(
|
||||||
includes = mutableListOf<String>().apply {
|
includes = mutableListOf<String>().apply {
|
||||||
@@ -109,7 +111,8 @@ class StubIrDriver(
|
|||||||
val entryPoint: String?,
|
val entryPoint: String?,
|
||||||
val moduleName: String,
|
val moduleName: String,
|
||||||
val outCFile: File,
|
val outCFile: File,
|
||||||
val outKtFileCreator: () -> File
|
val outKtFileCreator: () -> File,
|
||||||
|
val dumpBridges: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed class Result {
|
sealed class Result {
|
||||||
@@ -128,6 +131,13 @@ class StubIrDriver(
|
|||||||
emitCFile(context, it, entryPoint, bridgeBuilderResult.nativeBridges)
|
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) {
|
return when (context.generationMode) {
|
||||||
GenerationMode.SOURCE_CODE -> {
|
GenerationMode.SOURCE_CODE -> {
|
||||||
emitSourceCode(outKtFile(), builderResult, bridgeBuilderResult)
|
emitSourceCode(outKtFile(), builderResult, bridgeBuilderResult)
|
||||||
|
|||||||
+171
-50
@@ -78,14 +78,25 @@ internal class StructStubBuilder(
|
|||||||
AnnotationStub.CStruct(it)
|
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)
|
val classifier = context.getKotlinClassForPointed(decl)
|
||||||
|
|
||||||
var methods: List<FunctionStub> =
|
val methods: List<FunctionStub> =
|
||||||
def.methods
|
def.methods
|
||||||
.filter { it.isCxxInstanceMethod }
|
.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 ->
|
.map { func ->
|
||||||
try {
|
try {
|
||||||
(FunctionStubBuilder(context, func).build() as List<FunctionStub>).single()
|
(FunctionStubBuilder(context, func, skipOverloads = true).build().map { it as FunctionStub }).single()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -191,18 +202,52 @@ internal class StructStubBuilder(
|
|||||||
context.generationMode == GenerationMode.METADATA
|
context.generationMode == GenerationMode.METADATA
|
||||||
}
|
}
|
||||||
|
|
||||||
var classMethods: List<FunctionStub> =
|
val classMethods: List<FunctionStub> =
|
||||||
def.methods
|
def.methods
|
||||||
.filter { !it.isCxxInstanceMethod }
|
.filter { !it.isCxxInstanceMethod }
|
||||||
.map { func ->
|
.map { func ->
|
||||||
try {
|
try {
|
||||||
(FunctionStubBuilder(context, func).build() as List<FunctionStub>).single()
|
FunctionStubBuilder(context, func, skipOverloads = true).build().map { it as FunctionStub }.single()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}.filterNotNull()
|
}.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
|
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(
|
val companion = ClassStub.Companion(
|
||||||
companionClassifier,
|
companionClassifier,
|
||||||
@@ -215,10 +260,10 @@ internal class StructStubBuilder(
|
|||||||
classifier,
|
classifier,
|
||||||
origin = origin,
|
origin = origin,
|
||||||
properties = fields.filterNotNull() + if (platform == KotlinPlatform.NATIVE) bitFields else emptyList(),
|
properties = fields.filterNotNull() + if (platform == KotlinPlatform.NATIVE) bitFields else emptyList(),
|
||||||
constructors = listOf(primaryConstructor),
|
constructors = listOf(primaryConstructor) + secondaryConstructors,
|
||||||
methods = methods,
|
methods = methods,
|
||||||
modality = ClassStubModality.NONE,
|
modality = ClassStubModality.NONE,
|
||||||
annotations = listOfNotNull(structAnnotation),
|
annotations = structAnnotations,
|
||||||
superClassInit = superClassInit,
|
superClassInit = superClassInit,
|
||||||
companion = companion
|
companion = companion
|
||||||
))
|
))
|
||||||
@@ -491,18 +536,22 @@ internal class EnumStubBuilder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class FunctionStubBuilder(
|
internal abstract class FunctionalStubBuilder(
|
||||||
override val context: StubsBuildingContext,
|
override val context: StubsBuildingContext,
|
||||||
private val func: FunctionDecl,
|
protected val func: FunctionDecl,
|
||||||
private val skipOverloads: Boolean = false
|
protected val skipOverloads: Boolean = false
|
||||||
) : StubElementBuilder {
|
) : StubElementBuilder {
|
||||||
|
|
||||||
override fun build(): List<StubIrElement> {
|
abstract override fun build(): List<StubIrElement>
|
||||||
val platform = context.platform
|
|
||||||
val parameters = mutableListOf<FunctionParameterStub>()
|
|
||||||
|
|
||||||
|
fun buildParameters(parameters: MutableList<FunctionParameterStub>, platform: KotlinPlatform): Boolean {
|
||||||
var hasStableParameterNames = true
|
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 {
|
val parameterName = parameter.name.let {
|
||||||
if (it == null || it.isEmpty()) {
|
if (it == null || it.isEmpty()) {
|
||||||
hasStableParameterNames = false
|
hasStableParameterNames = false
|
||||||
@@ -544,45 +593,13 @@ internal class FunctionStubBuilder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return hasStableParameterNames
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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? {
|
private fun representCFunctionParameterAsValuesRef(type: Type): KotlinType? {
|
||||||
val pointeeType = when (type) {
|
val pointeeType = when (type) {
|
||||||
@@ -638,6 +655,105 @@ internal class FunctionStubBuilder(
|
|||||||
&& !noStringConversion.contains(function.name)
|
&& !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(
|
internal class GlobalStubBuilder(
|
||||||
override val context: StubsBuildingContext,
|
override val context: StubsBuildingContext,
|
||||||
private val global: GlobalDecl
|
private val global: GlobalDecl
|
||||||
@@ -719,6 +835,7 @@ internal class GlobalStubBuilder(
|
|||||||
}
|
}
|
||||||
kind = PropertyStub.Kind.Val(getter)
|
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))
|
return listOf(PropertyStub(global.name, kotlinType.toStubIrType(), kind, origin = origin))
|
||||||
@@ -748,6 +865,10 @@ internal class TypedefStubBuilder(
|
|||||||
val varTypeAliasee = baseMirror.pointedType
|
val varTypeAliasee = baseMirror.pointedType
|
||||||
listOf(TypealiasStub(varType, varTypeAliasee.toStubIrType(), origin))
|
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 &&
|
fun FunctionStub.isOptionalObjCMethod(): Boolean = this.origin is StubOrigin.ObjCMethod &&
|
||||||
this.origin.isOptional
|
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
|
val StubContainer.isInterface: Boolean
|
||||||
get() = if (this is ClassStub.Simple) {
|
get() = if (this is ClassStub.Simple) {
|
||||||
modality == ClassStubModality.INTERFACE
|
modality == ClassStubModality.INTERFACE
|
||||||
|
|||||||
+2
@@ -413,6 +413,7 @@ private class MappingExtensions(
|
|||||||
is AnnotationStub.CCall.Symbol -> mapOfNotNull(
|
is AnnotationStub.CCall.Symbol -> mapOfNotNull(
|
||||||
("id" to symbolName).asAnnotationArgument()
|
("id" to symbolName).asAnnotationArgument()
|
||||||
)
|
)
|
||||||
|
is AnnotationStub.CCall.CppClassConstructor -> emptyMap()
|
||||||
is AnnotationStub.CStruct -> mapOfNotNull(
|
is AnnotationStub.CStruct -> mapOfNotNull(
|
||||||
("spelling" to struct).asAnnotationArgument()
|
("spelling" to struct).asAnnotationArgument()
|
||||||
)
|
)
|
||||||
@@ -446,6 +447,7 @@ private class MappingExtensions(
|
|||||||
("size" to KmAnnotationArgument.LongValue(size)),
|
("size" to KmAnnotationArgument.LongValue(size)),
|
||||||
("align" to KmAnnotationArgument.IntValue(align))
|
("align" to KmAnnotationArgument.IntValue(align))
|
||||||
)
|
)
|
||||||
|
is AnnotationStub.CStruct.ManagedType -> emptyMap()
|
||||||
}
|
}
|
||||||
return KmAnnotation(classifier.fqNameSerialized, args)
|
return KmAnnotation(classifier.fqNameSerialized, args)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -184,8 +184,7 @@ class StubIrTextEmitter(
|
|||||||
if (element in bridgeBuilderResult.excludedStubs) return
|
if (element in bridgeBuilderResult.excludedStubs) return
|
||||||
|
|
||||||
val header = run {
|
val header = run {
|
||||||
val parameters = (if (element.isCxxInstanceMember()) element.parameters.drop(1) else element.parameters).
|
val parameters = element.parameters.joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
||||||
joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
|
||||||
val receiver = element.receiver?.let { renderFunctionReceiver(it) + "." } ?: ""
|
val receiver = element.receiver?.let { renderFunctionReceiver(it) + "." } ?: ""
|
||||||
val typeParameters = renderTypeParameters(element.typeParameters)
|
val typeParameters = renderTypeParameters(element.typeParameters)
|
||||||
val override = if (element.isOverride) "override " else ""
|
val override = if (element.isOverride) "override " else ""
|
||||||
@@ -485,10 +484,14 @@ class StubIrTextEmitter(
|
|||||||
"@CCall.WCString"
|
"@CCall.WCString"
|
||||||
is AnnotationStub.CCall.Symbol ->
|
is AnnotationStub.CCall.Symbol ->
|
||||||
"@CCall(${annotationStub.symbolName.quoteAsKotlinLiteral()})"
|
"@CCall(${annotationStub.symbolName.quoteAsKotlinLiteral()})"
|
||||||
|
AnnotationStub.CCall.CppClassConstructor ->
|
||||||
|
"@CCall.CppClassConstructor"
|
||||||
is AnnotationStub.CStruct ->
|
is AnnotationStub.CStruct ->
|
||||||
"@CStruct(${annotationStub.struct.quoteAsKotlinLiteral()})"
|
"@CStruct(${annotationStub.struct.quoteAsKotlinLiteral()})"
|
||||||
is AnnotationStub.CNaturalStruct ->
|
is AnnotationStub.CNaturalStruct ->
|
||||||
"@CNaturalStruct(${annotationStub.members.joinToString { it.name.quoteAsKotlinLiteral() }})"
|
"@CNaturalStruct(${annotationStub.members.joinToString { it.name.quoteAsKotlinLiteral() }})"
|
||||||
|
is AnnotationStub.CStruct.ManagedType ->
|
||||||
|
"@CStruct.ManagedType"
|
||||||
is AnnotationStub.CLength ->
|
is AnnotationStub.CLength ->
|
||||||
"@CLength(${annotationStub.length})"
|
"@CLength(${annotationStub.length})"
|
||||||
is AnnotationStub.Deprecated ->
|
is AnnotationStub.Deprecated ->
|
||||||
|
|||||||
+7
-3
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.gen
|
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.*
|
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||||
|
|
||||||
val EnumDef.isAnonymous: Boolean
|
val EnumDef.isAnonymous: Boolean
|
||||||
@@ -30,10 +32,10 @@ val StructDecl.isAnonymous: Boolean
|
|||||||
*
|
*
|
||||||
* TODO: use libclang to implement?
|
* TODO: use libclang to implement?
|
||||||
*/
|
*/
|
||||||
fun Type.getStringRepresentation(): String = when (this) {
|
fun Type.getStringRepresentation(plugin: Plugin = DefaultPlugin): String = when (this) {
|
||||||
VoidType -> "void"
|
VoidType -> "void"
|
||||||
CharType -> "char"
|
CharType -> "char"
|
||||||
CBoolType -> "_Bool"
|
CBoolType -> if (plugin.name == "Skia") "bool" else "_Bool"
|
||||||
ObjCBoolType -> "BOOL"
|
ObjCBoolType -> "BOOL"
|
||||||
is IntegerType -> this.spelling
|
is IntegerType -> this.spelling
|
||||||
is FloatingType -> this.spelling
|
is FloatingType -> this.spelling
|
||||||
@@ -50,7 +52,7 @@ fun Type.getStringRepresentation(): String = when (this) {
|
|||||||
this.def.spelling
|
this.def.spelling
|
||||||
}
|
}
|
||||||
|
|
||||||
is Typedef -> this.def.aliased.getStringRepresentation()
|
is Typedef -> this.def.aliased.getStringRepresentation(plugin)
|
||||||
|
|
||||||
is ObjCPointer -> when (this) {
|
is ObjCPointer -> when (this) {
|
||||||
is ObjCIdType -> "id$protocolQualifier"
|
is ObjCIdType -> "id$protocolQualifier"
|
||||||
@@ -60,6 +62,8 @@ fun Type.getStringRepresentation(): String = when (this) {
|
|||||||
is ObjCBlockPointer -> "id"
|
is ObjCBlockPointer -> "id"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ManagedType -> with(plugin) { this@getStringRepresentation.stringRepresentation }
|
||||||
|
|
||||||
else -> throw NotImplementedError()
|
else -> throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -31,6 +31,7 @@ const val NOPACK = "nopack"
|
|||||||
const val COMPILE_SOURCES = "Xcompile-source"
|
const val COMPILE_SOURCES = "Xcompile-source"
|
||||||
const val SHORT_MODULE_NAME = "Xshort-module-name"
|
const val SHORT_MODULE_NAME = "Xshort-module-name"
|
||||||
const val FOREIGN_EXCEPTION_MODE = "Xforeign-exception-mode"
|
const val FOREIGN_EXCEPTION_MODE = "Xforeign-exception-mode"
|
||||||
|
const val DUMP_BRIDGES = "Xdump-bridges"
|
||||||
|
|
||||||
// TODO: unify camel and snake cases.
|
// TODO: unify camel and snake cases.
|
||||||
// Possible solution is to accept both 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,
|
val foreignExceptionMode by argParser.option(ArgType.String, FOREIGN_EXCEPTION_MODE,
|
||||||
description = "Handle native exception in Kotlin: <terminate|objc-wrap>")
|
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",
|
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) {
|
private fun runCmd(command: Array<String>, verbose: Boolean = false) {
|
||||||
|
if (verbose) println("COMMAND: " + command.joinToString(" "))
|
||||||
Command(*command).getOutputLines(true).let { lines ->
|
Command(*command).getOutputLines(true).let { lines ->
|
||||||
if (verbose) lines.forEach(::println)
|
if (verbose) lines.forEach(::println)
|
||||||
}
|
}
|
||||||
@@ -121,19 +122,22 @@ private fun Properties.putAndRunOnReplace(key: Any, newValue: Any, beforeReplace
|
|||||||
this[key] = newValue
|
this[key] = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectNativeLanguage(config: DefFile.DefFileConfig, hintIsCPP: Boolean = false): Language {
|
private fun selectNativeLanguage(config: DefFile.DefFileConfig): Language {
|
||||||
val languages = mapOf(
|
val languages = mapOf(
|
||||||
"C" to Language.C,
|
"C" to Language.C,
|
||||||
"C++" to Language.CPP,
|
"C++" to Language.CPP,
|
||||||
"Objective-C" to Language.OBJECTIVE_C
|
"Objective-C" to Language.OBJECTIVE_C
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// C++ is not publicly supported.
|
||||||
|
val publicLanguages = languages.keys.minus("C++")
|
||||||
|
|
||||||
val lang = config.language?.let {
|
val lang = config.language?.let {
|
||||||
languages[it]
|
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
|
} ?: 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 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
|
// Our current approach to arm64_32 support is to compile armv7k version of bitcode
|
||||||
// for arm64_32. That's the reason for this substitution.
|
// 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 stubIrOutput = run {
|
||||||
val outKtFileCreator = {
|
val outKtFileCreator = {
|
||||||
val outKtFileName = fqParts.last() + ".kt"
|
val outKtFileName = fqParts.last() + ".kt"
|
||||||
@@ -313,7 +319,13 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
|||||||
file.parentFile.mkdirs()
|
file.parentFile.mkdirs()
|
||||||
file
|
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)
|
val stubIrDriver = StubIrDriver(stubIrContext, driverOptions)
|
||||||
stubIrDriver.run()
|
stubIrDriver.run()
|
||||||
}
|
}
|
||||||
@@ -330,7 +342,6 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
|||||||
def.manifestAddendProperties["ir_provider"] = KLIB_INTEROP_IR_PROVIDER_IDENTIFIER
|
def.manifestAddendProperties["ir_provider"] = KLIB_INTEROP_IR_PROVIDER_IDENTIFIER
|
||||||
}
|
}
|
||||||
stubIrContext.addManifestProperties(def.manifestAddendProperties)
|
stubIrContext.addManifestProperties(def.manifestAddendProperties)
|
||||||
|
|
||||||
// cinterop command line option overrides def file property
|
// cinterop command line option overrides def file property
|
||||||
val foreignExceptionMode = cinteropArguments.foreignExceptionMode?: def.config.foreignExceptionMode
|
val foreignExceptionMode = cinteropArguments.foreignExceptionMode?: def.config.foreignExceptionMode
|
||||||
foreignExceptionMode?.let {
|
foreignExceptionMode?.let {
|
||||||
@@ -360,7 +371,6 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
|||||||
val outLib = File(nativeLibsDir, "$libName.bc")
|
val outLib = File(nativeLibsDir, "$libName.bc")
|
||||||
val compilerCmd = arrayOf(compiler, *compilerArgs,
|
val compilerCmd = arrayOf(compiler, *compilerArgs,
|
||||||
"-emit-llvm", "-c", outCFile.absolutePath, "-o", outLib.absolutePath)
|
"-emit-llvm", "-c", outCFile.absolutePath, "-o", outLib.absolutePath)
|
||||||
|
|
||||||
runCmd(compilerCmd, verbose)
|
runCmd(compilerCmd, verbose)
|
||||||
outLib.absolutePath
|
outLib.absolutePath
|
||||||
}
|
}
|
||||||
@@ -453,14 +463,6 @@ internal fun prepareTool(target: String?, flavor: KotlinPlatform): ToolConfig {
|
|||||||
return tool
|
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(
|
internal fun buildNativeLibrary(
|
||||||
tool: ToolConfig,
|
tool: ToolConfig,
|
||||||
def: DefFile,
|
def: DefFile,
|
||||||
@@ -472,8 +474,7 @@ internal fun buildNativeLibrary(
|
|||||||
arguments.compilerOptions + arguments.compilerOption).toTypedArray()
|
arguments.compilerOptions + arguments.compilerOption).toTypedArray()
|
||||||
|
|
||||||
val headerFiles = def.config.headers + additionalHeaders
|
val headerFiles = def.config.headers + additionalHeaders
|
||||||
val cppOptions = isCxxOptions(def.config.compilerOpts + additionalCompilerOpts)
|
val language = selectNativeLanguage(def.config)
|
||||||
val language = selectNativeLanguage(def.config, cppOptions)
|
|
||||||
val compilerOpts: List<String> = mutableListOf<String>().apply {
|
val compilerOpts: List<String> = mutableListOf<String>().apply {
|
||||||
addAll(def.config.compilerOpts)
|
addAll(def.config.compilerOpts)
|
||||||
addAll(tool.defaultCompilerOpts)
|
addAll(tool.defaultCompilerOpts)
|
||||||
@@ -487,7 +488,7 @@ internal fun buildNativeLibrary(
|
|||||||
addAll(getCompilerFlagsForVfsOverlay(arguments.headerFilterPrefix.toTypedArray(), def))
|
addAll(getCompilerFlagsForVfsOverlay(arguments.headerFilterPrefix.toTypedArray(), def))
|
||||||
addAll(when (language) {
|
addAll(when (language) {
|
||||||
Language.C -> emptyList()
|
Language.C -> emptyList()
|
||||||
Language.CPP -> if (cppOptions) emptyList() else listOf("-x", "c++")
|
Language.CPP -> emptyList()
|
||||||
Language.OBJECTIVE_C -> {
|
Language.OBJECTIVE_C -> {
|
||||||
// "Objective-C" within interop means "Objective-C with ARC":
|
// "Objective-C" within interop means "Objective-C with ARC":
|
||||||
listOf("-fobjc-arc")
|
listOf("-fobjc-arc")
|
||||||
|
|||||||
+40
-22
@@ -6,48 +6,66 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
|||||||
import org.jetbrains.kotlin.konan.exec.Command
|
import org.jetbrains.kotlin.konan.exec.Command
|
||||||
import org.jetbrains.kotlin.konan.file.*
|
import org.jetbrains.kotlin.konan.file.*
|
||||||
import org.jetbrains.kotlin.konan.target.ClangArgs
|
import org.jetbrains.kotlin.konan.target.ClangArgs
|
||||||
import org.jetbrains.kotlin.konan.target.Family
|
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
|
||||||
|
private const val dumpBridges = false
|
||||||
|
|
||||||
class CStubsManager(private val target: KonanTarget) {
|
class CStubsManager(private val target: KonanTarget) {
|
||||||
|
|
||||||
fun getUniqueName(prefix: String) = "$prefix${counter++}"
|
fun getUniqueName(prefix: String) = "$prefix${counter++}"
|
||||||
|
|
||||||
fun addStub(kotlinLocation: CompilerMessageLocation?, lines: List<String>) {
|
fun addStub(kotlinLocation: CompilerMessageLocation?, lines: List<String>, language: String) {
|
||||||
|
val stubs = languageToStubs.getOrPut(language) { mutableListOf() }
|
||||||
stubs += Stub(kotlinLocation, lines)
|
stubs += Stub(kotlinLocation, lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compile(clang: ClangArgs, messageCollector: MessageCollector, verbose: Boolean): File? {
|
fun compile(clang: ClangArgs, messageCollector: MessageCollector, verbose: Boolean): List<File> {
|
||||||
if (stubs.isEmpty()) return null
|
if (languageToStubs.isEmpty()) return emptyList()
|
||||||
|
|
||||||
val compilerOptions = mutableListOf<String>()
|
val bitcodes = languageToStubs.entries.map { (language, stubs) ->
|
||||||
val sourceFileExtension = when {
|
val compilerOptions = mutableListOf<String>()
|
||||||
target.family.isAppleFamily -> {
|
val sourceFileExtension = when {
|
||||||
compilerOptions += "-fobjc-arc"
|
language == "C++" -> ".cpp"
|
||||||
".m" // TODO: consider managing C and Objective-C stubs separately.
|
target.family.isAppleFamily -> {
|
||||||
|
compilerOptions += "-fobjc-arc"
|
||||||
|
".m" // TODO: consider managing C and Objective-C stubs separately.
|
||||||
|
}
|
||||||
|
else -> ".c"
|
||||||
}
|
}
|
||||||
else -> ".c"
|
val cSource = createTempFile("cstubs", sourceFileExtension).deleteOnExit()
|
||||||
}
|
cSource.writeLines(stubs.flatMap { it.lines })
|
||||||
val cSource = createTempFile("cstubs", sourceFileExtension).deleteOnExit()
|
|
||||||
cSource.writeLines(stubs.flatMap { it.lines })
|
|
||||||
|
|
||||||
val bitcode = createTempFile("cstubs", ".bc").deleteOnExit()
|
val bitcode = createTempFile("cstubs", ".bc").deleteOnExit()
|
||||||
|
|
||||||
val cSourcePath = cSource.absolutePath
|
val cSourcePath = cSource.absolutePath
|
||||||
|
|
||||||
val clangCommand = clang.clangC(*compilerOptions.toTypedArray(), "-O2",
|
val clangCommand = clang.clangC(
|
||||||
cSourcePath, "-emit-llvm", "-c", "-o", bitcode.absolutePath)
|
*compilerOptions.toTypedArray(), "-O2",
|
||||||
|
cSourcePath, "-emit-llvm", "-c", "-o", bitcode.absolutePath
|
||||||
|
)
|
||||||
|
if (dumpBridges) {
|
||||||
|
println("CSTUBS for ${language}")
|
||||||
|
stubs.flatMap { it.lines }.forEach {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
println("CSTUBS in ${cSource.absolutePath}")
|
||||||
|
println("CSTUBS CLANG COMMAND:")
|
||||||
|
println(clangCommand.joinToString(" "))
|
||||||
|
}
|
||||||
|
|
||||||
val result = Command(clangCommand).getResult(withErrors = true)
|
val result = Command(clangCommand).getResult(withErrors = true)
|
||||||
if (result.exitCode != 0) {
|
if (result.exitCode != 0) {
|
||||||
reportCompilationErrors(cSourcePath, result, messageCollector, verbose)
|
reportCompilationErrors(cSourcePath, stubs, result, messageCollector, verbose)
|
||||||
|
}
|
||||||
|
bitcode
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitcode
|
return bitcodes
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportCompilationErrors(
|
private fun reportCompilationErrors(
|
||||||
cSourcePath: String,
|
cSourcePath: String,
|
||||||
|
stubs: List<Stub>,
|
||||||
result: Command.Result,
|
result: Command.Result,
|
||||||
messageCollector: MessageCollector,
|
messageCollector: MessageCollector,
|
||||||
verbose: Boolean
|
verbose: Boolean
|
||||||
@@ -88,7 +106,7 @@ class CStubsManager(private val target: KonanTarget) {
|
|||||||
throw KonanCompilationException()
|
throw KonanCompilationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val stubs = mutableListOf<Stub>()
|
private val languageToStubs = mutableMapOf<String, MutableList<Stub>>()
|
||||||
private class Stub(val kotlinLocation: CompilerMessageLocation?, val lines: List<String>)
|
private class Stub(val kotlinLocation: CompilerMessageLocation?, val lines: List<String>)
|
||||||
private var counter = 0
|
private var counter = 0
|
||||||
}
|
}
|
||||||
+7
-6
@@ -8,16 +8,13 @@ import llvm.*
|
|||||||
import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion
|
import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
|
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.linkObjC
|
import org.jetbrains.kotlin.backend.konan.llvm.objc.linkObjC
|
||||||
import org.jetbrains.kotlin.konan.CURRENT
|
import org.jetbrains.kotlin.konan.CURRENT
|
||||||
import org.jetbrains.kotlin.library.KotlinAbiVersion
|
|
||||||
import org.jetbrains.kotlin.konan.CompilerVersion
|
import org.jetbrains.kotlin.konan.CompilerVersion
|
||||||
import org.jetbrains.kotlin.konan.file.isBitcode
|
import org.jetbrains.kotlin.konan.file.isBitcode
|
||||||
import org.jetbrains.kotlin.library.*
|
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
|
||||||
import org.jetbrains.kotlin.konan.target.Family
|
|
||||||
import org.jetbrains.kotlin.konan.library.impl.buildLibrary
|
import org.jetbrains.kotlin.konan.library.impl.buildLibrary
|
||||||
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
|
import org.jetbrains.kotlin.library.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supposed to be true for a single LLVM module within final binary.
|
* Supposed to be true for a single LLVM module within final binary.
|
||||||
@@ -48,7 +45,11 @@ val CompilerOutputKind.isCache: Boolean
|
|||||||
|
|
||||||
internal fun produceCStubs(context: Context) {
|
internal fun produceCStubs(context: Context) {
|
||||||
val llvmModule = context.llvmModule!!
|
val llvmModule = context.llvmModule!!
|
||||||
context.cStubsManager.compile(context.config.clang, context.messageCollector, context.inVerbosePhase)?.let {
|
context.cStubsManager.compile(
|
||||||
|
context.config.clang,
|
||||||
|
context.messageCollector,
|
||||||
|
context.inVerbosePhase
|
||||||
|
).forEach {
|
||||||
parseAndLinkBitcodeFile(llvmModule, it.absolutePath)
|
parseAndLinkBitcodeFile(llvmModule, it.absolutePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -116,7 +116,18 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
|||||||
val exportObjCClass = packageScope.getContributedClass("ExportObjCClass")
|
val exportObjCClass = packageScope.getContributedClass("ExportObjCClass")
|
||||||
|
|
||||||
val CreateNSStringFromKString = packageScope.getContributedFunctions("CreateNSStringFromKString").single()
|
val CreateNSStringFromKString = packageScope.getContributedFunctions("CreateNSStringFromKString").single()
|
||||||
|
val nativeHeap = packageScope.getContributedClass("nativeHeap")
|
||||||
|
val cPointed = packageScope.getContributedClass("CPointed")
|
||||||
|
val interopGetPtr = packageScope.getContributedVariables("ptr").single {
|
||||||
|
val singleTypeParameter = it.typeParameters.singleOrNull()
|
||||||
|
val singleTypeParameterUpperBound = singleTypeParameter?.upperBounds?.singleOrNull()
|
||||||
|
val extensionReceiverParameter = it.extensionReceiverParameter
|
||||||
|
|
||||||
|
singleTypeParameterUpperBound != null &&
|
||||||
|
extensionReceiverParameter != null &&
|
||||||
|
TypeUtils.getClassDescriptor(singleTypeParameterUpperBound) == cPointed &&
|
||||||
|
extensionReceiverParameter.type == singleTypeParameter.defaultType
|
||||||
|
}.getter!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MemberScope.getContributedVariables(name: String) =
|
private fun MemberScope.getContributedVariables(name: String) =
|
||||||
|
|||||||
+2
@@ -13,6 +13,8 @@ object RuntimeNames {
|
|||||||
val cStructMemberAt = FqName("kotlinx.cinterop.internal.CStruct.MemberAt")
|
val cStructMemberAt = FqName("kotlinx.cinterop.internal.CStruct.MemberAt")
|
||||||
val cStructArrayMemberAt = FqName("kotlinx.cinterop.internal.CStruct.ArrayMemberAt")
|
val cStructArrayMemberAt = FqName("kotlinx.cinterop.internal.CStruct.ArrayMemberAt")
|
||||||
val cStructBitField = FqName("kotlinx.cinterop.internal.CStruct.BitField")
|
val cStructBitField = FqName("kotlinx.cinterop.internal.CStruct.BitField")
|
||||||
|
val cStruct = FqName("kotlinx.cinterop.internal.CStruct")
|
||||||
|
val managedType = FqName("kotlinx.cinterop.internal.CStruct.ManagedType")
|
||||||
val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
|
val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
|
||||||
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
||||||
val independent = FqName("kotlin.native.internal.Independent")
|
val independent = FqName("kotlin.native.internal.Independent")
|
||||||
|
|||||||
+33
-14
@@ -8,12 +8,9 @@ import org.jetbrains.kotlin.backend.common.lower.at
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.irNot
|
import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||||
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
||||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
|
|
||||||
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation
|
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.getAnnotationArgumentValue
|
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.typeWithStarProjections
|
|
||||||
import org.jetbrains.kotlin.backend.konan.isObjCMetaClass
|
import org.jetbrains.kotlin.backend.konan.isObjCMetaClass
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering
|
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
@@ -50,6 +47,7 @@ internal interface KotlinStubs {
|
|||||||
val irBuiltIns: IrBuiltIns
|
val irBuiltIns: IrBuiltIns
|
||||||
val symbols: KonanSymbols
|
val symbols: KonanSymbols
|
||||||
val target: KonanTarget
|
val target: KonanTarget
|
||||||
|
val language: String
|
||||||
fun addKotlin(declaration: IrDeclaration)
|
fun addKotlin(declaration: IrDeclaration)
|
||||||
fun addC(lines: List<String>)
|
fun addC(lines: List<String>)
|
||||||
fun getUniqueCName(prefix: String): String
|
fun getUniqueCName(prefix: String): String
|
||||||
@@ -111,10 +109,10 @@ private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrMemberA
|
|||||||
transformCall
|
transformCall
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun IrType.isManagedType(): Boolean= this.classOrNull?.owner?.hasAnnotation(RuntimeNames.managedType) ?: false
|
||||||
|
|
||||||
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
|
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
|
||||||
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
|
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
|
||||||
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
|
|
||||||
|
|
||||||
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false, foreignExceptionMode)
|
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false, foreignExceptionMode)
|
||||||
|
|
||||||
val callee = expression.symbol.owner
|
val callee = expression.symbol.owner
|
||||||
@@ -125,6 +123,7 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
|||||||
val targetFunctionName: String
|
val targetFunctionName: String
|
||||||
|
|
||||||
if (isInvoke) {
|
if (isInvoke) {
|
||||||
|
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
|
||||||
targetPtrParameter = callBuilder.passThroughBridge(
|
targetPtrParameter = callBuilder.passThroughBridge(
|
||||||
expression.extensionReceiver!!,
|
expression.extensionReceiver!!,
|
||||||
symbols.interopCPointer.typeWithStarProjections,
|
symbols.interopCPointer.typeWithStarProjections,
|
||||||
@@ -148,7 +147,14 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
|||||||
val arguments = (0 until expression.valueArgumentsCount).map {
|
val arguments = (0 until expression.valueArgumentsCount).map {
|
||||||
expression.getValueArgument(it)
|
expression.getValueArgument(it)
|
||||||
}
|
}
|
||||||
callBuilder.addArguments(arguments, callee)
|
|
||||||
|
val receiverParameter = expression.symbol.owner.dispatchReceiverParameter
|
||||||
|
val self: List<IrExpression> = when {
|
||||||
|
receiverParameter == null -> emptyList()
|
||||||
|
receiverParameter.type.classOrNull?.owner?.isCompanion == true -> emptyList()
|
||||||
|
else -> listOf(expression.dispatchReceiver!!)
|
||||||
|
}
|
||||||
|
callBuilder.addArguments(self + arguments, callee)
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnValuePassing = if (isInvoke) {
|
val returnValuePassing = if (isInvoke) {
|
||||||
@@ -176,7 +182,13 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
|||||||
|
|
||||||
private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, callee: IrFunction) {
|
private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, callee: IrFunction) {
|
||||||
arguments.forEachIndexed { index, argument ->
|
arguments.forEachIndexed { index, argument ->
|
||||||
val parameter = callee.valueParameters[index]
|
val parameter = if (callee.dispatchReceiverParameter != null &&
|
||||||
|
(callee.dispatchReceiverParameter?.type?.isManagedType() == true)) {
|
||||||
|
|
||||||
|
if (index == 0) callee.dispatchReceiverParameter!! else callee.valueParameters[index-1]
|
||||||
|
} else {
|
||||||
|
callee.valueParameters[index]
|
||||||
|
}
|
||||||
if (parameter.isVararg) {
|
if (parameter.isVararg) {
|
||||||
require(index == arguments.lastIndex) { stubs.renderCompilerError(argument) }
|
require(index == arguments.lastIndex) { stubs.renderCompilerError(argument) }
|
||||||
addVariadicArguments(argument)
|
addVariadicArguments(argument)
|
||||||
@@ -472,7 +484,7 @@ private fun CCallbackBuilder.buildCFunction(): String {
|
|||||||
|
|
||||||
val cLines = mutableListOf<String>()
|
val cLines = mutableListOf<String>()
|
||||||
|
|
||||||
cLines += "${cFunctionBuilder.buildSignature(result)} {"
|
cLines += "${cFunctionBuilder.buildSignature(result, stubs.language)} {"
|
||||||
cLines += cBodyLines
|
cLines += cBodyLines
|
||||||
cLines += "}"
|
cLines += "}"
|
||||||
|
|
||||||
@@ -724,12 +736,18 @@ private fun KotlinStubs.mapType(
|
|||||||
val cStructType = getNamedCStructType(kotlinClass)
|
val cStructType = getNamedCStructType(kotlinClass)
|
||||||
require(cStructType != null) { renderCompilerError(location) }
|
require(cStructType != null) { renderCompilerError(location) }
|
||||||
|
|
||||||
StructValuePassing(kotlinClass, cStructType)
|
if (type.isManagedType()) {
|
||||||
|
// TODO: this should probably be better abstracted in a plugin.
|
||||||
|
// For Skia plugin we release sk_sp on the C++ side passing just the raw pointer.
|
||||||
|
// So managed by value is handled as voidPtr here for now.
|
||||||
|
TrivialValuePassing(type, CTypes.voidPtr)
|
||||||
|
} else {
|
||||||
|
StructValuePassing(kotlinClass, cStructType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type.classOrNull?.isSubtypeOfClass(symbols.nativePointed) == true -> {
|
type.classOrNull?.isSubtypeOfClass(symbols.nativePointed) == true ->
|
||||||
TrivialValuePassing(type, CTypes.voidPtr)
|
TrivialValuePassing(type, CTypes.voidPtr)
|
||||||
}
|
|
||||||
|
|
||||||
type.isFunction() -> {
|
type.isFunction() -> {
|
||||||
require(!variadic) { renderCompilerError(location) }
|
require(!variadic) { renderCompilerError(location) }
|
||||||
@@ -738,7 +756,7 @@ private fun KotlinStubs.mapType(
|
|||||||
|
|
||||||
type.isObjCReferenceType(target, irBuiltIns) -> ObjCReferenceValuePassing(symbols, type, retained = retained)
|
type.isObjCReferenceType(target, irBuiltIns) -> ObjCReferenceValuePassing(symbols, type, retained = retained)
|
||||||
|
|
||||||
else -> throwCompilerError(location, "doesn't correspond to any C type")
|
else -> throwCompilerError(location, "doesn't correspond to any C type: ${type.render()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CExpression(val expression: String, val type: CType)
|
private class CExpression(val expression: String, val type: CType)
|
||||||
@@ -883,6 +901,7 @@ private class StructValuePassing(private val kotlinClass: IrClass, override val
|
|||||||
bridgeCallBuilder.prepare += kotlinPointed
|
bridgeCallBuilder.prepare += kotlinPointed
|
||||||
|
|
||||||
val cPointer = passThroughBridge(irGet(kotlinPointed), kotlinPointedType, CTypes.pointer(cType))
|
val cPointer = passThroughBridge(irGet(kotlinPointed), kotlinPointedType, CTypes.pointer(cType))
|
||||||
|
|
||||||
cBridgeBodyLines += "*${cPointer.name} = $expression;"
|
cBridgeBodyLines += "*${cPointer.name} = $expression;"
|
||||||
|
|
||||||
buildKotlinBridgeCall {
|
buildKotlinBridgeCall {
|
||||||
@@ -1258,7 +1277,7 @@ private class ObjCBlockPointerValuePassing(
|
|||||||
|
|
||||||
val block = buildString {
|
val block = buildString {
|
||||||
append('^')
|
append('^')
|
||||||
append(callbackBuilder.cFunctionBuilder.buildSignature(""))
|
append(callbackBuilder.cFunctionBuilder.buildSignature("", stubs.language))
|
||||||
append(" { ")
|
append(" { ")
|
||||||
callbackBuilder.cBodyLines.forEach {
|
callbackBuilder.cBodyLines.forEach {
|
||||||
append(it)
|
append(it)
|
||||||
|
|||||||
+16
-14
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
|||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
|
import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
|
||||||
import org.jetbrains.kotlin.ir.util.constructors
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.irBuilder
|
import org.jetbrains.kotlin.ir.util.irBuilder
|
||||||
import org.jetbrains.kotlin.ir.util.irCatch
|
import org.jetbrains.kotlin.ir.util.irCatch
|
||||||
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
@@ -51,17 +51,19 @@ internal class CFunctionBuilder {
|
|||||||
|
|
||||||
fun getType(): CType = CTypes.function(returnType, parameters.map { it.type }, variadic)
|
fun getType(): CType = CTypes.function(returnType, parameters.map { it.type }, variadic)
|
||||||
|
|
||||||
fun buildSignature(name: String): String = returnType.render(buildString {
|
fun buildSignature(name: String, language: String): String =
|
||||||
append(name)
|
(if (language == "C++") "extern \"C\" const " else "") +
|
||||||
append('(')
|
returnType.render(buildString {
|
||||||
parameters.joinTo(this)
|
append(name)
|
||||||
if (parameters.isEmpty()) {
|
append('(')
|
||||||
if (!variadic) append("void")
|
parameters.joinTo(this)
|
||||||
} else {
|
if (parameters.isEmpty()) {
|
||||||
if (variadic) append(", ...")
|
if (!variadic) append("void")
|
||||||
}
|
} else {
|
||||||
append(')')
|
if (variadic) append(", ...")
|
||||||
})
|
}
|
||||||
|
append(')')
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +147,7 @@ internal class KotlinCBridgeBuilder(
|
|||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
cName: String,
|
cName: String,
|
||||||
stubs: KotlinStubs,
|
val stubs: KotlinStubs,
|
||||||
isKotlinToC: Boolean,
|
isKotlinToC: Boolean,
|
||||||
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default
|
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default
|
||||||
) {
|
) {
|
||||||
@@ -163,7 +165,7 @@ internal class KotlinCBridgeBuilder(
|
|||||||
cBridgeBuilder.setReturnType(cReturnType)
|
cBridgeBuilder.setReturnType(cReturnType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildCSignature(name: String): String = cBridgeBuilder.buildSignature(name)
|
fun buildCSignature(name: String): String = cBridgeBuilder.buildSignature(name, stubs.language)
|
||||||
|
|
||||||
fun buildKotlinBridge() = kotlinBridgeBuilder.build()
|
fun buildKotlinBridge() = kotlinBridgeBuilder.build()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ internal fun IrType.isCEnumType(): Boolean {
|
|||||||
private val cCall = RuntimeNames.cCall
|
private val cCall = RuntimeNames.cCall
|
||||||
|
|
||||||
// Make sure external stubs always get proper annotaions.
|
// Make sure external stubs always get proper annotaions.
|
||||||
private fun IrDeclaration.hasCCallAnnotation(name: String): Boolean =
|
fun IrDeclaration.hasCCallAnnotation(name: String): Boolean =
|
||||||
this.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
this.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
||||||
// LazyIr doesn't pass annotations from descriptor to IrValueParameter.
|
// LazyIr doesn't pass annotations from descriptor to IrValueParameter.
|
||||||
|| this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
|| this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
||||||
|
|||||||
+5
@@ -63,6 +63,7 @@ internal class KonanSymbols(
|
|||||||
val nativePointed = symbolTable.referenceClass(context.interopBuiltIns.nativePointed)
|
val nativePointed = symbolTable.referenceClass(context.interopBuiltIns.nativePointed)
|
||||||
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
|
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
|
||||||
val nonNullNativePtr = symbolTable.referenceClass(context.nonNullNativePtr)
|
val nonNullNativePtr = symbolTable.referenceClass(context.nonNullNativePtr)
|
||||||
|
val nonNullNativePtrType = nonNullNativePtr.typeWith(arguments = emptyList())
|
||||||
|
|
||||||
val immutableBlobOf = symbolTable.referenceSimpleFunction(context.immutableBlobOf)
|
val immutableBlobOf = symbolTable.referenceSimpleFunction(context.immutableBlobOf)
|
||||||
|
|
||||||
@@ -205,6 +206,10 @@ internal class KonanSymbols(
|
|||||||
|
|
||||||
val nativeMemUtils = symbolTable.referenceClass(context.interopBuiltIns.nativeMemUtils)
|
val nativeMemUtils = symbolTable.referenceClass(context.interopBuiltIns.nativeMemUtils)
|
||||||
|
|
||||||
|
val nativeHeap = symbolTable.referenceClass(context.interopBuiltIns.nativeHeap)
|
||||||
|
|
||||||
|
val interopGetPtr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.interopGetPtr)
|
||||||
|
|
||||||
val readBits = interopFunction("readBits")
|
val readBits = interopFunction("readBits")
|
||||||
val writeBits = interopFunction("writeBits")
|
val writeBits = interopFunction("writeBits")
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -148,7 +148,8 @@ internal interface DescriptorToIrTranslationMixin {
|
|||||||
|
|
||||||
private fun IrDeclaration.generateAnnotations() {
|
private fun IrDeclaration.generateAnnotations() {
|
||||||
annotations += descriptor.annotations.map {
|
annotations += descriptor.annotations.map {
|
||||||
typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(it)!!
|
typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(it)
|
||||||
|
?: error("Could not generate annotations for $it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -54,7 +54,7 @@ internal class IrProviderForCEnumAndCStructStubs(
|
|||||||
private val cStructCompanionGenerator =
|
private val cStructCompanionGenerator =
|
||||||
CStructVarCompanionGenerator(context, interopBuiltIns)
|
CStructVarCompanionGenerator(context, interopBuiltIns)
|
||||||
private val cStructClassGenerator =
|
private val cStructClassGenerator =
|
||||||
CStructVarClassGenerator(context, interopBuiltIns, cStructCompanionGenerator)
|
CStructVarClassGenerator(context, interopBuiltIns, cStructCompanionGenerator, symbols)
|
||||||
|
|
||||||
fun isCEnumOrCStruct(declarationDescriptor: DeclarationDescriptor): Boolean =
|
fun isCEnumOrCStruct(declarationDescriptor: DeclarationDescriptor): Boolean =
|
||||||
declarationDescriptor.run { findCEnumDescriptor(interopBuiltIns) ?: findCStructDescriptor(interopBuiltIns) } != null
|
declarationDescriptor.run { findCEnumDescriptor(interopBuiltIns) ?: findCStructDescriptor(interopBuiltIns) } != null
|
||||||
@@ -92,6 +92,10 @@ internal class IrProviderForCEnumAndCStructStubs(
|
|||||||
fun getDeclaration(descriptor: DeclarationDescriptor, idSignature: IdSignature, file: IrFile, symbolKind: BinarySymbolData.SymbolKind): IrSymbolOwner {
|
fun getDeclaration(descriptor: DeclarationDescriptor, idSignature: IdSignature, file: IrFile, symbolKind: BinarySymbolData.SymbolKind): IrSymbolOwner {
|
||||||
return symbolTable.run {
|
return symbolTable.run {
|
||||||
when (symbolKind) {
|
when (symbolKind) {
|
||||||
|
BinarySymbolData.SymbolKind.CONSTRUCTOR_SYMBOL -> declareConstructorFromLinker(descriptor as ClassConstructorDescriptor, idSignature) { s ->
|
||||||
|
generateIrIfNeeded(s, file)
|
||||||
|
s.owner
|
||||||
|
}
|
||||||
BinarySymbolData.SymbolKind.CLASS_SYMBOL -> declareClassFromLinker(descriptor as ClassDescriptor, idSignature) { s ->
|
BinarySymbolData.SymbolKind.CLASS_SYMBOL -> declareClassFromLinker(descriptor as ClassDescriptor, idSignature) { s ->
|
||||||
generateIrIfNeeded(s, file)
|
generateIrIfNeeded(s, file)
|
||||||
s.owner
|
s.owner
|
||||||
|
|||||||
+35
-13
@@ -5,17 +5,15 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
|
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
|
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
|
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||||
|
import org.jetbrains.kotlin.ir.builders.irCall
|
||||||
import org.jetbrains.kotlin.ir.builders.irGet
|
import org.jetbrains.kotlin.ir.builders.irGet
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.builders.irGetObject
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.addMember
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
@@ -24,7 +22,8 @@ import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
|
|||||||
internal class CStructVarClassGenerator(
|
internal class CStructVarClassGenerator(
|
||||||
context: GeneratorContext,
|
context: GeneratorContext,
|
||||||
private val interopBuiltIns: InteropBuiltIns,
|
private val interopBuiltIns: InteropBuiltIns,
|
||||||
private val companionGenerator: CStructVarCompanionGenerator
|
private val companionGenerator: CStructVarCompanionGenerator,
|
||||||
|
private val symbols: KonanSymbols
|
||||||
) : DescriptorToIrTranslationMixin {
|
) : DescriptorToIrTranslationMixin {
|
||||||
|
|
||||||
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
|
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
|
||||||
@@ -48,12 +47,25 @@ internal class CStructVarClassGenerator(
|
|||||||
createClass(descriptor) { irClass ->
|
createClass(descriptor) { irClass ->
|
||||||
irClass.addMember(createPrimaryConstructor(irClass))
|
irClass.addMember(createPrimaryConstructor(irClass))
|
||||||
irClass.addMember(companionGenerator.generate(descriptor))
|
irClass.addMember(companionGenerator.generate(descriptor))
|
||||||
|
descriptor.constructors
|
||||||
|
.filterNot { it.isPrimary }
|
||||||
|
.map {
|
||||||
|
val constructor = createSecondaryConstructor(it)
|
||||||
|
irClass.addMember(constructor)
|
||||||
|
}
|
||||||
descriptor.unsubstitutedMemberScope
|
descriptor.unsubstitutedMemberScope
|
||||||
.getContributedDescriptors()
|
.getContributedDescriptors()
|
||||||
.filterIsInstance<PropertyDescriptor>()
|
.filterIsInstance<CallableMemberDescriptor>()
|
||||||
.filter { it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
|
.filterNot { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
|
||||||
.map(this::createProperty)
|
.map {
|
||||||
.forEach(irClass::addMember)
|
when (it) {
|
||||||
|
is PropertyDescriptor -> createProperty(it)
|
||||||
|
is SimpleFunctionDescriptor -> createFunction(it)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filterNotNull()
|
||||||
|
.forEach(irClass::addMember)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
|
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
|
||||||
@@ -74,4 +86,14 @@ internal class CStructVarClassGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createSecondaryConstructor(descriptor: ClassConstructorDescriptor): IrConstructor {
|
||||||
|
return createConstructor(descriptor).also {
|
||||||
|
postLinkageSteps.add {
|
||||||
|
it.body = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||||
|
// Empty. The real body is constructed at the call site by the interop lowering phase.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+17
@@ -8,12 +8,16 @@ import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
|||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull
|
import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
|
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
|
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||||
import org.jetbrains.kotlin.ir.builders.irInt
|
import org.jetbrains.kotlin.ir.builders.irInt
|
||||||
import org.jetbrains.kotlin.ir.builders.irLong
|
import org.jetbrains.kotlin.ir.builders.irLong
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||||
import org.jetbrains.kotlin.ir.declarations.addMember
|
import org.jetbrains.kotlin.ir.declarations.addMember
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||||
@@ -41,6 +45,19 @@ internal class CStructVarCompanionGenerator(
|
|||||||
val size = annotation.getArgumentValueOrNull<Long>("size")!!
|
val size = annotation.getArgumentValueOrNull<Long>("size")!!
|
||||||
val align = annotation.getArgumentValueOrNull<Int>("align")!!
|
val align = annotation.getArgumentValueOrNull<Int>("align")!!
|
||||||
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, size, align))
|
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, size, align))
|
||||||
|
|
||||||
|
companionIrClass.descriptor.unsubstitutedMemberScope
|
||||||
|
.getContributedDescriptors()
|
||||||
|
.filterIsInstance<CallableMemberDescriptor>()
|
||||||
|
.filterNot { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
|
||||||
|
.mapNotNull {
|
||||||
|
when (it) {
|
||||||
|
is PropertyDescriptor -> createProperty(it)
|
||||||
|
is SimpleFunctionDescriptor -> createFunction(it)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.forEach(companionIrClass::addMember)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, size: Long, align: Int): IrConstructor {
|
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, size: Long, align: Int): IrConstructor {
|
||||||
|
|||||||
+5
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.atMostOne
|
|||||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||||
import org.jetbrains.kotlin.backend.common.lower.*
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.cgen.hasCCallAnnotation
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
@@ -297,6 +298,10 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
|
|||||||
if (declaration.constructedClass.isNativePrimitiveType()) {
|
if (declaration.constructedClass.isNativePrimitiveType()) {
|
||||||
// Constructors for these types aren't used and actually are malformed (e.g. lack the parameter).
|
// Constructors for these types aren't used and actually are malformed (e.g. lack the parameter).
|
||||||
// Skipping here for simplicity.
|
// Skipping here for simplicity.
|
||||||
|
} else if (declaration.hasCCallAnnotation("CppClassConstructor") && !declaration.isPrimary) {
|
||||||
|
// At this point secondary cpp constructor calls have already been transformed
|
||||||
|
// by interop lowering. So don't mess with them.
|
||||||
|
// Otherwise we could assert having assumptions on (empty at the moment) body of the constructor.
|
||||||
} else {
|
} else {
|
||||||
buildLoweredConstructor(declaration)
|
buildLoweredConstructor(declaration)
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-10
@@ -6,10 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.lower
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.*
|
import org.jetbrains.kotlin.backend.common.*
|
||||||
import org.jetbrains.kotlin.backend.common.ir.allParameters
|
import org.jetbrains.kotlin.backend.common.ir.*
|
||||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.*
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.cgen.*
|
import org.jetbrains.kotlin.backend.konan.cgen.*
|
||||||
@@ -17,6 +14,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
|||||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.companionObject
|
import org.jetbrains.kotlin.backend.konan.ir.companionObject
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.isFinalClass
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||||
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
||||||
@@ -45,6 +43,7 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
|
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||||
|
|
||||||
internal class InteropLowering(context: Context) : FileLoweringPass {
|
internal class InteropLowering(context: Context) : FileLoweringPass {
|
||||||
// TODO: merge these lowerings.
|
// TODO: merge these lowerings.
|
||||||
@@ -83,12 +82,19 @@ private abstract class BaseInteropIrTransformer(private val context: Context) :
|
|||||||
override val irBuiltIns get() = context.irBuiltIns
|
override val irBuiltIns get() = context.irBuiltIns
|
||||||
override val symbols get() = context.ir.symbols
|
override val symbols get() = context.ir.symbols
|
||||||
|
|
||||||
|
val klib: KonanLibrary? get() {
|
||||||
|
return (element as? IrCall)?.symbol?.owner?.konanLibrary as? KonanLibrary
|
||||||
|
}
|
||||||
|
|
||||||
|
override val language: String
|
||||||
|
get() = klib?.manifestProperties?.getProperty("language") ?: "C"
|
||||||
|
|
||||||
override fun addKotlin(declaration: IrDeclaration) {
|
override fun addKotlin(declaration: IrDeclaration) {
|
||||||
addTopLevel(declaration)
|
addTopLevel(declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addC(lines: List<String>) {
|
override fun addC(lines: List<String>) {
|
||||||
context.cStubsManager.addStub(location, lines)
|
context.cStubsManager.addStub(location, lines, language)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getUniqueCName(prefix: String) =
|
override fun getUniqueCName(prefix: String) =
|
||||||
@@ -763,6 +769,10 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
if (expression.symbol.owner.hasCCallAnnotation("CppClassConstructor")) {
|
||||||
|
return transformSecondaryCppConstructorCall(expression)
|
||||||
|
}
|
||||||
|
|
||||||
val callee = expression.symbol.owner
|
val callee = expression.symbol.owner
|
||||||
val inlinedClass = callee.returnType.getInlinedClassNative()
|
val inlinedClass = callee.returnType.getInlinedClassNative()
|
||||||
require(inlinedClass?.descriptor != interop.cPointer) { renderCompilerError(expression) }
|
require(inlinedClass?.descriptor != interop.cPointer) { renderCompilerError(expression) }
|
||||||
@@ -793,6 +803,58 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun transformSecondaryCppConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||||
|
val irConstructor = expression.symbol.owner
|
||||||
|
val irClass = irConstructor.constructedClass
|
||||||
|
val primaryConstructor = irClass.primaryConstructor!!.symbol
|
||||||
|
|
||||||
|
// TODO: don't use it is deprecated.
|
||||||
|
val alloc = symbols.interopAllocType
|
||||||
|
val nativeHeap = symbols.nativeHeap
|
||||||
|
val interopGetPtr = symbols.interopGetPtr
|
||||||
|
|
||||||
|
val correspondingInit = irClass.companionObject()!!
|
||||||
|
.declarations
|
||||||
|
.filterIsInstance<IrSimpleFunction>()
|
||||||
|
.filter { it.name.toString() == "__init__"}
|
||||||
|
.filter { it.valueParameters.size == irConstructor.valueParameters.size + 1}
|
||||||
|
.single {
|
||||||
|
it.valueParameters.drop(1).mapIndexed() { index, initParameter ->
|
||||||
|
initParameter.type == irConstructor.valueParameters[index].type
|
||||||
|
}.all{ it }
|
||||||
|
}
|
||||||
|
|
||||||
|
val irBlock = builder.at(expression)
|
||||||
|
.irBlock {
|
||||||
|
val call = irCall(primaryConstructor).also {
|
||||||
|
val nativePointed = irCall(alloc).apply {
|
||||||
|
extensionReceiver = irGetObject(nativeHeap)
|
||||||
|
putValueArgument(0, irGetObject(irClass.companionObject()!!.symbol))
|
||||||
|
}
|
||||||
|
val nativePtr = irCall(symbols.interopNativePointedGetRawPointer).apply {
|
||||||
|
extensionReceiver = nativePointed
|
||||||
|
}
|
||||||
|
it.putValueArgument(0, nativePtr)
|
||||||
|
}
|
||||||
|
val tmp = irTemporary(call)
|
||||||
|
val initCall = irCall(correspondingInit.symbol).apply {
|
||||||
|
putValueArgument(0,
|
||||||
|
irCall(interopGetPtr).apply {
|
||||||
|
extensionReceiver = irGet(tmp)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for (index in 0 until expression.valueArgumentsCount) {
|
||||||
|
putValueArgument(index+1, expression.getValueArgument(index)!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val initCCall = generateCCall(initCall)
|
||||||
|
+initCCall
|
||||||
|
+irGet(tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return irBlock
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle `const val`s that come from interop libraries.
|
* Handle `const val`s that come from interop libraries.
|
||||||
*
|
*
|
||||||
@@ -817,6 +879,16 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
return initializer.shallowCopy()
|
return initializer.shallowCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateCCall(expression: IrCall): IrExpression {
|
||||||
|
val function = expression.symbol.owner
|
||||||
|
|
||||||
|
context.llvmImports.add(function.llvmSymbolOrigin)
|
||||||
|
val exceptionMode = ForeignExceptionMode.byValue(
|
||||||
|
function.konanLibrary?.manifestProperties?.getProperty(ForeignExceptionMode.manifestKey)
|
||||||
|
)
|
||||||
|
return generateWithStubs(expression) { generateCCall(expression, builder, isInvoke = false, exceptionMode) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
val intrinsicType = tryGetIntrinsicType(expression)
|
val intrinsicType = tryGetIntrinsicType(expression)
|
||||||
if (intrinsicType == IntrinsicType.OBJC_INIT_BY) {
|
if (intrinsicType == IntrinsicType.OBJC_INIT_BY) {
|
||||||
@@ -858,11 +930,7 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (function.annotations.hasAnnotation(RuntimeNames.cCall)) {
|
if (function.annotations.hasAnnotation(RuntimeNames.cCall)) {
|
||||||
context.llvmImports.add(function.llvmSymbolOrigin)
|
return generateCCall(expression)
|
||||||
val exceptionMode = ForeignExceptionMode.byValue(
|
|
||||||
function.konanLibrary?.manifestProperties?.getProperty(ForeignExceptionMode.manifestKey)
|
|
||||||
)
|
|
||||||
return generateWithStubs { generateCCall(expression, builder, isInvoke = false, exceptionMode) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val failCompilation = { msg: String -> error(irFile, expression, msg) }
|
val failCompilation = { msg: String -> error(irFile, expression, msg) }
|
||||||
|
|||||||
+2
-1
@@ -322,7 +322,8 @@ private class BackendChecker(val context: Context, val irFile: IrFile) : IrEleme
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callee.returnType.isNativePointed(symbols))
|
if (callee.returnType.isNativePointed(symbols) &&
|
||||||
|
!callee.hasCCallAnnotation("CppClassConstructor"))
|
||||||
reportError(expression, "Native interop types constructors must not be called directly")
|
reportError(expression, "Native interop types constructors must not be called directly")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ externalStdlibTestsDir.mkdirs()
|
|||||||
|
|
||||||
ext.platformManager = project.project(":kotlin-native").platformManager
|
ext.platformManager = project.project(":kotlin-native").platformManager
|
||||||
ext.target = platformManager.targetManager(project.testTarget).target
|
ext.target = platformManager.targetManager(project.testTarget).target
|
||||||
|
ext.llvmHome = platformManager.platform(target).configurables.absoluteLlvmHome
|
||||||
|
|
||||||
ext.testLibraryDir = "${ext.testOutputRoot}/klib/platform/${project.target.name}"
|
ext.testLibraryDir = "${ext.testOutputRoot}/klib/platform/${project.target.name}"
|
||||||
|
|
||||||
@@ -3824,13 +3825,29 @@ createInterop("leakMemoryWithRunningThread") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createInterop("cppClass") {
|
createInterop("cppClass") {
|
||||||
|
if (isAppleTarget(project)) {
|
||||||
|
// TODO: For cpp we need a header with `new`.
|
||||||
|
it.extraOpts "-compiler-option", "-I$llvmHome/include/c++/v1"
|
||||||
|
}
|
||||||
it.defFile 'interop/cpp/cppClass.def'
|
it.defFile 'interop/cpp/cppClass.def'
|
||||||
}
|
}
|
||||||
|
|
||||||
createInterop("cppTypes") {
|
createInterop("cppTypes") {
|
||||||
|
if (isAppleTarget(project)) {
|
||||||
|
// TODO: For cpp we need a header with `new`.
|
||||||
|
it.extraOpts "-compiler-option", "-I$llvmHome/include/c++/v1"
|
||||||
|
}
|
||||||
it.defFile 'interop/cpp/types.def'
|
it.defFile 'interop/cpp/types.def'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createInterop("cppSkia") {
|
||||||
|
if (PlatformInfo.isMac()) {
|
||||||
|
// TODO: For cpp we need a header with `new`.
|
||||||
|
it.extraOpts "-compiler-option", "-I$llvmHome/include/c++/v1"
|
||||||
|
}
|
||||||
|
it.defFile 'interop/cpp/skia.def'
|
||||||
|
}
|
||||||
|
|
||||||
if (PlatformInfo.isAppleTarget(project)) {
|
if (PlatformInfo.isAppleTarget(project)) {
|
||||||
createInterop("objcSmoke") {
|
createInterop("objcSmoke") {
|
||||||
it.defFile 'interop/objc/objcSmoke.def'
|
it.defFile 'interop/objc/objcSmoke.def'
|
||||||
@@ -4202,6 +4219,13 @@ interopTest("interop_cppTypes") {
|
|||||||
interop = 'cppTypes'
|
interop = 'cppTypes'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interopTest("interop_cppSkia") {
|
||||||
|
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||||
|
source = "interop/cpp/skia.kt"
|
||||||
|
interop = 'cppSkia'
|
||||||
|
goldValue = "17 17\n"
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: This test isn't run automatically
|
TODO: This test isn't run automatically
|
||||||
task interop_echo_server(type: RunInteropKonanTest) {
|
task interop_echo_server(type: RunInteropKonanTest) {
|
||||||
|
|||||||
@@ -85,10 +85,12 @@ fun main() {
|
|||||||
val testRun = FeatureTest()
|
val testRun = FeatureTest()
|
||||||
testRun.ctorDefault()
|
testRun.ctorDefault()
|
||||||
testRun.ctorWithParam()
|
testRun.ctorWithParam()
|
||||||
|
|
||||||
val a0 = retByValue(null)
|
// By value for C++ requires further design of stubs mechanism.
|
||||||
println("a0.useContents {iPub} = ${a0.useContents {iPub}}" )
|
// So not supported for now.
|
||||||
println("a0.useContents { foo() } = ${a0.useContents { foo() }}" )
|
//val a0 = retByValue(null)
|
||||||
|
//println("a0.useContents {iPub} = ${a0.useContents {iPub}}" )
|
||||||
|
//println("a0.useContents { foo() } = ${a0.useContents { foo() }}" )
|
||||||
|
|
||||||
// retByValue(null)!!.getValue().foo()
|
// retByValue(null)!!.getValue().foo()
|
||||||
// val a1 = interpretPointed<CppTest>(retByValue(null).rawValue)
|
// val a1 = interpretPointed<CppTest>(retByValue(null).rawValue)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
language = C++
|
||||||
|
compilerOpts = -std=c++17
|
||||||
|
plugin = org.jetbrains.kotlin.native.interop.skia
|
||||||
|
|
||||||
|
---
|
||||||
|
// TODO: this one checks the syntactic aspect for now.
|
||||||
|
// To be updated for proper c++ destructor and
|
||||||
|
// kotlin garbage collection interaction.
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> class sk_sp {
|
||||||
|
public:
|
||||||
|
sk_sp(T* obj) : data(obj) {}
|
||||||
|
T* release() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
T* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> sk_sp<T> sk_ref_sp(T* obj) {
|
||||||
|
return sk_sp<T>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Value {
|
||||||
|
public:
|
||||||
|
int data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public:
|
||||||
|
Foo() { }
|
||||||
|
|
||||||
|
virtual sk_sp<Value> foo(Value *obj) {
|
||||||
|
return sk_sp<Value>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Value* bar(sk_sp<Value> obj) {
|
||||||
|
return obj.release();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
import skia.*
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val f = Foo()
|
||||||
|
val a = nativeHeap.alloc<Value>()
|
||||||
|
a.data = 17
|
||||||
|
val x = f.foo(a.ptr)
|
||||||
|
val z = f.bar(x)
|
||||||
|
println("${a?.data} ${z?.pointed?.data}")
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import kotlin.test.*
|
|||||||
import kotlin.random.*
|
import kotlin.random.*
|
||||||
|
|
||||||
import cpptypes.*
|
import cpptypes.*
|
||||||
|
/*
|
||||||
@Test
|
@Test
|
||||||
fun test_retByValue(k: Int) {
|
fun test_retByValue(k: Int) {
|
||||||
memScoped {
|
memScoped {
|
||||||
@@ -11,6 +11,7 @@ fun test_retByValue(k: Int) {
|
|||||||
assertEquals(k, x.get())
|
assertEquals(k, x.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_retByPtr(k: Int) {
|
fun test_retByPtr(k: Int) {
|
||||||
@@ -35,7 +36,7 @@ fun test_retByRefConst(k: Int) {
|
|||||||
val x = interpretPointed<CppTest>(retByRefConst(k).rawValue)
|
val x = interpretPointed<CppTest>(retByRefConst(k).rawValue)
|
||||||
assertEquals(k, x.get())
|
assertEquals(k, x.get())
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
@Test
|
@Test
|
||||||
fun test_paramByValue(k: Int) {
|
fun test_paramByValue(k: Int) {
|
||||||
val x = nativeHeap.alloc<CppTest>() {}
|
val x = nativeHeap.alloc<CppTest>() {}
|
||||||
@@ -43,7 +44,7 @@ fun test_paramByValue(k: Int) {
|
|||||||
assertEquals(k, paramByValue(x.readValue()))
|
assertEquals(k, paramByValue(x.readValue()))
|
||||||
nativeHeap.free(x)
|
nativeHeap.free(x)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun test_paramByPtr(k: Int) {
|
fun test_paramByPtr(k: Int) {
|
||||||
val x = nativeHeap.alloc<CppTest>() {}
|
val x = nativeHeap.alloc<CppTest>() {}
|
||||||
@@ -80,12 +81,12 @@ fun main() {
|
|||||||
val seed = Random.nextInt()
|
val seed = Random.nextInt()
|
||||||
val r = Random(seed)
|
val r = Random(seed)
|
||||||
|
|
||||||
test_retByValue(r.nextInt())
|
//test_retByValue(r.nextInt())
|
||||||
test_retByPtr(r.nextInt())
|
test_retByPtr(r.nextInt())
|
||||||
test_retByPtrConst(r.nextInt())
|
test_retByPtrConst(r.nextInt())
|
||||||
test_retByRef(r.nextInt())
|
test_retByRef(r.nextInt())
|
||||||
test_retByRefConst(r.nextInt())
|
test_retByRefConst(r.nextInt())
|
||||||
test_paramByValue(r.nextInt())
|
//test_paramByValue(r.nextInt())
|
||||||
test_paramByPtr(r.nextInt())
|
test_paramByPtr(r.nextInt())
|
||||||
test_paramByPtrConst(r.nextInt())
|
test_paramByPtrConst(r.nextInt())
|
||||||
test_paramByRef(r.nextInt())
|
test_paramByRef(r.nextInt())
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ dependencies {
|
|||||||
distPack project(':kotlin-native:Interop:Runtime')
|
distPack project(':kotlin-native:Interop:Runtime')
|
||||||
distPack project(':kotlin-native:Interop:Indexer')
|
distPack project(':kotlin-native:Interop:Indexer')
|
||||||
distPack project(':kotlin-native:Interop:StubGenerator')
|
distPack project(':kotlin-native:Interop:StubGenerator')
|
||||||
|
distPack project(':kotlin-native:Interop:Skia')
|
||||||
distPack project(':kotlin-native:backend.native')
|
distPack project(':kotlin-native:backend.native')
|
||||||
distPack project(':kotlin-native:utilities:cli-runner')
|
distPack project(':kotlin-native:utilities:cli-runner')
|
||||||
distPack project(':kotlin-native:utilities:basic-utils')
|
distPack project(':kotlin-native:utilities:basic-utils')
|
||||||
|
|||||||
+2
@@ -59,6 +59,8 @@ object DefaultDeclarationHeaderRenderer : DeclarationHeaderRenderer {
|
|||||||
val DEFAULT = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions {
|
val DEFAULT = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions {
|
||||||
modifiers = DescriptorRendererModifier.ALL
|
modifiers = DescriptorRendererModifier.ALL
|
||||||
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
|
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
|
||||||
|
|
||||||
|
eachAnnotationOnNewLine = true
|
||||||
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
|
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
|
||||||
excludedAnnotationClasses += StandardNames.FqNames.suppress
|
excludedAnnotationClasses += StandardNames.FqNames.suppress
|
||||||
|
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
Что я хотел, и что из этого вышло
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
Я думал, что быстро прикостыляю базовый С++ к cinterop, используя plain C обёртки над CXXMethod и передавая адрес объекта (receiver) дополнительным (скрытым) параметром. Так сделан C++ runtime и так сделана куча биндингов в C++ из разных языков. Так что - должно получиться.
|
|
||||||
|
|
||||||
Вышло сплошное разочаровние. Если обобщить все встреченные беды - вывод такой: существующий cinterop не годится для этого от слова "совсем". Что обидно: если бы я не тратил время на борьбу с cinterop - я бы, наверное, за это время написал прототип с нуля. Делать глубокий рефакторинг в рамках тестового задания я счёл неправильным - я не имею возможности даже обсудить грабли, тем более - протестить то что получится. (на самом деле, бОльшую часть кода и инфраструктуры cinterop можно переиспользовать, но рефаторинг коснётся всех сценариев - так что надо всё тестить)
|
|
||||||
|
|
||||||
Что получилось в итоге. Ну я всё же прикостылял базовый C++, не всё и с разной степенью проработки (C++ бесконечен, see below for details). Можно запустить тесты. Можно попытаться затащить библиотеку или, например, свои собственные legacy наработки. Я пытаюсь игнорировать неподдержанные (пока) типы и конструкции, но не все учтены, и на реальном примере придется подхачить C++, чтобы пройти компиляцию. Но это не проблема: дописать поддержку недостающего - вопрос времени/эффортов, принципиальных проблем нет. (В сторону: интероп с собственным legacy кодом я вижу как, потенциально, основной business case. У меня много знакомых плюсеров, которые сидят на легаси огромного объёма и были бы счастливы патчить бизнес-логику на более динамичном и функциональном языке).
|
|
||||||
|
|
||||||
Но пока результат разочаровывает, и вот почему:
|
|
||||||
|
|
||||||
* Этим неудобно пользоваться из-за разницы между CValue<CxxClass> и сгенерённым котлиновским CxxClass. (Никто и не пользуется: даже Indexer.kt всюду использует CValue, а сгенерённые классы остались без дела)
|
|
||||||
|
|
||||||
* cinterop всё время пытается делать свои копии нативных структур: readValue, useContents, placeTo etc. Это не работает для нетривиальных объектов. Хуже того: это абсолютный тупик для move семантики, которая в современном C++ является основным трендом. Пример: сейчас модно писать API, в котором все std::string передаются by value, в расчете на move. При попытке оттащить string в какую-то "другую" память (даже с использованием правильных move/copy конструкторов) я получаю маленький объект, из которого торчит указатель в C++ heap, причём указатель будет всё время меняться, immutability ему противопоказана. Или std::shared_ptr ... в общем, понятно. И. наконец, эти копирования не добавляют ничего полезного в случае kotlin-native (или я не понял).
|
|
||||||
|
|
||||||
* Похоже, cinterop был заточен на то, что основной workflow такой: аллоцировать в котлине memory block, инициализировать поля вручную, дальше побитово копировать туда где нужно. У объектного языка другой flow: get handler (using create or new), keep it and use, destroy. Я прикостылял placement new, чтобы сконструировать объект там где его аллоцировал котлин, но смысла в этом ноль. И это не работает для Factory, у объекта может не быть публичного конструктора.
|
|
||||||
|
|
||||||
* Я не стал фиксить by value параметры и return, это будет глючить на нетривиальных объектах (в тестах таких нет). Фикс простой, но концептуальный и задевает весь существующий cinterop.
|
|
||||||
|
|
||||||
Что я предлагаю сделать:
|
|
||||||
|
|
||||||
* Отказаться от попыток менеджерить нативные объекты. Это невозможно для объектов и не имеет смысла для kotlin-native. Используем handler с нативным указателем внутри. Котлиновский класс сейчас так и генерится - отлично. Никаких CValue. Параметр by value - компилятор подставит звёздочку, *rawPtr. Возврат by value: я использую placement new и copy ctor, чтобы заставить C++ вернуть значение не на стеке, а в хипе. Собственно, это сделано. Но дальше надо в котлин отдать handler с указателем в хип, а не readValue. Это тривиально и это я тоже сделал, но это ломает совместимость с существующим кодом.
|
|
||||||
* Упростить сгенерённый для котлина API, в смысле типов. Никаких CValue, ptr и прочего. Все параметры и возвращаемые типы - только котлиновский прокси класс, handler. Компилятор подставит нужное преобразование в значение, указатель или ссылку. Где-то здесь придётся покувыркаться с move и с temporary, но это feasible. Манглингом или типами показать разницу между const-nonconst параметрами (in/out). Разницы между by value и const ref вообще нет. В случае неоднозначности (конфликт имён из-за того, что не показываем в котлине разницу между by value и by ptr), и только в этом случае - дополнительно манглинг, или программист подскажет компилятору как-то, например напишет ptr.
|
|
||||||
* Вместо бесполезного для деструкторов mamscope сделать cxxscope, чтобы автоматически вызывать зарегистрированные деструкторы. Я попробовал сделать, используя std::unique_ptr - но не закончил.
|
|
||||||
|
|
||||||
|
|
||||||
TODO
|
|
||||||
----
|
|
||||||
|
|
||||||
0. Design style. Костыли. Во многих местах в реальной продуктовой задаче я бы предпочёл сначала сделать подготовительный рефакторинг, цикл review-commit-tests, потом поверх этого сделать фичу. Т.е. разделить коммит на два: изменение (без добавления) и добавление (без изменений). Для тестового задания я в основном выбирал вариант минимальных изменений, чтобы проще ревьюить за один раз. В результате - костыли и рост технического долга, но это осознанный выбор.
|
|
||||||
|
|
||||||
0. Сейчас могут сгенериться имена с конфликтами в двух популярных случаях: 1) const и non-const метод с одинаковой сигнатуройг 2) функции с похожей сигнатурой, одна получает параметр по указателю, другая по ссылке (оба биндятся как указатель). В первом случае: добавим mangling, во втором надо мапить LVReference в not nullable, и всё развяжется. Руки не дошли.
|
|
||||||
|
|
||||||
0. NativeIndex имеет плоскую структуру создаваемых сущностей. Хотелось иметь дерево, так проще поддержать вложенные классы и неймспейсы. Не успел. В результате вложенные структуры я сделал простым манглингом (quick and dirty). Это работает, но выглядит ужасно, надо переделать. В качестве варианта я рассматривал идею: оставить indexer плоским, но пусть он содержит только один уровень семантической вложенности (один уровень semantic parent в терминологии clang), и я буду строить дерево из множества маленьких одноуровневых индексеров.
|
|
||||||
|
|
||||||
0. Хотелось замапить namespace в package. Вроде просто, но уткнулся в тяжелый рефакторинг в генераторах: там единственность выходного файла и package прибита гвоздями. Один translation unit = один выходной файл. А тут translation unit должен создать целое дерево с перекрестными импортами.
|
|
||||||
|
|
||||||
0. Многие типы и конструкции не поддержаны, но каких-то принципиальных проблем в реализации я не вижу. Темплейты - почти мапятся в темплейты, а инстанцированный темплейт - это обычный класс. Exceptions надо просто ретранслировать. RValueRef кажется должен работать легко. Операторы по разному... их много и это надолго. Можно мапить в обычные функции. Но например operator-> я бы предложил не мудрить, а вместо этого написать удобный аналог unique_ptr и shared_ptr вручную, и тогда их почти не останется. Как мапить лямбду в блок и обратно, похоже без поддержки рантайма не обойдется. И т.д., фичи бесконечны...
|
|
||||||
|
|
||||||
0. Override С++ методов в котлине (callbacks) Можно сделать через обертки: генерим C++ класс-наследник, у которого каждый метод перевызывает соответствующий котлинвский метод как plain C. Это сработает, но сгенерим и скомпилируем много лишнего. Хорошо бы генерить эти обертки только on demand, т.е. когда компилятор видит, что котлин хотел бы перегрузить соответствующий метод. Но замечу, что в современном C++ для колбэка скорее будут использовать лямбду и std::function, чем наследование.
|
|
||||||
|
|
||||||
0. Надо замапить самые важные std типы: string, containers, smart pointers
|
|
||||||
|
|
||||||
0. Сейчас работа с C/C++ из котлина выглядит неестественно, многословная. Interop должен быть seamless. Без лишних interpretPointed, rawValue etc. Но это требует определенного пересмотра концепции (type matching). Уже сказал об этом выше.
|
|
||||||
|
|
||||||
0. Костыль visitChildren, или франкенштейн из двух параллельных indexDeclaration.
|
|
||||||
Проблема в том, что IndexDeclaration(CXIdxDeclInfo) не отличает класс от темплейта (и функцию от темплейтной функции). Курсор внутри дерева indexDeclaration для темплейтов имеет kind = CXCursor_ClassDecl (или StructDecl) вместо ожидаемого CXCursor_ClassTemplate, и я не нашел способа вытащить "правильный" курсор. Но если разобрать дерево через visitChildren - то курсор правильный. Поэтому, если язык C++, то я разбираю классы через visitChildren (и отбрасываю темплейты, пока они не поддержаны). Я временно оставил такого франкенштейна, когда часть сущностей парсится через CXIdxDeclInfo (из clang_indexTranslationUnit), а часть через CXCursor (из visitChildren). Я думаю, что надо всё перенести в visitChildren, но у меня не было возможности протестить все сценарии в plain C и ObjC. Такой рефакторинг - не для тестового задания. Кстати, такой костыль уже был до меня, для парсинга ObjC, но я не стал втыкать свой код в него, а написал отдельный - потому что мне нужен был CXChildVisit_Recurse, а там был CXChildVisit_Continue и я опять боялся сломать ObjC, который не умею как следует протестить.
|
|
||||||
|
|
||||||
|
|
||||||
Conceptual
|
|
||||||
----------
|
|
||||||
|
|
||||||
0. Internal linkage.
|
|
||||||
Сейчас сущности с internal linkage (static & anon namespace) не фильтруются, биндинг на них создаётся (все функции в .def в тестах написаны со словом static). Фактически, строим так, как будто C включён в котлиновский исходник. В таком случае, эти сущности должны биндиться как internal, причем каждый С-ник надо компилировать, как отдельный translation unit, иначе будут конфликты.
|
|
||||||
- Это нормальный подход, если моё намерение - расширить (extend) нативную имплементацию на котлине.
|
|
||||||
- Если моё намерение - затащить нативную библиотеку, то я компилирую все публичные хедеры как один translation unit, при этом лучше зафильтровать static.
|
|
||||||
- Проблема некритична, потому что в публичных хедерах static и anon namespace не встречается. Зато часто в c/cpp имплементации. Лучше быть последовательным в этом вопросе.
|
|
||||||
|
|
||||||
0. Что делать с protected и private? Я пока выключил. Технически, сейчас котлиновский класс соответствует C++ ссылке на объект, а не derived class, т.е. должен иметь доступ только к public. IMHO: это неправильно, в такой логике невозможно реализовать C++ callbacks (оверрайдить виртуальные методы, в т.ч. приватные). See below.
|
|
||||||
|
|
||||||
0. Derive vs Use. Хотелось бы имитироватиь наследование от C++ класса. Это можно сделать в концепции kotlin object is a handler, через делегирование, но надо решить вопрос автоматического вызова деструктора на созданном объекте.
|
|
||||||
|
|
||||||
0. Конструкторы-деструкторы надо доработать. Вопрос концептуальный, требует обсуждения. И это довольно большая работа. Моё предложение: надо различать 3 сущности:
|
|
||||||
- Явно аллоцированный объект с владением: alloc + init (т.е. placement new) и парный к нему destroy (это this->~MyClass()), деаллокация автоматическая или ручная
|
|
||||||
- Holder, владеющий указатель на объект, созданный в нативном хипе по new MyClass() и парный к нему delete. Аналог std::unique_ptr. Семантически не отличается от предыдущего.
|
|
||||||
- Non-owning holder без передачи ownership, т.е. невладеюший указатель. Освобождать не надо. Аналог C raw pointer.
|
|
||||||
- Можно замапить std::shared_ptr на котлин тип с acquire/release парой.
|
|
||||||
|
|
||||||
|
|
||||||
Design issues
|
|
||||||
-------------
|
|
||||||
|
|
||||||
0. В контейнере members может лежать IncompleteField. Это имело бы смысл, если Incomplete - такой lazy тип, который позже можно, например, зарезолвить. Но это не предусмотрено - так что зачем добавлять dummy, надо было просто пропустить. C FunctionDecl ещё хуже: в registry складываем неподдерживаемые функции, их отличие только в том, что у них может быть UnsupportedType у параметра. В результате exception в stub builder'е. Я заменил такие невалидные объекты на null, их легче потом фильтровать. Но вообще-то их добавлять не следовало. Проблема в том, как сделано добавление: через getOrPut. Если я только в середине построения объекта понял, что валидный объект создать не получается - он всё равно уже добавлен в map, я уже внутри initializer'а. И это не эксепшн концептуально, потому что это нормальная часть контракта, не исключение. Здесь лучше подошла бы логика factory: пытаюсь создать объект и добавляю только если создался успешно.
|
|
||||||
|
|
||||||
0. Часто приватный метод класса является, по сути, чистой функцией и не работает со стейтом объекта - только с параметром. Такие функции надо вытаскивать из класса и делать чистой функцией, или экстеншном на типе параметра. Пример:. getArrayLength(type: ArrayType) сделана приватным методом StructStubBuilder'а, но никакого отношения к этому классу не имеет. Таких примеров много.
|
|
||||||
|
|
||||||
0. Спорное решение про throw Error("Native interop types constructors must not be called directly"). В чем смысл? Мой тип - handler, и это его конструктор. Точно так работает `std::make_unique` и `make_shared`, при этом я могу и конструктором воспользоваться: `auto x = unique_ptr<T>(new T())`. Другого способа сделать биндинг на класс, у которого только factory method - вообще нет. interpretPointed по сути то же самое, но непонятно, и зачем плодить сущности? Наконец, это runtime exception, а не синтаксическая ошибка, которую мог бы подсветить IDE - и это неудобно.
|
|
||||||
В порядке эксперимента я отключил этот error, в результате и клиентский код, и генеренный стал проще и читабельнее. От чего защищаемся, ведь я все равно могу написать такой wrapper вручную, хранить как long то, что вернул фактори метод, а потом вызывать методы класса на нём? Пусть лучше это сделает генератор.
|
|
||||||
|
|
||||||
0. Parameter by value.
|
|
||||||
На входе - C declaration:
|
|
||||||
```
|
|
||||||
struct CTest {};
|
|
||||||
int paramByValue(struct CTest x);
|
|
||||||
```
|
|
||||||
На выходе
|
|
||||||
```
|
|
||||||
fun paramByValue(x: CValue<CTest>): Int {
|
|
||||||
memScoped {
|
|
||||||
return kniBridge0(x.getPointer(memScope).rawValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t c1_kniBridge0 (void* p0) {
|
|
||||||
return (int32_t)paramByValue(*(struct CTest*)p0);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Здесь копия сделана дважды: сначала CValue.getPointer, а потом при вызове paramByValue. Причем во втором случае C++ компилятор корректно использует конструктор копирования, а getPointer - увы...
|
|
||||||
Нет, это еще не всё. Поскольку функция принимает CValue, а у меня в руках есть только `class CTest`, который `CStructVar`, то придется сделать третье копирование уже в клиентском коде: readValue.
|
|
||||||
При возврате то же самое:
|
|
||||||
```
|
|
||||||
void c1_kniBridge1 (void* p0) {
|
|
||||||
struct CTest kniStructResult = retByValue();
|
|
||||||
memcpy(p0, &kniStructResult, sizeof(kniStructResult));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Здесь уже два копирования. Достаточно было бы
|
|
||||||
```
|
|
||||||
void c1_kniBridge1 (void* p0) {
|
|
||||||
*(struct CTest*) p0 = retByValue();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
(Я знаю, что это не сработает при наличии const fields, но это значит, что автор явно запретил копирование. Такую структуру на C невозможно создать в хипе, только на стеке - такой "тёмный угол" стандарта C. На C++ это решается с помощью placement new.)
|
|
||||||
Но есть ещё и третье копирование в котлиновской обёртке:
|
|
||||||
```
|
|
||||||
return kniRetVal.readValue()
|
|
||||||
```
|
|
||||||
и это ещё не конец: kniRetVal это CVariable, так что readValue делает копию дважды. Четыре копирования на каждый вызов retByValue - только чтобы получить экземпляр структуры.
|
|
||||||
|
|
||||||
0. Я попытался постичь дзен этого пассажа:
|
|
||||||
```
|
|
||||||
* [CValuesRef] is designed to be used as Kotlin representation of pointer-typed parameters of C functions.
|
|
||||||
* When passing [CPointer] as [CValuesRef] to the Kotlin binding method, the C function receives exactly this pointer.
|
|
||||||
* Passing [CValues] has nearly the same semantics as passing by value: the C function receives
|
|
||||||
* the pointer to the temporary copy of these values, and the caller can't observe the modifications to this copy.
|
|
||||||
```
|
|
||||||
Первые две фразы - ok. Третью я не смог расшифровать. `Passing [CValues]` - куда passing? Если C API принимает MyStruct* то это мутатор с input параметром и отдавать туда нужно указатель на исходный объект. Иначе в чём смысл - вызывать мутатор на временном объекте? Если параметр MyStruct const* то компилятор уже позаботился о нас (трюки типа mutable или const_cast не в счёт - тот кто делает это в публичном API - сам берет на себя ответственность). Если параметр по значению, то C компилятор и так сделает копию.
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
Build native:
|
|
||||||
|
|
||||||
g++ -std=c++14 -o src/native/features.o -c src/native/features.cpp
|
|
||||||
|
|
||||||
c++ interop:
|
|
||||||
|
|
||||||
../../dist/bin/cinterop -def features.def -compiler-options "-I." -o features
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
headers = src/native/features.h
|
|
||||||
headerFilter = src/native/features.h
|
|
||||||
package = test
|
|
||||||
language = C++
|
|
||||||
#compilerOpts = -std=c++14
|
|
||||||
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
//package test.cpp
|
|
||||||
|
|
||||||
|
|
||||||
import kotlinx.cinterop.*
|
|
||||||
import platform.posix.*
|
|
||||||
import platform.posix.memcpy
|
|
||||||
import test.*
|
|
||||||
import kotlin.test.*
|
|
||||||
|
|
||||||
class FeatureTest {
|
|
||||||
|
|
||||||
@Test fun ctorDefault() {
|
|
||||||
memScoped {
|
|
||||||
val x = alloc<ns__CppTest>()
|
|
||||||
ns__CppTest.__init__(x.ptr)
|
|
||||||
assertEquals(42, x.iPub)
|
|
||||||
assertEquals(42, x.foo(null))
|
|
||||||
assertEquals(43, x.foo(x.ptr))
|
|
||||||
// dtor is not called, leak is intentional for the purpose of UT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun ctorWithParam() {
|
|
||||||
memScoped {
|
|
||||||
val x = alloc<ns__CppTest>()
|
|
||||||
ns__CppTest.__init__(x.ptr, 1001, 0.0)
|
|
||||||
assertEquals(1001, x.iPub)
|
|
||||||
assertEquals(1001, x.foo(null))
|
|
||||||
assertEquals(1002, x.foo(x.ptr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun copyCtor(y: ns__CppTest) {
|
|
||||||
val x = nativeHeap.alloc<ns__CppTest>() {}
|
|
||||||
ns__CppTest.__init__(x.ptr, y.ptr)
|
|
||||||
|
|
||||||
assertEquals(y.iPub, x.iPub)
|
|
||||||
nativeHeap.free(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun reinitWithCtorAndDtor(y: ns__CppTest) {
|
|
||||||
val count = ns__CppTest.getCount()
|
|
||||||
val x = nativeHeap.alloc<ns__CppTest>() {}
|
|
||||||
ns__CppTest.__init__(x.ptr, y.ptr)
|
|
||||||
assertEquals(ns__CppTest.getCount(), count + 1)
|
|
||||||
assertEquals(y.iPub, x.iPub)
|
|
||||||
|
|
||||||
ns__CppTest.__destroy__(x.ptr)
|
|
||||||
y.iPub = -11
|
|
||||||
assertEquals(y.iPub, -11)
|
|
||||||
ns__CppTest.__init__(x.ptr, y.ptr)
|
|
||||||
assertEquals(x.iPub, -11)
|
|
||||||
|
|
||||||
ns__CppTest.__destroy__(x.ptr)
|
|
||||||
assertEquals(ns__CppTest.getCount(), count)
|
|
||||||
|
|
||||||
nativeHeap.free(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun publicField(x : ns__CppTest) {
|
|
||||||
x.iPub = 21
|
|
||||||
assertEquals(22, x.foo(x.ptr))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun lvRefParameter() {
|
|
||||||
memScoped {
|
|
||||||
val x = alloc<ns__NoName>()
|
|
||||||
var i = alloc<IntVar>()
|
|
||||||
i.value = 758
|
|
||||||
assertEquals(x.noNameMember(i.ptr), 759)
|
|
||||||
assertEquals(i.value, 759)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun staticField() {
|
|
||||||
val save = ns__CppTest.getCount()
|
|
||||||
assertEquals(ns__CppTest.getCount(), ns__CppTest.counter)
|
|
||||||
|
|
||||||
ns__CppTest.counter = 654
|
|
||||||
assertEquals(ns__CppTest.getCount(), 654)
|
|
||||||
assertEquals(ns__CppTest.getCount(), ns__CppTest.counter)
|
|
||||||
|
|
||||||
ns__CppTest.counter = save
|
|
||||||
assertEquals(ns__CppTest.getCount(), save)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
|
|
||||||
val testRun = FeatureTest()
|
|
||||||
testRun.ctorDefault()
|
|
||||||
testRun.ctorWithParam()
|
|
||||||
|
|
||||||
val a1 = interpretPointed<ns__CppTest>(ns__create().rawValue)
|
|
||||||
testRun.publicField(a1)
|
|
||||||
|
|
||||||
testRun.staticField()
|
|
||||||
|
|
||||||
testRun.lvRefParameter()
|
|
||||||
|
|
||||||
a1.iPub = 112
|
|
||||||
testRun.copyCtor(a1)
|
|
||||||
testRun.reinitWithCtorAndDtor(a1)
|
|
||||||
|
|
||||||
|
|
||||||
//******************************
|
|
||||||
println("*** UT passed ***")
|
|
||||||
//******************************
|
|
||||||
|
|
||||||
testStatic()
|
|
||||||
|
|
||||||
testCtor()
|
|
||||||
// testCtor1()
|
|
||||||
|
|
||||||
// testCtor2()
|
|
||||||
// testCtor3()
|
|
||||||
test2()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testStatic() {
|
|
||||||
println("ns__CppTest.s_fun() returns ${ns__CppTest.s_fun()}")
|
|
||||||
println("ns__CppTest.s_fun() returns ${ns__CppTest.s_fun()}")
|
|
||||||
println("ns__CppTest.s_fun() returns ${ns__CppTest.s_fun()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testCtor() {
|
|
||||||
println("testCtor")
|
|
||||||
val cxx = nativeHeap.alloc<ns__CppTest>() {
|
|
||||||
memcpy(ptr, ns__create(), typeOf<ns__CppTest>().size.convert()) // use placement new here
|
|
||||||
}
|
|
||||||
cxx.foo(null)
|
|
||||||
nativeHeap.free(cxx)
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
fun testCtor1() {
|
|
||||||
println("testCtor1: interpretPointed<ns__CppTest>(ns__CppTest.__create__().rawValue)")
|
|
||||||
val theStruct = interpretPointed<ns__CppTest>(ns__CppTest.__create__().rawValue)
|
|
||||||
theStruct.iPub = 33
|
|
||||||
theStruct.foo(theStruct.ptr)
|
|
||||||
|
|
||||||
println("testCtor1: ns__CppTest(ns__CppTest.__create__().rawValue)")
|
|
||||||
val xs = ns__CppTest(ns__CppTest.__create__().rawValue)
|
|
||||||
xs.foo(null)
|
|
||||||
xs.foo(xs.ptr)
|
|
||||||
|
|
||||||
println("testCtor1: ns__CppTest(ns__CppTest.__create__(1001).rawValue)")
|
|
||||||
val x2 = ns__CppTest(ns__CppTest.__create__(1001, 2.718).rawValue)
|
|
||||||
x2.foo(null)
|
|
||||||
x2.foo(x2.ptr)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testCtor2() {
|
|
||||||
println("testCtor2: ns__CppTest(ns__create().rawValue)")
|
|
||||||
val xs = ns__CppTest(ns__create().rawValue)
|
|
||||||
xs.foo(null)
|
|
||||||
xs.foo(xs.ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testCtor3() {
|
|
||||||
println("testCtor3: MyStruct()")
|
|
||||||
val xs = MyStruct()
|
|
||||||
xs.foo()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fun test2() {
|
|
||||||
println("test2")
|
|
||||||
val x = ns__bar(null)
|
|
||||||
// val theS = interpretPointed<ns__CppTest>(ns__bar(null).rawValue)
|
|
||||||
// theS.foo(null)
|
|
||||||
|
|
||||||
println("x.useContents {iPub} = ${x.useContents {iPub}}" )
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#include "features.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace ns {
|
|
||||||
|
|
||||||
int NoName::noNameMember(int& iRef) {
|
|
||||||
cout << __PRETTY_FUNCTION__ << " invoked" << endl;
|
|
||||||
return ++iRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CppTest::counter;
|
|
||||||
|
|
||||||
int CppTest::s_fun() {
|
|
||||||
static int counter = 777;
|
|
||||||
cout << __PRETTY_FUNCTION__ << " invoked" << endl;
|
|
||||||
return counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CppTest::foo(const CppTest* x) {
|
|
||||||
int res = x == this;
|
|
||||||
cout << "This is CppTest::foo: result is iPub + (int)(param == this): " << iPub + res << endl;
|
|
||||||
return iPub + res;
|
|
||||||
}
|
|
||||||
|
|
||||||
CppTest::CppTest() {
|
|
||||||
cout << ++counter << "\t" << __PRETTY_FUNCTION__ << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
CppTest::CppTest(const CppTest& c) {
|
|
||||||
*this = c;
|
|
||||||
cout << ++counter << "\t" << __PRETTY_FUNCTION__ << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
CppTest::CppTest(int i, double j) : iPub(i) {
|
|
||||||
cout << ++counter << "\t" << __PRETTY_FUNCTION__ << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CppTest::~CppTest() {
|
|
||||||
cout << --counter << "\t" << __PRETTY_FUNCTION__ << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CppTest bar(CppTest* s) {
|
|
||||||
if (s)
|
|
||||||
return *s;
|
|
||||||
else
|
|
||||||
return * new CppTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
CppTest* create() {
|
|
||||||
return new CppTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // ns
|
|
||||||
|
|
||||||
CppTest* create() {
|
|
||||||
cout << __PRETTY_FUNCTION__ << " declared in global ns" << endl;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
::CppTest* ns2::create() {
|
|
||||||
cout << __PRETTY_FUNCTION__ << " declared in ns2" << endl;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test() {
|
|
||||||
char buf[sizeof(ns::CppTest)];
|
|
||||||
ns::CppTest* x = new((ns::CppTest*)buf) ns::CppTest();
|
|
||||||
}
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
|
|
||||||
class MyClass {
|
|
||||||
};
|
|
||||||
|
|
||||||
MyClass retByValue();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct OuterCStruct {
|
|
||||||
const int plainCField;
|
|
||||||
struct {
|
|
||||||
int innerCField;
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
int innerUnionF1;
|
|
||||||
double innerUnionF2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Inner {
|
|
||||||
};
|
|
||||||
struct Inner innerImpl;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
// Illegal C. ok in C++
|
|
||||||
typedef struct {
|
|
||||||
} NestedStructInnerT;
|
|
||||||
NestedStructInnerT nestedStructT;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
const int g_IntVal = 0;
|
|
||||||
|
|
||||||
|
|
||||||
typedef int (*funT)(int);
|
|
||||||
void fooFuncParam( funT );
|
|
||||||
void barFuncParam( int (*)(int) );
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T> struct TmplStruct {
|
|
||||||
public:
|
|
||||||
void baz() const {}
|
|
||||||
};
|
|
||||||
|
|
||||||
void funTmplParam(TmplStruct<int>& t);
|
|
||||||
|
|
||||||
template <class X> void tmplFunction();
|
|
||||||
template <class X> void tmplFunction(X x);
|
|
||||||
|
|
||||||
//int funVariadic(const char* format, ...);
|
|
||||||
|
|
||||||
//#include <cstdarg>
|
|
||||||
//void appendVAList(const char format[], va_list); // doesn't work
|
|
||||||
|
|
||||||
|
|
||||||
int plainCFreeFunction();
|
|
||||||
static inline void plainCInternalFunction(); // [Conceptual] should be mapped as internal fun or not eligible for binding at all
|
|
||||||
|
|
||||||
int g_Var;
|
|
||||||
|
|
||||||
struct PlainCStruct {
|
|
||||||
int plainCField;
|
|
||||||
struct {
|
|
||||||
int innerCField;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct PlainCStruct PlainCStructT;
|
|
||||||
|
|
||||||
using PlainCStructAlias = PlainCStruct;
|
|
||||||
|
|
||||||
void wrappingFun() {
|
|
||||||
class NestedInFunction {
|
|
||||||
int varInLocalClass;
|
|
||||||
} var; // local types shall be ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UnknownT;
|
|
||||||
UnknownT* cFunUnknownParams(const UnknownT*);
|
|
||||||
|
|
||||||
|
|
||||||
struct ForwardT; // declaratuin
|
|
||||||
ForwardT* cFunForwardParams(const ForwardT*); // decl
|
|
||||||
|
|
||||||
struct ForwardT {}; // definition
|
|
||||||
ForwardT* cFunForwardParams(const ForwardT*) { // def
|
|
||||||
return new ForwardT();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ns0 {
|
|
||||||
class clDeclaredInNS;
|
|
||||||
extern int varWithDefinition;
|
|
||||||
int varWithDefFirst = 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace ns0_alias = ns0;
|
|
||||||
class ns0_alias::clDeclaredInNS{};
|
|
||||||
extern int ns0_alias::varWithDefFirst;
|
|
||||||
|
|
||||||
using clDeclaredInNSAlias = ns0_alias::clDeclaredInNS;
|
|
||||||
|
|
||||||
// int ns0_alias::varInNSAlias; // illegal declaration
|
|
||||||
int ns0_alias::varWithDefinition = 21;
|
|
||||||
|
|
||||||
namespace ns {
|
|
||||||
|
|
||||||
int g_varInNS;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void funInInnerAnonNS();
|
|
||||||
namespace nsInAnonNS {
|
|
||||||
int memberOf_nsInAnonNS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace NestedNS {
|
|
||||||
int funInNestedNS();
|
|
||||||
int g_varInNestedNS;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void funInInnerAnonNS();
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef class {
|
|
||||||
public:
|
|
||||||
int noNameMember(int& iRef);
|
|
||||||
// int noNameMember(const int& iRef); need mangling
|
|
||||||
static int noNameStaticFun(); // won't work
|
|
||||||
int fieldInAnonClass;
|
|
||||||
// static int s_fieldInAnonClass; not legal C++
|
|
||||||
} NoName;
|
|
||||||
|
|
||||||
enum EnumInNS {one, two, three};
|
|
||||||
|
|
||||||
enum class EnumClass : char { one, two, three};
|
|
||||||
|
|
||||||
class CppTest {
|
|
||||||
public:
|
|
||||||
static int s_fun();
|
|
||||||
|
|
||||||
CppTest();
|
|
||||||
|
|
||||||
CppTest(const CppTest&);
|
|
||||||
|
|
||||||
explicit CppTest(int i, double j = 3.14);
|
|
||||||
|
|
||||||
~CppTest();
|
|
||||||
|
|
||||||
operator NoName() const;
|
|
||||||
|
|
||||||
int iPub = 42;
|
|
||||||
|
|
||||||
virtual int foo(const CppTest*);
|
|
||||||
// virtual int foo(const CppTest*) const;
|
|
||||||
|
|
||||||
static int counter;
|
|
||||||
|
|
||||||
static NoName compStaticField;
|
|
||||||
NoName compField;
|
|
||||||
|
|
||||||
static int getCount() { return counter; }
|
|
||||||
template <class X> void fooTmplMember() const;
|
|
||||||
|
|
||||||
class Nested {
|
|
||||||
public:
|
|
||||||
int nestedFoo();
|
|
||||||
};
|
|
||||||
|
|
||||||
enum NestedEnum {one, two, three};
|
|
||||||
void funEnumParam(NestedEnum e);
|
|
||||||
|
|
||||||
private:
|
|
||||||
CppTest* funPrivate() const;
|
|
||||||
static int s_funPrivate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int iPriv;
|
|
||||||
};
|
|
||||||
|
|
||||||
CppTest funRetByValue(CppTest* s);
|
|
||||||
|
|
||||||
CppTest* create();
|
|
||||||
|
|
||||||
} // ns
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
ns::CppTest* fooInAnonNamespace();
|
|
||||||
}
|
|
||||||
|
|
||||||
class CppTest;
|
|
||||||
CppTest* create();
|
|
||||||
|
|
||||||
namespace ns2 {
|
|
||||||
::CppTest* create();
|
|
||||||
|
|
||||||
} // ns2
|
|
||||||
/*
|
|
||||||
template <typename T> class TmplClass {
|
|
||||||
public:
|
|
||||||
void baz() const {}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef class TmplStruct<int> IntTmplStruct;
|
|
||||||
IntTmplStruct intTmplStruct;
|
|
||||||
|
|
||||||
struct Smth {
|
|
||||||
IntTmplStruct intTmplStruct;
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
g++ -std=c++14 main.cpp -I$HOME/work/cpptools/skia /Users/vdi/work/cpptools/skia/out/Static/libskia.a -framework CoreServices -framework CoreText -framework CoreGraphics
|
|
||||||
|
|
||||||
../../dist/bin/cinterop -def Skia.def -o Skia -compiler-options "-I/Volumes/vdi/work/cpptools/skia"
|
|
||||||
|
|
||||||
../../dist/bin/kotlinc src/skiaMain/kotlin/test_SkString.kt -o test_SkString -l Skia -linker-options "$HOME/work/cpptools/skia/out/Static/libskia.a" -linker-options "-framework CoreServices" -linker-options "-framework CoreText" -linker-options "-framework CoreGraphics"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
headers = include/core/SkString.h include/core/SkTime.h
|
|
||||||
headerFilter = include/core/SkTime.h include/core/SkString.h
|
|
||||||
package = skia
|
|
||||||
compilerOpts = -std=c++14
|
|
||||||
compilerOpts = -x c++
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#include "include/core/SkTime.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
auto nsec = SkTime::GetNSecs();
|
|
||||||
cout << nsec << endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import kotlinx.cinterop.*
|
|
||||||
import SkTime.*
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
println("Skia sample")
|
|
||||||
println("${SkTime.GetNSecs()}")
|
|
||||||
println("${SkTime.GetNSecs()}")
|
|
||||||
println("${SkTime.GetNSecs()}")
|
|
||||||
test_GetDateTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun test_GetDateTime() {
|
|
||||||
memScoped {
|
|
||||||
val dateTime = alloc<SkTime__DateTime>()
|
|
||||||
SkTime.GetDateTime(dateTime.ptr)
|
|
||||||
println("${dateTime.fYear}-${dateTime.fMonth}-${dateTime.fDay} ${dateTime.fHour}:${dateTime.fMinute}.${dateTime.fSecond}" )
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
import kotlinx.cinterop.*
|
|
||||||
import kotlin.test.*
|
|
||||||
|
|
||||||
import skia.*
|
|
||||||
|
|
||||||
fun hello() {
|
|
||||||
memScoped {
|
|
||||||
val cString = "Hello Skia".cstr.getPointer(memScope)
|
|
||||||
val s = alloc<SkString>() {
|
|
||||||
SkString.__init__(ptr, cString)
|
|
||||||
}
|
|
||||||
println(s.c_str()?.toKString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SkString_create(from: String): SkString {
|
|
||||||
val res = nativeHeap.alloc<SkString>() {}
|
|
||||||
memScoped {
|
|
||||||
val cString = from.cstr.getPointer(memScope)
|
|
||||||
SkString.__init__(res.ptr, cString)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SkString.clone(): SkString {
|
|
||||||
return nativeHeap.alloc<SkString>() {
|
|
||||||
SkString.__init__(ptr, this@clone.ptr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SkString.delete(): Unit {
|
|
||||||
SkString.__destroy__(this.ptr)
|
|
||||||
nativeHeap.free(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SkString.toKString() = this.c_str()!!.toKString()
|
|
||||||
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
|
|
||||||
hello()
|
|
||||||
|
|
||||||
/*
|
|
||||||
with (CppContext) {
|
|
||||||
val go = SkString("Let's go fishing!")
|
|
||||||
val goback = SkString(go)
|
|
||||||
val pos = goback.find("fish")
|
|
||||||
println(go)
|
|
||||||
if (pos >= 0) {
|
|
||||||
goback.insert(pos, "back!")
|
|
||||||
goback.resize((pos + "back!".length)
|
|
||||||
}
|
|
||||||
println(goback)
|
|
||||||
oback.swap(go)
|
|
||||||
goback.resize(goback.size() - 1U)
|
|
||||||
goback.append(" again!")
|
|
||||||
println(goback)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
val go = SkString_create("Let's go fishing!")
|
|
||||||
println(go.toKString())
|
|
||||||
|
|
||||||
val goback = go.clone()
|
|
||||||
memScoped {
|
|
||||||
val pos = goback.find("fish".cstr.getPointer(memScope))
|
|
||||||
if (pos >= 0) {
|
|
||||||
goback.insert(pos.convert<ULong>(), "back!".cstr.getPointer(memScope))
|
|
||||||
goback.resize((pos + "back!".length).convert<ULong>())
|
|
||||||
}
|
|
||||||
println(goback.toKString())
|
|
||||||
|
|
||||||
goback.swap(go.ptr)
|
|
||||||
|
|
||||||
goback.resize(goback.size() - 1U)
|
|
||||||
goback.append(" again!".cstr.getPointer(memScope))
|
|
||||||
println(goback.toKString())
|
|
||||||
}
|
|
||||||
go.delete()
|
|
||||||
goback.delete()
|
|
||||||
}
|
|
||||||
@@ -118,6 +118,10 @@ class DefFile(val file:File?, val config:DefFileConfig, val manifestAddendProper
|
|||||||
properties.getProperty("foreignExceptionMode")
|
properties.getProperty("foreignExceptionMode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val pluginName by lazy {
|
||||||
|
properties.getProperty("plugin")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -598,6 +598,7 @@ if (buildProperties.isKotlinNativeEnabled) {
|
|||||||
include ':kotlin-native:Interop:Runtime'
|
include ':kotlin-native:Interop:Runtime'
|
||||||
include ':kotlin-native:Interop:Indexer'
|
include ':kotlin-native:Interop:Indexer'
|
||||||
include ':kotlin-native:Interop:JsRuntime'
|
include ':kotlin-native:Interop:JsRuntime'
|
||||||
|
include ':kotlin-native:Interop:Skia'
|
||||||
include ':kotlin-native:utilities:basic-utils'
|
include ':kotlin-native:utilities:basic-utils'
|
||||||
include ':kotlin-native:utilities:cli-runner'
|
include ':kotlin-native:utilities:cli-runner'
|
||||||
include ':kotlin-native:klib'
|
include ':kotlin-native:klib'
|
||||||
|
|||||||
Reference in New Issue
Block a user