JVM IR: Refactor EnumClassLowering

This commit is contained in:
Steven Schäfer
2020-01-09 15:06:04 +01:00
committed by max-kammerer
parent b0e61ab470
commit 1b5109b6ee
2 changed files with 129 additions and 426 deletions
@@ -83,7 +83,7 @@ import static org.jetbrains.org.objectweb.asm.Opcodes.*;
import static org.jetbrains.org.objectweb.asm.Type.getObjectType; import static org.jetbrains.org.objectweb.asm.Type.getObjectType;
public class ImplementationBodyCodegen extends ClassBodyCodegen { public class ImplementationBodyCodegen extends ClassBodyCodegen {
private static final String ENUM_VALUES_FIELD_NAME = "$VALUES"; public static final String ENUM_VALUES_FIELD_NAME = "$VALUES";
private Type superClassAsmType; private Type superClassAsmType;
@NotNull @NotNull
private SuperClassInfo superClassInfo; private SuperClassInfo superClassInfo;
@@ -7,39 +7,38 @@ package org.jetbrains.kotlin.backend.jvm.lower
import gnu.trove.TObjectIntHashMap import gnu.trove.TObjectIntHashMap
import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.irArray import org.jetbrains.kotlin.backend.jvm.ir.irArray
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.declarations.addField
import org.jetbrains.kotlin.ir.builders.irExprBody import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isEnumClass
import org.jetbrains.kotlin.ir.util.isEnumEntry
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import java.util.* import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal val enumClassPhase = makeIrFilePhase( internal val enumClassPhase = makeIrFilePhase(
::EnumClassLowering, ::EnumClassLowering,
@@ -50,460 +49,164 @@ internal val enumClassPhase = makeIrFilePhase(
private class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass { private class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
if (!irClass.isEnumClass) return if (!irClass.isEnumClass) return
EnumClassTransformer(irClass).run() EnumClassTransformer(irClass).run()
} }
private interface EnumConstructorCallTransformer {
fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression
fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression
}
private inner class EnumClassTransformer(val irClass: IrClass) { private inner class EnumClassTransformer(val irClass: IrClass) {
private val loweredEnumConstructors = hashMapOf<IrConstructorSymbol, IrConstructorImpl>()
private val loweredEnumConstructorParameters = hashMapOf<IrValueParameterSymbol, IrValueParameter>()
private val enumEntryOrdinals = TObjectIntHashMap<IrEnumEntry>() private val enumEntryOrdinals = TObjectIntHashMap<IrEnumEntry>()
private val enumEntryClassToEntry = HashMap<IrClass, IrEnumEntry>() private val declarationToEnumEntry = mutableMapOf<IrDeclaration, IrEnumEntry>()
private val loweredEnumConstructors = HashMap<IrConstructorSymbol, IrConstructorImpl>()
private val loweredEnumConstructorParameters = HashMap<IrValueParameterSymbol, IrValueParameter>()
private val enumEntriesByField = HashMap<IrField, IrEnumEntry>()
private val enumEntryFields = ArrayList<IrField>()
private lateinit var valuesField: IrField
private lateinit var valuesFunction: IrFunction
private lateinit var valueOfFunction: IrFunction
fun run() { fun run() {
assignOrdinalsToEnumEntries() // Lower IrEnumEntry into IrField and IrClass members
lowerEnumConstructors(irClass) irClass.declarations.asSequence().filterIsInstance<IrEnumEntry>().withIndex().forEach { (index, enumEntry) ->
lowerEnumEntries() enumEntryOrdinals.put(enumEntry, index)
setupSynthesizedEnumClassMembers() enumEntry.correspondingClass?.let { entryClass -> declarationToEnumEntry[entryClass] = enumEntry }
lowerEnumClassBody() declarationToEnumEntry[buildEnumEntryField(enumEntry)] = enumEntry
}
private fun assignOrdinalsToEnumEntries() {
var ordinal = 0
irClass.declarations.forEach {
if (it is IrEnumEntry) {
enumEntryOrdinals.put(it, ordinal)
it.correspondingClass?.run {
enumEntryClassToEntry.put(this, it)
}
ordinal++
}
} }
irClass.declarations.removeAll { it is IrEnumEntry }
irClass.declarations += declarationToEnumEntry.keys
// Construct the synthetic $VALUES field, which contains an array of all enum entries
val valuesField = buildValuesField()
// Add synthetic parameters to enum constructors and implement the values and valueOf functions
irClass.transformChildrenVoid(EnumClassDeclarationsTransformer(valuesField))
// Add synthetic arguments to enum constructor calls and remap enum constructor paramters
irClass.transformChildrenVoid(EnumClassCallTransformer())
} }
private fun lowerEnumConstructors(irClass: IrClass) { private fun buildEnumEntryField(enumEntry: IrEnumEntry): IrField =
irClass.declarations.transform { declaration -> context.declarationFactory.getFieldForEnumEntry(enumEntry).apply {
if (declaration is IrConstructor) initializer = IrExpressionBodyImpl(enumEntry.initializerExpression!!.patchDeclarationParents(this))
transformEnumConstructor(declaration, irClass) annotations += enumEntry.annotations
else
declaration
} }
}
private fun transformEnumConstructor( private fun buildValuesField(): IrField = irClass.addField {
enumConstructor: IrConstructor, name = Name.identifier(ImplementationBodyCodegen.ENUM_VALUES_FIELD_NAME)
enumClass: IrClass type = context.irBuiltIns.arrayClass.typeWith(irClass.defaultType)
): IrConstructor { visibility = Visibilities.PRIVATE
val descriptor = WrappedClassConstructorDescriptor(enumConstructor.descriptor.annotations, enumConstructor.descriptor.source) origin = IrDeclarationOrigin.FIELD_FOR_ENUM_VALUES
return IrConstructorImpl( isFinal = true
enumConstructor.startOffset, enumConstructor.endOffset, isStatic = true
enumConstructor.origin, }.apply {
IrConstructorSymbolImpl(descriptor), initializer = context.createJvmIrBuilder(symbol).run {
enumConstructor.name, irExprBody(irArray(type) {
enumConstructor.visibility, for (irField in declarationToEnumEntry.keys.filterIsInstance<IrField>()) {
returnType = enumConstructor.returnType, +irGetField(null, irField)
isInline = enumConstructor.isInline,
isExternal = enumConstructor.isExternal,
isPrimary = enumConstructor.isPrimary,
isExpect = enumConstructor.isExpect
).apply {
val newConstructor = this
descriptor.bind(this)
parent = enumClass
val nameParameter = makeNameValueParameter(newConstructor)
val ordinalParameter = makeOrdinalValueParameter(newConstructor)
valueParameters.add(0, nameParameter)
valueParameters.add(1, ordinalParameter)
valueParameters.addAll(enumConstructor.valueParameters.map { param ->
param.copyTo(newConstructor, index = param.index + 2).also { newParam ->
loweredEnumConstructorParameters[param.symbol] = newParam
} }
}) })
body = enumConstructor.body?.patchDeclarationParents(this)
loweredEnumConstructors[enumConstructor.symbol] = this
metadata = enumConstructor.metadata
} }
} }
private fun makeNameValueParameter(constructor: IrConstructor): IrValueParameter { private inner class EnumClassDeclarationsTransformer(val valuesField: IrField) : IrElementTransformerVoid() {
val descriptor = WrappedValueParameterDescriptor() override fun visitClass(declaration: IrClass): IrStatement =
return IrValueParameterImpl( if (declaration.isEnumEntry) super.visitClass(declaration) else declaration
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER,
IrValueParameterSymbolImpl(descriptor),
Name.identifier("\$enum\$name"),
index = 0,
type = context.irBuiltIns.stringType,
varargElementType = null,
isCrossinline = false,
isNoinline = false
).apply {
descriptor.bind(this)
parent = constructor
}
}
private fun makeOrdinalValueParameter(constructor: IrConstructor): IrValueParameter { override fun visitConstructor(declaration: IrConstructor): IrStatement =
val descriptor = WrappedValueParameterDescriptor() buildConstructor {
return IrValueParameterImpl( updateFrom(declaration)
UNDEFINED_OFFSET, UNDEFINED_OFFSET, returnType = declaration.returnType
JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER, }.apply {
IrValueParameterSymbolImpl(descriptor), parent = declaration.parent
Name.identifier("\$enum\$ordinal"),
index = 1,
type = context.irBuiltIns.intType,
varargElementType = null,
isCrossinline = false,
isNoinline = false
).apply {
descriptor.bind(this)
parent = constructor
}
}
private fun lowerEnumEntries() { addValueParameter(
irClass.transformDeclarationsFlat { declaration -> "\$enum\$name", context.irBuiltIns.stringType, JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER
if (declaration is IrEnumEntry) {
listOfNotNull(
createFieldForEnumEntry(declaration),
lowerEnumEntryClass(declaration.correspondingClass)
) )
} else null addValueParameter(
} "\$enum\$ordinal", context.irBuiltIns.intType, JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER
} )
declaration.valueParameters.mapTo(valueParameters) { param ->
private fun lowerEnumEntryClass(enumEntryClass: IrClass?): IrClass? { param.copyTo(this, index = param.index + 2).also { newParam ->
if (enumEntryClass == null) return null loweredEnumConstructorParameters[param.symbol] = newParam
}
lowerEnumConstructors(enumEntryClass)
return enumEntryClass
}
private fun createFieldForEnumEntry(enumEntry: IrEnumEntry): IrField =
context.declarationFactory.getFieldForEnumEntry(enumEntry).also {
it.initializer = IrExpressionBodyImpl(
enumEntry.initializerExpression!!.patchDeclarationParents(it)
)
it.annotations.addAll(enumEntry.annotations)
enumEntryFields.add(it)
enumEntriesByField[it] = enumEntry
}
private fun setupSynthesizedEnumClassMembers() {
val irField = createSyntheticValuesFieldDeclaration()
irClass.declarations.add(irField)
valuesFunction = findFunctionForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUES)
valueOfFunction = findFunctionForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUEOF)
}
private fun findFunctionForMemberWithSyntheticBodyKind(kind: IrSyntheticBodyKind): IrFunction =
irClass.declarations.asSequence().filterIsInstance<IrFunction>()
.first {
it.body.let { body ->
body is IrSyntheticBody && body.kind == kind
} }
body = declaration.body?.patchDeclarationParents(this)
loweredEnumConstructors[declaration.symbol] = this
metadata = declaration.metadata
} }
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
val body = declaration.body?.safeAs<IrSyntheticBody>()
?: return declaration
private fun createSyntheticValuesFieldDeclaration(): IrFieldImpl { declaration.body = context.createJvmIrBuilder(declaration.symbol).run {
val valuesArrayType = context.irBuiltIns.arrayClass.typeWith(irClass.defaultType) irExprBody(
when (body.kind) {
IrSyntheticBodyKind.ENUM_VALUES ->
irArray(valuesField.type) { addSpread(irGetField(null, valuesField)) }
val irValuesInitializer = createSyntheticValuesFieldInitializerExpression() IrSyntheticBodyKind.ENUM_VALUEOF ->
irCall(backendContext.ir.symbols.enumValueOfFunction).apply {
val descriptor = WrappedFieldDescriptor() putValueArgument(0, with(CallableReferenceLowering) {
return IrFieldImpl( javaClassReference(irClass.defaultType, backendContext)
UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.FIELD_FOR_ENUM_VALUES, })
IrFieldSymbolImpl(descriptor), putValueArgument(1, irGet(declaration.valueParameters[0]))
Name.identifier("\$VALUES"), }
valuesArrayType, }
Visibilities.PRIVATE, )
isFinal = true, }
isExternal = false, return declaration
isStatic = true,
isFakeOverride = false
).also {
descriptor.bind(it)
it.parent = irClass
it.initializer = IrExpressionBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irValuesInitializer)
valuesField = it
} }
} }
private fun createSyntheticValuesFieldInitializerExpression(): IrExpression = private inner class EnumClassCallTransformer : IrElementTransformerVoidWithContext() {
context.createJvmIrBuilder(irClass.symbol).irArray(context.irBuiltIns.arrayClass.typeWith(irClass.defaultType)) { override fun visitClassNew(declaration: IrClass): IrStatement =
enumEntryFields.forEach { irField -> if (declaration.isEnumEntry) super.visitClassNew(declaration) else declaration
+IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irField.symbol, irField.type)
}
}
private fun lowerEnumClassBody() { override fun visitGetValue(expression: IrGetValue): IrExpression =
irClass.transformChildrenVoid(EnumClassBodyTransformer()) loweredEnumConstructorParameters[expression.symbol]?.let {
} IrGetValueImpl(expression.startOffset, expression.endOffset, it.type, it.symbol, expression.origin)
} ?: expression
private inner class InEnumClassConstructor(val irEnumConstructor: IrConstructor) :
EnumConstructorCallTransformer {
override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression {
val startOffset = enumConstructorCall.startOffset
val endOffset = enumConstructorCall.endOffset
val origin = enumConstructorCall.origin
val result = IrDelegatingConstructorCallImpl(
startOffset,
endOffset,
context.irBuiltIns.unitType,
enumConstructorCall.symbol,
enumConstructorCall.typeArgumentsCount
)
assert(enumConstructorCall.typeArgumentsCount == 1) { "Enum<T> call expected:\n${result.dump()}" }
result.putTypeArgument(0, enumConstructorCall.getTypeArgument(0))
assert(result.symbol.owner.valueParameters.size == 2) {
"Enum(String, Int) constructor call expected:\n${result.dump()}"
}
val nameParameter = irEnumConstructor.valueParameters.getOrElse(0) {
throw AssertionError("No 'name' parameter in enum constructor: ${irEnumConstructor.dump()}")
}
val ordinalParameter = irEnumConstructor.valueParameters.getOrElse(1) {
throw AssertionError("No 'ordinal' parameter in enum constructor: ${irEnumConstructor.dump()}")
}
result.putValueArgument(0, IrGetValueImpl(startOffset, endOffset, nameParameter.symbol, origin))
result.putValueArgument(1, IrGetValueImpl(startOffset, endOffset, ordinalParameter.symbol, origin))
return result
}
override fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression {
val startOffset = delegatingConstructorCall.startOffset
val endOffset = delegatingConstructorCall.endOffset
val loweredDelegatedConstructor = loweredEnumConstructors.getOrElse(delegatingConstructorCall.symbol) {
throw AssertionError("Constructor called in enum entry initializer should've been lowered: ${delegatingConstructorCall.symbol}")
}
val result = IrDelegatingConstructorCallImpl(
startOffset,
endOffset,
context.irBuiltIns.unitType,
loweredDelegatedConstructor.symbol,
loweredDelegatedConstructor.typeParameters.size
)
assert(loweredDelegatedConstructor.typeParameters.size == 0) { "Enum delegating call expected:\n${result.dump()}" }
result.putValueArgument(0, IrGetValueImpl(startOffset, endOffset, irEnumConstructor.valueParameters[0].symbol))
result.putValueArgument(1, IrGetValueImpl(startOffset, endOffset, irEnumConstructor.valueParameters[1].symbol))
delegatingConstructorCall.symbol.owner.valueParameters.forEach { valueParameter ->
val i = valueParameter.index
result.putValueArgument(i + 2, delegatingConstructorCall.getValueArgument(i))
}
return result
}
}
private abstract inner class InEnumEntry(private val enumEntry: IrEnumEntry) : EnumConstructorCallTransformer {
override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression {
val name = enumEntry.name.asString()
val ordinal = enumEntryOrdinals[enumEntry]
val startOffset = enumConstructorCall.startOffset
val endOffset = enumConstructorCall.endOffset
val loweredConstructor = loweredEnumConstructors.getOrElse(enumConstructorCall.symbol) {
throw AssertionError("Constructor called in enum entry initializer should've been lowered:\n${enumConstructorCall.dump()}")
}
val result = createConstructorCall(startOffset, endOffset, loweredConstructor)
result.putValueArgument(0, IrConstImpl.string(startOffset, endOffset, context.irBuiltIns.stringType, name))
result.putValueArgument(1, IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, ordinal))
enumConstructorCall.symbol.owner.valueParameters.forEach { valueParameter ->
val i = valueParameter.index
result.putValueArgument(i + 2, enumConstructorCall.getValueArgument(i))
}
return result
}
override fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression {
throw AssertionError("Unexpected delegating constructor call within enum entry: $enumEntry")
}
abstract fun createConstructorCall(
startOffset: Int,
endOffset: Int,
loweredConstructor: IrConstructor
): IrMemberAccessExpression
}
private inner class InEnumEntryClassConstructor(enumEntry: IrEnumEntry) : InEnumEntry(enumEntry) {
override fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: IrConstructor) =
IrDelegatingConstructorCallImpl(
startOffset,
endOffset,
context.irBuiltIns.unitType,
loweredConstructor.symbol,
loweredConstructor.typeParameters.size
)
}
private inner class InEnumEntryInitializer(enumEntry: IrEnumEntry) : InEnumEntry(enumEntry) {
override fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: IrConstructor) =
IrConstructorCallImpl.fromSymbolDescriptor(
startOffset,
endOffset,
loweredConstructor.symbol.owner.parentAsClass.defaultType,
loweredConstructor.symbol
)
}
private inner class EnumClassBodyTransformer : IrElementTransformerVoid() {
private var enumConstructorCallTransformer: EnumConstructorCallTransformer? = null
override fun visitField(declaration: IrField): IrStatement {
val enumEntry = enumEntriesByField[declaration]
if (enumEntry == null) {
declaration.transformChildrenVoid(this)
return declaration
}
assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" }
enumConstructorCallTransformer = InEnumEntryInitializer(enumEntry)
val result = super.visitField(declaration)
enumConstructorCallTransformer = null
return result
}
override fun visitConstructor(declaration: IrConstructor): IrStatement {
val containingClass = declaration.parent as IrClass
// TODO local (non-enum) class in enum class constructor?
val previous = enumConstructorCallTransformer
if (containingClass.isEnumEntry) {
assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" }
enumConstructorCallTransformer = InEnumEntryClassConstructor(enumEntryClassToEntry[containingClass]!!)
} else if (containingClass.isEnumClass) {
assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" }
enumConstructorCallTransformer = InEnumClassConstructor(declaration)
}
val result = super.visitConstructor(declaration)
enumConstructorCallTransformer = previous
return result
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression { override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
val scopeOwnerSymbol = currentScope!!.scope.scopeOwnerSymbol
return context.createIrBuilder(scopeOwnerSymbol).at(expression).run {
val constructor = loweredEnumConstructors[expression.symbol] ?: expression.symbol.owner
val callTransformer = enumConstructorCallTransformer ?: throw AssertionError( if (scopeOwnerSymbol is IrConstructorSymbol) {
"Enum constructor call outside of enum entry initialization or enum class constructor:\n" + irDelegatingConstructorCall(constructor)
irClass.dump() } else {
) irCall(constructor)
}.also {
return callTransformer.transform(expression) passConstructorArguments(it, expression, declarationToEnumEntry[scopeOwnerSymbol.owner as IrDeclaration])
}
}
} }
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
val replacement = loweredEnumConstructors[expression.symbol]
?: return expression
if (expression.symbol.owner.parentAsClass.kind == ClassKind.ENUM_CLASS) { return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).at(expression).run {
val callTransformer = enumConstructorCallTransformer ?: throw AssertionError( irDelegatingConstructorCall(replacement).also { passConstructorArguments(it, expression) }
"Enum constructor call outside of enum entry initialization or enum class constructor:\n" +
irClass.dump()
)
return callTransformer.transform(expression)
} }
return expression
} }
override fun visitGetValue(expression: IrGetValue): IrExpression { private fun IrBuilderWithScope.passConstructorArguments(
val loweredParameter = loweredEnumConstructorParameters[expression.symbol] call: IrFunctionAccessExpression,
return if (loweredParameter != null) { original: IrFunctionAccessExpression,
IrGetValueImpl(expression.startOffset, expression.endOffset, loweredParameter.symbol, expression.origin) enumEntry: IrEnumEntry? = null
) {
call.copyTypeArgumentsFrom(original)
if (enumEntry != null) {
call.putValueArgument(0, irString(enumEntry.name.asString()))
call.putValueArgument(1, irInt(enumEntryOrdinals[enumEntry]))
} else { } else {
expression val constructor = currentScope!!.scope.scopeOwnerSymbol as IrConstructorSymbol
call.putValueArgument(0, irGet(constructor.owner.valueParameters[0]))
call.putValueArgument(1, irGet(constructor.owner.valueParameters[1]))
} }
} for (index in 0 until original.valueArgumentsCount) {
original.getValueArgument(index)?.let { call.putValueArgument(index + 2, it) }
override fun visitSyntheticBody(body: IrSyntheticBody): IrBody {
return when (body.kind) {
IrSyntheticBodyKind.ENUM_VALUES ->
createEnumValuesBody(valuesField)
IrSyntheticBodyKind.ENUM_VALUEOF ->
createEnumValueOfBody()
else ->
body
} }
} }
private fun createEnumValueOfBody(): IrBody =
context.createJvmIrBuilder(valueOfFunction.symbol).run {
irExprBody(
irCall(backendContext.ir.symbols.enumValueOfFunction).apply {
putValueArgument(0, with(CallableReferenceLowering) {
javaClassReference(irClass.defaultType, backendContext)
})
putValueArgument(1, irGet(valueOfFunction.valueParameters[0]))
}
)
}
private fun createEnumValuesBody(valuesField: IrField): IrBody {
val cloneFun = context.irBuiltIns.arrayClass.owner.functions.find { it.name.asString() == "clone" }!!
val returnType = valuesFunction.symbol.owner.returnType
val irCloneValues =
IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, returnType, cloneFun.symbol, 1).apply {
putTypeArgument(0, irClass.defaultType)
dispatchReceiver =
IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesField.symbol, valuesField.symbol.owner.type)
}
return IrBlockBodyImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
listOf(
IrReturnImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
context.irBuiltIns.nothingType,
valuesFunction.symbol,
irCloneValues
)
)
)
}
} }
} }
} }