[K/N][IR] Refactored enums lowering

This commit is contained in:
Igor Chevdar
2022-08-10 18:36:36 +03:00
committed by Space
parent f4c5f5d8eb
commit 0ea8fc5b08
6 changed files with 241 additions and 416 deletions
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.backend.konan.lower.BridgesSupport
import org.jetbrains.kotlin.backend.konan.lower.EnumsSupport
import org.jetbrains.kotlin.backend.konan.lower.InlineFunctionsSupport
import org.jetbrains.kotlin.backend.konan.lower.InnerClassesSupport
import org.jetbrains.kotlin.backend.konan.lower.InternalLoweredEnum
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis
import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG
@@ -60,11 +59,12 @@ internal class NativeMapping : DefaultMapping() {
data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections)
val outerThisFields = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
val enumImplObjects = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrClass>()
val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrFunction>()
val enumEntriesMaps = mutableMapOf<IrClass, Map<Name, LoweredEnumEntryDescription>>()
val bridges = mutableMapOf<BridgeKey, IrSimpleFunction>()
val notLoweredInlineFunctions = mutableMapOf<IrFunctionSymbol, IrFunction>()
val loweredInlineFunctions = mutableMapOf<IrFunction, InlineFunctionOriginInfo>()
val internalLoweredEnums = mutableMapOf<IrClass, InternalLoweredEnum>()
val externalLoweredEnums = mutableMapOf<IrClass, ExternalLoweredEnum>()
}
internal class Context(config: KonanConfig) : KonanBackendContext(config) {
@@ -104,7 +104,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
val innerClassesSupport by lazy { InnerClassesSupport(mapping, irFactory) }
val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) }
val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) }
val enumsSupport by lazy { EnumsSupport(this) }
val enumsSupport by lazy { EnumsSupport(mapping, ir.symbols, irBuiltIns, irFactory) }
open class LazyMember<T>(val initializer: Context.() -> T) {
operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this)
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMo
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.CacheInfoBuilder
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_ENUM
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.lower.SamSuperTypesChecker
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
@@ -427,6 +429,21 @@ internal val exportInternalAbiPhase = makeKonanModuleOpPhase(
}
context.internalAbi.declare(function, declaration.module)
}
if (declaration.isEnumClass) {
val function = context.irFactory.buildFun {
name = InternalAbi.getEnumValuesAccessorName(declaration)
returnType = context.ir.symbols.array.typeWith(declaration.defaultType)
origin = InternalAbi.INTERNAL_ABI_ORIGIN
}
context.createIrBuilder(function.symbol).run {
function.body = irBlockBody {
+irReturn(with(context.enumsSupport) { irGetValuesField(declaration) })
}
}
context.internalAbi.declare(function, declaration.module)
}
}
override fun visitProperty(declaration: IrProperty) {
@@ -470,6 +487,7 @@ internal val useInternalAbiPhase = makeKonanModuleOpPhase(
val companionObjectAccessors = mutableMapOf<IrClass, IrSimpleFunction>()
val outerThisAccessors = mutableMapOf<IrClass, IrSimpleFunction>()
val lateinitPropertyAccessors = mutableMapOf<IrProperty, IrSimpleFunction>()
val enumValuesAccessors = mutableMapOf<IrClass, IrSimpleFunction>()
val transformer = object : IrElementTransformerVoid() {
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
@@ -561,6 +579,23 @@ internal val useInternalAbiPhase = makeKonanModuleOpPhase(
}
}
field.origin == DECLARATION_ORIGIN_ENUM -> {
val enumClass = irClass?.parentClassOrNull
require(enumClass != null) { "Unexpected usage of enum VALUES field" }
require(enumClass.isEnumClass) { "Expected a enum class: ${enumClass.render()}" }
val accessor = enumValuesAccessors.getOrPut(enumClass) {
context.irFactory.buildFun {
name = InternalAbi.getEnumValuesAccessorName(enumClass)
returnType = context.ir.symbols.array.typeWith(enumClass.defaultType)
origin = InternalAbi.INTERNAL_ABI_ORIGIN
isExternal = true
}.also {
context.internalAbi.reference(it, enumClass.module)
}
}
return IrCallImpl(expression.startOffset, expression.endOffset, expression.type, accessor.symbol, accessor.typeParameters.size, accessor.valueParameters.size)
}
else -> expression
}
}
@@ -1366,19 +1366,10 @@ internal abstract class FunctionGenerationContext(
*/
fun getEnumEntry(enumEntry: IrEnumEntry, exceptionHandler: ExceptionHandler): LLVMValueRef {
val enumClass = enumEntry.parentAsClass
val loweredEnum = context.enumsSupport.getLoweredEnum(enumClass)
val getterId = loweredEnum.entriesMap[enumEntry.name]!!.getterId
val values = call(
loweredEnum.valuesGetter.llvmFunction.llvmValue,
emptyList(),
Lifetime.ARGUMENT,
exceptionHandler
)
val getterId = context.enumsSupport.enumEntriesMap(enumClass)[enumEntry.name]!!.getterId
return call(
loweredEnum.itemGetterSymbol.owner.llvmFunction.llvmValue,
listOf(values, Int32(getterId).llvm),
context.enumsSupport.getValueGetter(enumClass).llvmFunction.llvmValue,
listOf(Int32(getterId).llvm),
Lifetime.GLOBAL,
exceptionHandler
)
@@ -18,53 +18,27 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
private class EnumSyntheticFunctionsBuilder(val context: Context) {
fun IrBuilderWithScope.enumValues(enumClass: IrClass): IrExpression {
val loweredEnum = this@EnumSyntheticFunctionsBuilder.context.enumsSupport.getLoweredEnum(enumClass)
return irCall(genericValuesSymbol, listOf(enumClass.defaultType)).apply {
putValueArgument(0, loweredEnum.getValuesField(startOffset, endOffset))
}
}
fun IrBuilderWithScope.enumValueOf(enumClass: IrClass, value: IrExpression): IrExpression {
val loweredEnum = this@EnumSyntheticFunctionsBuilder.context.enumsSupport.getLoweredEnum(enumClass)
return irCall(genericValueOfSymbol, listOf(enumClass.defaultType)).apply {
putValueArgument(0, value)
putValueArgument(1, loweredEnum.getValuesField(startOffset, endOffset))
}
}
private val genericValueOfSymbol = context.ir.symbols.valueOfForEnum
private val genericValuesSymbol = context.ir.symbols.valuesForEnum
}
internal class NativeEnumWhenLowering constructor(context: Context) : EnumWhenLowering(context) {
override fun mapConstEnumEntry(entry: IrEnumEntry): Int {
val parent = entry.parentAsClass
val loweredEnum = (context as Context).enumsSupport.getLoweredEnumOrNull(parent)
?: return super.mapConstEnumEntry(entry)
return loweredEnum.entriesMap[entry.name]!!.ordinal
val enumEntriesMap = (context as Context).enumsSupport.enumEntriesMap(entry.parentAsClass)
return enumEntriesMap[entry.name]!!.ordinal
}
}
internal class EnumUsageLowering(val context: Context)
: IrElementTransformer<IrBuilderWithScope?>, FileLoweringPass {
private val enumSyntheticFunctionsBuilder = EnumSyntheticFunctionsBuilder(context)
internal class EnumUsageLowering(val context: Context) : IrElementTransformer<IrBuilderWithScope?>, FileLoweringPass {
private val enumsSupport = context.enumsSupport
private val symbols = context.ir.symbols
private val arrayGet = symbols.arrayGet[symbols.array]!!
override fun lower(irFile: IrFile) {
visitFile(irFile, data = null)
@@ -99,25 +73,28 @@ internal class EnumUsageLowering(val context: Context)
require(irClass.kind == ClassKind.ENUM_CLASS)
return with(enumSyntheticFunctionsBuilder) {
return with(enumsSupport) {
if (intrinsicType == IntrinsicType.ENUM_VALUES)
data.enumValues(irClass)
data.irEnumValues(irClass)
else
data.enumValueOf(irClass, expression.getValueArgument(0)!!)
data.irEnumValueOf(irClass, expression.getValueArgument(0)!!)
}
}
private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name): IrExpression {
val loweredEnum = this@EnumUsageLowering.context.enumsSupport.getLoweredEnum(enumClass)
val getterId = loweredEnum.entriesMap.getValue(name).getterId
return irCall(loweredEnum.itemGetterSymbol, enumClass.defaultType).apply {
dispatchReceiver = irCall(loweredEnum.valuesGetter)
putValueArgument(0, irInt(getterId))
}
}
private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name) =
irCall(arrayGet, enumClass.defaultType).apply {
dispatchReceiver = with(enumsSupport) { irGetValuesField(enumClass) }
putValueArgument(0, irInt(enumsSupport.enumEntriesMap(enumClass).getValue(name).getterId))
}
}
internal class EnumClassLowering(val context: Context) : FileLoweringPass {
private val enumsSupport = context.enumsSupport
private val symbols = context.ir.symbols
private val createUninitializedInstance = symbols.createUninitializedInstance
private val initInstance = symbols.initInstance
private val arrayGet = symbols.arrayGet[symbols.array]!!
private val constructorOfAny = context.irBuiltIns.anyClass.owner.constructors.first()
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
@@ -131,167 +108,137 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
}
private inner class EnumClassTransformer(val irClass: IrClass) {
private val loweredEnum = context.enumsSupport.getInternalLoweredEnum(irClass)
private val enumSyntheticFunctionsBuilder = EnumSyntheticFunctionsBuilder(context)
private val implObject = enumsSupport.getImplObject(irClass)
private val valuesField = enumsSupport.getValuesField(implObject)
private val enumEntriesMap = enumsSupport.enumEntriesMap(irClass)
fun run() {
pullUpEnumEntriesClasses()
createImplObject()
val enumEntries = transformEnumBody()
defineImplObject(enumEntries)
defineValueGetter()
}
private fun pullUpEnumEntriesClasses() {
irClass.declarations.transformFlat { declaration ->
if (declaration is IrEnumEntry) {
val correspondingClass = declaration.correspondingClass
declaration.correspondingClass = null
listOfNotNull(declaration, correspondingClass)
} else null
}
}
private fun createImplObject() {
val implObject = loweredEnum.implObject
private fun transformEnumBody(): List<IrEnumEntry> {
val enumEntries = mutableListOf<IrEnumEntry>()
var i = 0
while (i < irClass.declarations.size) {
val declaration = irClass.declarations[i]
var delete = false
irClass.declarations.transformFlat { declaration ->
when (declaration) {
is IrEnumEntry -> {
enumEntries.add(declaration)
delete = true
val correspondingClass = declaration.correspondingClass
declaration.correspondingClass = null
listOfNotNull(correspondingClass)
}
is IrFunction -> {
val body = declaration.body
if (body is IrSyntheticBody) {
when (body.kind) {
IrSyntheticBodyKind.ENUM_VALUEOF ->
declaration.body = createSyntheticValueOfMethodBody(declaration)
IrSyntheticBodyKind.ENUM_VALUES ->
declaration.body = createSyntheticValuesMethodBody(declaration)
declaration.body = when (body.kind) {
IrSyntheticBodyKind.ENUM_VALUEOF -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) {
+irReturn(with(enumsSupport) { irEnumValueOf(irClass, irGet(declaration.valueParameters[0])) })
}
IrSyntheticBodyKind.ENUM_VALUES -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) {
+irReturn(with(enumsSupport) { irEnumValues(irClass) })
}
IrSyntheticBodyKind.ENUM_ENTRIES -> TODO("KT-48872 is not yet supported")
}
}
null
}
else -> null
}
if (delete)
irClass.declarations.removeAt(i)
else
++i
}
return enumEntries
}
implObject.declarations += createSyntheticValuesPropertyDeclaration(enumEntries)
private fun defineValueGetter() {
val valueGetter = enumsSupport.getValueGetter(irClass)
context.createIrBuilder(valueGetter.symbol).run {
valueGetter.body = irBlockBody(valueGetter) {
+irReturn(irCall(arrayGet, irClass.defaultType).apply {
dispatchReceiver = with(enumsSupport) { irGetValuesField(irClass) }
putValueArgument(0, irGet(valueGetter.valueParameters[0]))
})
}
}
irClass.declarations.add(valueGetter)
}
private fun defineImplObject(enumEntries: List<IrEnumEntry>) {
implObject.createParameterDeclarations()
implObject.addSimpleDelegatingConstructor(constructorOfAny, context.irBuiltIns, true /* TODO: why primary? */)
implObject.addFakeOverrides(context.typeSystem)
irClass.declarations.add(implObject)
ImplConstructorBuilder(implObject.constructors.single()).build(enumEntries)
}
private inner class ImplConstructorBuilder(val constructor: IrConstructor) {
private val irBuilder = context.createIrBuilder(constructor.symbol, constructor.startOffset, constructor.endOffset)
fun build(enumEntries: List<IrEnumEntry>) {
val statements = (constructor.body as IrBlockBody).statements
val companion = irClass.companionObject()
if (companion != null) {
// The initialization order is the following:
// - first all enum entries in their declaration order
// - then companion object if it exists
val constructor = implObject.constructors.single()
context.createIrBuilder(constructor.symbol).run {
(constructor.body as IrBlockBody).statements += irGetObject(companion.symbol)
}
statements += buildValuesFieldInitializer(enumEntries)
// Split allocation and constructors calling because enum entries can reference one another.
statements += callEnumEntriesConstructors(enumEntries)
irClass.companionObject()?.let { statements += irBuilder.irGetObject(it.symbol) }
}
irClass.declarations += implObject
}
private val createUninitializedInstance = context.ir.symbols.createUninitializedInstance.owner
private fun createSyntheticValuesPropertyDeclaration(enumEntries: List<IrEnumEntry>): IrProperty {
val startOffset = irClass.startOffset
val endOffset = irClass.endOffset
val implObject = loweredEnum.implObject
val constructor = implObject.constructors.single()
val irValuesInitializer = context.createArrayOfExpression(
startOffset, endOffset,
irClass.defaultType,
enumEntries
.sortedBy { it.name }
.map {
val initializer = it.initializerExpression?.expression
val entryConstructorCall = when {
initializer is IrConstructorCall -> initializer
initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL ->
initializer.statements.last() as IrConstructorCall
else -> error("Unexpected initializer: $initializer")
}
val entryClass = entryConstructorCall.symbol.owner.constructedClass
irCall(startOffset, endOffset,
createUninitializedInstance,
listOf(entryClass.defaultType)
)
}
)
val irField = loweredEnum.valuesField
context.createIrBuilder(constructor.symbol).run {
(constructor.body as IrBlockBody).statements +=
irSetField(irGet(implObject.thisReceiver!!), irField, irValuesInitializer)
}
val getter = loweredEnum.valuesGetter
context.createIrBuilder(getter.symbol).run {
getter.body = irBlockBody(irClass) { +irReturn(irGetField(irGetObject(implObject.symbol), irField)) }
}
createValuesPropertyInitializer(enumEntries)
return with(loweredEnum.valuesField.descriptor) {
IrPropertyImpl(
startOffset, endOffset, DECLARATION_ORIGIN_ENUM, IrPropertySymbolImpl(this),
name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal
).apply {
this.backingField = irField
this.getter = getter
this.parent = implObject
}
}
}
private val initInstanceSymbol = context.ir.symbols.initInstance
private val arrayGetSymbol = context.ir.symbols.array.functions.single { it.owner.name == Name.identifier("get") }
private val arrayType = context.ir.symbols.array.typeWith(irClass.defaultType)
private fun createValuesPropertyInitializer(enumEntries: List<IrEnumEntry>) {
val startOffset = irClass.startOffset
val endOffset = irClass.endOffset
fun IrBlockBuilder.initInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall =
irCall(initInstanceSymbol).apply {
private fun IrBlockBuilder.irInitInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall =
irCall(initInstance).apply {
putValueArgument(0, instance)
putValueArgument(1, constructor)
}
val implObject = loweredEnum.implObject
val constructor = implObject.constructors.single()
val irBuilder = context.createIrBuilder(constructor.symbol, startOffset, endOffset)
val valuesInitializer = irBuilder.irBlock(startOffset, endOffset) {
private fun buildValuesFieldInitializer(enumEntries: List<IrEnumEntry>) = irBuilder.run {
val irValuesInitializer = this@EnumClassLowering.context.createArrayOfExpression(
startOffset, endOffset,
irClass.defaultType,
enumEntries
.sortedBy { it.name }
.map {
val initializer = it.initializerExpression?.expression
val entryConstructorCall = when {
initializer is IrConstructorCall -> initializer
initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL ->
initializer.statements.last() as IrConstructorCall
else -> error("Unexpected initializer: $initializer")
}
val entryClass = entryConstructorCall.symbol.owner.constructedClass
irCall(createUninitializedInstance, listOf(entryClass.defaultType))
}
)
irSetField(irGet(implObject.thisReceiver!!), valuesField, irValuesInitializer)
}
private fun callEnumEntriesConstructors(enumEntries: List<IrEnumEntry>) = irBuilder.irBlock {
val receiver = implObject.thisReceiver!!
val instances = irTemporary(irGetField(irGet(receiver), loweredEnum.valuesField))
val enumIndices = enumEntries.sortedBy { it.name }.withIndex().associate { it.value to it.index }
val instances = irTemporary(irGetField(irGet(receiver), valuesField))
enumEntries.forEach {
val instance = irCall(arrayGetSymbol).apply {
val instance = irCall(arrayGet).apply {
dispatchReceiver = irGet(instances)
putValueArgument(0, irInt(enumIndices[it]!!))
putValueArgument(0, irInt(enumEntriesMap[it.name]!!.getterId))
}
val initializer = it.initializerExpression!!.expression
initializer.setDeclarationsParent(constructor)
when {
initializer is IrConstructorCall -> +initInstanceCall(instance, initializer)
initializer is IrConstructorCall -> +irInitInstanceCall(instance, initializer)
initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> {
val statements = initializer.statements
val constructorCall = statements.last() as IrConstructorCall
statements[statements.lastIndex] = initInstanceCall(instance, constructorCall)
statements[statements.lastIndex] = irInitInstanceCall(instance, constructorCall)
+initializer
}
@@ -300,22 +247,11 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass {
}
// Needed for legacy MM targets that do not support threads.
if (this@EnumClassLowering.context.memoryModel != MemoryModel.EXPERIMENTAL) {
+irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(arrayType)).apply {
+irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(valuesField.type)).apply {
extensionReceiver = irGet(receiver)
}
}
}
(constructor.body as IrBlockBody).statements += valuesInitializer
}
private fun createSyntheticValuesMethodBody(declaration: IrFunction) =
context.createIrBuilder(declaration.symbol, irClass.startOffset, irClass.endOffset).irBlockBody {
+irReturn(with(enumSyntheticFunctionsBuilder) { enumValues(irClass) })
}
private fun createSyntheticValueOfMethodBody(declaration: IrFunction) =
context.createIrBuilder(declaration.symbol, irClass.startOffset, irClass.endOffset).irBlockBody {
+irReturn(with(enumSyntheticFunctionsBuilder) { enumValueOf(irClass, irGet(declaration.valueParameters[0])) })
}
}
}
@@ -228,7 +228,7 @@ internal class EnumConstructorsLowering(val context: Context) : ClassLoweringPas
override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression {
val name = enumEntry.name.asString()
val ordinal = context.enumsSupport.getEnumEntryOrdinal(enumEntry)
val ordinal = context.enumsSupport.enumEntriesMap(enumEntry.parentAsClass)[enumEntry.name]!!.ordinal
val startOffset = enumConstructorCall.startOffset
val endOffset = enumConstructorCall.endOffset
@@ -5,255 +5,118 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.InternalAbi
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.konan.NativeMapping
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
internal class EnumsSupport(private val context: Context) {
private val enumSpecialDeclarationsFactory = EnumSpecialDeclarationsFactory(context)
private val internalLoweredEnums = context.mapping.internalLoweredEnums
private val externalLoweredEnums = context.mapping.externalLoweredEnums
internal class EnumsSupport(
mapping: NativeMapping,
symbols: KonanSymbols,
private val irBuiltIns: IrBuiltIns,
private val irFactory: IrFactory,
) {
private val enumImplObjects = mapping.enumImplObjects
private val enumValueGetters = mapping.enumValueGetters
private val enumEntriesMaps = mapping.enumEntriesMaps
private val array = symbols.array
private val genericValueOfSymbol = symbols.valueOfForEnum
private val genericValuesSymbol = symbols.valuesForEnum
fun getLoweredEnumOrNull(enumClass: IrClass): LoweredEnumAccess? {
fun enumEntriesMap(enumClass: IrClass): Map<Name, LoweredEnumEntryDescription> {
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
return if (!context.llvmModuleSpecification.containsDeclaration(enumClass)) {
externalLoweredEnums[enumClass]
} else {
internalLoweredEnums[enumClass]
return enumEntriesMaps.getOrPut(enumClass) {
data class NameWithOrdinal(val name: Name, val ordinal: Int)
enumClass.declarations.asSequence()
.filterIsInstance<IrEnumEntry>()
.mapIndexed { index, it -> NameWithOrdinal(it.name, index) }
.sortedBy { it.name }
.withIndex()
.associate { it.value.name to LoweredEnumEntryDescription(it.value.ordinal, it.index) }
.toMap()
}
}
fun getLoweredEnum(enumClass: IrClass): LoweredEnumAccess {
fun getImplObject(enumClass: IrClass): IrClass {
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
return if (!context.llvmModuleSpecification.containsDeclaration(enumClass)) {
externalLoweredEnums.getOrPut(enumClass) {
enumSpecialDeclarationsFactory.createExternalLoweredEnum(enumClass)
}
} else {
internalLoweredEnums.getOrPut(enumClass) {
enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass)
return enumImplObjects.getOrPut(enumClass) {
irFactory.buildClass {
startOffset = enumClass.startOffset
endOffset = enumClass.endOffset
origin = DECLARATION_ORIGIN_ENUM
name = "OBJECT".synthesizedName
kind = ClassKind.OBJECT
}.apply {
superTypes = listOf(irBuiltIns.anyType)
parent = enumClass
addChild(irFactory.buildField {
startOffset = enumClass.startOffset
endOffset = enumClass.endOffset
origin = DECLARATION_ORIGIN_ENUM
name = "VALUES".synthesizedName
type = array.typeWith(enumClass.defaultType)
visibility = DescriptorVisibilities.PRIVATE
isFinal = true
})
}
}
}
fun getInternalLoweredEnum(enumClass: IrClass): InternalLoweredEnum {
fun getValueGetter(enumClass: IrClass): IrFunction {
require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" }
require(context.llvmModuleSpecification.containsDeclaration(enumClass)) { "Expected enum class from current module." }
return internalLoweredEnums.getOrPut(enumClass) {
enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass)
return enumValueGetters.getOrPut(enumClass) {
irFactory.buildFun {
startOffset = enumClass.startOffset
endOffset = enumClass.endOffset
origin = DECLARATION_ORIGIN_ENUM
name = "getEnumAt".synthesizedName
returnType = enumClass.defaultType
}.apply {
parent = enumClass
addValueParameter {
name = Name.identifier("getterId")
origin = DECLARATION_ORIGIN_ENUM
type = irBuiltIns.intType
}
}
}
}
fun getEnumEntryOrdinal(enumEntry: IrEnumEntry) =
enumEntry.parentAsClass.declarations.filterIsInstance<IrEnumEntry>().indexOf(enumEntry)
fun getValuesField(implObject: IrClass) = implObject.fields.single()
fun IrBuilderWithScope.irGetValuesField(enumClass: IrClass): IrExpression {
val implObject = getImplObject(enumClass)
val valuesField = getValuesField(implObject)
return irGetField(irGetObject(implObject.symbol), valuesField)
}
fun IrBuilderWithScope.irEnumValues(enumClass: IrClass) =
irCall(genericValuesSymbol, listOf(enumClass.defaultType)).apply {
putValueArgument(0, irGetValuesField(enumClass))
}
fun IrBuilderWithScope.irEnumValueOf(enumClass: IrClass, value: IrExpression) =
irCall(genericValueOfSymbol, listOf(enumClass.defaultType)).apply {
putValueArgument(0, value)
putValueArgument(1, irGetValuesField(enumClass))
}
}
internal object DECLARATION_ORIGIN_ENUM : IrDeclarationOriginImpl("ENUM")
internal data class LoweredEnumEntryDescription(val ordinal: Int, val getterId: Int)
/**
* Common interface for both [InternalLoweredEnum] and [ExternalLoweredEnum]
* that allows to work with lowered enum regardless of its location.
*/
internal interface LoweredEnumAccess {
val valuesGetter: IrSimpleFunction
val itemGetterSymbol: IrSimpleFunctionSymbol
val entriesMap: Map<Name, LoweredEnumEntryDescription>
fun getValuesField(startOffset: Int, endOffset: Int): IrExpression
}
/**
* Represents lowered enum from current module.
*/
internal data class InternalLoweredEnum(
val implObject: IrClass,
val valuesField: IrField,
val valuesGetterWrapper: IrSimpleFunction,
override val valuesGetter: IrSimpleFunction,
override val itemGetterSymbol: IrSimpleFunctionSymbol,
override val entriesMap: Map<Name, LoweredEnumEntryDescription>
) : LoweredEnumAccess {
private fun internalObjectGetter(startOffset: Int, endOffset: Int) =
IrGetObjectValueImpl(startOffset, endOffset,
implObject.defaultType,
implObject.symbol
)
override fun getValuesField(startOffset: Int, endOffset: Int): IrExpression = IrGetFieldImpl(
startOffset,
endOffset,
valuesField.symbol,
valuesField.type,
internalObjectGetter(startOffset, endOffset)
)
}
/**
* Represents lowered enum that's located in external module.
*/
internal data class ExternalLoweredEnum(
override val valuesGetter: IrSimpleFunction,
override val itemGetterSymbol: IrSimpleFunctionSymbol,
override val entriesMap: Map<Name, LoweredEnumEntryDescription>
) : LoweredEnumAccess {
override fun getValuesField(startOffset: Int, endOffset: Int): IrExpression =
IrCallImpl(startOffset, endOffset, valuesGetter.returnType, valuesGetter.symbol, valuesGetter.typeParameters.size, valuesGetter.valueParameters.size)
}
internal class EnumSpecialDeclarationsFactory(val context: Context) {
private val symbols = context.ir.symbols
private fun enumEntriesMap(enumClass: IrClass): Map<Name, LoweredEnumEntryDescription> {
data class NameWithOrdinal(val name: Name, val ordinal: Int)
return enumClass.declarations.asSequence()
.filterIsInstance<IrEnumEntry>()
.mapIndexed { index, it -> NameWithOrdinal(it.name, index) }
.sortedBy { it.name }
.withIndex()
.associate { it.value.name to LoweredEnumEntryDescription(it.value.ordinal, it.index) }
.toMap()
}
private fun findItemGetterSymbol(): IrSimpleFunctionSymbol =
symbols.array.functions.single { it.descriptor.name == Name.identifier("get") }
private fun valuesArrayType(enumClass: IrClass): IrType =
symbols.array.typeWith(enumClass.defaultType)
// We can't move property getter to the top-level scope.
// So add a wrapper instead.
private fun createValuesGetterWrapper(enumClass: IrClass, isExternal: Boolean): IrSimpleFunction =
context.irFactory.buildFun {
name = InternalAbi.getEnumValuesAccessorName(enumClass)
returnType = valuesArrayType(enumClass)
origin = InternalAbi.INTERNAL_ABI_ORIGIN
this.isExternal = isExternal
}.also {
if (isExternal) {
context.internalAbi.reference(it, enumClass.module)
} else {
context.internalAbi.declare(it, enumClass.module)
}
}
fun createExternalLoweredEnum(enumClass: IrClass): ExternalLoweredEnum {
val enumEntriesMap = enumEntriesMap(enumClass)
val itemGetterSymbol = findItemGetterSymbol()
val valuesGetterWrapper = createValuesGetterWrapper(enumClass, isExternal = true)
return ExternalLoweredEnum(valuesGetterWrapper, itemGetterSymbol, enumEntriesMap)
}
fun createInternalLoweredEnum(enumClass: IrClass): InternalLoweredEnum {
val startOffset = enumClass.startOffset
val endOffset = enumClass.endOffset
val implObject =
IrClassImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_ENUM,
IrClassSymbolImpl(),
"OBJECT".synthesizedName,
ClassKind.OBJECT,
DescriptorVisibilities.PUBLIC,
Modality.FINAL,
isCompanion = false,
isInner = false,
isData = false,
isExternal = false,
isValue = false,
isExpect = false,
isFun = false
).apply {
parent = enumClass
createParameterDeclarations()
}
val valuesType = valuesArrayType(enumClass)
val valuesField =
IrFieldImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_ENUM,
IrFieldSymbolImpl(),
"VALUES".synthesizedName,
valuesType,
DescriptorVisibilities.PRIVATE,
isFinal = true,
isExternal = false,
isStatic = false,
).apply {
parent = implObject
}
val valuesGetter =
IrFunctionImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_ENUM,
IrSimpleFunctionSymbolImpl(),
"get-VALUES".synthesizedName,
DescriptorVisibilities.PUBLIC,
Modality.FINAL,
valuesType,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = false,
isExpect = false,
isFakeOverride = false,
isOperator = false,
isInfix = false
).apply {
parent = implObject
}
val constructorOfAny = context.irBuiltIns.anyClass.owner.constructors.first()
implObject.addSimpleDelegatingConstructor(
constructorOfAny,
context.irBuiltIns,
true // TODO: why primary?
)
implObject.superTypes += context.irBuiltIns.anyType
implObject.addFakeOverrides(context.typeSystem)
val itemGetterSymbol = findItemGetterSymbol()
val enumEntriesMap = enumEntriesMap(enumClass)
val valuesGetterWrapper = createValuesGetterWrapper(enumClass, isExternal = false)
context.createIrBuilder(valuesGetterWrapper.symbol).run {
valuesGetterWrapper.body = irBlockBody {
+irReturn(irCall(valuesGetter))
}
}
return InternalLoweredEnum(
implObject,
valuesField,
valuesGetterWrapper,
valuesGetter,
itemGetterSymbol,
enumEntriesMap)
}
}