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

.kotlin.kotlin-compiler.1.3.11.source-code.FunctionCodegen Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2018 JetBrains s.r.o. 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.codegen;

import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.backend.common.bridges.Bridge;
import org.jetbrains.kotlin.backend.common.bridges.ImplKt;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy;
import org.jetbrains.kotlin.codegen.coroutines.SuspendInlineFunctionGenerationStrategy;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.JvmDefaultMode;
import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.SpecialBuiltinMembers;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.KClassValue;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;

import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.generateBridgeForMainFunctionIfNecessary;
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
import static org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt.isEffectivelyInlineOnly;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
import static org.jetbrains.kotlin.resolve.jvm.InlineClassManglingRulesKt.shouldHideConstructorDueToInlineClassTypeValueParameters;
import static org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt.hasJvmDefaultAnnotation;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;

public class FunctionCodegen {
    public final GenerationState state;
    private final KotlinTypeMapper typeMapper;
    private final BindingContext bindingContext;
    private final CodegenContext owner;
    private final ClassBuilder v;
    private final MemberCodegen memberCodegen;

    private final Function1 DECLARATION_AND_DEFINITION_CHECKER = new Function1() {
        @Override
        public Boolean invoke(CallableMemberDescriptor descriptor) {
            return !isInterface(descriptor.getContainingDeclaration()) ||
                   (state.getTarget() != JvmTarget.JVM_1_6 &&
                   hasJvmDefaultAnnotation(descriptor));
        }
    };

    public FunctionCodegen(
            @NotNull CodegenContext owner,
            @NotNull ClassBuilder v,
            @NotNull GenerationState state,
            @NotNull MemberCodegen memberCodegen
    ) {
        this.owner = owner;
        this.v = v;
        this.state = state;
        this.typeMapper = state.getTypeMapper();
        this.bindingContext = state.getBindingContext();
        this.memberCodegen = memberCodegen;
    }

