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

org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.kt Maven / Gradle / Ivy

/*
 * Copyright 2010-2019 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.codegen

import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.visitAnnotableParameterCount
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.STRICTFP_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.SYNCHRONIZED_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter

open class FunctionCodegen(
    private val irFunction: IrFunction,
    private val classCodegen: ClassCodegen,
    private val isInlineLambda: Boolean = false
) {
    val context = classCodegen.context
    val state = classCodegen.state

    fun generate(): JvmMethodGenericSignature =
        try {
            doGenerate()
        } catch (e: Throwable) {
            throw RuntimeException("Exception while generating code for:\n${irFunction.dump()}", e)
        }

    private fun doGenerate(): JvmMethodGenericSignature {
        val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context)
        val signature = classCodegen.methodSignatureMapper.mapSignatureWithGeneric(functionView)

        val flags = calculateMethodFlags(functionView.isStatic)
        var methodVisitor = createMethod(flags, signature)

        val hasSyntheticFlag = flags.and(Opcodes.ACC_SYNTHETIC) != 0
        if (state.generateParametersMetadata && !hasSyntheticFlag) {
            generateParameterNames(irFunction, methodVisitor, signature, state)
        }

        if (irFunction.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) {
            AnnotationCodegen(classCodegen, context, methodVisitor::visitAnnotation).genAnnotations(
                functionView,
                signature.asmMethod.returnType
            )
        }

        // FIXME: The following test is a workaround for a bug in anonymous object regeneration.
        //        We currently need to avoid parameter annotations on the (synthetic) constructors of inlined anonymous objects,
        //        since otherwise anonymous object regeneration can fail with an ArrayIndexOutOfBounds exception if the number
        //        or arguments to the constructor changes.
        if (!hasSyntheticFlag ||
            irFunction.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS ||
            //TODO: investigate this case: annotation here is generated twice in lowered function and in interface method overload
            irFunction.origin == JvmLoweredDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION
        ) {
            generateParameterAnnotations(functionView, methodVisitor, signature, classCodegen, context)
        }

        if (!state.classBuilderMode.generateBodies || flags.and(Opcodes.ACC_ABSTRACT) != 0 || irFunction.isExternal) {
            generateAnnotationDefaultValueIfNeeded(methodVisitor)
        } else {
            val frameMap = createFrameMapWithReceivers(signature)
            val irClass = context.suspendFunctionContinuations[irFunction]
            val element = (irFunction.symbol.descriptor.psiElement
                ?: context.suspendLambdaToOriginalFunctionMap[irFunction.parent]?.symbol?.descriptor?.psiElement) as? KtElement
            val continuationClassBuilder = context.continuationClassBuilders[irClass]
            methodVisitor = when {
                irFunction.isSuspend &&
                        // We do not generate continuation and state-machine for synthetic accessors, in a sense, they are tail-call
                        !irFunction.isKnownToBeTailCall() &&
                        // TODO: We should generate two versions of inline suspend function: one with state-machine and one without
                        !irFunction.isInline ->
                    generateStateMachineForNamedFunction(
                        irFunction, classCodegen, methodVisitor, flags, signature, continuationClassBuilder, element!!
                    )
                irFunction.isInvokeSuspendOfLambda(context) -> generateStateMachineForLambda(
                    classCodegen, methodVisitor, flags, signature, element!!
                )
                else -> methodVisitor
            }
            ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, isInlineLambda).generate()
            methodVisitor.visitMaxs(-1, -1)
            continuationClassBuilder?.done()
        }
        methodVisitor.visitEnd()

        return signature
    }

    private fun IrFunction.isKnownToBeTailCall(): Boolean =
        origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || origin == JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR

    private fun calculateMethodFlags(isStatic: Boolean): Int {
        if (irFunction.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) {
            return Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC.let {
                if (irFunction is IrConstructor) it else it or Opcodes.ACC_STATIC
            }
        }

        val visibility = irFunction.getVisibilityAccessFlag()
        val staticFlag = if (isStatic) Opcodes.ACC_STATIC else 0
        val varargFlag = if (irFunction.valueParameters.any { it.varargElementType != null }) Opcodes.ACC_VARARGS else 0
        val deprecation = irFunction.deprecationFlags
        val bridgeFlag = if (
            irFunction.origin == IrDeclarationOrigin.BRIDGE ||
            irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL
        ) Opcodes.ACC_BRIDGE else 0
        val modalityFlag = when ((irFunction as? IrSimpleFunction)?.modality) {
            Modality.FINAL -> if (!classCodegen.irClass.isAnnotationClass || irFunction.isStatic) Opcodes.ACC_FINAL else Opcodes.ACC_ABSTRACT
            Modality.ABSTRACT -> Opcodes.ACC_ABSTRACT
            else -> if (classCodegen.irClass.isJvmInterface && irFunction.body == null) Opcodes.ACC_ABSTRACT else 0 //TODO transform interface modality on lowering to DefaultImpls
        }
        val nativeFlag = if (irFunction.isExternal) Opcodes.ACC_NATIVE else 0
        val syntheticFlag =
            if (irFunction.origin.isSynthetic || irFunction.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) Opcodes.ACC_SYNTHETIC
            else 0
        val strictFpFlag = if (irFunction.hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME)) Opcodes.ACC_STRICT else 0
        val synchronizedFlag = if (irFunction.hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME)) Opcodes.ACC_SYNCHRONIZED else 0

        return visibility or
                modalityFlag or
                staticFlag or
                varargFlag or
                deprecation or
                nativeFlag or
                bridgeFlag or
                syntheticFlag or
                strictFpFlag or
                synchronizedFlag
    }

    protected open fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor {
        // @Throws(vararg exceptionClasses: KClass)
        val exceptions = irFunction.getAnnotation(FqName("kotlin.jvm.Throws"))?.getValueArgument(0)?.let {
            (it as IrVararg).elements.map { exceptionClass ->
                classCodegen.typeMapper.mapType((exceptionClass as IrClassReference).classType).internalName
            }.toTypedArray()
        }

        return classCodegen.visitor.newMethod(
            irFunction.OtherOrigin,
            flags,
            signature.asmMethod.name, signature.asmMethod.descriptor,
            if (flags.and(Opcodes.ACC_SYNTHETIC) != 0) null else signature.genericsSignature,
            exceptions
        )
    }

    private fun generateAnnotationDefaultValueIfNeeded(methodVisitor: MethodVisitor) {
        getAnnotationDefaultValueExpression()?.let { defaultValueExpression ->
            val annotationCodegen = AnnotationCodegen(classCodegen, context) { _, _ -> methodVisitor.visitAnnotationDefault() }
            annotationCodegen.generateAnnotationDefaultValue(defaultValueExpression)
        }
    }

    private fun getAnnotationDefaultValueExpression(): IrExpression? {
        if (!classCodegen.irClass.isAnnotationClass) return null
        // TODO: any simpler way to get to the value expression?
        // Are there other valid IR structures that represent the default value?
        return irFunction.safeAs()
            ?.correspondingPropertySymbol?.owner
            ?.backingField
            ?.initializer.safeAs()
            ?.expression?.safeAs()
            ?.symbol?.owner?.safeAs()
            ?.defaultValue?.safeAs()
            ?.expression
    }

    private fun IrFrameMap.enterDispatchReceiver(parameter: IrValueParameter) {
        val type = classCodegen.typeMapper.mapTypeAsDeclaration(parameter.type)
        enter(parameter, type)
    }

    private fun createFrameMapWithReceivers(signature: JvmMethodSignature): IrFrameMap {
        val frameMap = IrFrameMap()
        val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context)

        if (irFunction is IrConstructor) {
            frameMap.enterDispatchReceiver(irFunction.constructedClass.thisReceiver!!)
        } else if (functionView.dispatchReceiverParameter != null) {
            frameMap.enterDispatchReceiver(functionView.dispatchReceiverParameter!!)
        }

        for (parameter in signature.valueParameters) {
            if (parameter.kind == JvmMethodParameterKind.RECEIVER) {
                val receiverParameter = functionView.extensionReceiverParameter
                if (receiverParameter != null) {
                    frameMap.enter(receiverParameter, classCodegen.typeMapper.mapType(receiverParameter))
                } else {
                    frameMap.enterTemp(parameter.asmType)
                }
            } else if (parameter.kind != JvmMethodParameterKind.VALUE) {
                frameMap.enterTemp(parameter.asmType)
            }
        }

        for (parameter in functionView.valueParameters) {
            frameMap.enter(parameter, classCodegen.typeMapper.mapType(parameter.type))
        }

        return frameMap
    }
}

// Borrowed from org.jetbrains.kotlin.codegen.FunctionCodegen.java
fun generateParameterAnnotations(
    irFunction: IrFunction,
    mv: MethodVisitor,
    jvmSignature: JvmMethodSignature,
    innerClassConsumer: InnerClassConsumer,
    context: JvmBackendContext
) {
    val iterator = irFunction.valueParameters.iterator()
    val kotlinParameterTypes = jvmSignature.valueParameters
    var syntheticParameterCount = 0
    kotlinParameterTypes.forEachIndexed { i, parameterSignature ->
        val kind = parameterSignature.kind
        if (kind.isSkippedInGenericSignature) {
            if (AsmUtil.IS_BUILT_WITH_ASM6) {
                markEnumOrInnerConstructorParameterAsSynthetic(mv, i, ClassBuilderMode.FULL)
            } else {
                syntheticParameterCount++
            }
        }
    }
    if (!AsmUtil.IS_BUILT_WITH_ASM6) {
        visitAnnotableParameterCount(mv, kotlinParameterTypes.size - syntheticParameterCount)
    }

    kotlinParameterTypes.forEachIndexed { i, parameterSignature ->
        val kind = parameterSignature.kind
        if (kind.isSkippedInGenericSignature) return@forEachIndexed

        val annotated = when (kind) {
            JvmMethodParameterKind.VALUE -> iterator.next()
            JvmMethodParameterKind.RECEIVER -> irFunction.extensionReceiverParameter
            else -> null
        }

        if (annotated != null) {
            AnnotationCodegen(innerClassConsumer, context) { descriptor, visible ->
                mv.visitParameterAnnotation(
                    if (AsmUtil.IS_BUILT_WITH_ASM6) i else i - syntheticParameterCount,
                    descriptor,
                    visible
                )
            }.genAnnotations(annotated, parameterSignature.asmType)
        }
    }
}

private fun markEnumOrInnerConstructorParameterAsSynthetic(mv: MethodVisitor, i: Int, mode: ClassBuilderMode) {
    // IDEA's ClsPsi builder fails to annotate synthetic parameters
    if (mode === ClassBuilderMode.LIGHT_CLASSES) return

    // This is needed to avoid RuntimeInvisibleParameterAnnotations error in javac:
    // see MethodWriter.visitParameterAnnotation()

    val av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", true)
    av?.visitEnd()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy