All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.backend.jvm.lower.EnumClassLowering.kt Maven / Gradle / Ivy

/*
 * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.backend.jvm.lower

import gnu.trove.TObjectIntHashMap
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedFieldDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.irArray
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
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.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
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.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.TypeSubstitutor
import java.util.*

internal val enumClassPhase = makeIrFilePhase(
    ::EnumClassLowering,
    name = "EnumClass",
    description = "Handle enum classes"
)

private class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass {
    override fun lower(irClass: IrClass) {
        if (!irClass.isEnumClass) return

        EnumClassTransformer(irClass).run()
    }

    private interface EnumConstructorCallTransformer {
        fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression
        fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression
    }

    private inner class EnumClassTransformer(val irClass: IrClass) {
        private val enumEntryOrdinals = TObjectIntHashMap()
        private val enumEntryClassToEntry = HashMap()
        private val loweredEnumConstructors = HashMap()
        private val loweredEnumConstructorParameters = HashMap()
        private val enumEntriesByField = HashMap()
        private val enumEntryFields = ArrayList()

        private lateinit var valuesField: IrField
        private lateinit var valuesFunction: IrFunction
        private lateinit var valueOfFunction: IrFunction

        fun run() {
            assignOrdinalsToEnumEntries()
            lowerEnumConstructors(irClass)
            lowerEnumEntries()
            setupSynthesizedEnumClassMembers()
            lowerEnumClassBody()
        }

        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++
                }
            }
        }

        private fun lowerEnumConstructors(irClass: IrClass) {
            irClass.declarations.transform { declaration ->
                if (declaration is IrConstructor)
                    transformEnumConstructor(declaration, irClass)
                else
                    declaration
            }
        }

        private fun transformEnumConstructor(
            enumConstructor: IrConstructor,
            enumClass: IrClass
        ): IrConstructor {
            val descriptor = WrappedClassConstructorDescriptor(enumConstructor.descriptor.annotations, enumConstructor.descriptor.source)
            return IrConstructorImpl(
                enumConstructor.startOffset, enumConstructor.endOffset,
                enumConstructor.origin,
                IrConstructorSymbolImpl(descriptor),
                enumConstructor.name,
                Visibilities.PRIVATE,
                returnType = enumConstructor.returnType,
                isInline = enumConstructor.isInline,
                isExternal = enumConstructor.isExternal,
                isPrimary = enumConstructor.isPrimary
            ).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 {
            val descriptor = WrappedValueParameterDescriptor()
            return IrValueParameterImpl(
                UNDEFINED_OFFSET, UNDEFINED_OFFSET,
                IrDeclarationOrigin.DEFINED,
                IrValueParameterSymbolImpl(descriptor),
                Name.identifier("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 {
            val descriptor = WrappedValueParameterDescriptor()
            return IrValueParameterImpl(
                UNDEFINED_OFFSET, UNDEFINED_OFFSET,
                IrDeclarationOrigin.DEFINED,
                IrValueParameterSymbolImpl(descriptor),
                Name.identifier("ordinal"),
                index = 1,
                type = context.irBuiltIns.intType,
                varargElementType = null,
                isCrossinline = false,
                isNoinline = false
            ).apply {
                descriptor.bind(this)
                parent = constructor
            }
        }

        private fun lowerEnumEntries() {
            irClass.transformDeclarationsFlat { declaration ->
                if (declaration is IrEnumEntry) {
                    listOfNotNull(
                        createFieldForEnumEntry(declaration),
                        lowerEnumEntryClass(declaration.correspondingClass)
                    )
                } else null
            }
        }

        private fun lowerEnumEntryClass(enumEntryClass: IrClass?): IrClass? {
            if (enumEntryClass == null) return null

            lowerEnumConstructors(enumEntryClass)

            return enumEntryClass
        }

        private fun createFieldForEnumEntry(enumEntry: IrEnumEntry): IrField =
            context.declarationFactory.getFieldForEnumEntry(
                enumEntry, (enumEntry.correspondingClass ?: enumEntry.parentAsClass).defaultType
            ).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()
                .first {
                    it.body.let { body ->
                        body is IrSyntheticBody && body.kind == kind
                    }
                }


        private fun createSyntheticValuesFieldDeclaration(): IrFieldImpl {
            val valuesArrayType = context.irBuiltIns.arrayClass.typeWith(irClass.defaultType)

            val irValuesInitializer = createSyntheticValuesFieldInitializerExpression()

            val descriptor = WrappedFieldDescriptor()
            // TODO: mark ACC_SYNTHETIC
            return IrFieldImpl(
                UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.FIELD_FOR_ENUM_VALUES,
                IrFieldSymbolImpl(descriptor),
                Name.identifier("\$VALUES"),
                valuesArrayType,
                Visibilities.PRIVATE,
                isFinal = true,
                isExternal = false,
                isStatic = true
            ).also {
                descriptor.bind(it)
                it.parent = irClass
                it.initializer = IrExpressionBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irValuesInitializer)

                valuesField = it
            }
        }

        private fun createSyntheticValuesFieldInitializerExpression(): IrExpression =
            context.createJvmIrBuilder(irClass.symbol).irArray(context.irBuiltIns.arrayClass.typeWith(irClass.defaultType)) {
                enumEntryFields.forEach { irField ->
                    +IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irField.symbol, irField.type)
                }
            }

        private fun lowerEnumClassBody() {
            irClass.transformChildrenVoid(EnumClassBodyTransformer())
        }

        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.descriptor,
                    enumConstructorCall.typeArgumentsCount
                )

                assert(enumConstructorCall.typeArgumentsCount == 1) { "Enum 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.descriptor,
                    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.descriptor,
                    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 {
                expression.transformChildrenVoid(this)

                val callTransformer = enumConstructorCallTransformer ?: throw AssertionError(
                    "Enum constructor call outside of enum entry initialization or enum class constructor:\n" +
                            irClass.dump()
                )

                return callTransformer.transform(expression)
            }

            override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
                expression.transformChildrenVoid(this)

                if (expression.symbol.owner.parentAsClass.kind == ClassKind.ENUM_CLASS) {
                    val callTransformer = enumConstructorCallTransformer ?: throw AssertionError(
                        "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 {
                val loweredParameter = loweredEnumConstructorParameters[expression.symbol]
                return if (loweredParameter != null) {
                    IrGetValueImpl(expression.startOffset, expression.endOffset, loweredParameter.symbol, expression.origin)
                } else {
                    expression
                }
            }

            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 {
                val enumValueOf = context.irBuiltIns.enumValueOfSymbol
                val returnType = irClass.defaultType

                val unsubstitutedValueOfDescriptor = enumValueOf.descriptor
                val typeParameter0 = unsubstitutedValueOfDescriptor.typeParameters[0]
                val enumClassType = irClass.descriptor.defaultType
                val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameter0.typeConstructor to TypeProjectionImpl(enumClassType)))
                val substitutedValueOfDescriptor = unsubstitutedValueOfDescriptor.substitute(typeSubstitutor)!!

                val irValueOfCall =
                    IrCallImpl(
                        UNDEFINED_OFFSET,
                        UNDEFINED_OFFSET,
                        returnType,
                        enumValueOf,
                        substitutedValueOfDescriptor,
                        enumValueOf.owner.typeParameters.size
                    )
                irValueOfCall.putTypeArgument(0, irClass.defaultType)
                irValueOfCall.putValueArgument(
                    0, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valueOfFunction.valueParameters[0].symbol)
                )

                return IrBlockBodyImpl(
                    UNDEFINED_OFFSET, UNDEFINED_OFFSET,
                    listOf(
                        IrReturnImpl(
                            UNDEFINED_OFFSET,
                            UNDEFINED_OFFSET,
                            returnType,
                            valueOfFunction.symbol,
                            irValueOfCall
                        )
                    )
                )
            }

            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, cloneFun.descriptor, 0).apply {
                        dispatchReceiver =
                            IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesField.symbol, valuesField.symbol.owner.type)
                    }

                return IrBlockBodyImpl(
                    UNDEFINED_OFFSET, UNDEFINED_OFFSET,
                    listOf(
                        IrReturnImpl(
                            UNDEFINED_OFFSET,
                            UNDEFINED_OFFSET,
                            returnType,
                            valuesFunction.symbol,
                            irCloneValues
                        )
                    )
                )
            }
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy