Garbage collection capable wrappers for skia interop
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import kotlinx.cinterop.CStructVar
|
||||
|
||||
interface SkiaRefCnt
|
||||
interface CPlusPlusClass
|
||||
|
||||
abstract class ManagedType<T : CStructVar>(val cpp: T)
|
||||
|
||||
val <T : CStructVar> ManagedType<T>.ptr: CPointer<T> get() = this.cpp.ptr
|
||||
+4
@@ -24,6 +24,10 @@ annotation class CStruct(val spelling: String) {
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class VarType(val size: Long, val align: Int)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class CPlusPlusClass
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ManagedType
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
Skia interop support
|
||||
====================
|
||||
|
||||
@@ -15,6 +16,33 @@ plugin = org.jetbrains.kotlin.native.cinterop.plugin.skia
|
||||
language = C++
|
||||
```
|
||||
|
||||
Interop
|
||||
-------
|
||||
|
||||
There are two kinds of C++ classes used in Skia.
|
||||
The ones with ref()/unref() reference counting are abstracted with SkiaRefCnt interface.
|
||||
The ones without are abstracted with CPlusPlusClass interface.
|
||||
|
||||
So, for example, for SkPaint one has
|
||||
|
||||
class SkPaint(rawPtr) : CStructVar(rawPtr), CPlusPlusClass {
|
||||
// A C interop-like function having methods,
|
||||
// as well as low level __init__ and __destroy__
|
||||
}
|
||||
|
||||
class Paint(cpp: SkPaint, managed: Boolean) : ManagedType(cpp) {
|
||||
// A wrapper capable of garbage collection delegating all calls and fields to SkPaint
|
||||
}
|
||||
|
||||
|
||||
* pointer returned from C++ -> Wrapper(cpp, managed = false)
|
||||
* sk_sp returned from C++ -> calls .release() ; Wrapper(cpp, managed = true)
|
||||
* pointer passed to C++ -> kotlin.cpp.ptr
|
||||
* sk_sp passed to C++ -> sk_ref_sp(kotlin.cpp.ptr)
|
||||
* constructor call -> allocate Cpp; Wrapper(cpp, managed = true)
|
||||
* garbage collection -> if (managed==true): calls __destroy__() for CPluaPlusClass or unref() for SkiaRefCnt
|
||||
|
||||
|
||||
Implementation details
|
||||
======================
|
||||
|
||||
|
||||
+18
@@ -9,6 +9,7 @@ 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
|
||||
import org.jetbrains.kotlin.native.interop.indexer.StructDef
|
||||
|
||||
class SkiaStubsBuildingContextImpl(stubIrContext: StubIrContext) : StubsBuildingContextImpl(stubIrContext) {
|
||||
override val declarationMapper = SkiaDeclarationMapperImpl()
|
||||
@@ -23,4 +24,21 @@ class SkiaStubsBuildingContextImpl(stubIrContext: StubIrContext) : StubsBuilding
|
||||
return getKotlinClassForPointed(structArgument)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isCppClass(spelling: String): Boolean {
|
||||
val decl = nativeIndex.structs.firstOrNull { it.spelling == spelling } ?: return false
|
||||
return decl.def?.kind == StructDef.Kind.CLASS
|
||||
}
|
||||
|
||||
override fun managedWrapperClassifier(cppClassifier: Classifier): Classifier? {
|
||||
if (cppClassifier.pkg != "org.jetbrains.skiko.skia.native") return null
|
||||
// TODO: it'd be nice to check inheritance from SkiaRefCnt or CPlusPlusClass,
|
||||
// but unable to do that at StubType level.
|
||||
if (!(cppClassifier.topLevelName.startsWith("Sk") || cppClassifier.topLevelName.startsWith("Gr"))) return null
|
||||
if (cppClassifier.topLevelName == "SkString") return null
|
||||
// TODO: We only managed C++ classes, not structs for now.
|
||||
if (!isCppClass(cppClassifier.topLevelName)) return null
|
||||
|
||||
return Classifier.topLevel(cppClassifier.pkg, cppClassifier.topLevelName.drop(2))
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
class DeepCopyForManagedWrapper(val originalClass: ClassStub, val context: StubsBuildingContext) : StubIrTransformer {
|
||||
override fun visitClass(element: ClassStub): ClassStub = error("Not implemented")
|
||||
|
||||
override fun visitTypealias(element: TypealiasStub): TypealiasStub = error("Not implemented")
|
||||
|
||||
override fun visitFunction(element: FunctionStub): FunctionStub {
|
||||
return FunctionStub(
|
||||
name = element.name,
|
||||
returnType = transformCPointerToManaged(element.returnType),
|
||||
parameters = element.parameters.map { visitFunctionParameter(it) },
|
||||
origin = element.origin,
|
||||
annotations = emptyList(),
|
||||
external = false,
|
||||
receiver = element.receiver?.let { visitReceiverParameter(it) },
|
||||
modality = element.modality,
|
||||
typeParameters = element.typeParameters,
|
||||
isOverride = element.isOverride,
|
||||
hasStableParameterNames = element.hasStableParameterNames
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitProperty(element: PropertyStub): PropertyStub {
|
||||
return PropertyStub(
|
||||
name = element.name,
|
||||
type = element.type,
|
||||
origin = element.origin,
|
||||
annotations = emptyList(),
|
||||
receiverType = element.receiverType,
|
||||
modality = element.modality,
|
||||
isOverride = element.isOverride,
|
||||
kind = when(element.kind) {
|
||||
is PropertyStub.Kind.Val -> PropertyStub.Kind.Val(
|
||||
visitPropertyAccessor(element.kind.getter) as PropertyAccessor.Getter
|
||||
)
|
||||
is PropertyStub.Kind.Var -> PropertyStub.Kind.Var(
|
||||
visitPropertyAccessor(element.kind.getter) as PropertyAccessor.Getter,
|
||||
visitPropertyAccessor(element.kind.setter) as PropertyAccessor.Setter
|
||||
)
|
||||
is PropertyStub.Kind.Constant -> PropertyStub.Kind.Constant(element.kind.constant)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitConstructor(constructorStub: ConstructorStub): ConstructorStub {
|
||||
return if (constructorStub.isPrimary)
|
||||
ConstructorStub(
|
||||
parameters = listOf(
|
||||
FunctionParameterStub(
|
||||
name = "cpp",
|
||||
type = ClassifierStubType(originalClass.classifier)
|
||||
),
|
||||
FunctionParameterStub(
|
||||
name = "managed",
|
||||
type = ClassifierStubType(Classifier.topLevel("kotlin", "Boolean"))
|
||||
)
|
||||
),
|
||||
isPrimary = true,
|
||||
visibility = constructorStub.visibility,
|
||||
origin = constructorStub.origin
|
||||
)
|
||||
else ConstructorStub(
|
||||
parameters = constructorStub.parameters.map { visitFunctionParameter(it) },
|
||||
annotations = emptyList(),
|
||||
isPrimary = constructorStub.isPrimary,
|
||||
visibility = constructorStub.visibility,
|
||||
origin = constructorStub.origin
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessor(propertyAccessor: PropertyAccessor): PropertyAccessor {
|
||||
return when (propertyAccessor) {
|
||||
is PropertyAccessor.Getter.SimpleGetter ->
|
||||
PropertyAccessor.Getter.SimpleGetter(
|
||||
propertyAccessor.annotations.map { visitAnnotation(it) },
|
||||
constant = propertyAccessor.constant
|
||||
)
|
||||
is PropertyAccessor.Getter.ExternalGetter ->
|
||||
PropertyAccessor.Getter.SimpleGetter( // TODO: is it right?
|
||||
propertyAccessor.annotations.map { visitAnnotation(it) },
|
||||
constant = null
|
||||
)
|
||||
is PropertyAccessor.Setter.SimpleSetter ->
|
||||
PropertyAccessor.Setter.SimpleSetter(
|
||||
propertyAccessor.annotations.map { visitAnnotation(it) }
|
||||
)
|
||||
is PropertyAccessor.Setter.ExternalSetter ->
|
||||
PropertyAccessor.Setter.SimpleSetter( // TODO: is it right?
|
||||
propertyAccessor.annotations.map { visitAnnotation(it) }
|
||||
)
|
||||
else -> error("Not implemented yet $propertyAccessor")
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer): SimpleStubContainer = SimpleStubContainer()
|
||||
|
||||
private fun transformCPointerToManaged(type: StubType): StubType {
|
||||
if (type !is ClassifierStubType) return type
|
||||
if (type.classifier.topLevelName != "CPointer" && type.classifier.topLevelName != "CValuesRef") return type
|
||||
if (type.classifier.pkg != "kotlinx.cinterop") return type
|
||||
val argument = type.typeArguments.single() as? TypeArgumentStub ?: return type
|
||||
if (argument.type !is ClassifierStubType) return type
|
||||
val newClassifier = managedWrapperClassifier(argument.type.classifier) ?: return type
|
||||
|
||||
return ClassifierStubType(newClassifier, nullable = type.nullable)
|
||||
}
|
||||
|
||||
fun managedWrapperClassifier(cppClassifier: Classifier): Classifier? =
|
||||
(context as StubsBuildingContextImpl).managedWrapperClassifier(cppClassifier)
|
||||
|
||||
override fun visitFunctionParameter(element: FunctionParameterStub): FunctionParameterStub {
|
||||
return FunctionParameterStub(
|
||||
name = element.name,
|
||||
type = transformCPointerToManaged(element.type),
|
||||
annotations = element.annotations.map { visitAnnotation(it) },
|
||||
isVararg = element.isVararg
|
||||
)
|
||||
}
|
||||
override fun visitReceiverParameter(element: ReceiverParameterStub): ReceiverParameterStub {
|
||||
return ReceiverParameterStub(
|
||||
type = element.type
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitAnnotation(element: AnnotationStub): AnnotationStub = element
|
||||
}
|
||||
+1
-1
@@ -133,7 +133,7 @@ sealed class TypeMirror(val pointedType: KotlinClassifierType, val info: TypeInf
|
||||
) : TypeMirror(pointedType, info) {
|
||||
|
||||
override val argType: KotlinType
|
||||
get() = pointedType
|
||||
get() = KotlinTypes.cPointer.typeWith(pointedType)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -113,6 +113,11 @@ sealed class StubOrigin {
|
||||
* E.CEnumVar.value.
|
||||
*/
|
||||
class EnumVarValueField(val enum: EnumDef) : Synthetic()
|
||||
|
||||
/**
|
||||
* Other synthetic values.
|
||||
*/
|
||||
object ManagedTypeDetails : StubOrigin()
|
||||
}
|
||||
|
||||
class ObjCCategoryInitMethod(
|
||||
@@ -210,6 +215,8 @@ sealed class AnnotationStub(val classifier: Classifier) {
|
||||
class VarType(val size: Long, val align: Int) : AnnotationStub(cStructClassifier.nested("VarType"))
|
||||
|
||||
object ManagedType : AnnotationStub(cStructClassifier.nested("ManagedType"))
|
||||
|
||||
object CPlusPlusClass : AnnotationStub(cStructClassifier.nested("CPlusPlusClass"))
|
||||
}
|
||||
|
||||
class CNaturalStruct(val members: List<StructMember>) :
|
||||
@@ -324,11 +331,11 @@ sealed class ClassStub : StubContainer(), StubElementWithOrigin, AnnotationHolde
|
||||
abstract val companion : Companion?
|
||||
abstract val classifier: Classifier
|
||||
|
||||
class Simple(
|
||||
open class Simple(
|
||||
override val classifier: Classifier,
|
||||
val modality: ClassStubModality,
|
||||
constructors: List<ConstructorStub> = emptyList(),
|
||||
methods: List<FunctionStub> = emptyList(),
|
||||
val constructors: List<ConstructorStub> = emptyList(),
|
||||
val methods: List<FunctionStub> = emptyList(),
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
@@ -343,7 +350,7 @@ sealed class ClassStub : StubContainer(), StubElementWithOrigin, AnnotationHolde
|
||||
|
||||
class Companion(
|
||||
override val classifier: Classifier,
|
||||
methods: List<FunctionStub> = emptyList(),
|
||||
val methods: List<FunctionStub> = emptyList(),
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
|
||||
+6
-1
@@ -246,6 +246,12 @@ open class StubsBuildingContextImpl(
|
||||
return classifier
|
||||
}
|
||||
|
||||
open fun isCppClass(spelling: String): Boolean =
|
||||
error("Only meaningful with a proper cpp plugin")
|
||||
|
||||
open fun managedWrapperClassifier(cppClassifier: Classifier): Classifier? =
|
||||
error("Only meaningful with a proper cpp plugin")
|
||||
|
||||
open inner class DeclarationMapperImpl : DeclarationMapper {
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val baseName = structDecl.kotlinName
|
||||
@@ -277,7 +283,6 @@ open class StubsBuildingContextImpl(
|
||||
KotlinPlatform.NATIVE -> true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class StubIrBuilderResult(
|
||||
|
||||
+95
-11
@@ -78,16 +78,30 @@ internal class StructStubBuilder(
|
||||
AnnotationStub.CStruct(it)
|
||||
}
|
||||
}
|
||||
val managedAnnotation = if (context.configuration.library.language == Language.CPP
|
||||
val cPlusPlusClassAnnotation = if (context.configuration.library.language == Language.CPP
|
||||
&& def.kind == StructDef.Kind.CLASS) {
|
||||
AnnotationStub.CStruct.ManagedType
|
||||
AnnotationStub.CStruct.CPlusPlusClass
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val structAnnotations = listOfNotNull(structAnnotation, managedAnnotation)
|
||||
val structAnnotations = listOfNotNull(structAnnotation, cPlusPlusClassAnnotation)
|
||||
|
||||
val classifier = context.getKotlinClassForPointed(decl)
|
||||
|
||||
val destructor = if (def.methods.any { it.isCxxDestructor && it.name == "__destroy__" }) {
|
||||
listOf(
|
||||
FunctionStub(
|
||||
name = "__destroy__",
|
||||
returnType = ClassifierStubType(Classifier("kotlin", "Unit")),
|
||||
parameters = emptyList(),
|
||||
origin = StubOrigin.Synthetic.ManagedTypeDetails,
|
||||
annotations = emptyList(),
|
||||
receiver = null, // ReceiverParameterStub()
|
||||
modality = MemberStubModality.FINAL
|
||||
)
|
||||
)
|
||||
} else emptyList()
|
||||
|
||||
val methods: List<FunctionStub> =
|
||||
def.methods
|
||||
.filter { it.isCxxInstanceMethod }
|
||||
@@ -100,7 +114,7 @@ internal class StructStubBuilder(
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
}.filterNotNull()
|
||||
}.filterNotNull() + destructor
|
||||
|
||||
val fields: List<PropertyStub?> = def.fields.map { field ->
|
||||
try {
|
||||
@@ -214,17 +228,19 @@ internal class StructStubBuilder(
|
||||
}.filterNotNull()
|
||||
|
||||
// Here's what we have for C++.
|
||||
// Note that we account for constructors twice.
|
||||
// Note that we account for constructors and the destructor 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)
|
||||
// __destroy__()
|
||||
//
|
||||
// // These go into `methods`
|
||||
// foo()
|
||||
// bar(x, y)
|
||||
//
|
||||
// Companion {
|
||||
// // These all go to `classMethods`
|
||||
@@ -256,7 +272,20 @@ internal class StructStubBuilder(
|
||||
properties = classFields,
|
||||
methods = classMethods
|
||||
)
|
||||
return listOf(ClassStub.Simple(
|
||||
|
||||
val interfaces = if (context.configuration.library.language == Language.CPP && def.kind == StructDef.Kind.CLASS) {
|
||||
listOfNotNull(
|
||||
ClassifierStubType(Classifier.topLevel("kotlinx.cinterop", "CPlusPlusClass")),
|
||||
// TODO: Only if skia plugin is active!
|
||||
if (methods.any { it.name == "unref" }) {
|
||||
ClassifierStubType(Classifier.topLevel("kotlinx.cinterop", "SkiaRefCnt"))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
} else emptyList()
|
||||
|
||||
val classStub = ClassStub.Simple(
|
||||
classifier,
|
||||
origin = origin,
|
||||
properties = fields.filterNotNull() + if (platform == KotlinPlatform.NATIVE) bitFields else emptyList(),
|
||||
@@ -265,8 +294,63 @@ internal class StructStubBuilder(
|
||||
modality = ClassStubModality.NONE,
|
||||
annotations = structAnnotations,
|
||||
superClassInit = superClassInit,
|
||||
companion = companion
|
||||
))
|
||||
companion = companion,
|
||||
interfaces = interfaces
|
||||
)
|
||||
|
||||
return if (context.configuration.library.language == Language.CPP && def.kind == StructDef.Kind.CLASS) {
|
||||
try {
|
||||
listOfNotNull(classStub, buildManagedWrapper(classStub))
|
||||
} catch (e: Throwable) {
|
||||
emptyList()
|
||||
}
|
||||
} else {
|
||||
listOf(classStub)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildManagedWrapper(classStub: ClassStub.Simple): ClassStub.Simple? {
|
||||
val copier = DeepCopyForManagedWrapper(classStub, context)
|
||||
|
||||
val managedName = copier.managedWrapperClassifier(classStub.classifier) ?: run {
|
||||
return null
|
||||
}
|
||||
|
||||
val managed = PropertyStub(
|
||||
name = "managed",
|
||||
type = ClassifierStubType(Classifier.topLevel("kotlin", "Boolean")),
|
||||
kind = PropertyStub.Kind.Val(getter = PropertyAccessor.Getter.SimpleGetter()),
|
||||
modality = MemberStubModality.FINAL,
|
||||
origin = StubOrigin.Synthetic.ManagedTypeDetails
|
||||
)
|
||||
|
||||
val constructors = classStub.constructors.map { copier.visitConstructor(it) }
|
||||
|
||||
val managedWrapper = ClassStub.Simple(
|
||||
managedName,
|
||||
classStub.modality,
|
||||
constructors,
|
||||
classStub.methods.map { copier.visitFunction(it) } ,
|
||||
superClassInit = SuperClassInit(
|
||||
ClassifierStubType(
|
||||
Classifier.topLevel("kotlinx.cinterop", "ManagedType"),
|
||||
listOf(TypeArgumentStub(ClassifierStubType(classStub.classifier)))
|
||||
),
|
||||
listOf( GetConstructorParameter(constructors.single { it.isPrimary }.parameters.first()) )
|
||||
),
|
||||
interfaces = emptyList(),
|
||||
properties = listOf(managed) + classStub.properties.map { copier.visitProperty(it) },
|
||||
classStub.origin,
|
||||
annotations = listOf(AnnotationStub.CStruct.ManagedType),
|
||||
childrenClasses = emptyList(),
|
||||
companion = ClassStub.Companion(
|
||||
classifier = managedName.nested("Companion"),
|
||||
methods = classStub.companion!!.methods
|
||||
.filterNot { it.name == "__init__" || it.name == "__destroy__" }
|
||||
.map { copier.visitFunction(it) },
|
||||
)
|
||||
)
|
||||
return managedWrapper
|
||||
}
|
||||
|
||||
private fun getArrayLength(type: ArrayType): Long {
|
||||
|
||||
+1
@@ -447,6 +447,7 @@ private class MappingExtensions(
|
||||
("size" to KmAnnotationArgument.LongValue(size)),
|
||||
("align" to KmAnnotationArgument.IntValue(align))
|
||||
)
|
||||
is AnnotationStub.CStruct.CPlusPlusClass -> emptyMap()
|
||||
is AnnotationStub.CStruct.ManagedType -> emptyMap()
|
||||
}
|
||||
return KmAnnotation(classifier.fqNameSerialized, args)
|
||||
|
||||
+2
@@ -490,6 +490,8 @@ class StubIrTextEmitter(
|
||||
"@CStruct(${annotationStub.struct.quoteAsKotlinLiteral()})"
|
||||
is AnnotationStub.CNaturalStruct ->
|
||||
"@CNaturalStruct(${annotationStub.members.joinToString { it.name.quoteAsKotlinLiteral() }})"
|
||||
is AnnotationStub.CStruct.CPlusPlusClass ->
|
||||
"@CStruct.CPlusPlusClass"
|
||||
is AnnotationStub.CStruct.ManagedType ->
|
||||
"@CStruct.ManagedType"
|
||||
is AnnotationStub.CLength ->
|
||||
|
||||
+25
-1
@@ -19,4 +19,28 @@ interface StubIrVisitor<T, R> {
|
||||
fun visitPropertyAccessor(propertyAccessor: PropertyAccessor, data: T): R
|
||||
|
||||
fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer, data: T): R
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface StubIrTransformer {
|
||||
fun visitClass(element: ClassStub): ClassStub
|
||||
|
||||
fun visitTypealias(element: TypealiasStub): TypealiasStub
|
||||
|
||||
fun visitFunction(element: FunctionStub): FunctionStub
|
||||
|
||||
fun visitProperty(element: PropertyStub): PropertyStub
|
||||
|
||||
fun visitConstructor(constructorStub: ConstructorStub): ConstructorStub
|
||||
|
||||
fun visitPropertyAccessor(propertyAccessor: PropertyAccessor): PropertyAccessor
|
||||
|
||||
fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer): SimpleStubContainer
|
||||
|
||||
fun visitFunctionParameter(element: FunctionParameterStub): FunctionParameterStub
|
||||
|
||||
fun visitAnnotation(element: AnnotationStub): AnnotationStub
|
||||
|
||||
fun visitReceiverParameter(element: ReceiverParameterStub): ReceiverParameterStub
|
||||
|
||||
}
|
||||
|
||||
+15
@@ -118,6 +118,10 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
val CreateNSStringFromKString = packageScope.getContributedFunctions("CreateNSStringFromKString").single()
|
||||
val nativeHeap = packageScope.getContributedClass("nativeHeap")
|
||||
val cPointed = packageScope.getContributedClass("CPointed")
|
||||
val managedType = packageScope.getContributedClass("ManagedType")
|
||||
val cPlusPlusClass = packageScope.getContributedClass("CPlusPlusClass")
|
||||
val skiaRefCnt = packageScope.getContributedClass("SkiaRefCnt")
|
||||
|
||||
val interopGetPtr = packageScope.getContributedVariables("ptr").single {
|
||||
val singleTypeParameter = it.typeParameters.singleOrNull()
|
||||
val singleTypeParameterUpperBound = singleTypeParameter?.upperBounds?.singleOrNull()
|
||||
@@ -128,6 +132,17 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
TypeUtils.getClassDescriptor(singleTypeParameterUpperBound) == cPointed &&
|
||||
extensionReceiverParameter.type == singleTypeParameter.defaultType
|
||||
}.getter!!
|
||||
|
||||
val interopManagedGetPtr = 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) == cStructVar &&
|
||||
TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == managedType
|
||||
}.getter!!
|
||||
}
|
||||
|
||||
private fun MemberScope.getContributedVariables(name: String) =
|
||||
|
||||
+3
@@ -14,7 +14,9 @@ object RuntimeNames {
|
||||
val cStructArrayMemberAt = FqName("kotlinx.cinterop.internal.CStruct.ArrayMemberAt")
|
||||
val cStructBitField = FqName("kotlinx.cinterop.internal.CStruct.BitField")
|
||||
val cStruct = FqName("kotlinx.cinterop.internal.CStruct")
|
||||
val cppClass = FqName("kotlinx.cinterop.internal.CStruct.CPlusPlusClass")
|
||||
val managedType = FqName("kotlinx.cinterop.internal.CStruct.ManagedType")
|
||||
val skiaRefCnt = FqName("kotlinx.cinterop.SkiaRefCnt") // TODO: move me to the plugin?
|
||||
val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
|
||||
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
||||
val independent = FqName("kotlin.native.internal.Independent")
|
||||
@@ -22,4 +24,5 @@ object RuntimeNames {
|
||||
val kotlinNativeInternalPackageName = FqName.fromSegments(listOf("kotlin", "native", "internal"))
|
||||
val associatedObjectKey = FqName("kotlin.reflect.AssociatedObjectKey")
|
||||
val typedIntrinsicAnnotation = FqName("kotlin.native.internal.TypedIntrinsic")
|
||||
val cleaner = FqName("kotlin.native.internal.Cleaner")
|
||||
}
|
||||
|
||||
+3
-3
@@ -109,7 +109,7 @@ private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrMemberA
|
||||
transformCall
|
||||
)
|
||||
|
||||
private fun IrType.isManagedType(): Boolean= this.classOrNull?.owner?.hasAnnotation(RuntimeNames.managedType) ?: false
|
||||
private fun IrType.isCppClass(): Boolean= this.classOrNull?.owner?.hasAnnotation(RuntimeNames.cppClass) ?: false
|
||||
|
||||
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
|
||||
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
|
||||
@@ -183,7 +183,7 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
||||
private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, callee: IrFunction) {
|
||||
arguments.forEachIndexed { index, argument ->
|
||||
val parameter = if (callee.dispatchReceiverParameter != null &&
|
||||
(callee.dispatchReceiverParameter?.type?.isManagedType() == true)) {
|
||||
(callee.dispatchReceiverParameter?.type?.isCppClass() == true)) {
|
||||
|
||||
if (index == 0) callee.dispatchReceiverParameter!! else callee.valueParameters[index-1]
|
||||
} else {
|
||||
@@ -736,7 +736,7 @@ private fun KotlinStubs.mapType(
|
||||
val cStructType = getNamedCStructType(kotlinClass)
|
||||
require(cStructType != null) { renderCompilerError(location) }
|
||||
|
||||
if (type.isManagedType()) {
|
||||
if (type.isCppClass()) {
|
||||
// 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.
|
||||
|
||||
+1
@@ -85,6 +85,7 @@ internal fun IrType.isObjCReferenceType(target: KonanTarget, irBuiltIns: IrBuilt
|
||||
|
||||
internal fun IrType.isCPointer(symbols: KonanSymbols): Boolean = this.classOrNull == symbols.interopCPointer
|
||||
internal fun IrType.isCValue(symbols: KonanSymbols): Boolean = this.classOrNull == symbols.interopCValue
|
||||
internal fun IrType.isCValuesRef(symbols: KonanSymbols): Boolean = this.classOrNull == symbols.interopCValuesRef
|
||||
|
||||
internal fun IrType.isNativePointed(symbols: KonanSymbols): Boolean = isSubtypeOfClass(symbols.nativePointed)
|
||||
|
||||
|
||||
+6
@@ -206,6 +206,12 @@ internal class KonanSymbols(
|
||||
|
||||
val interopGetPtr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.interopGetPtr)
|
||||
|
||||
val interopManagedGetPtr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.interopManagedGetPtr)
|
||||
|
||||
val interopManagedType = symbolTable.referenceClass(context.interopBuiltIns.managedType)
|
||||
val interopCPlusPlusClass = symbolTable.referenceClass(context.interopBuiltIns.cPlusPlusClass)
|
||||
val interopSkiaRefCnt = symbolTable.referenceClass(context.interopBuiltIns.skiaRefCnt)
|
||||
|
||||
val readBits = interopFunction("readBits")
|
||||
val writeBits = interopFunction("writeBits")
|
||||
|
||||
|
||||
+4
-1
@@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan.ir.interop
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
|
||||
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilder
|
||||
@@ -187,4 +188,6 @@ internal fun IrSymbol.findCStructDescriptor(interopBuiltIns: InteropBuiltIns): C
|
||||
descriptor.findCStructDescriptor(interopBuiltIns)
|
||||
|
||||
internal fun DeclarationDescriptor.findCStructDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
|
||||
parentsWithSelf.filterIsInstance<ClassDescriptor>().firstOrNull { it.inheritsFromCStructVar(interopBuiltIns) }
|
||||
parentsWithSelf.filterIsInstance<ClassDescriptor>().firstOrNull {
|
||||
it.inheritsFromCStructVar(interopBuiltIns) || it.annotations.hasAnnotation(RuntimeNames.managedType)
|
||||
}
|
||||
+251
-36
@@ -5,19 +5,26 @@
|
||||
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||
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.irInstanceInitializer
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.addMember
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.toIrConst
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
|
||||
|
||||
internal class CStructVarClassGenerator(
|
||||
@@ -30,6 +37,7 @@ internal class CStructVarClassGenerator(
|
||||
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
|
||||
override val symbolTable: SymbolTable = context.symbolTable
|
||||
override val typeTranslator: TypeTranslator = context.typeTranslator
|
||||
val irFactory: IrFactory = context.irFactory
|
||||
override val postLinkageSteps: MutableList<() -> Unit> = mutableListOf()
|
||||
|
||||
fun findOrGenerateCStruct(classDescriptor: ClassDescriptor, parent: IrDeclarationContainer): IrClass {
|
||||
@@ -49,40 +57,247 @@ internal class CStructVarClassGenerator(
|
||||
irClass.addMember(createPrimaryConstructor(irClass))
|
||||
irClass.addMember(companionGenerator.generate(descriptor))
|
||||
descriptor.constructors
|
||||
.filterNot { it.isPrimary }
|
||||
.map {
|
||||
val constructor = createSecondaryConstructor(it)
|
||||
irClass.addMember(constructor)
|
||||
}
|
||||
descriptor.unsubstitutedMemberScope
|
||||
.getContributedDescriptors()
|
||||
.filterIsInstance<CallableMemberDescriptor>()
|
||||
.filterNot { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
|
||||
.map {
|
||||
when (it) {
|
||||
is PropertyDescriptor -> createProperty(it)
|
||||
is SimpleFunctionDescriptor -> createFunction(it)
|
||||
else -> null
|
||||
.filterNot { it.isPrimary }
|
||||
.map {
|
||||
val constructor = createSecondaryConstructor(it)
|
||||
irClass.addMember(constructor)
|
||||
}
|
||||
descriptor.unsubstitutedMemberScope
|
||||
.getContributedDescriptors()
|
||||
.filterIsInstance<CallableMemberDescriptor>()
|
||||
.filterNot { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
|
||||
.map {
|
||||
when (it) {
|
||||
is PropertyDescriptor -> createProperty(it)
|
||||
is SimpleFunctionDescriptor -> createFunction(it)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
.forEach(irClass::addMember)
|
||||
}.also { irClass ->
|
||||
if (irClass.descriptor.annotations.hasAnnotation(RuntimeNames.cppClass)) {
|
||||
postLinkageSteps.add {
|
||||
setupCppClass(irClass)
|
||||
}
|
||||
.filterNotNull()
|
||||
.forEach(irClass::addMember)
|
||||
}
|
||||
if (irClass.descriptor.annotations.hasAnnotation(RuntimeNames.managedType)) {
|
||||
postLinkageSteps.add {
|
||||
setupManagedClass(irClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
|
||||
val enumVarConstructorSymbol = symbolTable.referenceConstructor(
|
||||
interopBuiltIns.cStructVar.unsubstitutedPrimaryConstructor!!
|
||||
)
|
||||
return createConstructor(irClass.descriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
|
||||
postLinkageSteps.add {
|
||||
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.unitType, enumVarConstructorSymbol
|
||||
).also {
|
||||
it.putValueArgument(0, irGet(irConstructor.valueParameters[0]))
|
||||
private fun setupCppClass(irClass: IrClass) {
|
||||
val companionDestroy = irClass.companionObject()!!.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.filter { it.name.toString() == "__destroy__" }
|
||||
.singleOrNull() ?: return
|
||||
|
||||
val destroy = irClass.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.filter { it.name.toString() == "__destroy__" }
|
||||
.single()
|
||||
|
||||
val getPtr = symbols.interopGetPtr
|
||||
|
||||
destroy.body = irBuilder(irBuiltIns, destroy.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
|
||||
.irBlockBody {
|
||||
+irCall(companionDestroy).apply {
|
||||
dispatchReceiver = irGetObject(irClass.companionObject()!!.symbol)
|
||||
putValueArgument(0,
|
||||
irCall(getPtr).apply {
|
||||
extensionReceiver = irGet(destroy.dispatchReceiverParameter!!)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move me to InteropLowering.kt?
|
||||
private fun setupManagedClass(irClass: IrClass) {
|
||||
|
||||
// class Wrapper(cpp: CppClass, managed: Boolean) : ManagedType(cpp) {
|
||||
// val managed = managed
|
||||
// field cleaner = createCleaner(cpp) { it ->
|
||||
// $Inner.Companion.__destroy__(it) // For general CPlusPlusClass
|
||||
// or
|
||||
// it.unref() // for SkiaRefCnt
|
||||
// }
|
||||
// }
|
||||
|
||||
val traceCleaners = false
|
||||
|
||||
val cppParam = irClass.primaryConstructor!!.valueParameters.first().also {
|
||||
assert(it.name.toString() == "cpp")
|
||||
}
|
||||
|
||||
val cppType = cppParam.type
|
||||
val cppClass = cppType.classOrNull!!.owner
|
||||
|
||||
val superClassFqNames = cppClass.superTypes.map {
|
||||
it.classOrNull?.owner?.fqNameWhenAvailable
|
||||
}.filterNotNull()
|
||||
|
||||
val isSkiaRefCnt = superClassFqNames.contains(RuntimeNames.skiaRefCnt)
|
||||
|
||||
val managedVal = irClass.declarations
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { it.name.toString() == "managed" }
|
||||
.single()
|
||||
|
||||
val managedValType = managedVal.getter!!.returnType
|
||||
|
||||
managedVal.backingField = symbolTable.declareField(
|
||||
SYNTHETIC_OFFSET,
|
||||
SYNTHETIC_OFFSET,
|
||||
IrDeclarationOrigin.PROPERTY_BACKING_FIELD,
|
||||
managedVal.descriptor,
|
||||
managedValType,
|
||||
DescriptorVisibilities.PRIVATE
|
||||
).also {
|
||||
it.parent = irClass
|
||||
it.initializer = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run {
|
||||
irExprBody(irGet(irClass.primaryConstructor!!.valueParameters[1]))
|
||||
}
|
||||
}
|
||||
|
||||
managedVal.getter!!.body = irBuilder(irBuiltIns, managedVal.getter!!.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
|
||||
.irBlockBody {
|
||||
+irReturn(irGetField(irGet(managedVal.getter!!.dispatchReceiverParameter!!), managedVal.backingField!!))
|
||||
|
||||
}
|
||||
|
||||
val cleanerField = irFactory.createField(
|
||||
SYNTHETIC_OFFSET,
|
||||
SYNTHETIC_OFFSET,
|
||||
IrDeclarationOrigin.DEFINED,
|
||||
IrFieldSymbolImpl(),
|
||||
Name.identifier("cleaner"),
|
||||
symbols.createCleaner.owner.returnType,
|
||||
DescriptorVisibilities.PRIVATE,
|
||||
isFinal = true,
|
||||
isExternal = false,
|
||||
isStatic = false
|
||||
).also { field ->
|
||||
field.parent = irClass
|
||||
field.initializer = irBuilder(irBuiltIns, field.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run {
|
||||
val lambda = context.irFactory.buildFun {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
name = Name.special("<anonymous>")
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
returnType = irBuiltIns.unitType
|
||||
}.apply {
|
||||
parent = field
|
||||
valueParameters = listOf (
|
||||
buildValueParameter(this) {
|
||||
origin = IrDeclarationOrigin.DEFINED
|
||||
name = Name.identifier("field")
|
||||
index = 0
|
||||
type = cppType
|
||||
}
|
||||
)
|
||||
body = irBlockBody {
|
||||
val itCpp = irGet(valueParameters.single())
|
||||
if (traceCleaners) {
|
||||
+irCall(symbols.println).apply {
|
||||
putValueArgument(0,
|
||||
"Cleaning ${irClass.name} with ${if (isSkiaRefCnt) "unref" else "__destroy__"}"
|
||||
.toIrConst(irBuiltIns.stringType)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isSkiaRefCnt) {
|
||||
val unref = cppClass.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.single { it.name.toString() == "unref" }
|
||||
+irCall(unref).apply {
|
||||
dispatchReceiver = itCpp
|
||||
}
|
||||
} else {
|
||||
val destroy = cppClass.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.singleOrNull() { it.name.toString() == "__destroy__" }
|
||||
if (destroy!= null) {
|
||||
+irCall(destroy).apply {
|
||||
dispatchReceiver = itCpp
|
||||
}
|
||||
}
|
||||
val nativeHeap = symbols.nativeHeap
|
||||
val free = nativeHeap.owner.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.single { it.name.toString() == "free" }
|
||||
+irCall(free).apply {
|
||||
dispatchReceiver = irGetObject(nativeHeap)
|
||||
putValueArgument(0,
|
||||
irCall(symbols.interopNativePointedGetRawPointer).apply {
|
||||
extensionReceiver = itCpp
|
||||
}
|
||||
)
|
||||
}
|
||||
// TODO: need to nativePlacement.free(cpp.rawPtr)
|
||||
// TODO: } // managed
|
||||
}
|
||||
}
|
||||
}
|
||||
val callCreateCleaner = irCall(symbols.createCleaner).apply {
|
||||
dispatchReceiver = null
|
||||
putTypeArgument(0, cppType)
|
||||
putValueArgument(0,
|
||||
irGet(irClass.primaryConstructor!!.valueParameters[0])
|
||||
)
|
||||
putValueArgument(1,
|
||||
IrFunctionExpressionImpl(
|
||||
startOffset = SYNTHETIC_OFFSET,
|
||||
endOffset = SYNTHETIC_OFFSET,
|
||||
type = irBuiltIns.functionN(1).typeWith(cppType, irBuiltIns.unitType),
|
||||
origin = IrStatementOrigin.LAMBDA,
|
||||
function = lambda
|
||||
)
|
||||
)
|
||||
}
|
||||
irExprBody(irIfThenElse(callCreateCleaner.type.makeNullable(), irGet(irClass.primaryConstructor!!.valueParameters[1]), callCreateCleaner, irNull()))
|
||||
}
|
||||
}
|
||||
irClass.declarations += cleanerField
|
||||
}
|
||||
|
||||
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
|
||||
if (!irClass.descriptor.annotations.hasAnnotation(RuntimeNames.managedType)) {
|
||||
val cStructVarConstructorSymbol = symbolTable.referenceConstructor(
|
||||
interopBuiltIns.cStructVar.unsubstitutedPrimaryConstructor!!
|
||||
)
|
||||
return createConstructor(irClass.descriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
|
||||
postLinkageSteps.add {
|
||||
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.unitType, cStructVarConstructorSymbol
|
||||
).also {
|
||||
it.putValueArgument(0, irGet(irConstructor.valueParameters[0]))
|
||||
}
|
||||
+irInstanceInitializer(symbolTable.referenceClass(irClass.descriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return createConstructor(irClass.descriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
|
||||
val managedTypeConstructor = symbolTable.referenceConstructor(
|
||||
interopBuiltIns.managedType.unsubstitutedPrimaryConstructor!!
|
||||
)
|
||||
postLinkageSteps.add {
|
||||
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.unitType, managedTypeConstructor
|
||||
).also {
|
||||
it.putTypeArgument(0, irConstructor.valueParameters[0].type)
|
||||
it.putValueArgument(0, irGet(irConstructor.valueParameters[0]))
|
||||
}
|
||||
+irInstanceInitializer(symbolTable.referenceClass(irClass.descriptor))
|
||||
}
|
||||
+irInstanceInitializer(symbolTable.referenceClass(irClass.descriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,4 +312,4 @@ internal class CStructVarClassGenerator(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
-31
@@ -5,6 +5,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||
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.irInstanceInitializer
|
||||
@@ -20,10 +21,7 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.addMember
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
||||
import org.jetbrains.kotlin.ir.util.irBuilder
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
|
||||
|
||||
@@ -41,39 +39,56 @@ internal class CStructVarCompanionGenerator(
|
||||
|
||||
fun generate(structDescriptor: ClassDescriptor): IrClass =
|
||||
createClass(structDescriptor.companionObjectDescriptor!!) { companionIrClass ->
|
||||
val annotation = companionIrClass.descriptor.annotations
|
||||
.findAnnotation(varTypeAnnotationFqName)!!
|
||||
val size = annotation.getArgumentValueOrNull<Long>("size")!!
|
||||
val align = annotation.getArgumentValueOrNull<Int>("align")!!
|
||||
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, size, align))
|
||||
|
||||
if (structDescriptor.annotations.hasAnnotation(RuntimeNames.managedType)) {
|
||||
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, 0L, 0))
|
||||
} else {
|
||||
val annotation = companionIrClass.descriptor.annotations
|
||||
.findAnnotation(varTypeAnnotationFqName)!!
|
||||
val size = annotation.getArgumentValueOrNull<Long>("size")!!
|
||||
val align = annotation.getArgumentValueOrNull<Int>("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
|
||||
.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)
|
||||
.forEach(companionIrClass::addMember)
|
||||
}
|
||||
|
||||
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, size: Long, align: Int): IrConstructor {
|
||||
val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cStructVarType.unsubstitutedPrimaryConstructor!!)
|
||||
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
|
||||
postLinkageSteps.add {
|
||||
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset, endOffset, context.irBuiltIns.unitType,
|
||||
superConstructorSymbol
|
||||
).also {
|
||||
it.putValueArgument(0, irLong(size))
|
||||
it.putValueArgument(1, irInt(align))
|
||||
if (companionObjectDescriptor.containingDeclaration.annotations.hasAnnotation(RuntimeNames.managedType)) {
|
||||
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
|
||||
postLinkageSteps.add {
|
||||
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset, endOffset, context.irBuiltIns.unitType,
|
||||
irBuiltIns.anyClass.owner.primaryConstructor!!.symbol
|
||||
)
|
||||
+irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cStructVarType.unsubstitutedPrimaryConstructor!!)
|
||||
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
|
||||
postLinkageSteps.add {
|
||||
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
|
||||
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset, endOffset, context.irBuiltIns.unitType,
|
||||
superConstructorSymbol
|
||||
).also {
|
||||
it.putValueArgument(0, irLong(size))
|
||||
it.putValueArgument(1, irInt(align))
|
||||
}
|
||||
+irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor))
|
||||
}
|
||||
+irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.common.ir.allParameters
|
||||
import org.jetbrains.kotlin.backend.common.ir.allParametersCount
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.common.lower.inline.InlinerExpressionLocationHint
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin
|
||||
@@ -228,7 +228,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
* During function code generation [FunctionScope] should be set up.
|
||||
*/
|
||||
private object TopLevelCodeContext : CodeContext {
|
||||
private fun unsupported(any: Any? = null): Nothing = throw UnsupportedOperationException(any?.toString() ?: "")
|
||||
private fun unsupported(any: Any? = null): Nothing = throw UnsupportedOperationException(if (any is IrElement) any.render() else any?.toString() ?: "")
|
||||
|
||||
override fun genReturn(target: IrSymbolOwner, value: LLVMValueRef?) = unsupported(target)
|
||||
|
||||
@@ -910,12 +910,12 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateGetObjectValue(value: IrGetObjectValue): LLVMValueRef =
|
||||
functionGenerationContext.getObjectValue(
|
||||
value.symbol.owner,
|
||||
currentCodeContext.exceptionHandler,
|
||||
value.startLocation,
|
||||
value.endLocation
|
||||
)
|
||||
functionGenerationContext.getObjectValue(
|
||||
value.symbol.owner,
|
||||
currentCodeContext.exceptionHandler,
|
||||
value.startLocation,
|
||||
value.endLocation
|
||||
)
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
+4
-1
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal fun IrElement.needDebugInfo(context: Context) = context.shouldContainDebugInfo() || (this is IrVariable && this.isVar)
|
||||
@@ -70,7 +71,9 @@ internal class VariableManager(val functionGenerationContext: FunctionGeneration
|
||||
|
||||
internal fun createMutable(valueDeclaration: IrValueDeclaration,
|
||||
isVar: Boolean, value: LLVMValueRef? = null, variableLocation: VariableDebugLocation?) : Int {
|
||||
assert(!contextVariablesToIndex.contains(valueDeclaration))
|
||||
assert(!contextVariablesToIndex.contains(valueDeclaration)) {
|
||||
"Could not find ${valueDeclaration.render()} in contextVariablesToIndex"
|
||||
}
|
||||
val index = variables.size
|
||||
val type = functionGenerationContext.getLLVMType(valueDeclaration.type)
|
||||
val slot = functionGenerationContext.alloca(type, valueDeclaration.name.asString(), variableLocation)
|
||||
|
||||
+239
-2
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.toIrConst
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
@@ -766,11 +767,31 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
private fun generateCFunctionPointer(function: IrSimpleFunction, expression: IrExpression): IrExpression =
|
||||
generateWithStubs { generateCFunctionPointer(function, function, expression) }
|
||||
|
||||
// ?.foo() part
|
||||
fun IrBuilderWithScope.irSafeCall(extensionReceiverExpression: IrExpression, typeArguments: List<IrTypeArgument>, callee: IrSimpleFunctionSymbol): IrExpression =
|
||||
irBlock {
|
||||
val tmp = irTemporary(extensionReceiverExpression)
|
||||
+irIfThenElse(callee.owner.returnType.makeNullable(),
|
||||
irEqeqeq(irGet(tmp), irNull()),
|
||||
irNull(),
|
||||
irCall(callee).apply {
|
||||
extensionReceiver = irGet(tmp)
|
||||
typeArguments.forEachIndexed { index, arg ->
|
||||
putTypeArgument(index, arg.typeOrNull!!)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
if (expression.symbol.owner.hasCCallAnnotation("CppClassConstructor")) {
|
||||
return transformSecondaryCppConstructorCall(expression)
|
||||
return transformCppConstructorCall(expression)
|
||||
}
|
||||
|
||||
if (expression.symbol.owner.constructedClass.hasAnnotation(RuntimeNames.managedType)) {
|
||||
return transformManagedCppConstructorCall(expression)
|
||||
}
|
||||
|
||||
val callee = expression.symbol.owner
|
||||
@@ -806,8 +827,10 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformSecondaryCppConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||
private fun transformCppConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||
val irConstructor = expression.symbol.owner
|
||||
if (irConstructor.isPrimary) return expression
|
||||
|
||||
val irClass = irConstructor.constructedClass
|
||||
val primaryConstructor = irClass.primaryConstructor!!.symbol
|
||||
|
||||
@@ -844,6 +867,9 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
putValueArgument(0,
|
||||
irCall(interopGetPtr).apply {
|
||||
extensionReceiver = irGet(tmp)
|
||||
putTypeArgument(0,
|
||||
(correspondingInit.valueParameters.first().type as IrSimpleType).arguments.single().typeOrNull!!
|
||||
)
|
||||
}
|
||||
)
|
||||
for (index in 0 until expression.valueArgumentsCount) {
|
||||
@@ -858,6 +884,58 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
return irBlock
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.transformManagedArguments(oldCall: IrFunctionAccessExpression, oldFunction: IrFunction, newCall: IrFunctionAccessExpression, newFunction: IrFunction) {
|
||||
for (index in 0 until oldCall.valueArgumentsCount) {
|
||||
val newArgument = irBlock {
|
||||
val oldArgument = irTemporary(oldCall.getValueArgument(index)!!)
|
||||
if (oldFunction.valueParameters[index].type.isManagedType()) {
|
||||
+irSafeCall(
|
||||
irGet(oldArgument),
|
||||
listOf((newFunction.valueParameters[index].type as IrSimpleType).arguments.single()),
|
||||
symbols.interopManagedGetPtr
|
||||
// symbols.interopGetPtr
|
||||
)
|
||||
} else {
|
||||
+irGet(oldArgument)
|
||||
}
|
||||
}
|
||||
newCall.putValueArgument(index, newArgument)
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformManagedCppConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||
val irConstructor = expression.symbol.owner
|
||||
if (irConstructor.isPrimary) return expression
|
||||
|
||||
val irClass = irConstructor.constructedClass
|
||||
val primaryConstructor = irClass.primaryConstructor!!.symbol
|
||||
|
||||
val correspondingCppClass = primaryConstructor.owner.valueParameters.first().type.classOrNull?.owner!!
|
||||
|
||||
val correspondingCppConstructor = correspondingCppClass
|
||||
.declarations
|
||||
.filterIsInstance<IrConstructor>()
|
||||
.filter { it.valueParameters.size == irConstructor.valueParameters.size}
|
||||
.singleOrNull {
|
||||
it.valueParameters.mapIndexed() { index, initParameter ->
|
||||
managedTypeMatch(irConstructor.valueParameters[index].type, initParameter.type)
|
||||
}.all{ it }
|
||||
} ?: error("Could not find a match for ${irConstructor.render()}")
|
||||
|
||||
val irBlock = builder.at(expression)
|
||||
.irBlock {
|
||||
val cppConstructorCall = irCall(correspondingCppConstructor.symbol).apply {
|
||||
transformManagedArguments(expression, irConstructor, this, correspondingCppConstructor)
|
||||
}
|
||||
val call = irCall(primaryConstructor).also {
|
||||
it.putValueArgument(0, transformCppConstructorCall(cppConstructorCall))
|
||||
it.putValueArgument(1, true.toIrConst(context.irBuiltIns.booleanType))
|
||||
}
|
||||
+call
|
||||
}
|
||||
return irBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle `const val`s that come from interop libraries.
|
||||
*
|
||||
@@ -936,6 +1014,15 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
return generateCCall(expression)
|
||||
}
|
||||
|
||||
// TODO: what's the proper condition?
|
||||
val funcClass = function.dispatchReceiverParameter?.type?.classOrNull?.owner
|
||||
if (funcClass?.hasAnnotation(RuntimeNames.managedType) ?: false) {
|
||||
return transformManagedCall(expression)
|
||||
}
|
||||
if ((funcClass?.isCompanion == true) && ((funcClass.parent as? IrClass)?.hasAnnotation(RuntimeNames.managedType) ?: false)) {
|
||||
return transformManagedCompanionCall(expression)
|
||||
}
|
||||
|
||||
val failCompilation = { msg: String -> error(irFile, expression, msg) }
|
||||
tryGenerateInteropMemberAccess(expression, symbols, builder, failCompilation)?.let { return it }
|
||||
|
||||
@@ -1080,6 +1167,156 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrType.isManagedType() = this.isSubtypeOfClass(symbols.interopManagedType)
|
||||
private fun IrType.isCPlusPlusClass() = this.isSubtypeOfClass(symbols.interopCPlusPlusClass)
|
||||
private fun IrType.isSkiaRefCnt() = this.isSubtypeOfClass(symbols.interopSkiaRefCnt)
|
||||
|
||||
private fun transformManagedCall(expression: IrCall): IrExpression {
|
||||
val function = expression.symbol.owner
|
||||
|
||||
val irClass = function.dispatchReceiverParameter!!.type.classOrNull!!.owner
|
||||
val cppProperty = irClass.declarations
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { it.name.toString() == "cpp" }
|
||||
.single()
|
||||
|
||||
val managedProperty = irClass.declarations
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { it.name.toString() == "managed" }
|
||||
.single()
|
||||
|
||||
if (function == cppProperty.getter || function == managedProperty.getter) return expression
|
||||
|
||||
val cppParam = irClass.primaryConstructor!!.valueParameters.first().also {
|
||||
assert(it.name.toString() == "cpp")
|
||||
}
|
||||
|
||||
val cppType = cppParam.type
|
||||
val cppClass = cppType.classOrNull!!.owner
|
||||
|
||||
val newFunction = cppClass.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.filter { it.name == function.name }
|
||||
.filter { it.valueParameters.size == function.valueParameters.size }
|
||||
.filter {
|
||||
it.valueParameters.mapIndexed() { index, parameter ->
|
||||
managedTypeMatch(function.valueParameters[index].type, parameter.type)
|
||||
}.all { it }
|
||||
}.singleOrNull() ?: error("Could not find ${function.name} in ${cppClass}")
|
||||
|
||||
val newFunctionType = newFunction.returnType
|
||||
|
||||
val newCall = with (builder.at(expression)) {
|
||||
irCall(newFunction).apply {
|
||||
dispatchReceiver = irCall(cppProperty.getter!!).apply {
|
||||
dispatchReceiver = expression.dispatchReceiver
|
||||
}
|
||||
transformManagedArguments(expression, function, this, newFunction)
|
||||
}
|
||||
}
|
||||
val ccall = generateCCall(newCall as IrCall)
|
||||
return if (function.returnType.isManagedType()) {
|
||||
assert(newFunctionType.isCPointer(symbols))
|
||||
val pointed = (newFunctionType as IrSimpleType).arguments.single().typeOrNull!!
|
||||
with (builder.at(ccall)) {
|
||||
irCall(function.returnType.classOrNull!!.owner.primaryConstructor!!.symbol).apply {
|
||||
val managed = when {
|
||||
pointed.isSkiaRefCnt() -> true
|
||||
pointed.isCPlusPlusClass() -> false
|
||||
else -> error("Unexpected pointer argument for ManagedType")
|
||||
}.toIrConst(context.irBuiltIns.booleanType)
|
||||
putValueArgument(0,
|
||||
irCall(symbols.interopInterpretNullablePointed).apply {
|
||||
putValueArgument(0,
|
||||
irCall(symbols.interopCPointerGetRawValue).apply {
|
||||
extensionReceiver = ccall
|
||||
}
|
||||
)
|
||||
putTypeArgument(0, pointed)
|
||||
}
|
||||
)
|
||||
putValueArgument(1, managed)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ccall
|
||||
}
|
||||
}
|
||||
|
||||
private fun managedTypeMatch(one: IrType, another: IrType): Boolean {
|
||||
if (one == another) return true
|
||||
if (one.classOrNull?.owner?.hasAnnotation(RuntimeNames.managedType) != true) return false
|
||||
if (!another.isCPointer(symbols) && !another.isCValuesRef(symbols)) return false
|
||||
|
||||
val cppType = one.classOrNull!!.owner.primaryConstructor?.valueParameters?.first()?.type ?: return false
|
||||
val pointedType = (another as? IrSimpleType)?.arguments?.single() as? IrSimpleType ?: return false
|
||||
return cppType == pointedType
|
||||
}
|
||||
|
||||
private fun transformManagedCompanionCall(expression: IrCall): IrExpression {
|
||||
val function = expression.symbol.owner
|
||||
|
||||
val companion = function.parent as IrClass
|
||||
assert(companion.isCompanion)
|
||||
|
||||
val cppInClass = (companion.parent as IrClass).declarations
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { it.name.toString() == "cpp" }
|
||||
.single()
|
||||
|
||||
val cppCompanion = cppInClass.getter!!.returnType.classOrNull!!.owner
|
||||
.declarations
|
||||
.filterIsInstance<IrClass>()
|
||||
.single{ it.isCompanion }
|
||||
|
||||
val newFunction = cppCompanion.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.filter { it.name == function.name }
|
||||
.filter { it.valueParameters.size == function.valueParameters.size }
|
||||
.filter {
|
||||
it.valueParameters.mapIndexed() { index, parameter ->
|
||||
managedTypeMatch(function.valueParameters[index].type, parameter.type)
|
||||
}.all { it }
|
||||
}.single()
|
||||
|
||||
val newFunctionType = newFunction.returnType
|
||||
|
||||
val newCall = with (builder.at(expression)) {
|
||||
irCall(newFunction).apply {
|
||||
dispatchReceiver = irGetObject(cppCompanion.symbol)
|
||||
transformManagedArguments(expression, function, this, newFunction)
|
||||
}
|
||||
}
|
||||
// TODO: this is exactly the same code as in transformManagedCall
|
||||
val ccall = generateCCall(newCall as IrCall)
|
||||
return if (function.returnType.isManagedType()) {
|
||||
assert(newFunctionType.isCPointer(symbols))
|
||||
val pointed = (newFunctionType as IrSimpleType).arguments.single().typeOrNull!!
|
||||
with (builder.at(ccall)) {
|
||||
irCall(function.returnType.classOrNull!!.constructors.single { it.owner.isPrimary }).apply {
|
||||
val managed = when {
|
||||
pointed.isCPlusPlusClass() -> false
|
||||
pointed.isSkiaRefCnt() -> true
|
||||
else -> error("Unexpected pointer argument for ManagedType")
|
||||
}.toIrConst(context.irBuiltIns.booleanType)
|
||||
putValueArgument(0,
|
||||
irCall(symbols.interopInterpretNullablePointed).apply {
|
||||
putValueArgument(0,
|
||||
irCall(symbols.interopCPointerGetRawValue).apply {
|
||||
extensionReceiver = ccall
|
||||
}
|
||||
)
|
||||
putTypeArgument(0, pointed)
|
||||
}
|
||||
)
|
||||
putValueArgument(1, managed)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ccall
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.irConvertInteger(
|
||||
source: IrClassSymbol,
|
||||
target: IrClassSymbol,
|
||||
|
||||
@@ -3906,6 +3906,10 @@ createInterop("cppSkia") {
|
||||
it.defFile 'interop/cpp/skia.def'
|
||||
}
|
||||
|
||||
createInterop("cppSkiaSignature") {
|
||||
it.defFile 'interop/cpp/skiaSignature.def'
|
||||
}
|
||||
|
||||
createInterop("threadStates") {
|
||||
it.defFile "interop/threadStates/threadStates.def"
|
||||
it.extraOpts "-Xcompile-source", "$projectDir/interop/threadStates/threadStates.cpp"
|
||||
@@ -4320,10 +4324,26 @@ interopTest("interop_cppTypes") {
|
||||
}
|
||||
|
||||
interopTest("interop_cppSkia") {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
disabled = (project.testTarget == 'wasm32' || // No interop for wasm yet.
|
||||
isExperimentalMM)
|
||||
source = "interop/cpp/skia.kt"
|
||||
interop = 'cppSkia'
|
||||
goldValue = "17 17\n"
|
||||
goldValue =
|
||||
"SkFoo()\nSkData()\nSkData()\nSkValue()\nSkData()\n" +
|
||||
"MANAGED: f: true, a: true, b: false, v: true, c: false\n" +
|
||||
"DATA: 17 19 17 17\n" +
|
||||
"unref\n~SkData()\n~SkFoo()\n"
|
||||
UtilsKt.dependsOnPlatformLibs(it)
|
||||
}
|
||||
|
||||
interopTest("interop_cppSkiaSignature") {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
source = "interop/cpp/skiaSignature.kt"
|
||||
interop = "cppSkiaSignature"
|
||||
goldValue =
|
||||
"SkData()\nSkData(19)\nSkData(SkData*)\nSkData(SkData*, SkData*)\nSkData(200)\nSkData(436)\n"+
|
||||
"true true true true true\n"
|
||||
UtilsKt.dependsOnPlatformLibs(it)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
language = C++
|
||||
compilerOpts = -std=c++14
|
||||
|
||||
plugin = org.jetbrains.kotlin.native.interop.skia
|
||||
|
||||
---
|
||||
class CppTest {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
language = C++
|
||||
compilerOpts = -std=c++17
|
||||
plugin = org.jetbrains.kotlin.native.interop.skia
|
||||
package = org.jetbrains.skiko.skia.native
|
||||
headers = stdio.h
|
||||
|
||||
---
|
||||
// TODO: this one checks the syntactic aspect for now.
|
||||
@@ -22,20 +24,64 @@ template <typename T> sk_sp<T> sk_ref_sp(T* obj) {
|
||||
return sk_sp<T>(obj);
|
||||
}
|
||||
|
||||
class Value {
|
||||
public:
|
||||
int data;
|
||||
class SkValue {
|
||||
public:
|
||||
SkValue() {
|
||||
printf("SkValue()\n");
|
||||
};
|
||||
|
||||
void unref() {
|
||||
printf("unref\n");
|
||||
};
|
||||
|
||||
int data;
|
||||
void setData(int val) {
|
||||
data = val;
|
||||
};
|
||||
};
|
||||
|
||||
class Foo {
|
||||
public:
|
||||
Foo() { }
|
||||
class SkData {
|
||||
public:
|
||||
SkData() {
|
||||
printf("SkData()\n");
|
||||
};
|
||||
|
||||
virtual sk_sp<Value> foo(Value *obj) {
|
||||
return sk_sp<Value>(obj);
|
||||
}
|
||||
~SkData() {
|
||||
printf("~SkData()\n");
|
||||
};
|
||||
|
||||
virtual Value* bar(sk_sp<Value> obj) {
|
||||
return obj.release();
|
||||
}
|
||||
int data;
|
||||
|
||||
void setData(int val) {
|
||||
data = val;
|
||||
};
|
||||
};
|
||||
|
||||
class SkFoo {
|
||||
public:
|
||||
SkFoo() {
|
||||
printf("SkFoo()\n");
|
||||
};
|
||||
|
||||
~SkFoo() {
|
||||
printf("~SkFoo()\n");
|
||||
};
|
||||
|
||||
virtual sk_sp<SkValue> foo(SkData *obj) {
|
||||
SkValue* value = new SkValue();
|
||||
value->setData(obj->data);
|
||||
return sk_sp<SkValue>(value);
|
||||
};
|
||||
|
||||
virtual SkData* bar(sk_sp<SkValue> obj) {
|
||||
SkValue* value = obj.release();
|
||||
SkData* data = new SkData();
|
||||
data->setData(value->data);
|
||||
return data;
|
||||
};
|
||||
|
||||
virtual SkData* qux() {
|
||||
return new SkData();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,32 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE_ERROR")
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import kotlin.native.internal.*
|
||||
|
||||
import skia.*
|
||||
import org.jetbrains.skiko.skia.native.*
|
||||
import platform.posix.printf
|
||||
|
||||
fun main() {
|
||||
|
||||
// TODO: the test used to work with forceCheckedShutdown,
|
||||
// but it is broken now. Revisit after it is fixed.
|
||||
// kotlin.native.internal.Debugging.forceCheckedShutdown = true
|
||||
Platform.isCleanersLeakCheckerActive = true
|
||||
|
||||
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}")
|
||||
|
||||
val a = Data()
|
||||
a.setData(17)
|
||||
val b = f.qux()!!
|
||||
b.setData(19)
|
||||
|
||||
val v = f.foo(a)
|
||||
val c = f.bar(v)!!
|
||||
|
||||
// Use printf instead of println to avoid messages
|
||||
// appearing out of order with the native code.
|
||||
// The native code uses printf.
|
||||
printf("MANAGED: f: ${f.managed}, a: ${a.managed}, b: ${b.managed}, v: ${v.managed}, c: ${c.managed}\n")
|
||||
printf("DATA: ${a.cpp.data} ${b.cpp.data} ${v.cpp.data} ${c.cpp.data}\n")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
language = C++
|
||||
compilerOpts = -std=c++17
|
||||
plugin = org.jetbrains.kotlin.native.interop.skia
|
||||
package = org.jetbrains.skiko.skia.native
|
||||
headers = stdio.h
|
||||
|
||||
---
|
||||
// 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 SkData {
|
||||
public:
|
||||
SkData() {
|
||||
printf("SkData()\n");
|
||||
};
|
||||
|
||||
SkData(int val) {
|
||||
printf("SkData(%d)\n", val);
|
||||
data = val + 100;
|
||||
};
|
||||
|
||||
SkData(SkData* obj) {
|
||||
printf("SkData(SkData*)\n");
|
||||
data = obj->data + 200;
|
||||
};
|
||||
|
||||
SkData(SkData* obj, SkData* obj2) {
|
||||
printf("SkData(SkData*, SkData*)\n");
|
||||
data = obj->data + obj2->data + 300;
|
||||
};
|
||||
|
||||
SkData* foo(SkData* obj, SkData* obj2) {
|
||||
return new SkData(obj->data + obj2->data + data);
|
||||
};
|
||||
|
||||
int data;
|
||||
|
||||
void setData(int val) {
|
||||
data = val;
|
||||
};
|
||||
|
||||
int checkData(int val) {
|
||||
return val == data;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE_ERROR")
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import kotlin.native.internal.*
|
||||
|
||||
import org.jetbrains.skiko.skia.native.*
|
||||
import platform.posix.printf
|
||||
|
||||
fun main() {
|
||||
val a = Data()
|
||||
a.setData(17)
|
||||
val b = Data(19)
|
||||
val c = Data(a)
|
||||
val d = Data(a, b)
|
||||
val e = Data(200).foo(a, b)!!
|
||||
|
||||
val a1 = a.checkData(17) != 0
|
||||
val b1 = b.checkData(119) != 0
|
||||
val c1 = c.checkData(217) != 0
|
||||
val d1 = d.checkData(436) != 0
|
||||
val e1 = e.checkData(536) != 0
|
||||
|
||||
// Use printf instead of println to avoid messages
|
||||
// appearing out of order with the native code.
|
||||
// The native code uses printf.
|
||||
printf("$a1 $b1 $c1 $d1 $e1\n")
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
language = C++
|
||||
compilerOpts = -std=c++14
|
||||
plugin = org.jetbrains.kotlin.native.interop.skia
|
||||
package = cpptypes
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user