Garbage collection capable wrappers for skia interop

This commit is contained in:
Alexander Gorshenev
2021-05-06 19:11:05 +03:00
parent d9483ccb08
commit 9148094bbd
30 changed files with 1114 additions and 121 deletions
@@ -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
}
@@ -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)
}
}
@@ -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(),
@@ -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(
@@ -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 {
@@ -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)
@@ -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 ->
@@ -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
}