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

There is a newer version: 2.0.0
Show newest version
/*
 * 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.IrElementTransformerVoidWithContext
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.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.irArray
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addField
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
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.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.typeWith
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.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs

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 inner class EnumClassTransformer(val irClass: IrClass) {
        private val loweredEnumConstructors = hashMapOf()
        private val loweredEnumConstructorParameters = hashMapOf()
        private val enumEntryOrdinals = TObjectIntHashMap()
        private val declarationToEnumEntry = mutableMapOf()

        fun run() {
            // Lower IrEnumEntry into IrField and IrClass members
            irClass.declarations.asSequence().filterIsInstance().withIndex().forEach { (index, enumEntry) ->
                enumEntryOrdinals.put(enumEntry, index)
                enumEntry.correspondingClass?.let { entryClass -> declarationToEnumEntry[entryClass] = enumEntry }
                declarationToEnumEntry[buildEnumEntryField(enumEntry)] = enumEntry
            }

            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 buildEnumEntryField(enumEntry: IrEnumEntry): IrField =
            context.cachedDeclarations.getFieldForEnumEntry(enumEntry).apply {
                initializer = IrExpressionBodyImpl(enumEntry.initializerExpression!!.expression.patchDeclarationParents(this))
                annotations += enumEntry.annotations
            }

        private fun buildValuesField(): IrField = irClass.addField {
            name = Name.identifier(ImplementationBodyCodegen.ENUM_VALUES_FIELD_NAME)
            type = context.irBuiltIns.arrayClass.typeWith(irClass.defaultType)
            visibility = DescriptorVisibilities.PRIVATE
            origin = IrDeclarationOrigin.FIELD_FOR_ENUM_VALUES
            isFinal = true
            isStatic = true
        }.apply {
            initializer = context.createJvmIrBuilder(symbol).run {
                irExprBody(irArray(type) {
                    for (irField in declarationToEnumEntry.keys.filterIsInstance()) {
                        +irGetField(null, irField)
                    }
                })
            }
        }

        private inner class EnumClassDeclarationsTransformer(val valuesField: IrField) : IrElementTransformerVoid() {
            override fun visitClass(declaration: IrClass): IrStatement =
                if (declaration.isEnumEntry) super.visitClass(declaration) else declaration

            override fun visitConstructor(declaration: IrConstructor): IrStatement =
                context.irFactory.buildConstructor {
                    updateFrom(declaration)
                    returnType = declaration.returnType
                }.apply {
                    parent = declaration.parent

                    addValueParameter(
                        "\$enum\$name", context.irBuiltIns.stringType, JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER
                    )
                    addValueParameter(
                        "\$enum\$ordinal", context.irBuiltIns.intType, JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER
                    )
                    valueParameters += declaration.valueParameters.map { param ->
                        param.copyTo(this, index = param.index + 2).also { newParam ->
                            loweredEnumConstructorParameters[param.symbol] = newParam
                        }
                    }

                    body = declaration.body?.patchDeclarationParents(this)
                    loweredEnumConstructors[declaration.symbol] = this
                    metadata = declaration.metadata
                }

            override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
                val body = declaration.body?.safeAs()
                    ?: return declaration

                declaration.body = context.createJvmIrBuilder(declaration.symbol).run {
                    irExprBody(
                        when (body.kind) {
                            IrSyntheticBodyKind.ENUM_VALUES ->
                                irArray(valuesField.type) { addSpread(irGetField(null, valuesField)) }

                            IrSyntheticBodyKind.ENUM_VALUEOF ->
                                irCall(backendContext.ir.symbols.enumValueOfFunction).apply {
                                    putValueArgument(0, with(FunctionReferenceLowering) {
                                        javaClassReference(irClass.defaultType, backendContext)
                                    })
                                    putValueArgument(1, irGet(declaration.valueParameters[0]))
                                }
                        }
                    )
                }
                return declaration
            }
        }

        private inner class EnumClassCallTransformer : IrElementTransformerVoidWithContext() {
            override fun visitClassNew(declaration: IrClass): IrStatement =
                if (declaration.isEnumEntry) super.visitClassNew(declaration) else declaration

            override fun visitGetValue(expression: IrGetValue): IrExpression =
                loweredEnumConstructorParameters[expression.symbol]?.let {
                    IrGetValueImpl(expression.startOffset, expression.endOffset, it.type, it.symbol, expression.origin)
                } ?: expression

            override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
                expression.transformChildrenVoid(this)
                val scopeOwnerSymbol = currentScope!!.scope.scopeOwnerSymbol
                return context.createIrBuilder(scopeOwnerSymbol).at(expression).run {
                    val constructor = loweredEnumConstructors[expression.symbol] ?: expression.symbol.owner

                    if (scopeOwnerSymbol is IrConstructorSymbol) {
                        irDelegatingConstructorCall(constructor)
                    } else {
                        irCall(constructor)
                    }.also {
                        passConstructorArguments(it, expression, declarationToEnumEntry[scopeOwnerSymbol.owner as IrDeclaration])
                    }
                }
            }

            override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
                expression.transformChildrenVoid(this)
                val replacement = loweredEnumConstructors[expression.symbol]
                    ?: return expression

                return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).at(expression).run {
                    irDelegatingConstructorCall(replacement).also { passConstructorArguments(it, expression) }
                }
            }

            private fun IrBuilderWithScope.passConstructorArguments(
                call: IrFunctionAccessExpression,
                original: IrFunctionAccessExpression,
                enumEntry: IrEnumEntry? = null
            ) {
                call.copyTypeArgumentsFrom(original)
                if (enumEntry != null) {
                    call.putValueArgument(0, irString(enumEntry.name.asString()))
                    call.putValueArgument(1, irInt(enumEntryOrdinals[enumEntry]))
                } else {
                    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) }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy