[Interop] Generate IR for CEnum inheritors

Since there is non-trivial logic inside code that we generate for interop enums, we generate it inside special IrProvider.
This commit is contained in:
Sergey Bogolepov
2020-01-29 16:49:16 +07:00
committed by Sergey Bogolepov
parent 8de328e269
commit 44552393ad
8 changed files with 676 additions and 6 deletions
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isForwardDeclarationModule
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.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
@@ -201,14 +202,16 @@ internal val psiToIrPhase = konanUnitPhase(
val functionIrClassFactory = BuiltInFictitiousFunctionIrClassFactory(
symbolTable, generatorContext.irBuiltIns, reflectionTypes)
// TODO: Add special handling for enums and structs.
val irProviderForInteropStubs = IrProviderForInteropStubs { false }
val symbols = KonanSymbols(this, symbolTable, symbolTable.lazyWrapper, functionIrClassFactory)
val stubGenerator = DeclarationStubGenerator(
moduleDescriptor, symbolTable,
config.configuration.languageVersionSettings
)
val irProviders = listOf(irProviderForInteropStubs, functionIrClassFactory, deserializer, stubGenerator)
val irProviderForCEnums = IrProviderForCEnumStubs(generatorContext, interopBuiltIns, stubGenerator, symbols)
val irProviderForInteropStubs = IrProviderForInteropStubs { symbol ->
irProviderForCEnums.canHandleSymbol(symbol)
}
val irProviders = listOf(irProviderForCEnums, irProviderForInteropStubs, functionIrClassFactory, deserializer, stubGenerator)
stubGenerator.setIrProviders(irProviders)
expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
@@ -236,6 +239,8 @@ internal val psiToIrPhase = konanUnitPhase(
functionIrClassFactory.module =
(listOf(irModule!!) + deserializer.modules.values)
.single { it.descriptor.isKonanStdlib() }
irProviderForCEnums.module = module
},
name = "Psi2Ir",
description = "Psi to IR conversion",
@@ -0,0 +1,113 @@
/*
* Copyright 2010-2019 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
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.builders.IrBuilder
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression
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.getSuperInterfaces
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType
internal inline fun <reified T: DeclarationDescriptor> ClassDescriptor.findDeclarationByName(name: String): T? =
unsubstitutedMemberScope
.getContributedDescriptors()
.filterIsInstance<T>()
.firstOrNull { it.name.identifier == name }
/**
* Provides a set of functions and properties that helps
* to translate descriptor declarations to corresponding IR.
*/
internal interface DescriptorToIrTranslationMixin {
val symbolTable: SymbolTable
val irBuiltIns: IrBuiltIns
val typeTranslator: TypeTranslator
val stubGenerator: DeclarationStubGenerator
fun KotlinType.toIrType() = typeTranslator.translateType(this)
/**
* Declares [IrClass] instance from [descriptor] and populates it with
* supertypes, <this> parameter declaration and fake overrides.
* Additional elements are passed via [builder] callback.
*/
fun createClass(descriptor: ClassDescriptor, builder: (IrClass) -> Unit): IrClass =
symbolTable.declareClass(
startOffset = SYNTHETIC_OFFSET,
endOffset = SYNTHETIC_OFFSET,
origin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
descriptor = descriptor
).also { irClass ->
symbolTable.withScope(descriptor) {
descriptor.typeConstructor.supertypes.mapTo(irClass.superTypes) {
it.toIrType()
}
irClass.createParameterDeclarations()
builder(irClass)
createFakeOverrides(descriptor).forEach(irClass::addMember)
}
}
private fun createFakeOverrides(classDescriptor: ClassDescriptor): List<IrDeclaration> {
val fakeOverrides = classDescriptor.unsubstitutedMemberScope
.getContributedDescriptors()
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
return fakeOverrides.map {
when (it) {
is PropertyDescriptor -> createProperty(it)
is FunctionDescriptor -> createFunction(it, IrDeclarationOrigin.FAKE_OVERRIDE)
else -> error("Unexpected fake override descriptor: $it")
} as IrDeclaration // Assistance for type inference.
}
}
fun createConstructor(constructorDescriptor: ClassConstructorDescriptor): IrConstructor =
stubGenerator.generateMemberStub(constructorDescriptor) as IrConstructor
fun createProperty(propertyDescriptor: PropertyDescriptor): IrProperty =
stubGenerator.generateMemberStub(propertyDescriptor) as IrProperty
fun createFunction(
functionDescriptor: FunctionDescriptor,
origin: IrDeclarationOrigin? = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
): IrSimpleFunction =
stubGenerator.generateFunctionStub(functionDescriptor, createPropertyIfNeeded = false).also {
if (origin != null) it.origin = origin
}
}
internal fun IrBuilder.irInstanceInitializer(classSymbol: IrClassSymbol): IrExpression =
IrInstanceInitializerCallImpl(
startOffset, endOffset,
classSymbol,
context.irBuiltIns.unitType
)
internal fun ClassDescriptor.implementsCEnum(interopBuiltIns: InteropBuiltIns): Boolean =
interopBuiltIns.cEnum in this.getSuperInterfaces()
/**
* All enums that come from interop library implement CEnum interface.
* This function checks that given symbol located in subtree of
* CEnum inheritor.
*/
internal fun IrSymbol.findCEnumDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
descriptor.parentsWithSelf
.filterIsInstance<ClassDescriptor>()
.firstOrNull { it.implementsCEnum(interopBuiltIns) }
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2019 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
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumByValueFunctionGenerator
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.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.resolve.descriptorUtil.module
/**
* 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:
* 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
* compiler phases.
* 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(
context: GeneratorContext,
private val interopBuiltIns: InteropBuiltIns,
stubGenerator: DeclarationStubGenerator,
symbols: KonanSymbols
) : IrProvider {
private val symbolTable: SymbolTable = context.symbolTable
private val filesMap = mutableMapOf<PackageFragmentDescriptor, IrFile>()
private val cEnumByValueFunctionGenerator =
CEnumByValueFunctionGenerator(context, stubGenerator, symbols)
private val cEnumCompanionGenerator =
CEnumCompanionGenerator(context, stubGenerator, cEnumByValueFunctionGenerator)
private val cEnumVarClassGenerator =
CEnumVarClassGenerator(context, stubGenerator, interopBuiltIns)
private val cEnumClassGenerator =
CEnumClassGenerator(context, stubGenerator, cEnumCompanionGenerator, cEnumVarClassGenerator)
var module: IrModuleFragment? = null
set(value) {
if (value == null)
error("Provide a valid non-null module")
if (field != null)
error("Module has already been set")
field = value
value.files += filesMap.values
}
fun canHandleSymbol(symbol: IrSymbol): Boolean {
if (!symbol.descriptor.module.isFromInteropLibrary()) return false
return symbol.findCEnumDescriptor(interopBuiltIns) != null
}
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))
return when (symbol) {
is IrClassSymbol -> symbolTable.referenceClass(symbol.descriptor).owner
is IrEnumEntrySymbol -> symbolTable.referenceEnumEntry(symbol.descriptor).owner
is IrFunctionSymbol -> symbolTable.referenceFunction(symbol.descriptor).owner
is IrPropertySymbol -> symbolTable.referenceProperty(symbol.descriptor).owner
else -> error(symbol)
}
}
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)
}
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2019 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.cenum
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.findDeclarationByName
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
/**
* Generate IR for function that returns appropriate enum entry for the provided integral value.
*/
internal class CEnumByValueFunctionGenerator(
context: GeneratorContext,
override val stubGenerator: DeclarationStubGenerator,
private val symbols: KonanSymbols
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
fun generateByValueFunction(
companionIrClass: IrClass,
valuesIrFunctionSymbol: IrSimpleFunctionSymbol
): IrFunction {
val byValueFunctionDescriptor = companionIrClass.descriptor.findDeclarationByName<FunctionDescriptor>("byValue")!!
val byValueIrFunction = createFunction(byValueFunctionDescriptor)
val irValueParameter = byValueIrFunction.valueParameters.first()
// val values: Array<E> = values()
// var i: Int = 0
// val size: Int = values.size
// while (i < size) {
// val entry: E = values[i]
// if (entry.value == arg) {
// return entry
// }
// i++
// }
// throw NPE
byValueIrFunction.body = irBuilder(irBuiltIns, byValueIrFunction.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+irReturn(irBlock {
val values = irTemporaryVar(irCall(valuesIrFunctionSymbol))
val inductionVariable = irTemporaryVar(irInt(0))
val arrayClass = values.type.classOrNull!!
val valuesSize = irCall(symbols.arraySize.getValue(arrayClass)).also { irCall ->
irCall.dispatchReceiver = irGet(values)
}
val getElementFn = symbols.arrayGet.getValue(arrayClass)
val plusFun = symbols.intPlusInt
val lessFunctionSymbol = irBuiltIns.lessFunByOperandType.getValue(irBuiltIns.intClass)
+irWhile().also { loop ->
loop.condition = irCall(lessFunctionSymbol).also { irCall ->
irCall.putValueArgument(0, irGet(inductionVariable))
irCall.putValueArgument(1, valuesSize)
}
loop.body = irBlock {
val untypedEntry = irCall(getElementFn).also { irCall ->
irCall.dispatchReceiver = irGet(values)
irCall.putValueArgument(0, irGet(inductionVariable))
}
val entry = irTemporaryVar(irImplicitCast(untypedEntry, byValueIrFunction.returnType))
val valueGetter = entry.type.getClass()!!.getPropertyGetter("value")!!
val entryValue = irGet(irValueParameter.type, irGet(entry), valueGetter)
+irIfThenElse(
type = irBuiltIns.unitType,
condition = irEquals(entryValue, irGet(irValueParameter)),
thenPart = irReturn(irGet(entry)),
elsePart = irSetVar(
inductionVariable,
irCallOp(plusFun, irBuiltIns.intType,
irGet(inductionVariable),
irInt(1)
)
)
)
}
}
+irCall(symbols.ThrowNullPointerException)
})
}
return byValueIrFunction
}
}
@@ -0,0 +1,166 @@
/*
* Copyright 2010-2019 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.cenum
import org.jetbrains.kotlin.backend.konan.descriptors.enumEntries
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
import org.jetbrains.kotlin.backend.konan.ir.interop.findDeclarationByName
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrEnumConstructorCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.DeclarationGenerator
import org.jetbrains.kotlin.psi2ir.generators.EnumClassMembersGenerator
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
private fun extractConstantValue(descriptor: DeclarationDescriptor, type: String): ConstantValue<*>? =
descriptor.annotations
.findAnnotation(cEnumEntryValueAnnotationName.child(Name.identifier(type)))
?.allValueArguments
?.getValue(Name.identifier("value"))
private val cEnumEntryValueAnnotationName = FqName("kotlinx.cinterop.internal.ConstantValue")
private val cEnumEntryValueTypes = setOf(
"Byte", "Short", "Int", "Long",
"UByte", "UShort", "UInt", "ULong"
)
internal class CEnumClassGenerator(
val context: GeneratorContext,
override val stubGenerator: DeclarationStubGenerator,
private val cEnumCompanionGenerator: CEnumCompanionGenerator,
private val cEnumVarClassGenerator: CEnumVarClassGenerator
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
private val enumClassMembersGenerator = EnumClassMembersGenerator(DeclarationGenerator(context))
/**
* Searches for an IR class for [classDescriptor] in symbol table.
* Generates one if absent.
*/
fun findOrGenerateCEnum(classDescriptor: ClassDescriptor, parent: IrDeclarationContainer): IrClass {
val irClassSymbol = symbolTable.referenceClass(classDescriptor)
return if (!irClassSymbol.isBound) {
provideIrClassForCEnum(classDescriptor).also {
it.patchDeclarationParents(parent)
parent.declarations += it
}
} else {
irClassSymbol.owner
}
}
/**
* The main function that for given [descriptor] of the enum generates the whole
* IR tree including entries, CEnumVar class, and companion objects.
*/
private fun provideIrClassForCEnum(descriptor: ClassDescriptor): IrClass =
createClass(descriptor) { enumIrClass ->
enumIrClass.addMember(createEnumPrimaryConstructor(descriptor))
enumIrClass.addMember(createValueProperty(enumIrClass))
descriptor.enumEntries.mapTo(enumIrClass.declarations) { entryDescriptor ->
createEnumEntry(descriptor, entryDescriptor)
}
enumClassMembersGenerator.generateSpecialMembers(enumIrClass)
enumIrClass.addChild(cEnumCompanionGenerator.generate(enumIrClass))
enumIrClass.addChild(cEnumVarClassGenerator.generate(enumIrClass))
}
/**
* Creates `value` property that stores integral value of the enum.
*/
private fun createValueProperty(irClass: IrClass): IrProperty {
val propertyDescriptor = irClass.descriptor
.findDeclarationByName<PropertyDescriptor>("value")
?: error("No `value` property in ${irClass.name}")
val irProperty = createProperty(propertyDescriptor)
symbolTable.withScope(propertyDescriptor) {
irProperty.backingField = symbolTable.declareField(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.PROPERTY_BACKING_FIELD,
propertyDescriptor, propertyDescriptor.type.toIrType(), Visibilities.PRIVATE
).also {
it.initializer = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run {
irExprBody(irGet(irClass.primaryConstructor!!.valueParameters[0]))
}
}
irProperty.getter = createFunction(
propertyDescriptor.getter!!,
IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
).also { getter ->
getter.correspondingPropertySymbol = irProperty.symbol
getter.body = irBuilder(irBuiltIns, getter.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+irReturn(
irGetField(
irGet(getter.dispatchReceiverParameter!!),
irProperty.backingField!!
)
)
}
}
}
return irProperty
}
private fun createEnumEntry(enumDescriptor: ClassDescriptor, entryDescriptor: ClassDescriptor): IrEnumEntry {
return symbolTable.declareEnumEntry(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, entryDescriptor
).also { enumEntry ->
enumEntry.initializerExpression = IrEnumConstructorCallImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
type = irBuiltIns.unitType,
symbol = symbolTable.referenceConstructor(enumDescriptor.unsubstitutedPrimaryConstructor!!),
typeArgumentsCount = 0 // enums can't be generic
).also {
it.putValueArgument(0, extractEnumEntryValue(entryDescriptor))
}
}
}
/**
* Every enum entry that came from metadata-based interop library is annotated with
* [kotlinx.cinterop.internal.ConstantValue] annotation that holds internal constant value of the
* corresponding entry.
*
* This function extracts value from the annotation.
*/
private fun extractEnumEntryValue(entryDescriptor: ClassDescriptor): IrExpression =
cEnumEntryValueTypes.firstNotNullResult { extractConstantValue(entryDescriptor, it) } ?.let {
context.constantValueGenerator.generateConstantValueAsExpression(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, it)
} ?: error("Enum entry $entryDescriptor has no appropriate @$cEnumEntryValueAnnotationName annotation!")
private fun createEnumPrimaryConstructor(descriptor: ClassDescriptor): IrConstructor {
val irConstructor = createConstructor(descriptor.unsubstitutedPrimaryConstructor!!)
val enumConstructor = context.builtIns.enum.constructors.single()
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrEnumConstructorCallImpl(
startOffset, endOffset,
context.irBuiltIns.unitType,
symbolTable.referenceConstructor(enumConstructor),
typeArgumentsCount = 1 // kotlin.Enum<T> has a single type parameter.
).apply {
putTypeArgument(0, descriptor.defaultType.toIrType())
}
+irInstanceInitializer(symbolTable.referenceClass(descriptor))
}
return irConstructor
}
}
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2019 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.cenum
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.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
private val cEnumEntryAliasAnnonation = FqName("kotlinx.cinterop.internal.CEnumEntryAlias")
internal class CEnumCompanionGenerator(
context: GeneratorContext,
override val stubGenerator: DeclarationStubGenerator,
private val cEnumByValueFunctionGenerator: CEnumByValueFunctionGenerator
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
// Depends on already generated `.values()` irFunction.
fun generate(enumClass: IrClass): IrClass =
createClass(enumClass.descriptor.companionObjectDescriptor!!) { companionIrClass ->
companionIrClass.superTypes += irBuiltIns.anyType
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor))
val valuesFunction = enumClass.functions.single { it.name.identifier == "values" }.symbol
val byValueIrFunction = cEnumByValueFunctionGenerator
.generateByValueFunction(companionIrClass, valuesFunction)
companionIrClass.addMember(byValueIrFunction)
findEntryAliases(companionIrClass.descriptor)
.map { declareEntryAliasProperty(it, enumClass) }
.forEach(companionIrClass::addMember)
}
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor): IrConstructor {
val anyPrimaryConstructor = companionObjectDescriptor.builtIns.any.unsubstitutedPrimaryConstructor!!
val superConstructorSymbol = symbolTable.referenceConstructor(anyPrimaryConstructor)
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also {
it.body = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl(
startOffset, endOffset, context.irBuiltIns.unitType,
superConstructorSymbol
)
+irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor))
}
}
}
/**
* Returns all properties in companion object that represent aliases to
* enum entries.
*/
private fun findEntryAliases(companionDescriptor: ClassDescriptor) =
companionDescriptor.defaultType.memberScope.getContributedDescriptors()
.filterIsInstance<PropertyDescriptor>()
.filter { it.annotations.hasAnnotation(cEnumEntryAliasAnnonation) }
private fun fundCorrespondingEnumEntrySymbol(aliasDescriptor: PropertyDescriptor, irClass: IrClass): IrEnumEntrySymbol {
val enumEntryName = aliasDescriptor.annotations
.findAnnotation(cEnumEntryAliasAnnonation)!!
.getArgumentValueOrNull<String>("entryName")
return irClass.declarations.filterIsInstance<IrEnumEntry>()
.single { it.name.identifier == enumEntryName }.symbol
}
private fun generateAliasGetterBody(getter: IrSimpleFunction, entrySymbol: IrEnumEntrySymbol): IrBody =
irBuilder(irBuiltIns, getter.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+irReturn(
IrGetEnumValueImpl(startOffset, endOffset, entrySymbol.owner.parentAsClass.defaultType, entrySymbol)
)
}
private fun declareEntryAliasProperty(propertyDescriptor: PropertyDescriptor, enumClass: IrClass): IrProperty {
val entrySymbol = fundCorrespondingEnumEntrySymbol(propertyDescriptor, enumClass)
return createProperty(propertyDescriptor).also {
it.getter!!.body = generateAliasGetterBody(it.getter!!, entrySymbol)
}
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2019 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.cenum
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.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrProperty
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.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
private val typeSizeAnnotation = FqName("kotlinx.cinterop.internal.CEnumVarTypeSize")
internal class CEnumVarClassGenerator(
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(enumIrClass: IrClass): IrClass {
val enumVarClassDescriptor = enumIrClass.descriptor.unsubstitutedMemberScope
.getContributedClassifier(Name.identifier("Var"), NoLookupLocation.FROM_BACKEND)!! as ClassDescriptor
return createClass(enumVarClassDescriptor) { enumVarClass ->
enumVarClass.addMember(createPrimaryConstructor(enumVarClass))
enumVarClass.addMember(createCompanionObject(enumVarClass))
enumVarClass.addMember(createValueProperty(enumVarClass))
}
}
private fun createValueProperty(enumVarClass: IrClass): IrProperty {
val valuePropertyDescriptor = enumVarClass.descriptor.unsubstitutedMemberScope
.getContributedVariables(Name.identifier("value"), NoLookupLocation.FROM_BACKEND).single()
return createProperty(valuePropertyDescriptor)
}
private fun createPrimaryConstructor(enumVarClass: IrClass): IrConstructor {
val irConstructor = createConstructor(enumVarClass.descriptor.unsubstitutedPrimaryConstructor!!)
val enumVarConstructorSymbol = symbolTable.referenceConstructor(
interopBuiltIns.cEnumVar.unsubstitutedPrimaryConstructor!!
)
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(enumVarClass.descriptor))
}
return irConstructor
}
private fun createCompanionObject(enumVarClass: IrClass): IrClass =
createClass(enumVarClass.descriptor.companionObjectDescriptor!!) { companionIrClass ->
val typeSize = companionIrClass.descriptor.annotations
.findAnnotation(typeSizeAnnotation)!!
.getArgumentValueOrNull<Int>("size")!!
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, typeSize))
}
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, typeSize: Int): IrConstructor {
val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cPrimitiveVarType.unsubstitutedPrimaryConstructor!!)
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also {
it.body = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl(
startOffset, endOffset, context.irBuiltIns.unitType,
superConstructorSymbol
).also {
it.putValueArgument(0, irInt(typeSize))
}
+irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor))
}
}
}
}
@@ -49,12 +49,17 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import java.lang.reflect.Proxy
internal fun irBuilder(irBuiltIns: IrBuiltIns, scopeOwnerSymbol: IrSymbol): IrBuilderWithScope =
internal fun irBuilder(
irBuiltIns: IrBuiltIns,
scopeOwnerSymbol: IrSymbol,
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET
): IrBuilderWithScope =
object : IrBuilderWithScope(
IrGeneratorContextBase(irBuiltIns),
Scope(scopeOwnerSymbol),
UNDEFINED_OFFSET,
UNDEFINED_OFFSET
startOffset,
endOffset
) {}
//TODO: delete file on next kotlin dependency update