[Interop] Extend IrProviderForCEnumStubs to support structs.

Since current approach to generate IR for structs is the same as for enums
it makes sense to just plug in CStructVarClassGenerator into current provider.
This commit is contained in:
Sergey Bogolepov
2020-01-31 11:30:06 +07:00
committed by Sergey Bogolepov
parent e0117fd297
commit c1d20ae511
7 changed files with 217 additions and 24 deletions
@@ -2,7 +2,24 @@ package kotlinx.cinterop.internal
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class CStruct(val spelling: String)
annotation class CStruct(val spelling: String) {
@Retention(AnnotationRetention.BINARY)
@Target(
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
annotation class MemberAt(val offset: Long)
@Retention(AnnotationRetention.BINARY)
@Target(
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
annotation class BitField(val offset: Long, val size: Int)
@Retention(AnnotationRetention.BINARY)
annotation class VarType(val size: Long, val align: Int)
}
@Target(
AnnotationTarget.FUNCTION,
@@ -43,6 +43,8 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val cEnum = this.packageScope.getContributedClass("CEnum")
val cEnumVar = this.packageScope.getContributedClass("CEnumVar")
val cStructVar = this.packageScope.getContributedClass("CStructVar")
val cStructVarType = cStructVar.defaultType.memberScope.getContributedClass("Type")
val cPrimitiveVar = this.packageScope.getContributedClass("CPrimitiveVar")
val cPrimitiveVarType = cPrimitiveVar.defaultType.memberScope.getContributedClass("Type")
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForInteropStubs
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumStubs
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
@@ -207,11 +207,15 @@ internal val psiToIrPhase = konanUnitPhase(
moduleDescriptor, symbolTable,
config.configuration.languageVersionSettings
)
val irProviderForCEnums = IrProviderForCEnumStubs(generatorContext, interopBuiltIns, stubGenerator, symbols)
val irProviderForInteropStubs = IrProviderForInteropStubs { symbol ->
irProviderForCEnums.canHandleSymbol(symbol)
}
val irProviders = listOf(irProviderForCEnums, irProviderForInteropStubs, functionIrClassFactory, deserializer, stubGenerator)
val irProviderForCEnumsAndCStructs = IrProviderForCEnumAndCStructStubs(generatorContext, interopBuiltIns, stubGenerator, symbols)
val irProviderForInteropStubs = IrProviderForInteropStubs(irProviderForCEnumsAndCStructs::canHandleSymbol)
val irProviders = listOf(
irProviderForCEnumsAndCStructs,
irProviderForInteropStubs,
functionIrClassFactory,
deserializer,
stubGenerator
)
stubGenerator.setIrProviders(irProviders)
expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
@@ -233,7 +237,7 @@ internal val psiToIrPhase = konanUnitPhase(
}
modulesWithoutDCE
.filter(ModuleDescriptor::isFromInteropLibrary)
.forEach(irProviderForCEnums::buildAllEnumsFrom)
.forEach(irProviderForCEnumsAndCStructs::buildAllEnumsAndStructsFrom)
irModule = module
irModules = deserializer.modules.filterValues { llvmModuleSpecification.containsModule(it) }
@@ -243,7 +247,7 @@ internal val psiToIrPhase = konanUnitPhase(
(listOf(irModule!!) + deserializer.modules.values)
.single { it.descriptor.isKonanStdlib() }
irProviderForCEnums.module = module
irProviderForCEnumsAndCStructs.module = module
},
name = "Psi2Ir",
description = "Psi to IR conversion",
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType
@@ -57,6 +58,9 @@ internal interface DescriptorToIrTranslationMixin {
descriptor.typeConstructor.supertypes.mapTo(irClass.superTypes) {
it.toIrType()
}
descriptor.annotations.mapTo(irClass.annotations) {
typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(it)!!
}
irClass.createParameterDeclarations()
builder(irClass)
createFakeOverrides(descriptor).forEach(irClass::addMember)
@@ -102,6 +106,9 @@ internal fun IrBuilder.irInstanceInitializer(classSymbol: IrClassSymbol): IrExpr
internal fun ClassDescriptor.implementsCEnum(interopBuiltIns: InteropBuiltIns): Boolean =
interopBuiltIns.cEnum in this.getSuperInterfaces()
internal fun ClassDescriptor.inheritsFromCStructVar(interopBuiltIns: InteropBuiltIns): Boolean =
interopBuiltIns.cStructVar == this.getSuperClassNotAny()
/**
* All enums that come from interop library implement CEnum interface.
* This function checks that given symbol located in subtree of
@@ -110,4 +117,14 @@ internal fun ClassDescriptor.implementsCEnum(interopBuiltIns: InteropBuiltIns):
internal fun IrSymbol.findCEnumDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
descriptor.parentsWithSelf
.filterIsInstance<ClassDescriptor>()
.firstOrNull { it.implementsCEnum(interopBuiltIns) }
.firstOrNull { it.implementsCEnum(interopBuiltIns) }
/**
* All structs that come from interop library inherit from CStructVar class.
* This function checks that given symbol located in subtree of
* CStructVar inheritor.
*/
internal fun IrSymbol.findCStructDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
descriptor.parentsWithSelf
.filterIsInstance<ClassDescriptor>()
.firstOrNull { it.inheritsFromCStructVar(interopBuiltIns) }
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumByValueFunctionG
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumClassGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumCompanionGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumVarClassGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cstruct.CStructVarClassGenerator
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
@@ -30,7 +31,7 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
/**
* For the most of descriptors that come from metadata-based interop libraries
* we generate a lazy IR.
* We use a different approach for CEnums and generate IR eagerly. Motivation:
* We use a different approach for CEnums and CStructVars and generate IR eagerly. Motivation:
* 1. CEnums are "real" Kotlin enums. Thus, we need apply the same compilation approach
* as we use for usual Kotlin enums.
* Eager generation allows to reuse [EnumClassLowering], [EnumConstructorsLowering] and other
@@ -38,7 +39,7 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
* 2. It is an easier and more obvious approach. Since implementation of metadata-based
* libraries generation already took too much time we take an easier approach here.
*/
internal class IrProviderForCEnumStubs(
internal class IrProviderForCEnumAndCStructStubs(
context: GeneratorContext,
private val interopBuiltIns: InteropBuiltIns,
stubGenerator: DeclarationStubGenerator,
@@ -57,6 +58,8 @@ internal class IrProviderForCEnumStubs(
CEnumVarClassGenerator(context, stubGenerator, interopBuiltIns)
private val cEnumClassGenerator =
CEnumClassGenerator(context, stubGenerator, cEnumCompanionGenerator, cEnumVarClassGenerator)
private val cStructClassGenerator =
CStructVarClassGenerator(context, stubGenerator, interopBuiltIns)
var module: IrModuleFragment? = null
set(value) {
@@ -71,23 +74,36 @@ internal class IrProviderForCEnumStubs(
fun canHandleSymbol(symbol: IrSymbol): Boolean {
if (!symbol.descriptor.module.isFromInteropLibrary()) return false
return symbol.findCEnumDescriptor(interopBuiltIns) != null
|| symbol.findCStructDescriptor(interopBuiltIns) != null
}
fun buildAllEnumsFrom(interopModule: ModuleDescriptor) {
interopModule.getPackageFragments()
.flatMap { it.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) }
.filterIsInstance<ClassDescriptor>()
.filter { it.implementsCEnum(interopBuiltIns) }
.forEach { cEnumClassGenerator.findOrGenerateCEnum(it, irParentFor(it)) }
fun buildAllEnumsAndStructsFrom(interopModule: ModuleDescriptor) = interopModule.getPackageFragments()
.flatMap { it.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) }
.filterIsInstance<ClassDescriptor>()
.forEach {
when {
it.implementsCEnum(interopBuiltIns) ->
cEnumClassGenerator.findOrGenerateCEnum(it, irParentFor(it))
it.inheritsFromCStructVar(interopBuiltIns) ->
cStructClassGenerator.findOrGenerateCStruct(it, irParentFor(it))
}
}
private fun generateIrIfNeeded(symbol: IrSymbol) {
// TODO: These `findOrGenerate` calls generate a whole subtree.
// This a simple but clearly suboptimal solution.
symbol.findCEnumDescriptor(interopBuiltIns)?.let { enumDescriptor ->
cEnumClassGenerator.findOrGenerateCEnum(enumDescriptor, irParentFor(enumDescriptor))
}
symbol.findCStructDescriptor(interopBuiltIns)?.let { structDescriptor ->
cStructClassGenerator.findOrGenerateCStruct(structDescriptor, irParentFor(structDescriptor))
}
}
override fun getDeclaration(symbol: IrSymbol): IrDeclaration? {
if (symbol.isBound) return symbol.owner as IrDeclaration
if (!canHandleSymbol(symbol)) return null
val enumClassDescriptor = symbol.findCEnumDescriptor(interopBuiltIns)!!
// TODO: This call generates a whole subtree. This a simple but clearly suboptimal solution.
cEnumClassGenerator.findOrGenerateCEnum(enumClassDescriptor, irParentFor(enumClassDescriptor))
generateIrIfNeeded(symbol)
return when (symbol) {
is IrClassSymbol -> symbolTable.referenceClass(symbol.descriptor).owner
is IrEnumEntrySymbol -> symbolTable.referenceEnumEntry(symbol.descriptor).owner
@@ -100,8 +116,8 @@ internal class IrProviderForCEnumStubs(
private fun irParentFor(descriptor: ClassDescriptor): IrDeclarationContainer {
val packageFragmentDescriptor = descriptor.findPackage()
return filesMap.getOrPut(packageFragmentDescriptor) {
IrFileImpl(NaiveSourceBasedFileEntryImpl("CEnums"), packageFragmentDescriptor).also {
this@IrProviderForCEnumStubs.module?.files?.add(it)
IrFileImpl(NaiveSourceBasedFileEntryImpl("CTypeDefinitions"), packageFragmentDescriptor).also {
this@IrProviderForCEnumAndCStructStubs.module?.files?.add(it)
}
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
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.PropertyDescriptor
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.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
internal class CStructVarClassGenerator(
context: GeneratorContext,
override val stubGenerator: DeclarationStubGenerator,
private val interopBuiltIns: InteropBuiltIns
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
private val companionGenerator = CStructVarCompanionGenerator(context, stubGenerator, interopBuiltIns)
fun findOrGenerateCStruct(classDescriptor: ClassDescriptor, parent: IrDeclarationContainer): IrClass {
val irClassSymbol = symbolTable.referenceClass(classDescriptor)
return if (!irClassSymbol.isBound) {
provideIrClassForCStruct(classDescriptor).also {
it.patchDeclarationParents(parent)
parent.declarations += it
}
} else {
irClassSymbol.owner
}
}
private fun provideIrClassForCStruct(descriptor: ClassDescriptor): IrClass =
createClass(descriptor) { irClass ->
irClass.addMember(createPrimaryConstructor(irClass))
irClass.addMember(companionGenerator.generate(descriptor))
descriptor.unsubstitutedMemberScope
.getContributedDescriptors()
.filterIsInstance<PropertyDescriptor>()
.filter { it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
.map(this::createProperty)
.forEach(irClass::addMember)
}
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
val enumVarConstructorSymbol = symbolTable.referenceConstructor(
interopBuiltIns.cStructVar.unsubstitutedPrimaryConstructor!!
)
return createConstructor(irClass.descriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl(
startOffset, endOffset,
context.irBuiltIns.unitType, enumVarConstructorSymbol
).also {
it.putValueArgument(0, irGet(irConstructor.valueParameters[0]))
}
+irInstanceInitializer(symbolTable.referenceClass(irClass.descriptor))
}
}
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
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
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irLong
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.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.irBuilder
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
private val varTypeAnnotationFqName = FqName("kotlinx.cinterop.internal.CStruct.VarType")
internal class CStructVarCompanionGenerator(
context: GeneratorContext,
override val stubGenerator: DeclarationStubGenerator,
private val interopBuiltIns: InteropBuiltIns
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
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))
}
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, size: Long, align: Int): IrConstructor {
val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cStructVarType.unsubstitutedPrimaryConstructor!!)
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl(
startOffset, endOffset, context.irBuiltIns.unitType,
superConstructorSymbol
).also {
it.putValueArgument(0, irLong(size))
it.putValueArgument(1, irInt(align))
}
+irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor))
}
}
}
}