    public void gen(@NotNull KtNamedFunction function) {
        SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, function);
        if (bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor) != null) {
            functionDescriptor =
                    (SimpleFunctionDescriptor) bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor);
        }

        if (functionDescriptor == null) {
            throw ExceptionLogger.logDescriptorNotFound("No descriptor for function " + function.getName(), function);
        }

        if (owner.getContextKind() != OwnerKind.DEFAULT_IMPLS || function.hasBody()) {
            FunctionGenerationStrategy strategy;
            if (functionDescriptor.isSuspend()) {
                if (AnnotationUtilKt.isEffectivelyInlineOnly(functionDescriptor)) {
                    strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
                } else if (!functionDescriptor.isInline()) {
                    strategy = new SuspendFunctionGenerationStrategy(
                            state,
                            CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
                            function,
                            v.getThisName(),
                            state.getConstructorCallNormalizationMode()
                    );
                } else {
                    strategy = new SuspendInlineFunctionGenerationStrategy(
                            state,
                            CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
                            function,
                            v.getThisName(),
                            state.getConstructorCallNormalizationMode(),
                            this
                    );
                }
            } else {
                strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
            }

            generateMethod(JvmDeclarationOriginKt.OtherOrigin(function, functionDescriptor), functionDescriptor, strategy);
        }

        generateDefaultIfNeeded(owner.intoFunction(functionDescriptor, true), functionDescriptor, owner.getContextKind(),
                                DefaultParameterValueLoader.DEFAULT, function);

        generateOverloadsWithDefaultValues(function, functionDescriptor, functionDescriptor);
    }

    public void generateOverloadsWithDefaultValues(
            @Nullable KtNamedFunction function,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull FunctionDescriptor delegateFunctionDescriptor
    ) {
        new DefaultParameterValueSubstitutor(state).generateOverloadsIfNeeded(
                function, functionDescriptor, delegateFunctionDescriptor, owner.getContextKind(), v, memberCodegen
        );
    }

    public void generateMethod(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor descriptor,
            @NotNull FunctionGenerationStrategy strategy
    ) {
        if (CoroutineCodegenUtilKt.isSuspendFunctionNotSuspensionView(descriptor)) {
            generateMethod(origin, CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(descriptor, state), strategy);
            return;
        }

        generateMethod(origin, descriptor, owner.intoFunction(descriptor), strategy);
    }

    public void generateMethod(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodContext methodContext,
            @NotNull FunctionGenerationStrategy strategy
    ) {
        OwnerKind contextKind = methodContext.getContextKind();
        DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
        if (isInterface(containingDeclaration) &&
            !processInterfaceMethod(functionDescriptor, contextKind, false, false, state.getJvmDefaultMode())) {
            return;
        }

        if (!shouldGenerateMethodInsideInlineClass(origin, functionDescriptor, contextKind, containingDeclaration)) {
            return;
        }

        boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor);
        JvmMethodGenericSignature jvmSignature = strategy.mapMethodSignature(functionDescriptor, typeMapper, contextKind, hasSpecialBridge);
        Method asmMethod = jvmSignature.getAsmMethod();

        int flags = getMethodAsmFlags(functionDescriptor, contextKind, state);

        if (origin.getOriginKind() == JvmDeclarationOriginKind.SAM_DELEGATION) {
            flags |= ACC_SYNTHETIC;
        }

        if (functionDescriptor.isExternal() && owner instanceof MultifileClassFacadeContext) {
            // Native methods are only defined in facades and do not need package part implementations
            return;
        }

        MethodVisitor mv =
                strategy.wrapMethodVisitor(
                        newMethod(origin,
                                  flags,
                                  asmMethod.getName(),
                                  asmMethod.getDescriptor(),
                                  strategy.skipGenericSignature() ? null : jvmSignature.getGenericsSignature(),
                                  getThrownExceptions(functionDescriptor, typeMapper)
                        ),
                        flags, asmMethod.getName(),
                        asmMethod.getDescriptor()
                );

        recordMethodForFunctionIfAppropriate(functionDescriptor, asmMethod);

        generateMethodAnnotationsIfRequired(functionDescriptor, asmMethod, jvmSignature, mv);

        GenerateJava8ParameterNamesKt.generateParameterNames(functionDescriptor, mv, jvmSignature, state, (flags & ACC_SYNTHETIC) != 0);

        if (contextKind != OwnerKind.ERASED_INLINE_CLASS) {
            generateBridges(functionDescriptor);
        }

        boolean staticInCompanionObject = CodegenUtilKt.isJvmStaticInCompanionObject(functionDescriptor);
        if (staticInCompanionObject) {
            ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
            parentBodyCodegen.addAdditionalTask(new JvmStaticInCompanionObjectGenerator(functionDescriptor, origin, state, parentBodyCodegen));
        }

        generateBridgeForMainFunctionIfNecessary(state, v, functionDescriptor, jvmSignature, origin, methodContext.getParentContext());

        boolean isOpenSuspendInClass =
                functionDescriptor.isSuspend() &&
                functionDescriptor.getModality() != Modality.ABSTRACT && isOverridable(functionDescriptor) &&
                !isInterface(containingDeclaration) &&
                !(containingDeclaration instanceof PackageFragmentDescriptor) &&
                origin.getOriginKind() != JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL;

        if (isOpenSuspendInClass) {
            generateOpenMethodInSuspendClass(
                    origin, functionDescriptor, methodContext, strategy, mv, jvmSignature, asmMethod, flags, staticInCompanionObject
            );
        }
        else if (canDelegateMethodBodyToInlineClass(origin, functionDescriptor, contextKind, containingDeclaration)) {
            generateMethodInsideInlineClassWrapper(origin, functionDescriptor, (ClassDescriptor) containingDeclaration, mv, typeMapper);
        }
        else {
            generateMethodBody(
                    origin, functionDescriptor, methodContext, strategy, mv, jvmSignature, staticInCompanionObject
            );
        }
    }

    private void recordMethodForFunctionIfAppropriate(
            @NotNull FunctionDescriptor functionDescriptor,
            Method asmMethod
    ) {
        if (functionDescriptor instanceof AccessorForConstructorDescriptor) {
            ConstructorDescriptor originalConstructor = ((AccessorForConstructorDescriptor) functionDescriptor).getCalleeDescriptor();
            if (shouldHideConstructorDueToInlineClassTypeValueParameters(originalConstructor)) {
                functionDescriptor = originalConstructor;
            }
        }
        else if (shouldHideConstructorDueToInlineClassTypeValueParameters(functionDescriptor)) {
            return;
        }

        functionDescriptor = CodegenUtilKt.unwrapFrontendVersion(functionDescriptor);

        if (!CodegenContextUtil.isImplementationOwner(owner, functionDescriptor)) return;
        v.getSerializationBindings().put(METHOD_FOR_FUNCTION, functionDescriptor, asmMethod);
    }

    private void generateMethodAnnotationsIfRequired(
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull Method asmMethod,
            @NotNull JvmMethodGenericSignature jvmSignature,
            @NotNull MethodVisitor mv
    ) {
        FunctionDescriptor annotationsOwner;
        if (shouldHideConstructorDueToInlineClassTypeValueParameters(functionDescriptor)) {
            if (functionDescriptor instanceof AccessorForConstructorDescriptor) {
                annotationsOwner = ((AccessorForConstructorDescriptor) functionDescriptor).getCalleeDescriptor();
            }
            else {
                return;
            }
        }
        else {
            annotationsOwner = functionDescriptor;
        }

        AnnotationCodegen.forMethod(mv, memberCodegen, typeMapper).genAnnotations(annotationsOwner, asmMethod.getReturnType());
        generateParameterAnnotations(annotationsOwner, mv, jvmSignature, memberCodegen, state);
    }

    @NotNull
    public MethodVisitor newMethod(
            @NotNull JvmDeclarationOrigin origin,
            int access,
            @NotNull String name,
            @NotNull String desc,
            @Nullable String signature,
            @Nullable String[] exceptions
    ) {
        return v.newMethod(origin, access, name, desc, signature, exceptions);
    }

    private static boolean shouldGenerateMethodInsideInlineClass(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull OwnerKind contextKind,
            @NotNull DeclarationDescriptor containingDeclaration
    ) {
        return !canDelegateMethodBodyToInlineClass(origin, functionDescriptor, contextKind, containingDeclaration) ||
               !functionDescriptor.getOverriddenDescriptors().isEmpty();
    }

    private static boolean canDelegateMethodBodyToInlineClass(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull OwnerKind contextKind,
            @NotNull DeclarationDescriptor containingDeclaration
    ) {
        // special kind / function
        if (contextKind == OwnerKind.ERASED_INLINE_CLASS) return false;
        if (origin.getOriginKind() == JvmDeclarationOriginKind.UNBOX_METHOD_OF_INLINE_CLASS) return false;

        // descriptor corresponds to the underlying value
        if (functionDescriptor instanceof PropertyAccessorDescriptor) {
            PropertyDescriptor property = ((PropertyAccessorDescriptor) functionDescriptor).getCorrespondingProperty();
            if (InlineClassesUtilsKt.isUnderlyingPropertyOfInlineClass(property)) {
                return false;
            }
        }

        // base check
        boolean isInlineClass = InlineClassesUtilsKt.isInlineClass(containingDeclaration);
        boolean simpleFunctionOrProperty =
                !(functionDescriptor instanceof ConstructorDescriptor) && !KotlinTypeMapper.isAccessor(functionDescriptor);

        return isInlineClass && simpleFunctionOrProperty;
    }

    public static void generateMethodInsideInlineClassWrapper(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull ClassDescriptor containingDeclaration,
            @NotNull MethodVisitor mv,
            @NotNull KotlinTypeMapper typeMapper
    ) {
        mv.visitCode();

        Type fieldOwnerType = typeMapper.mapClass(containingDeclaration);
        Method erasedMethodImpl = typeMapper.mapAsmMethod(functionDescriptor.getOriginal(), OwnerKind.ERASED_INLINE_CLASS);

        ValueParameterDescriptor valueRepresentation = InlineClassesUtilsKt.underlyingRepresentation(containingDeclaration);
        if (valueRepresentation == null) return;

        Type fieldType = typeMapper.mapType(valueRepresentation);

        generateDelegateToStaticErasedVersion(
                mv, erasedMethodImpl,
                fieldOwnerType, valueRepresentation.getName().asString(), fieldType
        );

        endVisit(mv, null, origin.getElement());
    }

    private void generateOpenMethodInSuspendClass(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodContext methodContext,
            @NotNull FunctionGenerationStrategy strategy,
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmSignature,
            @NotNull Method asmMethod,
            int flags,
            boolean staticInCompanionObject
    ) {
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        int index = 1;
        for (Type type : asmMethod.getArgumentTypes()) {
            mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
            index += type.getSize();
        }

        Method asmMethodForOpenSuspendImpl = CoroutineCodegenUtilKt.getImplForOpenMethod(asmMethod, v.getThisName());
        // remove generic signature as it's unnecessary for synthetic methods
        JvmMethodSignature jvmSignatureForOpenSuspendImpl =
                new JvmMethodGenericSignature(
                        asmMethodForOpenSuspendImpl,
                        jvmSignature.getValueParameters(),
                        null
                );

        mv.visitMethodInsn(
                Opcodes.INVOKESTATIC,
                v.getThisName(), asmMethodForOpenSuspendImpl.getName(), asmMethodForOpenSuspendImpl.getDescriptor(),
                false
        );

        mv.visitInsn(Opcodes.ARETURN);
        mv.visitEnd();

        int flagsForOpenSuspendImpl = flags;
        flagsForOpenSuspendImpl |= Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
        flagsForOpenSuspendImpl &= ~getVisibilityAccessFlag(functionDescriptor);
        flagsForOpenSuspendImpl |= AsmUtil.NO_FLAG_PACKAGE_PRIVATE;

        MethodVisitor mvForOpenSuspendImpl = strategy.wrapMethodVisitor(
                v.newMethod(origin,
                            flagsForOpenSuspendImpl,
                            asmMethodForOpenSuspendImpl.getName(),
                            asmMethodForOpenSuspendImpl.getDescriptor(),
                            null,
                            getThrownExceptions(functionDescriptor, typeMapper)
                ),
                flagsForOpenSuspendImpl, asmMethodForOpenSuspendImpl.getName(),
                asmMethodForOpenSuspendImpl.getDescriptor()
        );

        generateMethodBody(
                origin, functionDescriptor, methodContext, strategy, mvForOpenSuspendImpl, jvmSignatureForOpenSuspendImpl,
                staticInCompanionObject
        );
    }

    private void generateMethodBody(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodContext methodContext,
            @NotNull FunctionGenerationStrategy strategy,
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmSignature,
            boolean staticInCompanionObject
    ) {
        OwnerKind contextKind = methodContext.getContextKind();
        if (!state.getClassBuilderMode().generateBodies || isAbstractMethod(functionDescriptor, contextKind)) {
            generateLocalVariableTable(
                    mv,
                    jvmSignature,
                    functionDescriptor,
                    getThisTypeForFunction(functionDescriptor, methodContext, typeMapper),
                    new Label(),
                    new Label(),
                    contextKind,
                    typeMapper,
                    Collections.emptyList(),
                    0);

            mv.visitEnd();
            return;
        }

        if (!functionDescriptor.isExternal()) {
            generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen, state.getJvmDefaultMode(),
                               state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
        }
        else if (staticInCompanionObject) {
            // native @JvmStatic foo() in companion object should delegate to the static native function moved to the outer class
            mv.visitCode();
            FunctionDescriptor staticFunctionDescriptor = JvmStaticInCompanionObjectGenerator
                    .createStaticFunctionDescriptor(functionDescriptor);
            Method accessorMethod =
                    typeMapper.mapAsmMethod(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null));
            Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration());
            generateDelegateToStaticMethodBody(false, mv, accessorMethod, owningType.getInternalName(), false);
        }

        endVisit(mv, null, origin.getElement());
    }

    public static void generateParameterAnnotations(
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmSignature,
            @NotNull InnerClassConsumer innerClassConsumer,
            @NotNull GenerationState state
    ) {
        generateParameterAnnotations(
                functionDescriptor, mv, jvmSignature, functionDescriptor.getValueParameters(), innerClassConsumer, state
        );
    }

    public static void generateParameterAnnotations(
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmSignature,
            @NotNull List valueParameters,
            @NotNull InnerClassConsumer innerClassConsumer,
            @NotNull GenerationState state
    ) {
        KotlinTypeMapper typeMapper = state.getTypeMapper();
        Iterator iterator = valueParameters.iterator();
        List kotlinParameterTypes = jvmSignature.getValueParameters();

        for (int i = 0; i < kotlinParameterTypes.size(); i++) {
            JvmMethodParameterSignature parameterSignature = kotlinParameterTypes.get(i);
            JvmMethodParameterKind kind = parameterSignature.getKind();
            if (kind.isSkippedInGenericSignature()) {
                markEnumOrInnerConstructorParameterAsSynthetic(mv, i, state.getClassBuilderMode());
                continue;
            }

            AnnotationCodegen annotationCodegen = AnnotationCodegen.forParameter(i, mv, innerClassConsumer, typeMapper);
            Annotated annotated =
                    kind == JvmMethodParameterKind.VALUE
                    ? iterator.next()
                    : kind == JvmMethodParameterKind.RECEIVER
                      ? JvmCodegenUtil.getDirectMember(functionDescriptor).getExtensionReceiverParameter()
                      : null;

            if (annotated != null) {
                annotationCodegen.genAnnotations(annotated, parameterSignature.getAsmType());
            }
        }
    }

    private static void markEnumOrInnerConstructorParameterAsSynthetic(MethodVisitor mv, int i, ClassBuilderMode mode) {
        // 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()

        AnnotationVisitor av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", true);
        if (av != null) {
            av.visitEnd();
        }
    }

    @Nullable
    private static Type getThisTypeForFunction(
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodContext context,
            @NotNull KotlinTypeMapper typeMapper
    ) {
        ReceiverParameterDescriptor dispatchReceiver = functionDescriptor.getDispatchReceiverParameter();
        // all functions inside erased version of inline class are static, so they don't have `this` as is,
        // but functions inside wrapper class should use type of wrapper class, not the underlying type
        if (functionDescriptor instanceof ConstructorDescriptor) {
            return typeMapper.mapTypeAsDeclaration(functionDescriptor);
        }
        else if (dispatchReceiver != null) {
            return typeMapper.mapTypeAsDeclaration(dispatchReceiver.getType());
        }
        else if (isFunctionLiteral(functionDescriptor) ||
                 isLocalFunction(functionDescriptor) ||
                 isFunctionExpression(functionDescriptor)) {
            return typeMapper.mapClass(context.getThisDescriptor());
        }
        else {
            return null;
        }
    }

    public static void generateMethodBody(
            @NotNull MethodVisitor mv,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodContext context,
            @NotNull JvmMethodSignature signature,
            @NotNull FunctionGenerationStrategy strategy,
            @NotNull MemberCodegen parentCodegen,
            @NotNull JvmDefaultMode jvmDefaultMode,
            boolean isReleaseCoroutines
    ) {
        mv.visitCode();

        Label methodBegin = new Label();
        mv.visitLabel(methodBegin);

        KotlinTypeMapper typeMapper = parentCodegen.typeMapper;
        if (BuiltinSpecialBridgesUtil.shouldHaveTypeSafeBarrier(functionDescriptor, typeMapper::mapAsmMethod)) {
            generateTypeCheckBarrierIfNeeded(
                    new InstructionAdapter(mv), functionDescriptor, signature.getReturnType(), null, typeMapper,
                    isReleaseCoroutines);
        }

        Label methodEnd;

        int functionFakeIndex = -1;
        int lambdaFakeIndex = -1;

        if (context.getParentContext() instanceof MultifileClassFacadeContext) {
            generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
            methodEnd = new Label();
        }
        else if (isCompatibilityStubInDefaultImpls(functionDescriptor, context, jvmDefaultMode)) {
            FunctionDescriptor compatibility = ((DefaultImplsClassContext) context.getParentContext()).getInterfaceContext()
                    .getAccessorForJvmDefaultCompatibility(functionDescriptor);
            int flags = AsmUtil.getMethodAsmFlags(functionDescriptor, OwnerKind.DEFAULT_IMPLS, context.getState());
            assert (flags & Opcodes.ACC_ABSTRACT) == 0 : "Interface method with body should be non-abstract" + functionDescriptor;
            CallableMethod method = typeMapper.mapToCallableMethod(compatibility, false);

            generateDelegateToStaticMethodBody(
                    true, mv,
                    method.getAsmMethod(),
                    method.getOwner().getInternalName(),
                    true);
            methodEnd = new Label();
        }
        else {
            FrameMap frameMap = createFrameMap(
                    parentCodegen.state, signature, functionDescriptor.getExtensionReceiverParameter(),
                    functionDescriptor.getValueParameters(), isStaticMethod(context.getContextKind(), functionDescriptor)
            );
            if (context.isInlineMethodContext()) {
                functionFakeIndex = frameMap.enterTemp(Type.INT_TYPE);
            }

            if (context instanceof InlineLambdaContext) {
                lambdaFakeIndex = frameMap.enterTemp(Type.INT_TYPE);
            }

            Label methodEntry = new Label();
            mv.visitLabel(methodEntry);
            context.setMethodStartLabel(methodEntry);

            if (!strategy.skipNotNullAssertionsForParameters()) {
                genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap);
            }

            parentCodegen.beforeMethodBody(mv);

            methodEnd = new Label();
            context.setMethodEndLabel(methodEnd);
            strategy.generateBody(mv, frameMap, signature, context, parentCodegen);
        }

        mv.visitLabel(methodEnd);

        Type thisType = getThisTypeForFunction(functionDescriptor, context, typeMapper);

        if (functionDescriptor instanceof AnonymousFunctionDescriptor && functionDescriptor.isSuspend()) {
            functionDescriptor = CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
                    functionDescriptor,
                    parentCodegen.state
            );
        }

        List destructuredParametersForSuspendLambda = new ArrayList<>();
        if (context.getParentContext() instanceof ClosureContext) {
            if (context instanceof InlineLambdaContext) {
                CallableMemberDescriptor lambdaDescriptor = context.getContextDescriptor();
                if (lambdaDescriptor instanceof FunctionDescriptor &&
                    ((FunctionDescriptor) lambdaDescriptor).isSuspend()) {
                    destructuredParametersForSuspendLambda.addAll(lambdaDescriptor.getValueParameters());
                }
            } else {
                FunctionDescriptor lambdaDescriptor = ((ClosureContext) context.getParentContext()).getOriginalSuspendLambdaDescriptor();
                if (lambdaDescriptor != null &&
                    CoroutineCodegenUtilKt.isResumeImplMethodName(
                            parentCodegen.state.getLanguageVersionSettings(), functionDescriptor.getName().asString()
                    )) {
                    destructuredParametersForSuspendLambda.addAll(lambdaDescriptor.getValueParameters());
                }
            }
        }

        generateLocalVariableTable(
                mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind(), typeMapper,
                destructuredParametersForSuspendLambda, (functionFakeIndex >= 0 ? 1 : 0) + (lambdaFakeIndex >= 0 ? 1 : 0)
        );

        //TODO: it's best to move all below logic to 'generateLocalVariableTable' method
        if (context.isInlineMethodContext() && functionFakeIndex != -1) {
            mv.visitLocalVariable(
                    JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION + typeMapper.mapAsmMethod(functionDescriptor).getName(),
                    Type.INT_TYPE.getDescriptor(), null,
                    methodBegin, methodEnd,
                    functionFakeIndex);
        }

        if (context instanceof InlineLambdaContext && thisType != null && lambdaFakeIndex != -1) {
            String name = thisType.getClassName();
            int indexOfLambdaOrdinal = name.lastIndexOf("$");
            if (indexOfLambdaOrdinal > 0) {
                int lambdaOrdinal = Integer.parseInt(name.substring(indexOfLambdaOrdinal + 1));

                KtPureElement functionArgument = parentCodegen.element;
                String functionName = "unknown";
                if (functionArgument instanceof KtFunction) {
                    ValueParameterDescriptor inlineArgumentDescriptor =
                            InlineUtil.getInlineArgumentDescriptor((KtFunction) functionArgument, parentCodegen.bindingContext);
                    if (inlineArgumentDescriptor != null) {
                        functionName = inlineArgumentDescriptor.getContainingDeclaration().getName().asString();
                    }
                }
                mv.visitLocalVariable(
                        JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + lambdaOrdinal +  "$" + functionName,
                        Type.INT_TYPE.getDescriptor(), null,
                        methodBegin, methodEnd,
                        lambdaFakeIndex);
            }
        }
    }

    private static boolean isCompatibilityStubInDefaultImpls(
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodContext context,
            @NotNull JvmDefaultMode jvmDefaultMode
    ) {
        return OwnerKind.DEFAULT_IMPLS == context.getContextKind() &&
               hasJvmDefaultAnnotation(functionDescriptor) &&
               jvmDefaultMode.isCompatibility();
    }

    private static void generateLocalVariableTable(
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmMethodSignature,
            @NotNull FunctionDescriptor functionDescriptor,
            @Nullable Type thisType,
            @NotNull Label methodBegin,
            @NotNull Label methodEnd,
            @NotNull OwnerKind ownerKind,
            @NotNull KotlinTypeMapper typeMapper,
            @NotNull List destructuredParametersForSuspendLambda,
            int shiftForDestructuringVariables
    ) {
        if (functionDescriptor.isSuspend() && !(functionDescriptor instanceof AnonymousFunctionDescriptor)) {
            FunctionDescriptor unwrapped = CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(
                    functionDescriptor
            );

            if (unwrapped != functionDescriptor) {
                generateLocalVariableTable(
                        mv,
                        new JvmMethodSignature(
                               jvmMethodSignature.getAsmMethod(),
                               jvmMethodSignature.getValueParameters().subList(
                                       0,
                                       jvmMethodSignature.getValueParameters().size() - 1
                               )
                        ),
                        unwrapped,
                        thisType, methodBegin, methodEnd, ownerKind, typeMapper, destructuredParametersForSuspendLambda,
                        shiftForDestructuringVariables
                );
                return;
            }
        }

        generateLocalVariablesForParameters(mv,
                                            jvmMethodSignature,
                                            thisType, methodBegin, methodEnd, functionDescriptor.getValueParameters(),
                                            destructuredParametersForSuspendLambda,
                                            AsmUtil.isStaticMethod(ownerKind, functionDescriptor), typeMapper, shiftForDestructuringVariables
        );
    }

    public static void generateLocalVariablesForParameters(
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmMethodSignature,
            @Nullable Type thisType,
            @NotNull Label methodBegin,
            @NotNull Label methodEnd,
            Collection valueParameters,
            boolean isStatic,
            KotlinTypeMapper typeMapper
    ) {
        generateLocalVariablesForParameters(
                mv, jvmMethodSignature, thisType, methodBegin, methodEnd, valueParameters, Collections.emptyList(), isStatic, typeMapper,
                0);
    }

    private static void generateLocalVariablesForParameters(
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmMethodSignature,
            @Nullable Type thisType,
            @NotNull Label methodBegin,
            @NotNull Label methodEnd,
            Collection valueParameters,
            @NotNull List destructuredParametersForSuspendLambda,
            boolean isStatic,
            KotlinTypeMapper typeMapper,
            int shiftForDestructuringVariables
    ) {
        Iterator valueParameterIterator = valueParameters.iterator();
        List params = jvmMethodSignature.getValueParameters();
        int shift = 0;

        if (!isStatic) {
            //add this
            if (thisType != null) {
                mv.visitLocalVariable("this", thisType.getDescriptor(), null, methodBegin, methodEnd, shift);
            }
            else {
                //TODO: provide thisType for callable reference
            }
            shift++;
        }

        for (int i = 0; i < params.size(); i++) {
            JvmMethodParameterSignature param = params.get(i);
            JvmMethodParameterKind kind = param.getKind();
            String parameterName;

            if (kind == JvmMethodParameterKind.VALUE) {
                ValueParameterDescriptor parameter = valueParameterIterator.next();
                String nameForDestructuredParameter = ValueParameterDescriptorImpl.getNameForDestructuredParameterOrNull(parameter);

                parameterName =
                        nameForDestructuredParameter == null
                        ? computeParameterName(i, parameter)
                        : nameForDestructuredParameter;
            }
            else {
                String lowercaseKind = kind.name().toLowerCase();
                parameterName = needIndexForVar(kind)
                                ? "$" + lowercaseKind + "$" + i
                                : "$" + lowercaseKind;
            }

            Type type = param.getAsmType();
            mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, methodEnd, shift);
            shift += type.getSize();
        }

        shift += shiftForDestructuringVariables;
        shift = generateDestructuredParameterEntries(mv, methodBegin, methodEnd, valueParameters, typeMapper, shift);
        shift = generateDestructuredParametersForSuspendLambda(mv, methodBegin, methodEnd, typeMapper, shift, destructuredParametersForSuspendLambda);
        generateDestructuredParameterEntries(mv, methodBegin, methodEnd, destructuredParametersForSuspendLambda, typeMapper, shift);
    }

    private static int generateDestructuredParameterEntries(
            @NotNull MethodVisitor mv,
            @NotNull Label methodBegin,
            @NotNull Label methodEnd,
            Collection valueParameters,
            KotlinTypeMapper typeMapper,
            int shift
    ) {
        for (ValueParameterDescriptor parameter : valueParameters) {
            List destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
            if (destructuringVariables == null) continue;

            for (VariableDescriptor entry : CodegenUtilKt.filterOutDescriptorsWithSpecialNames(destructuringVariables)) {
                Type type = typeMapper.mapType(entry.getType());
                mv.visitLocalVariable(entry.getName().asString(), type.getDescriptor(), null, methodBegin, methodEnd, shift);
                shift += type.getSize();
            }
        }
        return shift;
    }

    private static int generateDestructuredParametersForSuspendLambda(
            @NotNull MethodVisitor mv,
            @NotNull Label methodBegin,
            @NotNull Label methodEnd,
            KotlinTypeMapper typeMapper,
            int shift,
            List destructuredParametersForSuspendLambda
    ) {
        for (ValueParameterDescriptor parameter : destructuredParametersForSuspendLambda) {
            String nameForDestructuredParameter = ValueParameterDescriptorImpl.getNameForDestructuredParameterOrNull(parameter);
            if (nameForDestructuredParameter == null) continue;

            Type type = typeMapper.mapType(parameter.getType());
            mv.visitLocalVariable(nameForDestructuredParameter, type.getDescriptor(), null, methodBegin, methodEnd, shift);
            shift += type.getSize();
        }
        return shift;
    }

    private static String computeParameterName(int i, ValueParameterDescriptor parameter) {
        PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(parameter);
        if (element instanceof KtParameter && UnderscoreUtilKt.isSingleUnderscore((KtParameter) element)) {
            return "$noName_" + i;
        }

        return parameter.getName().asString();
    }

    public static void generateFacadeDelegateMethodBody(
            @NotNull MethodVisitor mv,
            @NotNull Method asmMethod,
            @NotNull MultifileClassFacadeContext context
    ) {
        generateDelegateToStaticMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName(), false);
    }

    private static void generateDelegateToMethodBody(
            // -1 means to add additional this parameter on stack
            int firstParamIndex,
            @NotNull MethodVisitor mv,
            @NotNull Method asmMethod,
            @NotNull String classToDelegateTo,
            int opcode,
            boolean isInterface
    ) {
        InstructionAdapter iv = new InstructionAdapter(mv);
        Type[] argTypes = asmMethod.getArgumentTypes();

        // The first line of some package file is written to the line number attribute of a static delegate to allow to 'step into' it
        // This is similar to what javac does with bridge methods
        Label label = new Label();
        iv.visitLabel(label);
        iv.visitLineNumber(1, label);

        int paramIndex = firstParamIndex;
        if (paramIndex == -1) {
            iv.load(0, AsmTypes.OBJECT_TYPE);
            paramIndex = 1;
        }

        for (Type argType : argTypes) {
            iv.load(paramIndex, argType);
            paramIndex += argType.getSize();
        }
        iv.visitMethodInsn(opcode, classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), isInterface);
        iv.areturn(asmMethod.getReturnType());
    }

    private static void generateDelegateToStaticErasedVersion(
            @NotNull MethodVisitor mv,
            @NotNull Method erasedStaticAsmMethod,
            @NotNull Type fieldOwnerType,
            @NotNull String fieldName,
            @NotNull Type fieldType
    ) {
        String internalName = fieldOwnerType.getInternalName();
        InstructionAdapter iv = new InstructionAdapter(mv);
        Type[] argTypes = erasedStaticAsmMethod.getArgumentTypes();

        Label label = new Label();
        iv.visitLabel(label);
        iv.visitLineNumber(1, label);

        iv.load(0, AsmTypes.OBJECT_TYPE);
        iv.visitFieldInsn(Opcodes.GETFIELD, internalName, fieldName, fieldType.getDescriptor());

        int k = 1;
        for (int i = 1; i < argTypes.length; i++) {
            Type argType = argTypes[i];
            iv.load(k, argType);
            k += argType.getSize();
        }

        iv.invokestatic(internalName, erasedStaticAsmMethod.getName(), erasedStaticAsmMethod.getDescriptor(), false);
        iv.areturn(erasedStaticAsmMethod.getReturnType());
    }

    private static void generateDelegateToStaticMethodBody(
            boolean isStatic,
            @NotNull MethodVisitor mv,
            @NotNull Method asmMethod,
            @NotNull String classToDelegateTo,
            boolean isInterfaceMethodCall
    ) {
        generateDelegateToMethodBody(isStatic ? 0 : 1, mv, asmMethod, classToDelegateTo, Opcodes.INVOKESTATIC, isInterfaceMethodCall);
    }

    private static boolean needIndexForVar(JvmMethodParameterKind kind) {
        return kind == JvmMethodParameterKind.CAPTURED_LOCAL_VARIABLE ||
               kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL ||
               kind == JvmMethodParameterKind.SUPER_CALL_PARAM;
    }

    public static void endVisit(MethodVisitor mv, @Nullable String description) {
        endVisit(mv, description, (PsiElement)null);
    }

    public static void endVisit(MethodVisitor mv, @Nullable String description, @Nullable KtPureElement method) {
        endVisit(mv, description, (PsiElement)(method == null ? null : method.getPsiOrParent()));
    }

    public static void endVisit(MethodVisitor mv, @Nullable String description, @NotNull KtElement method) {
        endVisit(mv, description, (PsiElement)method);
    }

    public static void endVisit(MethodVisitor mv, @Nullable String description, @Nullable PsiElement method) {
        try {
            mv.visitMaxs(-1, -1);
            mv.visitEnd();
        }
        catch (ProcessCanceledException e) {
            throw e;
        }
        catch (Throwable t) {
            String bytecode = renderByteCodeIfAvailable(mv);
            throw new CompilationException(
                    "wrong code generated\n" +
                    (description != null ? " for " + description : "") +
                    t.getClass().getName() +
                    " " +
                    t.getMessage() +
                    (bytecode != null ? "\nbytecode:\n" + bytecode : ""),
                    t, method);
        }
    }

    @SuppressWarnings("WeakerAccess") // Useful in debug
    @Nullable
    public static String renderByteCodeIfAvailable(@NotNull MethodVisitor mv) {
        String bytecode = null;

        if (mv instanceof TransformationMethodVisitor) {
            mv = ((TransformationMethodVisitor) mv).getTraceMethodVisitorIfPossible();
        }

        if (mv instanceof TraceMethodVisitor) {
            TraceMethodVisitor traceMethodVisitor = (TraceMethodVisitor) mv;
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            traceMethodVisitor.p.print(pw);
            pw.close();
            bytecode = sw.toString();
        }
        return bytecode;
    }

    private boolean hasSpecialBridgeMethod(@NotNull FunctionDescriptor descriptor) {
        if (SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) == null) return false;
        return !BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
                descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER
        ).isEmpty();
    }

    public void generateBridges(@NotNull FunctionDescriptor descriptor) {
        if (descriptor instanceof ConstructorDescriptor) return;
        if (owner.getContextKind() == OwnerKind.DEFAULT_IMPLS) return;
        if (!DECLARATION_AND_DEFINITION_CHECKER.invoke(descriptor)) return;

        // equals(Any?), hashCode(), toString() never need bridges
        if (isMethodOfAny(descriptor)) return;

        boolean isSpecial = SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) != null;

        Set> bridgesToGenerate;
        if (!isSpecial) {
            bridgesToGenerate =
                    ImplKt.generateBridgesForFunctionDescriptor(descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER);
            if (!bridgesToGenerate.isEmpty()) {
                PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null;
                boolean isSpecialBridge =
                        BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor) != null;

                for (Bridge bridge : bridgesToGenerate) {
                    generateBridge(origin, descriptor, bridge.getFrom(), bridge.getTo(), isSpecialBridge, false);
                }
            }
        }
        else {
            Set> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
                    descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER
            );

            if (!specials.isEmpty()) {
                PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null;
                for (BridgeForBuiltinSpecial bridge : specials) {
                    generateBridge(
                            origin, descriptor, bridge.getFrom(), bridge.getTo(),
                            bridge.isSpecial(), bridge.isDelegateToSuper());
                }
            }

            if (!descriptor.getKind().isReal() && isAbstractMethod(descriptor, OwnerKind.IMPLEMENTATION)) {
                CallableDescriptor overridden = SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor);
                assert overridden != null;

                if (!isThereOverriddenInKotlinClass(descriptor)) {
                    Method method = typeMapper.mapAsmMethod(descriptor);
                    int flags = ACC_ABSTRACT | getVisibilityAccessFlag(descriptor);
                    v.newMethod(JvmDeclarationOriginKt.AugmentedBuiltInApi(overridden), flags, method.getName(), method.getDescriptor(), null, null);
                }
            }
        }
    }

    public static boolean isThereOverriddenInKotlinClass(@NotNull CallableMemberDescriptor descriptor) {
        return CollectionsKt.any(
                getAllOverriddenDescriptors(descriptor),
                overridden -> !(overridden.getContainingDeclaration() instanceof JavaClassDescriptor) &&
                              isClass(overridden.getContainingDeclaration())
        );
    }

    public static boolean isMethodOfAny(@NotNull FunctionDescriptor descriptor) {
        String name = descriptor.getName().asString();
        List parameters = descriptor.getValueParameters();
        if (parameters.isEmpty()) {
            return name.equals("hashCode") || name.equals("toString");
        }
        else if (parameters.size() == 1 && name.equals("equals")) {
            return isNullableAny(parameters.get(0).getType());
        }
        return false;
    }

    @NotNull
    public static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull KotlinTypeMapper mapper) {
        AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.throws"));
        if (annotation == null) {
            annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.jvm.Throws"));
        }

        if (annotation == null) return ArrayUtil.EMPTY_STRING_ARRAY;

        Collection> values = annotation.getAllValueArguments().values();
        if (values.isEmpty()) return ArrayUtil.EMPTY_STRING_ARRAY;

        Object value = values.iterator().next();
        if (!(value instanceof ArrayValue)) return ArrayUtil.EMPTY_STRING_ARRAY;
        ArrayValue arrayValue = (ArrayValue) value;

        List strings = CollectionsKt.mapNotNull(
                arrayValue.getValue(),
                (ConstantValue constant) -> {
                    if (constant instanceof KClassValue) {
                        KClassValue classValue = (KClassValue) constant;
                        ClassDescriptor classDescriptor = DescriptorUtils.getClassDescriptorForType(classValue.getValue());
                        return mapper.mapClass(classDescriptor).getInternalName();
                    }
                    return null;
                }
        );
        return ArrayUtil.toStringArray(strings);
    }

    void generateDefaultIfNeeded(
            @NotNull MethodContext owner,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull OwnerKind kind,
            @NotNull DefaultParameterValueLoader loadStrategy,
            @Nullable KtNamedFunction function
    ) {
        DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration();

        if (isInterface(contextClass) &&
            !processInterfaceMethod(functionDescriptor, kind, true, false, state.getJvmDefaultMode())) {
            return;
        }

        if (!isDefaultNeeded(functionDescriptor, function)) {
            return;
        }

        // $default methods are never private to be accessible from other class files (e.g. inner) without the need of synthetic accessors
        // $default methods are never protected to be accessible from subclass nested classes
        int visibilityFlag = Visibilities.isPrivate(functionDescriptor.getVisibility()) ||
                             isEffectivelyInlineOnly(functionDescriptor) ?
                             AsmUtil.NO_FLAG_PACKAGE_PRIVATE : Opcodes.ACC_PUBLIC;
        int flags =  visibilityFlag | getDeprecatedAccessFlag(functionDescriptor) | ACC_SYNTHETIC;
        if (!(functionDescriptor instanceof ConstructorDescriptor)) {
            flags |= ACC_STATIC;
        }

        Method defaultMethod = typeMapper.mapDefaultMethod(functionDescriptor, kind);

        MethodVisitor mv = v.newMethod(
                JvmDeclarationOriginKt.Synthetic(function, functionDescriptor),
                flags,
                defaultMethod.getName(),
                defaultMethod.getDescriptor(), null,
                getThrownExceptions(functionDescriptor, typeMapper)
        );

        // Only method annotations are copied to the $default method. Parameter annotations are not copied until there are valid use cases;
        // enum constructors have two additional synthetic parameters which somewhat complicate this task
        AnnotationCodegen.forMethod(mv, memberCodegen, typeMapper).genAnnotations(functionDescriptor, defaultMethod.getReturnType());

        if (!state.getClassBuilderMode().generateBodies) {
            if (this.owner instanceof MultifileClassFacadeContext)
                endVisit(mv, "default method delegation", getSourceFromDescriptor(functionDescriptor));
            else
                endVisit(mv, "default method", getSourceFromDescriptor(functionDescriptor));
            return;
        }

        if (this.owner instanceof MultifileClassFacadeContext) {
            mv.visitCode();
            generateFacadeDelegateMethodBody(mv, defaultMethod, (MultifileClassFacadeContext) this.owner);
            endVisit(mv, "default method delegation", getSourceFromDescriptor(functionDescriptor));
        }
        else if (isCompatibilityStubInDefaultImpls(functionDescriptor, owner, state.getJvmDefaultMode())) {
            mv.visitCode();
            Method interfaceDefaultMethod = typeMapper.mapDefaultMethod(functionDescriptor, OwnerKind.IMPLEMENTATION);
            generateDelegateToStaticMethodBody(
                    true, mv,
                    interfaceDefaultMethod,
                    typeMapper.mapOwner(functionDescriptor).getInternalName(),
                    true
            );
            endVisit(mv, "default method delegation to interface one", getSourceFromDescriptor(functionDescriptor));
        }
        else {
            mv.visitCode();
            generateDefaultImplBody(owner, functionDescriptor, mv, loadStrategy, function, memberCodegen, defaultMethod);
            endVisit(mv, "default method", getSourceFromDescriptor(functionDescriptor));
        }
    }

    public static void generateDefaultImplBody(
            @NotNull MethodContext methodContext,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodVisitor mv,
            @NotNull DefaultParameterValueLoader loadStrategy,
            @Nullable KtNamedFunction function,
            @NotNull MemberCodegen parentCodegen,
            @NotNull Method defaultMethod
    ) {
        GenerationState state = parentCodegen.state;
        JvmMethodSignature signature = state.getTypeMapper().mapSignatureWithGeneric(functionDescriptor, methodContext.getContextKind());

        // 'null' because the "could not find expected declaration" error has been already reported in isDefaultNeeded earlier
        List valueParameters =
                CodegenUtil.getFunctionParametersForDefaultValueGeneration(functionDescriptor, null);

        boolean isStatic = isStaticMethod(methodContext.getContextKind(), functionDescriptor);
        FrameMap frameMap = createFrameMap(state, signature, functionDescriptor.getExtensionReceiverParameter(), valueParameters, isStatic);

        ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, signature.getReturnType(), methodContext, state, parentCodegen);

        CallGenerator generator = codegen.getOrCreateCallGeneratorForDefaultImplBody(functionDescriptor, function);

        InstructionAdapter iv = new InstructionAdapter(mv);
        genDefaultSuperCallCheckIfNeeded(iv, functionDescriptor, defaultMethod);

        List mappedParameters = signature.getValueParameters();
        int capturedArgumentsCount = 0;
        while (capturedArgumentsCount < mappedParameters.size() &&
               mappedParameters.get(capturedArgumentsCount).getKind() != JvmMethodParameterKind.VALUE) {
            capturedArgumentsCount++;
        }

        assert valueParameters.size() > 0 : "Expecting value parameters to generate default function " + functionDescriptor;
        int firstMaskIndex = frameMap.enterTemp(Type.INT_TYPE);
        for (int index = 1; index < valueParameters.size(); index++) {
            if (index % Integer.SIZE == 0) {
                frameMap.enterTemp(Type.INT_TYPE);
            }
        }
        //default handler or constructor marker
        frameMap.enterTemp(AsmTypes.OBJECT_TYPE);

        for (int index = 0; index < valueParameters.size(); index++) {
            int maskIndex = firstMaskIndex + index / Integer.SIZE;
            ValueParameterDescriptor parameterDescriptor = valueParameters.get(index);
            Type type = mappedParameters.get(capturedArgumentsCount + index).getAsmType();

            int parameterIndex = frameMap.getIndex(parameterDescriptor);
            if (parameterDescriptor.declaresDefaultValue()) {
                iv.load(maskIndex, Type.INT_TYPE);
                iv.iconst(1 << (index % Integer.SIZE));
                iv.and(Type.INT_TYPE);
                Label loadArg = new Label();
                iv.ifeq(loadArg);

                StackValue.local(parameterIndex, type).store(loadStrategy.genValue(parameterDescriptor, codegen), iv);

                iv.mark(loadArg);
            }
        }

        // load arguments after defaults generation to avoid redundant stack normalization operations
        loadExplicitArgumentsOnStack(OBJECT_TYPE, isStatic, signature, generator);

        for (int index = 0; index < valueParameters.size(); index++) {
            ValueParameterDescriptor parameterDescriptor = valueParameters.get(index);
            Type type = mappedParameters.get(capturedArgumentsCount + index).getAsmType();
            int parameterIndex = frameMap.getIndex(parameterDescriptor);
            generator.putValueIfNeeded(new JvmKotlinType(type, null), StackValue.local(parameterIndex, type));
        }

        CallableMethod method = state.getTypeMapper().mapToCallableMethod(functionDescriptor, false);

        generator.genCall(method, null, false, codegen);

        iv.areturn(signature.getReturnType());
    }

    private static void genDefaultSuperCallCheckIfNeeded(
            @NotNull InstructionAdapter iv, @NotNull FunctionDescriptor descriptor, @NotNull Method defaultMethod
    ) {
        if (descriptor instanceof ConstructorDescriptor) return;

        DeclarationDescriptor container = descriptor.getContainingDeclaration();
        if (!(container instanceof ClassDescriptor)) return;
        if (((ClassDescriptor) container).getModality() == Modality.FINAL) return;

        Label end = new Label();
        int handleIndex = (Type.getArgumentsAndReturnSizes(defaultMethod.getDescriptor()) >> 2) - 2; /*-1 for this, and -1 for handle*/
        iv.load(handleIndex, OBJECT_TYPE);
        iv.ifnull(end);
        AsmUtil.genThrow(
                iv, "java/lang/UnsupportedOperationException",
                "Super calls with default arguments not supported in this target, function: " + descriptor.getName().asString()
        );
        iv.visitLabel(end);
    }

    @NotNull
    private static FrameMap createFrameMap(
            @NotNull GenerationState state,
            @NotNull JvmMethodSignature signature,
            @Nullable ReceiverParameterDescriptor extensionReceiverParameter,
            @NotNull List valueParameters,
            boolean isStatic
    ) {
        FrameMap frameMap = new FrameMap();
        if (!isStatic) {
            frameMap.enterTemp(OBJECT_TYPE);
        }

        for (JvmMethodParameterSignature parameter : signature.getValueParameters()) {
            if (parameter.getKind() == JvmMethodParameterKind.RECEIVER) {
                if (extensionReceiverParameter != null) {
                    frameMap.enter(extensionReceiverParameter, state.getTypeMapper().mapType(extensionReceiverParameter));
                }
                else {
                    frameMap.enterTemp(parameter.getAsmType());
                }
            }
            else if (parameter.getKind() != JvmMethodParameterKind.VALUE) {
                frameMap.enterTemp(parameter.getAsmType());
            }
        }

        for (ValueParameterDescriptor parameter : valueParameters) {
            frameMap.enter(parameter, state.getTypeMapper().mapType(parameter));
        }

        return frameMap;
    }

    private static void loadExplicitArgumentsOnStack(
            @NotNull Type ownerType,
            boolean isStatic,
            @NotNull JvmMethodSignature signature,
            @NotNull CallGenerator callGenerator
    ) {
        int var = 0;
        if (!isStatic) {
            callGenerator.putValueIfNeeded(new JvmKotlinType(ownerType, null), StackValue.local(var, ownerType));
            var += ownerType.getSize();
        }

        for (JvmMethodParameterSignature parameterSignature : signature.getValueParameters()) {
            if (parameterSignature.getKind() != JvmMethodParameterKind.VALUE) {
                Type type = parameterSignature.getAsmType();
                callGenerator.putValueIfNeeded(new JvmKotlinType(type, null), StackValue.local(var, type));
                var += type.getSize();
            }
        }
    }

    private boolean isDefaultNeeded(@NotNull FunctionDescriptor descriptor, @Nullable KtNamedFunction function) {
        List parameters =
                CodegenUtil.getFunctionParametersForDefaultValueGeneration(descriptor, state.getDiagnostics());
        return CollectionsKt.any(parameters, ValueParameterDescriptor::declaresDefaultValue);
    }

    private void generateBridge(
            @Nullable PsiElement origin,
            @NotNull FunctionDescriptor descriptor,
            @NotNull Method bridge,
            @NotNull Method delegateTo,
            boolean isSpecialBridge,
            boolean isStubDeclarationWithDelegationToSuper
    ) {
        boolean isSpecialOrDelegationToSuper = isSpecialBridge || isStubDeclarationWithDelegationToSuper;
        int flags = ACC_PUBLIC | ACC_BRIDGE | (!isSpecialOrDelegationToSuper ? ACC_SYNTHETIC : 0) | (isSpecialBridge ? ACC_FINAL : 0); // TODO.

        String bridgeSignature =
                isSpecialBridge ? typeMapper.mapSignatureWithGeneric(descriptor, OwnerKind.IMPLEMENTATION).getGenericsSignature()
                                : null;

        MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.Bridge(descriptor, origin), flags,
                                       bridge.getName(), bridge.getDescriptor(), bridgeSignature, null);
        if (!state.getClassBuilderMode().generateBodies) return;

        mv.visitCode();
        InstructionAdapter iv = new InstructionAdapter(mv);

        Type[] argTypes = bridge.getArgumentTypes();
        Type[] originalArgTypes = delegateTo.getArgumentTypes();

        List allKotlinParameters = new ArrayList<>(originalArgTypes.length);
        if (descriptor.getExtensionReceiverParameter() != null) allKotlinParameters.add(descriptor.getExtensionReceiverParameter());
        allKotlinParameters.addAll(descriptor.getValueParameters());

        boolean safeToUseKotlinTypes = allKotlinParameters.size() == originalArgTypes.length;

        boolean isVarargInvoke = JvmCodegenUtil.isOverrideOfBigArityFunctionInvoke(descriptor);
        if (isVarargInvoke) {
            assert argTypes.length == 1 && argTypes[0].equals(AsmUtil.getArrayType(OBJECT_TYPE)) :
                    "Vararg invoke must have one parameter of type [Ljava/lang/Object;: " + bridge;
            AsmUtil.generateVarargInvokeArityAssert(iv, originalArgTypes.length);
        }
        else {
            assert argTypes.length == originalArgTypes.length :
                    "Number of parameters of the bridge and delegate must be the same.\n" +
                    "Descriptor: " + descriptor + "\nBridge: " + bridge + "\nDelegate: " + delegateTo;
        }

        MemberCodegen.markLineNumberForDescriptor(owner.getThisDescriptor(), iv);

        if (delegateTo.getArgumentTypes().length > 0 && isSpecialBridge) {
            generateTypeCheckBarrierIfNeeded(iv, descriptor, bridge.getReturnType(), delegateTo.getArgumentTypes(), typeMapper,
                                             state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
        }

        iv.load(0, OBJECT_TYPE);
        for (int i = 0, reg = 1; i < originalArgTypes.length; i++) {
            KotlinType kotlinType = safeToUseKotlinTypes ? allKotlinParameters.get(i).getType() : null;
            StackValue value;
            if (isVarargInvoke) {
                value = StackValue.arrayElement(OBJECT_TYPE, null, StackValue.local(1, argTypes[0]), StackValue.constant(i));
            }
            else {
                value = StackValue.local(reg, argTypes[i], kotlinType);
                //noinspection AssignmentToForLoopParameter
                reg += argTypes[i].getSize();
            }
            value.put(originalArgTypes[i], kotlinType, iv);
        }

        if (isStubDeclarationWithDelegationToSuper) {
            ClassDescriptor parentClass = getSuperClassDescriptor((ClassDescriptor) descriptor.getContainingDeclaration());
            assert parentClass != null;
            String parentInternalName = typeMapper.mapClass(parentClass).getInternalName();
            iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor(), false);
        }
        else {
            if (hasJvmDefaultAnnotation(descriptor)) {
                iv.invokeinterface(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
            }
            else {
                iv.invokevirtual(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor(), false);
            }
        }

        KotlinType returnType = descriptor.getReturnType();
        StackValue.coerce(delegateTo.getReturnType(), returnType, bridge.getReturnType(), returnType, iv);
        iv.areturn(bridge.getReturnType());

        endVisit(mv, "bridge method", origin);
    }

    private static void generateTypeCheckBarrierIfNeeded(
            @NotNull InstructionAdapter iv,
            @NotNull FunctionDescriptor descriptor,
            @NotNull Type returnType,
            @Nullable Type[] delegateParameterTypes,
            @NotNull KotlinTypeMapper typeMapper,
            boolean isReleaseCoroutines
    ) {
        BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription typeSafeBarrierDescription =
                BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor);
        if (typeSafeBarrierDescription == null) return;

        FunctionDescriptor overriddenBuiltin =
                BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor);

        assert overriddenBuiltin != null : "Overridden built-in method should not be null for " + descriptor;

        Label defaultBranch = new Label();

        for (int i = 0; i < descriptor.getValueParameters().size(); i++) {
            if (!typeSafeBarrierDescription.checkParameter(i)) continue;
            boolean isCheckForAny = delegateParameterTypes == null || OBJECT_TYPE.equals(delegateParameterTypes[i]);

            KotlinType kotlinType = descriptor.getValueParameters().get(i).getType();

            if (isCheckForAny && TypeUtils.isNullableType(kotlinType)) continue;

            iv.load(1 + i, OBJECT_TYPE);

            if (isCheckForAny) {
                assert !TypeUtils.isNullableType(kotlinType) : "Only bridges for not-nullable types are necessary";
                iv.ifnull(defaultBranch);
            }
            else {
                Type targetBoxedType;
                if (InlineClassesUtilsKt.isInlineClassType(kotlinType)) {
                    targetBoxedType = typeMapper.mapTypeAsDeclaration(kotlinType);
                } else {
                    targetBoxedType = boxType(delegateParameterTypes[i]);
                }
                CodegenUtilKt.generateIsCheck(iv, kotlinType, targetBoxedType, isReleaseCoroutines);
                iv.ifeq(defaultBranch);
            }
        }

        Label afterDefaultBranch = new Label();

        iv.goTo(afterDefaultBranch);

        iv.visitLabel(defaultBranch);

        if (typeSafeBarrierDescription.equals(BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT)) {
            iv.load(2, returnType);
        }
        else {
            StackValue.constant(typeSafeBarrierDescription.getDefaultValue(), returnType).put(returnType, iv);
        }
        iv.areturn(returnType);

        iv.visitLabel(afterDefaultBranch);
    }

    public void genSamDelegate(@NotNull FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) {
        FunctionDescriptor delegatedTo = overriddenDescriptor.getOriginal();
        JvmDeclarationOrigin declarationOrigin = JvmDeclarationOriginKt.SamDelegation(functionDescriptor);
        // Skip writing generic signature for the SAM wrapper class method because it may reference type parameters from the SAM interface
        // which would make little sense outside of that interface and may break Java reflection.
        // E.g. functionDescriptor for a SAM wrapper for java.util.function.Predicate is the method `test` with signature "(T) -> Boolean"
        genDelegate(
                functionDescriptor, delegatedTo, declarationOrigin,
                (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(),
                field, true
        );
    }

    public void genDelegate(@NotNull FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) {
        genDelegate(functionDescriptor, overriddenDescriptor.getOriginal(),
                    (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field);
    }

    public void genDelegate(
            @NotNull FunctionDescriptor delegateFunction,
            FunctionDescriptor delegatedTo,
            ClassDescriptor toClass,
            StackValue field
    ) {
        JvmDeclarationOrigin declarationOrigin =
                JvmDeclarationOriginKt.Delegation(DescriptorToSourceUtils.descriptorToDeclaration(delegatedTo), delegateFunction);
        genDelegate(delegateFunction, delegatedTo, declarationOrigin, toClass, field, false);
    }

    private void genDelegate(
            @NotNull FunctionDescriptor delegateFunction,
            FunctionDescriptor delegatedTo,
            @NotNull JvmDeclarationOrigin declarationOrigin,
            ClassDescriptor toClass,
            StackValue field,
            boolean skipGenericSignature
    ) {
        generateMethod(
                declarationOrigin, delegateFunction,
                new FunctionGenerationStrategy() {
                    @Override
                    public void generateBody(
                            @NotNull MethodVisitor mv,
                            @NotNull FrameMap frameMap,
                            @NotNull JvmMethodSignature signature,
                            @NotNull MethodContext context,
                            @NotNull MemberCodegen parentCodegen
                    ) {
                        Method delegateToMethod = typeMapper.mapToCallableMethod(delegatedTo, /* superCall = */ false).getAsmMethod();
                        Method delegateMethod = typeMapper.mapAsmMethod(delegateFunction);

                        Type[] argTypes = delegateMethod.getArgumentTypes();
                        Type[] originalArgTypes = delegateToMethod.getArgumentTypes();

                        InstructionAdapter iv = new InstructionAdapter(mv);
                        iv.load(0, OBJECT_TYPE);
                        field.put(field.type, iv);
                        for (int i = 0, reg = 1; i < argTypes.length; i++) {
                            StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv);
                            //noinspection AssignmentToForLoopParameter
                            reg += argTypes[i].getSize();
                        }

                        String internalName = typeMapper.mapType(toClass).getInternalName();
                        if (toClass.getKind() == ClassKind.INTERFACE) {
                            iv.invokeinterface(internalName, delegateToMethod.getName(), delegateToMethod.getDescriptor());
                        }
                        else {
                            iv.invokevirtual(internalName, delegateToMethod.getName(), delegateToMethod.getDescriptor(), false);
                        }

                        StackValue stackValue = AsmUtil.genNotNullAssertions(
                                state,
                                StackValue.onStack(delegateToMethod.getReturnType(), delegatedTo.getReturnType()),
                                RuntimeAssertionInfo.create(
                                        delegateFunction.getReturnType(),
                                        delegatedTo.getReturnType(),
                                        new RuntimeAssertionInfo.DataFlowExtras.OnlyMessage(delegatedTo.getName() + "(...)")
                                )
                        );

                        stackValue.put(delegateMethod.getReturnType(), delegatedTo.getReturnType(), iv);

                        iv.areturn(delegateMethod.getReturnType());
                    }

                    @Override
                    public boolean skipNotNullAssertionsForParameters() {
                        return false;
                    }

                    @Override
                    public boolean skipGenericSignature() {
                        return skipGenericSignature;
                    }
                }
        );
    }

    public static boolean processInterfaceMethod(
            @NotNull CallableMemberDescriptor memberDescriptor,
            @NotNull OwnerKind kind,
            boolean isDefault,
            boolean isSynthetic,
            JvmDefaultMode mode
    ) {
        DeclarationDescriptor containingDeclaration = memberDescriptor.getContainingDeclaration();
        assert isInterface(containingDeclaration) : "'processInterfaceMethod' method should be called only for interfaces, but: " +
                                                    containingDeclaration;

        if (hasJvmDefaultAnnotation(memberDescriptor)) {
            return (kind != OwnerKind.DEFAULT_IMPLS && !isSynthetic) ||
                   (kind == OwnerKind.DEFAULT_IMPLS && (isSynthetic || mode.isCompatibility()));
        } else {
            switch (kind) {
                case DEFAULT_IMPLS: return true;
                case IMPLEMENTATION: return !Visibilities.isPrivate(memberDescriptor.getVisibility()) && !isDefault && !isSynthetic;
                default: return false;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy