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

org.jetbrains.kotlin.fir.backend.generators.DataClassMembersGenerator.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2020 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.fir.backend.generators

import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
import org.jetbrains.kotlin.fir.backend.FirMetadataSource
import org.jetbrains.kotlin.fir.backend.declareThisReceiverParameter
import org.jetbrains.kotlin.fir.backend.toIrType
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeStarProjection
import org.jetbrains.kotlin.fir.types.constructType
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBooleanTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitIntTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitStringTypeRef
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
import org.jetbrains.kotlin.name.Name

/**
 * A generator that generates synthetic members of data class as well as part of inline class.
 *
 * This one uses [DataClassMembersGenerator] to generate function bodies, shared with the counterpart in psi. But, there are two main
 * differences. Unlike the counterpart in psi, which uses descriptor-based logic to determine which members to synthesize, this one uses
 * fir own logic that traverses class hierarchies in fir elements. Also, this one creates and passes IR elements, instead of providing how
 * to declare them, to [DataClassMembersGenerator].
 */
@OptIn(ObsoleteDescriptorBasedAPI::class)
class DataClassMembersGenerator(val components: Fir2IrComponents) {

    fun generateInlineClassMembers(klass: FirClass<*>, irClass: IrClass): List =
        MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER).generate(klass)

    fun generateDataClassMembers(klass: FirClass<*>, irClass: IrClass): List =
        MyDataClassMethodsGenerator(irClass, klass.symbol.toLookupTag(), IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER).generate(klass)

    fun generateDataClassComponentBody(irFunction: IrFunction, lookupTag: ConeClassLikeLookupTag) =
        MyDataClassMethodsGenerator(irFunction.parentAsClass, lookupTag, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
            .generateComponentBody(irFunction)

    fun generateDataClassCopyBody(irFunction: IrFunction, lookupTag: ConeClassLikeLookupTag) =
        MyDataClassMethodsGenerator(irFunction.parentAsClass, lookupTag, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER)
            .generateCopyBody(irFunction)

    private inner class MyDataClassMethodsGenerator(
        val irClass: IrClass,
        val lookupTag: ConeClassLikeLookupTag,
        val origin: IrDeclarationOrigin
    ) {
        private val irDataClassMembersGenerator = object : DataClassMembersGenerator(
            IrGeneratorContextBase(components.irBuiltIns),
            components.symbolTable,
            irClass,
            origin
        ) {
            override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction {
                throw IllegalStateException("Not expect to see function declaration.")
            }

            override fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) {
                // TODO
            }

            override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? =
                irValueParameter?.let {
                    irClass.properties.single { irProperty ->
                        irProperty.name == irValueParameter.name && irProperty.backingField?.type == irValueParameter.type
                    }
                }

            override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType {
                // TODO
                return components.irBuiltIns.anyType
            }

            override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>, descriptor: CallableDescriptor) {
                // TODO
            }
        }

        fun generateDispatchReceiverParameter(irFunction: IrFunction, valueParameterDescriptor: WrappedValueParameterDescriptor) =
            irFunction.declareThisReceiverParameter(
                components.symbolTable,
                irClass.defaultType,
                origin,
                UNDEFINED_OFFSET,
                UNDEFINED_OFFSET
            ).apply {
                valueParameterDescriptor.bind(this)
            }


        private val FirSimpleFunction.matchesEqualsSignature: Boolean
            get() = valueParameters.size == 1 &&
                    valueParameters[0].returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.anyNType &&
                    returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.booleanType

        private val FirSimpleFunction.matchesHashCodeSignature: Boolean
            get() = valueParameters.isEmpty() &&
                    returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.intType

        private val FirSimpleFunction.matchesToStringSignature: Boolean
            get() = valueParameters.isEmpty() &&
                    returnTypeRef.toIrType(components.typeConverter) == components.irBuiltIns.stringType

        private val FirSimpleFunction.matchesDataClassSyntheticMemberSignatures: Boolean
            get() = (this.name == equalsName && matchesEqualsSignature) ||
                    (this.name == hashCodeName && matchesHashCodeSignature) ||
                    (this.name == toStringName && matchesToStringSignature)

        fun generate(klass: FirClass<*>): List {
            val propertyParametersCount = irClass.primaryConstructor?.explicitParameters?.size ?: 0
            val properties = irClass.declarations
                .filterIsInstance()
                .take(propertyParametersCount)
                .map { it.descriptor }
            if (properties.isEmpty()) {
                return emptyList()
            }

            val result = mutableListOf()

            val contributedFunctionsInThisType = klass.declarations.mapNotNull {
                if (it is FirSimpleFunction && it.matchesDataClassSyntheticMemberSignatures) {
                    it.name
                } else
                    null
            }
            val contributedFunctionsInSupertypes =
                @OptIn(ExperimentalStdlibApi::class)
                buildMap {
                    for (name in listOf(equalsName, hashCodeName, toStringName)) {
                        klass.unsubstitutedScope(
                            components.session,
                            components.scopeSession,
                            withForcedTypeCalculator = true
                        ).processFunctionsByName(name) {
                            val declaration = it.fir
                            if (declaration is FirSimpleFunction &&
                                declaration.matchesDataClassSyntheticMemberSignatures
                            ) {
                                putIfAbsent(declaration.name, declaration)
                            }
                        }
                    }
                }

            fun isOverridableDeclaration(name: Name): Boolean {
                val declaration = contributedFunctionsInSupertypes[name] ?: return false
                return declaration.modality != Modality.FINAL
            }

            if (!contributedFunctionsInThisType.contains(equalsName) &&
                isOverridableDeclaration(equalsName)
            ) {
                result.add(contributedFunctionsInSupertypes.getValue(equalsName))
                val equalsFunction = createSyntheticIrFunction(
                    equalsName,
                    components.irBuiltIns.booleanType,
                    otherParameterNeeded = true
                )
                irDataClassMembersGenerator.generateEqualsMethod(equalsFunction, properties)
                irClass.declarations.add(equalsFunction)
            }

            if (!contributedFunctionsInThisType.contains(hashCodeName) &&
                isOverridableDeclaration(hashCodeName)
            ) {
                result.add(contributedFunctionsInSupertypes.getValue(hashCodeName))
                val hashCodeFunction = createSyntheticIrFunction(
                    hashCodeName,
                    components.irBuiltIns.intType,
                )
                irDataClassMembersGenerator.generateHashCodeMethod(hashCodeFunction, properties)
                irClass.declarations.add(hashCodeFunction)
            }

            if (!contributedFunctionsInThisType.contains(toStringName) &&
                isOverridableDeclaration(toStringName)
            ) {
                result.add(contributedFunctionsInSupertypes.getValue(toStringName))
                val toStringFunction = createSyntheticIrFunction(
                    toStringName,
                    components.irBuiltIns.stringType,
                )
                irDataClassMembersGenerator.generateToStringMethod(toStringFunction, properties)
                irClass.declarations.add(toStringFunction)
            }

            return result
        }

        fun generateComponentBody(irFunction: IrFunction) {
            val index = getComponentIndex(irFunction)!!
            val valueParameter = irClass.primaryConstructor!!.valueParameters[index - 1]
            val irProperty = irDataClassMembersGenerator.getProperty(null, valueParameter)!!
            irDataClassMembersGenerator.generateComponentFunction(irFunction, irProperty)
        }

        fun generateCopyBody(irFunction: IrFunction) =
            irDataClassMembersGenerator.generateCopyFunction(irFunction, irClass.primaryConstructor!!.symbol)

        private fun createSyntheticIrFunction(
            name: Name,
            returnType: IrType,
            otherParameterNeeded: Boolean = false
        ): IrFunction {
            val thisReceiverDescriptor = WrappedValueParameterDescriptor()
            val firFunction = buildSimpleFunction {
                origin = FirDeclarationOrigin.Synthetic
                this.name = name
                this.symbol = FirNamedFunctionSymbol(CallableId(lookupTag.classId, name))
                this.status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL)
                this.session = components.session
                this.returnTypeRef = when (returnType) {
                    components.irBuiltIns.booleanType -> FirImplicitBooleanTypeRef(null)
                    components.irBuiltIns.intType -> FirImplicitIntTypeRef(null)
                    components.irBuiltIns.stringType -> FirImplicitStringTypeRef(null)
                    else -> error("Unexpected synthetic data class function return type: $returnType")
                }
                if (otherParameterNeeded) {
                    this.valueParameters.add(
                        buildValueParameter {
                            this.name = Name.identifier("other")
                            origin = FirDeclarationOrigin.Synthetic
                            this.session = components.session
                            this.returnTypeRef = FirImplicitNullableAnyTypeRef(null)
                            this.symbol = FirVariableSymbol(this.name)
                            isCrossinline = false
                            isNoinline = false
                            isVararg = false
                        }
                    )
                }
                dispatchReceiverType = lookupTag.constructType(
                    (1..irClass.typeParameters.size).map { ConeStarProjection }.toTypedArray(), isNullable = false
                )
            }
            val signature = if (lookupTag.classId.isLocal) null else components.signatureComposer.composeSignature(firFunction)
            return components.declarationStorage.declareIrSimpleFunction(signature, null) { symbol ->
                components.irFactory.createFunction(
                    UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, symbol, name, DescriptorVisibilities.PUBLIC, Modality.OPEN, returnType,
                    isInline = false, isExternal = false, isTailrec = false, isSuspend = false, isOperator = false,
                    isInfix = false, isExpect = false, isFakeOverride = false,
                ).apply {
                    if (otherParameterNeeded) {
                        val irValueParameter = createSyntheticIrParameter(
                            this, firFunction.valueParameters.first().name, components.irBuiltIns.anyNType
                        )
                        this.valueParameters = listOf(irValueParameter)
                    }
                    metadata = FirMetadataSource.Function(
                        firFunction
                    )
                }
            }.apply {
                parent = irClass
                dispatchReceiverParameter = generateDispatchReceiverParameter(this, thisReceiverDescriptor)
                components.irBuiltIns.anyClass.descriptor.unsubstitutedMemberScope
                    .getContributedFunctions(this.name, NoLookupLocation.FROM_BACKEND)
                    .singleOrNull { function -> function.name == this.name }
                    ?.let {
                        overriddenSymbols = listOf(components.symbolTable.referenceSimpleFunction(it))
                    }
            }
        }

        private fun createSyntheticIrParameter(irFunction: IrFunction, name: Name, type: IrType, index: Int = 0): IrValueParameter {
            val descriptor = WrappedValueParameterDescriptor()
            return components.symbolTable.declareValueParameter(
                UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, descriptor, type
            ) { symbol ->
                components.irFactory.createValueParameter(
                    UNDEFINED_OFFSET, UNDEFINED_OFFSET, origin, symbol, name, index, type, null,
                    isCrossinline = false, isNoinline = false, isHidden = false, isAssignable = false
                )
            }.apply {
                parent = irFunction
                descriptor.bind(this)
            }
        }
    }

    companion object {
        private val copyName = Name.identifier("copy")
        private val equalsName = Name.identifier("equals")
        private val hashCodeName = Name.identifier("hashCode")
        private val toStringName = Name.identifier("toString")

        fun isCopy(irFunction: IrFunction): Boolean =
            irFunction.name == copyName

        fun isComponentN(irFunction: IrFunction): Boolean {
            if (irFunction.name.isSpecial) {
                return false
            }
            val name = irFunction.name.identifier
            if (!name.startsWith("component")) {
                return false
            }
            val n = getComponentIndex(irFunction)
            return n != null && n > 0
        }

        fun getComponentIndex(irFunction: IrFunction): Int? =
            irFunction.name.identifier.substring("component".length).toIntOrNull()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy