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

org.jetbrains.kotlin.codegen.ImplementationBodyCodegen Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
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.codegen;

import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import kotlin.Unit;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap;
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap.PlatformMutabilityMapping;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.binding.MutableClosure;
import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode;
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
import org.jetbrains.kotlin.metadata.ProtoBuf;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.synthetics.SyntheticClassOrObjectDescriptor;
import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.calls.util.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.SimpleType;
import org.jetbrains.org.objectweb.asm.FieldVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;

import java.util.*;

import static org.jetbrains.kotlin.builtins.StandardNames.ENUM_VALUES;
import static org.jetbrains.kotlin.builtins.StandardNames.ENUM_VALUE_OF;
import static org.jetbrains.kotlin.codegen.AsmUtil.CAPTURED_THIS_FIELD;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isGenericToArray;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isNonGenericToArray;
import static org.jetbrains.kotlin.codegen.DescriptorAsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.getDelegatedLocalVariableMetadata;
import static org.jetbrains.kotlin.load.java.DescriptorsJvmAbiUtil.*;
import static org.jetbrains.kotlin.load.java.JvmAbi.HIDDEN_INSTANCE_FIELD;
import static org.jetbrains.kotlin.load.java.JvmAbi.INSTANCE_FIELD;
import static org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_GET;
import static org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_SET;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getInlineClassRepresentation;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.kotlin.types.Variance.INVARIANT;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isLocalFunction;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
import static org.jetbrains.org.objectweb.asm.Type.getObjectType;

public class ImplementationBodyCodegen extends ClassBodyCodegen {
    public static final String ENUM_VALUES_FIELD_NAME = "$VALUES";
    private Type superClassAsmType;
    @NotNull
    private SuperClassInfo superClassInfo;
    private final Type classAsmType;
    private final boolean isLocal;

    private List companionObjectPropertiesToCopy;

    private final DelegationFieldsInfo delegationFieldsInfo;

    private final List> additionalTasks = new ArrayList<>();

    private final DescriptorSerializer serializer;

    private final ConstructorCodegen constructorCodegen;

    public ImplementationBodyCodegen(
            @NotNull KtPureClassOrObject aClass,
            @NotNull ClassContext context,
            @NotNull ClassBuilder v,
            @NotNull GenerationState state,
            @Nullable MemberCodegen parentCodegen,
            boolean isLocal
    ) {
        super(aClass, context, v, state, parentCodegen);
        this.classAsmType = getObjectType(typeMapper.classInternalName(descriptor));
        this.isLocal = isLocal;

        this.delegationFieldsInfo =
                new DelegationFieldsInfo(classAsmType, descriptor, state, bindingContext)
                        .getDelegationFieldsInfo(myClass.getSuperTypeListEntries());

        JvmSerializerExtension extension = new JvmSerializerExtension(v.getSerializationBindings(), state);
        this.serializer = DescriptorSerializer.create(
                descriptor, extension,
                parentCodegen instanceof ImplementationBodyCodegen
                ? ((ImplementationBodyCodegen) parentCodegen).serializer
                : DescriptorSerializer.createTopLevel(extension, state.getLanguageVersionSettings(), null),
                state.getLanguageVersionSettings(),
                state.getProject()
        );

        this.constructorCodegen = new ConstructorCodegen(
                descriptor, context, functionCodegen, this,
                this, state, kind, v, classAsmType, myClass, bindingContext
        );
    }

    @Override
    protected void generateDeclaration() {
        superClassInfo = SuperClassInfo.getSuperClassInfo(descriptor, typeMapper);
        superClassAsmType = superClassInfo.getType();

        JvmClassSignature signature = signature();

        boolean isAbstract = false;
        boolean isInterface = false;
        boolean isFinal = false;
        boolean isAnnotation = false;
        boolean isEnum = false;

        ClassKind kind = descriptor.getKind();

        Modality modality = descriptor.getModality();

        if (modality == Modality.ABSTRACT || modality == Modality.SEALED) {
            isAbstract = true;
        }

        if (kind == ClassKind.INTERFACE) {
            isAbstract = true;
            isInterface = true;
        }
        else if (kind == ClassKind.ANNOTATION_CLASS) {
            isAbstract = true;
            isInterface = true;
            isAnnotation = true;
        }
        else if (kind == ClassKind.ENUM_CLASS) {
            isAbstract = hasAbstractMembers(descriptor);
            isEnum = true;
        }

        if (modality != Modality.OPEN && !isAbstract) {
            isFinal = kind == ClassKind.OBJECT ||
                      // Light-class mode: Do not make enum classes final since PsiClass corresponding to enum is expected to be inheritable from
                      !(kind == ClassKind.ENUM_CLASS && !state.getClassBuilderMode().generateBodies);
        }

        int access = 0;

        if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES && !DescriptorUtils.isTopLevelDeclaration(descriptor)) {
            // !ClassBuilderMode.generateBodies means we are generating light classes & looking at a nested or inner class
            // Light class generation is implemented so that Cls-classes only read bare code of classes,
            // without knowing whether these classes are inner or not (see ClassStubBuilder.EMPTY_STRATEGY)
            // Thus we must write full accessibility flags on inner classes in this mode
            access |= getVisibilityAccessFlag(descriptor);
            // Same for STATIC
            if (!descriptor.isInner()) {
                access |= ACC_STATIC;
            }
        }
        else {
            access |= getVisibilityAccessFlagForClass(descriptor);
        }
        if (isAbstract) {
            access |= ACC_ABSTRACT;
        }
        if (isInterface) {
            access |= ACC_INTERFACE; // ACC_SUPER
        }
        else {
            access |= ACC_SUPER;
        }
        if (isFinal) {
            access |= ACC_FINAL;
        }
        if (isAnnotation) {
            access |= ACC_ANNOTATION;
        }
        if (KotlinBuiltIns.isDeprecated(descriptor)) {
            access |= ACC_DEPRECATED;
        }
        if (isEnum) {
            for (KtDeclaration declaration : myClass.getDeclarations()) {
                if (declaration instanceof KtEnumEntry) {
                    if (enumEntryNeedSubclass(bindingContext, (KtEnumEntry) declaration)) {
                        access &= ~ACC_FINAL;
                    }
                }
            }
            access |= ACC_ENUM;
        }

        if (JvmAnnotationUtilKt.isJvmRecord(descriptor)) {
            access |= VersionIndependentOpcodes.ACC_RECORD;
        }

        v.defineClass(
                myClass.getPsiOrParent(),
                state.getConfig().getClassFileVersion(),
                access,
                signature.getName(),
                signature.getJavaGenericSignature(),
                signature.getSuperclassName(),
                ArrayUtil.toStringArray(signature.getInterfaces())
        );

        v.visitSource(myClass.getContainingKtFile().getName(), null);

        writeEnclosingMethod();

        AnnotationCodegen.forClass(v.getVisitor(), this, state).genAnnotations(descriptor, null, null);

        generateEnumEntries();
    }

    @Override
    protected void generateDefaultImplsIfNeeded() {
        if (isInterface(descriptor) && !isLocal) {
            Type defaultImplsType = state.getTypeMapper().mapDefaultImpls(descriptor);
            ClassBuilder defaultImplsBuilder =
                    state.getFactory().newVisitor(JvmDeclarationOriginKt.DefaultImpls(myClass.getPsiOrParent(), descriptor), defaultImplsType, myClass.getContainingKtFile());

            CodegenContext parentContext = context.getParentContext();
            assert parentContext != null : "Parent context of interface declaration should not be null";

            ClassContext defaultImplsContext = parentContext.intoDefaultImplsClass(descriptor, (ClassContext) context, state);
            new InterfaceImplBodyCodegen(myClass, defaultImplsContext, defaultImplsBuilder, state, this).generate();
        }
    }

    @Override
    protected void generateErasedInlineClassIfNeeded() {
        if (!(myClass instanceof KtClass)) return;
        if (!InlineClassesUtilsKt.isInlineClass(descriptor)) return;

        ClassContext erasedInlineClassContext = context.intoWrapperForErasedInlineClass(descriptor, state);
        new ErasedInlineClassBodyCodegen((KtClass) myClass, erasedInlineClassContext, v, state, this).generate();
    }

    @Override
    protected void generateUnboxMethodForInlineClass() {
        if (!(myClass instanceof KtClass)) return;
        InlineClassRepresentation inlineClassRepresentation = getInlineClassRepresentation(descriptor);
        if (inlineClassRepresentation == null) return;

        Type ownerType = typeMapper.mapClass(descriptor);
        Type valueType = typeMapper.mapType(inlineClassRepresentation.getUnderlyingType());
        SimpleFunctionDescriptor functionDescriptor = InlineClassDescriptorResolver.createUnboxFunctionDescriptor(this.descriptor);

        functionCodegen.generateMethod(
                JvmDeclarationOriginKt.UnboxMethodOfInlineClass(functionDescriptor), functionDescriptor,
                new FunctionGenerationStrategy.CodegenBased(state) {
                    @Override
                    public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
                        InstructionAdapter iv = codegen.v;
                        iv.load(0, OBJECT_TYPE);
                        iv.getfield(
                                ownerType.getInternalName(),
                                inlineClassRepresentation.getUnderlyingPropertyName().asString(),
                                valueType.getDescriptor()
                        );
                        iv.areturn(valueType);
                    }
                }
        );
    }

    @Override
    protected void generateKotlinMetadataAnnotation() {
        ProtoBuf.Class classProto = serializer.classProto(descriptor).build();

        boolean publicAbi = InlineUtil.isInPublicInlineScope(descriptor);

        WriteAnnotationUtilKt.writeKotlinMetadata(v, state.getConfig(), KotlinClassHeader.Kind.CLASS, publicAbi, 0, av -> {
            writeAnnotationData(av, serializer, classProto);
            return Unit.INSTANCE;
        });
    }

    private void writeEnclosingMethod() {
        // Do not emit enclosing method in "light-classes mode" since currently we generate local light classes as if they're top level
        if (!state.getClassBuilderMode().generateBodies) {
            return;
        }

        //JVMS7: A class must have an EnclosingMethod attribute if and only if it is a local class or an anonymous class.
        if (isAnonymousObject(descriptor) || !(descriptor.getContainingDeclaration() instanceof ClassOrPackageFragmentDescriptor)) {
            writeOuterClassAndEnclosingMethod();
        }
    }

    private static final Map KOTLIN_MARKER_INTERFACES = new HashMap<>();

    static {
        for (PlatformMutabilityMapping platformMutabilityMapping : JavaToKotlinClassMap.INSTANCE.getMutabilityMappings()) {
            KOTLIN_MARKER_INTERFACES.put(
                    platformMutabilityMapping.getKotlinReadOnly().asSingleFqName(),
                    "kotlin/jvm/internal/markers/KMappedMarker");

            ClassId mutableClassId = platformMutabilityMapping.getKotlinMutable();
            KOTLIN_MARKER_INTERFACES.put(
                    mutableClassId.asSingleFqName(),
                    "kotlin/jvm/internal/markers/K" + mutableClassId.getRelativeClassName().asString()
                            .replace("MutableEntry", "Entry") // kotlin.jvm.internal.markers.KMutableMap.Entry for some reason
                            .replace(".", "$")
            );
        }
    }

    @NotNull
    private JvmClassSignature signature() {
        return signature(descriptor, classAsmType, superClassInfo, typeMapper);
    }

    @NotNull
    public static JvmClassSignature signature(
            @NotNull ClassDescriptor descriptor,
            @NotNull Type classAsmType,
            @NotNull SuperClassInfo superClassInfo,
            @NotNull KotlinTypeMapper typeMapper
    ) {
        JvmSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS);

        typeMapper.writeFormalTypeParameters(descriptor.getDeclaredTypeParameters(), sw);

        sw.writeSuperclass();
        if (superClassInfo.getKotlinType() == null) {
            sw.writeClassBegin(superClassInfo.getType());
            sw.writeClassEnd();
        }
        else {
            typeMapper.mapSupertype(superClassInfo.getKotlinType(), sw);
        }
        sw.writeSuperclassEnd();

        LinkedHashSet superInterfaces = new LinkedHashSet<>();
        Set kotlinMarkerInterfaces = new LinkedHashSet<>();

        for (KotlinType supertype : descriptor.getTypeConstructor().getSupertypes()) {
            if (isJvmInterface(supertype.getConstructor().getDeclarationDescriptor())) {
                FqName kotlinInterfaceName = DescriptorUtils.getFqName(supertype.getConstructor().getDeclarationDescriptor()).toSafe();

                sw.writeInterface();
                Type jvmInterfaceType = typeMapper.mapSupertype(supertype, sw);
                sw.writeInterfaceEnd();
                String jvmInterfaceInternalName = jvmInterfaceType.getInternalName();

                superInterfaces.add(jvmInterfaceInternalName);

                String kotlinMarkerInterfaceInternalName = KOTLIN_MARKER_INTERFACES.get(kotlinInterfaceName);
                if (kotlinMarkerInterfaceInternalName != null) {
                    if (typeMapper.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) {
                        sw.writeInterface();
                        Type kotlinCollectionType = typeMapper.mapType(supertype, sw, TypeMappingMode.SUPER_TYPE_KOTLIN_COLLECTIONS_AS_IS);
                        sw.writeInterfaceEnd();
                        superInterfaces.add(kotlinCollectionType.getInternalName());
                    }

                    kotlinMarkerInterfaces.add(kotlinMarkerInterfaceInternalName);
                }
            }
        }

        for (String kotlinMarkerInterface : kotlinMarkerInterfaces) {
            sw.writeInterface();
            sw.writeAsmType(getObjectType(kotlinMarkerInterface));
            sw.writeInterfaceEnd();
        }

        superInterfaces.addAll(kotlinMarkerInterfaces);

        return new JvmClassSignature(classAsmType.getInternalName(), superClassInfo.getType().getInternalName(),
                                     new ArrayList<>(superInterfaces), sw.makeJavaGenericSignature());
    }

    @Override
    protected void generateSyntheticPartsBeforeBody() {
        generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
    }

    @Override
    protected void generateSyntheticPartsAfterBody() {
        generateFieldForSingleton();

        initializeObjects();

        generateCompanionObjectBackingFieldCopies();

        generateDelegatesToDefaultImpl();

        generateDelegates(delegationFieldsInfo);

        generateSyntheticAccessors();

        generateEnumMethods();

        generateFunctionsForDataClasses();

        generateFunctionsFromAnyForInlineClasses();

        if (state.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) {
            new CollectionStubMethodGenerator(typeMapper, descriptor).generate(functionCodegen, v);

            generateToArray();
        }


        if (context.closure != null)
            genClosureFields(context.closure, v, typeMapper, state.getLanguageVersionSettings());

        for (ExpressionCodegenExtension extension : ExpressionCodegenExtension.Companion.getInstances(state.getProject())) {
            if (state.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES
                || extension.getShouldGenerateClassSyntheticPartsInLightClassesMode()
            ) {
                extension.generateClassSyntheticParts(this);
            }
        }
    }

    @Override
    protected void generateConstructors() {
        try {
            lookupConstructorExpressionsInClosureIfPresent();
            constructorCodegen.generatePrimaryConstructor(delegationFieldsInfo, superClassAsmType);
            if (!InlineClassesUtilsKt.isInlineClass(descriptor) && !(descriptor instanceof SyntheticClassOrObjectDescriptor)) {
                // Synthetic classes does not have declarations for secondary constructors
                for (ClassConstructorDescriptor secondaryConstructor : DescriptorUtilsKt.getSecondaryConstructors(descriptor)) {
                    constructorCodegen.generateSecondaryConstructor(secondaryConstructor, superClassAsmType);
                }
            }
        }
        catch (CompilationException | ProcessCanceledException e) {
            throw e;
        }
        catch (RuntimeException e) {
            throw new RuntimeException("Error generating constructors of class " + myClass.getName() + " with kind " + kind, e);
        }
    }

    private void generateToArray() {
        if (descriptor.getKind() == ClassKind.INTERFACE) return;

        KotlinBuiltIns builtIns = DescriptorUtilsKt.getBuiltIns(descriptor);
        if (!isSubclass(descriptor, builtIns.getCollection())) return;

        if (CollectionsKt.any(DescriptorUtilsKt.getAllSuperclassesWithoutAny(descriptor),
                              classDescriptor -> !(classDescriptor instanceof JavaClassDescriptor) &&
                                                 isSubclass(classDescriptor, builtIns.getCollection()))) {
            return;
        }

        Collection functions = descriptor.getDefaultType().getMemberScope().getContributedFunctions(
                Name.identifier("toArray"), NoLookupLocation.FROM_BACKEND
        );
        boolean hasGenericToArray = false;
        boolean hasNonGenericToArray = false;
        for (FunctionDescriptor function : functions) {
            hasGenericToArray |= isGenericToArray(function);
            hasNonGenericToArray |= isNonGenericToArray(function);
        }

        if (!hasNonGenericToArray) {
            MethodVisitor mv = v.newMethod(NO_ORIGIN, ACC_PUBLIC, "toArray", "()[Ljava/lang/Object;", null, null);

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

            iv.load(0, classAsmType);
            iv.invokestatic("kotlin/jvm/internal/CollectionToArray", "toArray", "(Ljava/util/Collection;)[Ljava/lang/Object;", false);
            iv.areturn(Type.getType("[Ljava/lang/Object;"));

            FunctionCodegen.endVisit(mv, "toArray", myClass);
        }

        if (!hasGenericToArray) {
            MethodVisitor mv = v.newMethod(
                    NO_ORIGIN, ACC_PUBLIC, "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;", "([TT;)[TT;", null);

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

            iv.load(0, classAsmType);
            iv.load(1, Type.getType("[Ljava/lang/Object;"));

            iv.invokestatic("kotlin/jvm/internal/CollectionToArray", "toArray",
                            "(Ljava/util/Collection;[Ljava/lang/Object;)[Ljava/lang/Object;", false);
            iv.areturn(Type.getType("[Ljava/lang/Object;"));

            FunctionCodegen.endVisit(mv, "toArray", myClass);
        }
    }

    public static JvmKotlinType genPropertyOnStack(
            InstructionAdapter iv,
            MethodContext context,
            @NotNull PropertyDescriptor propertyDescriptor,
            Type classAsmType,
            int index,
            GenerationState state
    ) {
        iv.load(index, classAsmType);
        if (couldUseDirectAccessToProperty(propertyDescriptor, /* forGetter = */ true,
                                               /* isDelegated = */ false, context, state.getConfig().getShouldInlineConstVals())) {
            KotlinType kotlinType = propertyDescriptor.getType();
            Type type = state.getTypeMapper().mapType(kotlinType);
            String fieldName = ((FieldOwnerContext) context.getParentContext()).getFieldName(propertyDescriptor, false);
            iv.getfield(classAsmType.getInternalName(), fieldName, type.getDescriptor());
            return new JvmKotlinType(type, kotlinType);
        }
        else {
            PropertyGetterDescriptor getter = propertyDescriptor.getGetter();

            //noinspection ConstantConditions
            Method method = state.getTypeMapper().mapAsmMethod(getter);
            iv.invokevirtual(classAsmType.getInternalName(), method.getName(), method.getDescriptor(), false);
            return new JvmKotlinType(method.getReturnType(), getter.getReturnType());
        }
    }

    private void generateFunctionsForDataClasses() {
        if (!descriptor.isData()) return;
        if (!(myClass instanceof KtClassOrObject)) return;
        new DataClassMethodGeneratorImpl((KtClassOrObject)myClass, bindingContext).generate();
    }

    private void generateFunctionsFromAnyForInlineClasses() {
        if (!InlineClassesUtilsKt.isInlineClass(descriptor)) return;
        if (!(myClass instanceof KtClassOrObject)) return;
        new FunctionsFromAnyGeneratorImpl(
                (KtClassOrObject) myClass, bindingContext, descriptor, classAsmType, context, v, state
        ).generate();
    }

    private class DataClassMethodGeneratorImpl extends DataClassMethodGenerator {
        private final FunctionsFromAnyGeneratorImpl functionsFromAnyGenerator;

        DataClassMethodGeneratorImpl(
                KtClassOrObject klass,
                BindingContext bindingContext
        ) {
            super(klass, bindingContext);
            this.functionsFromAnyGenerator = new FunctionsFromAnyGeneratorImpl(
                    klass, bindingContext, descriptor, classAsmType, ImplementationBodyCodegen.this.context, v, state
            );
        }

        @Override
        public void generateEqualsMethod(@NotNull FunctionDescriptor function, @NotNull List properties) {
            functionsFromAnyGenerator.generateEqualsMethod(function, properties);
        }

        @Override
        public void generateHashCodeMethod(@NotNull FunctionDescriptor function, @NotNull List properties) {
            functionsFromAnyGenerator.generateHashCodeMethod(function, properties);
        }

        @Override
        public void generateToStringMethod(@NotNull FunctionDescriptor function, @NotNull List properties) {
            functionsFromAnyGenerator.generateToStringMethod(function, properties);
        }

        @Override
        public void generateComponentFunction(@NotNull FunctionDescriptor function, @NotNull ValueParameterDescriptor parameter) {
            PsiElement originalElement = DescriptorToSourceUtils.descriptorToDeclaration(parameter);
            functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(originalElement, function), function, new FunctionGenerationStrategy() {
                @Override
                public void generateBody(
                        @NotNull MethodVisitor mv,
                        @NotNull FrameMap frameMap,
                        @NotNull JvmMethodSignature signature,
                        @NotNull MethodContext context,
                        @NotNull MemberCodegen parentCodegen
                ) {
                    Type componentType = signature.getReturnType();
                    InstructionAdapter iv = new InstructionAdapter(mv);
                    if (!componentType.equals(Type.VOID_TYPE)) {
                        PropertyDescriptor property =
                                bindingContext.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, descriptorToDeclaration(parameter));
                        assert property != null : "Property descriptor is not found for primary constructor parameter: " + parameter;

                        JvmKotlinType propertyType = genPropertyOnStack(
                                iv, context, property, ImplementationBodyCodegen.this.classAsmType, 0, state
                        );
                        StackValue.coerce(propertyType.getType(), componentType, iv);
                    }
                    iv.areturn(componentType);
                }

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

        @Override
        public void generateCopyFunction(
                @NotNull FunctionDescriptor function,
                @NotNull List constructorParameters
        ) {
            Type thisDescriptorType = typeMapper.mapType(descriptor);

            functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOriginFromPure(myClass, function), function, new FunctionGenerationStrategy() {
                @Override
                public void generateBody(
                        @NotNull MethodVisitor mv,
                        @NotNull FrameMap frameMap,
                        @NotNull JvmMethodSignature signature,
                        @NotNull MethodContext context,
                        @NotNull MemberCodegen parentCodegen
                ) {
                    InstructionAdapter iv = new InstructionAdapter(mv);

                    iv.anew(thisDescriptorType);
                    iv.dup();

                    ConstructorDescriptor constructor = getPrimaryConstructorOfDataClass(descriptor);
                    assert function.getValueParameters().size() == constructor.getValueParameters().size() :
                            "Number of parameters of copy function and constructor are different. " +
                            "Copy: " + function.getValueParameters().size() + ", " +
                            "constructor: " + constructor.getValueParameters().size();

                    MutableClosure closure = ImplementationBodyCodegen.this.context.closure;
                    if (closure != null) {
                        pushCapturedFieldsOnStack(iv, closure);
                    }

                    int parameterIndex = 1; // localVariable 0 = this
                    for (ValueParameterDescriptor parameterDescriptor : function.getValueParameters()) {
                        Type type = typeMapper.mapType(parameterDescriptor.getType());
                        iv.load(parameterIndex, type);
                        parameterIndex += type.getSize();
                    }

                    Method constructorAsmMethod = typeMapper.mapAsmMethod(constructor);
                    iv.invokespecial(thisDescriptorType.getInternalName(), "", constructorAsmMethod.getDescriptor(), false);

                    iv.areturn(thisDescriptorType);
                }

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

                private void pushCapturedFieldsOnStack(InstructionAdapter iv, MutableClosure closure) {
                    ClassDescriptor captureThis = closure.getCapturedOuterClassDescriptor();
                    if (captureThis != null) {
                        iv.load(0, classAsmType);
                        Type type = typeMapper.mapType(captureThis);
                        iv.getfield(classAsmType.getInternalName(), CAPTURED_THIS_FIELD, type.getDescriptor());
                    }

                    KotlinType captureReceiver = closure.getCapturedReceiverFromOuterContext();
                    if (captureReceiver != null) {
                        iv.load(0, classAsmType);
                        Type type = typeMapper.mapType(captureReceiver);
                        String fieldName = closure.getCapturedReceiverFieldName(bindingContext, state.getLanguageVersionSettings());
                        iv.getfield(classAsmType.getInternalName(), fieldName, type.getDescriptor());
                    }

                    for (Map.Entry entry : closure.getCaptureVariables().entrySet()) {
                        DeclarationDescriptor declarationDescriptor = entry.getKey();
                        EnclosedValueDescriptor enclosedValueDescriptor = entry.getValue();
                        StackValue capturedValue = enclosedValueDescriptor.getInstanceValue();
                        Type sharedVarType = typeMapper.getSharedVarType(declarationDescriptor);
                        if (sharedVarType == null) {
                            sharedVarType = typeMapper.mapType((VariableDescriptor) declarationDescriptor);
                        }
                        capturedValue.put(sharedVarType, iv);
                    }
                }
            });

            functionCodegen.generateDefaultIfNeeded(
                    context.intoFunction(function), function, OwnerKind.IMPLEMENTATION,
                    (valueParameter, codegen) -> {
                        assert ((ClassDescriptor) function.getContainingDeclaration()).isData()
                                : "Function container must have [data] modifier: " + function;
                        PropertyDescriptor property = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameter);
                        assert property != null : "Copy function doesn't correspond to any property: " + function;
                        return codegen.intermediateValueForProperty(property, false, null, StackValue.LOCAL_0);
                    },
                    null
            );
        }
    }

    @NotNull
    private static ConstructorDescriptor getPrimaryConstructorOfDataClass(@NotNull ClassDescriptor classDescriptor) {
        ConstructorDescriptor constructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
        assert constructor != null : "Data class must have primary constructor: " + classDescriptor;
        return constructor;
    }

    private void generateEnumMethods() {
        if (isEnumClass(descriptor)) {
            generateEnumValuesMethod();
            generateEnumValueOfMethod();
        }
    }

    private void generateEnumValuesMethod() {
        Type type = typeMapper.mapType(DescriptorUtilsKt.getBuiltIns(descriptor).getArrayType(INVARIANT, descriptor.getDefaultType()));

        FunctionDescriptor valuesFunction =
                CollectionsKt.single(descriptor.getStaticScope().getContributedFunctions(ENUM_VALUES, NoLookupLocation.FROM_BACKEND));
        MethodVisitor mv = v.newMethod(
                JvmDeclarationOriginKt.OtherOriginFromPure(myClass, valuesFunction), ACC_PUBLIC | ACC_STATIC, ENUM_VALUES.asString(),
                "()" + type.getDescriptor(), null, null
        );
        if (!state.getClassBuilderMode().generateBodies) return;

        mv.visitCode();
        mv.visitFieldInsn(GETSTATIC, classAsmType.getInternalName(), ENUM_VALUES_FIELD_NAME, type.getDescriptor());
        mv.visitMethodInsn(INVOKEVIRTUAL, type.getInternalName(), "clone", "()Ljava/lang/Object;", false);
        mv.visitTypeInsn(CHECKCAST, type.getInternalName());
        mv.visitInsn(ARETURN);
        FunctionCodegen.endVisit(mv, "values()", myClass);
    }

    private void generateEnumValueOfMethod() {
        FunctionDescriptor valueOfFunction =
                CollectionsKt.single(descriptor.getStaticScope().getContributedFunctions(ENUM_VALUE_OF, NoLookupLocation.FROM_BACKEND),
                                     DescriptorUtilsKt::isEnumValueOfMethod);
        MethodVisitor mv =
                v.newMethod(JvmDeclarationOriginKt.OtherOriginFromPure(myClass, valueOfFunction), ACC_PUBLIC | ACC_STATIC, ENUM_VALUE_OF.asString(),
                            "(Ljava/lang/String;)" + classAsmType.getDescriptor(), null, null);
        if (!state.getClassBuilderMode().generateBodies) return;

        mv.visitCode();
        mv.visitLdcInsn(classAsmType);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false);
        mv.visitTypeInsn(CHECKCAST, classAsmType.getInternalName());
        mv.visitInsn(ARETURN);
        FunctionCodegen.endVisit(mv, "valueOf()", myClass);
    }

    private void generateFieldForSingleton() {
        if (isCompanionObjectInInterfaceNotIntrinsic(descriptor)) {
            StackValue.Field field = StackValue.createSingletonViaInstance(descriptor, typeMapper, HIDDEN_INSTANCE_FIELD);
            //hidden instance in interface companion
            v.newField(JvmDeclarationOriginKt.OtherOrigin(descriptor),
                       ACC_SYNTHETIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null);
        }

        if (isEnumEntry(descriptor) || isCompanionObject(descriptor)) return;

        if (isNonCompanionObject(descriptor)) {
            StackValue.Field field = StackValue.createSingletonViaInstance(descriptor, typeMapper, INSTANCE_FIELD);
            FieldVisitor fv = v.newField(
                    JvmDeclarationOriginKt.OtherOriginFromPure(myClass),
                    ACC_PUBLIC | ACC_STATIC | ACC_FINAL,
                    field.name, field.type.getDescriptor(), null, null
            );
            AnnotationCodegen.forField(fv, this, state).visitAnnotation(Type.getDescriptor(NotNull.class), false).visitEnd();
            return;
        }

        ClassDescriptor companionObjectDescriptor = descriptor.getCompanionObjectDescriptor();
        if (companionObjectDescriptor == null) {
            return;
        }

        @Nullable KtObjectDeclaration companionObject = CollectionsKt.firstOrNull(myClass.getCompanionObjects());

        int properFieldVisibilityFlag = getVisibilityAccessFlag(companionObjectDescriptor);
        boolean deprecatedFieldForInvisibleCompanionObject =
                state.getLanguageVersionSettings().supportsFeature(LanguageFeature.DeprecatedFieldForInvisibleCompanionObject);
        boolean properVisibilityForCompanionObjectInstanceField =
                state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ProperVisibilityForCompanionObjectInstanceField);
        boolean hasPrivateOrProtectedProperVisibility = (properFieldVisibilityFlag & (ACC_PRIVATE | ACC_PROTECTED)) != 0;
        boolean hasPackagePrivateProperVisibility = (properFieldVisibilityFlag & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED)) == 0;
        boolean fieldShouldBeDeprecated =
                deprecatedFieldForInvisibleCompanionObject &&
                !properVisibilityForCompanionObjectInstanceField &&
                (hasPrivateOrProtectedProperVisibility || hasPackagePrivateProperVisibility ||
                 isNonIntrinsicPrivateCompanionObjectInInterface(companionObjectDescriptor));
        boolean fieldIsForcedToBePublic =
                JvmCodegenUtil.isJvmInterface(descriptor) ||
                !properVisibilityForCompanionObjectInstanceField;
        int fieldAccessFlags = ACC_STATIC | ACC_FINAL;
        if (fieldIsForcedToBePublic) {
            fieldAccessFlags |= ACC_PUBLIC;
        }
        else {
            fieldAccessFlags |= properFieldVisibilityFlag;
        }
        if (fieldShouldBeDeprecated) {
            fieldAccessFlags |= ACC_DEPRECATED;
        }
        if (properVisibilityForCompanionObjectInstanceField &&
            JvmCodegenUtil.isCompanionObjectInInterfaceNotIntrinsic(companionObjectDescriptor) &&
            DescriptorVisibilities.isPrivate(companionObjectDescriptor.getVisibility())) {
            fieldAccessFlags |= ACC_SYNTHETIC;
        }
        StackValue.Field field = StackValue.singleton(companionObjectDescriptor, typeMapper);
        FieldVisitor fv = v.newField(
                JvmDeclarationOriginKt.OtherOrigin(companionObject == null ? myClass.getPsiOrParent() : companionObject),
                fieldAccessFlags, field.name, field.type.getDescriptor(), null, null
        );
        AnnotationCodegen.forField(fv, this, state).visitAnnotation(Type.getDescriptor(NotNull.class), false).visitEnd();
        if (fieldShouldBeDeprecated) {
            AnnotationCodegen.forField(fv, this, state).visitAnnotation(Type.getDescriptor(Deprecated.class), true).visitEnd();
        }
    }

    private void initializeObjects() {
        if (!DescriptorUtils.isObject(descriptor)) return;
        if (!state.getClassBuilderMode().generateBodies) return;

        boolean isNonCompanionObject = isNonCompanionObject(descriptor);
        boolean isInterfaceCompanion = isCompanionObjectInInterfaceNotIntrinsic(descriptor);
        boolean isInterfaceCompanionWithBackingFieldsInOuter = isInterfaceCompanionWithBackingFieldsInOuter(descriptor);
        boolean isMappedIntrinsicCompanionObject = isMappedIntrinsicCompanionObject(descriptor);
        boolean isClassCompanionWithBackingFieldsInOuter = isClassCompanionObjectWithBackingFieldsInOuter(descriptor);
        if (isNonCompanionObject ||
            (isInterfaceCompanion && !isInterfaceCompanionWithBackingFieldsInOuter) ||
            isMappedIntrinsicCompanionObject
        ) {
            ExpressionCodegen clInitCodegen = createOrGetClInitCodegen();
            InstructionAdapter v = clInitCodegen.v;
            markLineNumberForElement(element.getPsiOrParent(), v);
            v.anew(classAsmType);
            v.dup();
            v.invokespecial(classAsmType.getInternalName(), "", "()V", false);

            //local0 emulates this in object constructor
            int local0Index = clInitCodegen.getFrameMap().enterTemp(classAsmType);
            assert local0Index == 0 : "Local variable with index 0 in clInit should be used only for singleton instance keeping";
            StackValue.Local local0 = StackValue.local(0, classAsmType);
            local0.store(StackValue.onStack(classAsmType), clInitCodegen.v);
            StackValue.Field singleton =
                    StackValue.createSingletonViaInstance(
                            descriptor, typeMapper, isInterfaceCompanion ? HIDDEN_INSTANCE_FIELD : INSTANCE_FIELD
            );
            singleton.store(local0, clInitCodegen.v);

            generateInitializers(clInitCodegen);

            if (isInterfaceCompanion) {
                //initialize singleton instance in outer by hidden instance
                StackValue.singleton(descriptor, typeMapper).store(
                        singleton, getParentCodegen().createOrGetClInitCodegen().v, true
                );
            }
        }
        else if (isClassCompanionWithBackingFieldsInOuter || isInterfaceCompanionWithBackingFieldsInOuter) {
            ImplementationBodyCodegen parentCodegen = (ImplementationBodyCodegen) getParentCodegen();
            ExpressionCodegen parentClInitCodegen = parentCodegen.createOrGetClInitCodegen();
            InstructionAdapter parentVisitor = parentClInitCodegen.v;

            FunctionDescriptor constructor = (FunctionDescriptor) parentCodegen.context.accessibleDescriptor(
                    CollectionsKt.single(descriptor.getConstructors()), /* superCallExpression = */ null
            );
            parentCodegen.generateMethodCallTo(constructor, null, parentVisitor);
            StackValue instance = StackValue.onStack(parentCodegen.typeMapper.mapClass(descriptor));
            StackValue.singleton(descriptor, parentCodegen.typeMapper).store(instance, parentVisitor, true);

            generateInitializers(parentClInitCodegen);
        }
        else {
            assert false : "Unknown object type: " + descriptor;
        }
    }

    private static boolean isInterfaceCompanionWithBackingFieldsInOuter(@NotNull DeclarationDescriptor declarationDescriptor) {
        DeclarationDescriptor interfaceClass = declarationDescriptor.getContainingDeclaration();
        if (!isCompanionObject(declarationDescriptor) || !isJvmInterface(interfaceClass)) return false;

        Collection descriptors = ((ClassDescriptor) declarationDescriptor).getUnsubstitutedMemberScope()
                .getContributedDescriptors(DescriptorKindFilter.ALL, MemberScope.Companion.getALL_NAME_FILTER());
        return CollectionsKt.any(descriptors, d -> d instanceof PropertyDescriptor && hasJvmFieldAnnotation((PropertyDescriptor) d));
    }

    private void generateCompanionObjectBackingFieldCopies() {
        if (companionObjectPropertiesToCopy == null || companionObjectPropertiesToCopy.isEmpty()) return;

        boolean isPrivateCompanion =
                DescriptorVisibilities.isPrivate(
                        ((ClassDescriptor) companionObjectPropertiesToCopy.get(0).descriptor.getContainingDeclaration()).getVisibility());

        int modifiers = ACC_STATIC | ACC_FINAL | ACC_PUBLIC | (isPrivateCompanion ? ACC_DEPRECATED : 0);
        List additionalVisibleAnnotations =
                isPrivateCompanion ? Collections.singletonList(CodegenUtilKt.JAVA_LANG_DEPRECATED) : Collections.emptyList();
        for (PropertyAndDefaultValue info : companionObjectPropertiesToCopy) {
            PropertyDescriptor property = info.descriptor;

            Type type = typeMapper.mapType(property);

            FieldVisitor fv = v.newField(JvmDeclarationOriginKt.Synthetic(DescriptorToSourceUtils.descriptorToDeclaration(property), property),
                                         modifiers, context.getFieldName(property, false),
                                         type.getDescriptor(), typeMapper.mapFieldSignature(property.getType(), property),
                                         info.defaultValue);

            AnnotationCodegen.forField(fv, this, state).genAnnotations(property, type, null, null, additionalVisibleAnnotations);

            //This field are always static and final so if it has constant initializer don't do anything in clinit,
            //field would be initialized via default value in v.newField(...) - see JVM SPEC Ch.4
            // TODO: test this code
            if (state.getClassBuilderMode().generateBodies && info.defaultValue == null) {
                ExpressionCodegen codegen = createOrGetClInitCodegen();
                int companionObjectIndex = putCompanionObjectInLocalVar(codegen);
                StackValue.local(companionObjectIndex, OBJECT_TYPE).put(codegen.v);
                copyFieldFromCompanionObject(property);
            }
        }
    }

    private int putCompanionObjectInLocalVar(ExpressionCodegen codegen) {
        FrameMap frameMap = codegen.myFrameMap;
        ClassDescriptor companionObjectDescriptor = descriptor.getCompanionObjectDescriptor();
        int companionObjectIndex = frameMap.getIndex(companionObjectDescriptor);
        if (companionObjectIndex == -1) {
            companionObjectIndex = frameMap.enter(companionObjectDescriptor, OBJECT_TYPE);
            StackValue companionObject = StackValue.singleton(companionObjectDescriptor, typeMapper);
            StackValue.local(companionObjectIndex, companionObject.type).store(companionObject, codegen.v);
        }
        return companionObjectIndex;
    }

    private void copyFieldFromCompanionObject(PropertyDescriptor propertyDescriptor) {
        ExpressionCodegen codegen = createOrGetClInitCodegen();
        StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, false, null, StackValue.none());
        StackValue.Field field = StackValue.field(
                property.type, property.kotlinType, classAsmType, propertyDescriptor.getName().asString(),
                true, StackValue.none(), propertyDescriptor
        );
        field.store(property, codegen.v);
    }

    public void generateInitializers(@NotNull ExpressionCodegen codegen) {
        generateInitializers(() -> codegen);
    }

    private void lookupConstructorExpressionsInClosureIfPresent() {
        if (!state.getClassBuilderMode().generateBodies || descriptor.getConstructors().isEmpty()) return;

        KtVisitorVoid visitor = new KtVisitorVoid() {
            @Override
            public void visitKtElement(@NotNull KtElement e) {
                e.acceptChildren(this);
            }

            @Override
            public void visitSimpleNameExpression(@NotNull KtSimpleNameExpression expr) {
                DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expr);

                if (isLocalFunction(descriptor)) {
                    lookupInContext(descriptor);
                }
                else if (descriptor instanceof CallableMemberDescriptor) {
                    ResolvedCall call = CallUtilKt.getResolvedCall(expr, bindingContext);
                    if (call != null) {
                        lookupReceivers(call);
                    }
                    if (call instanceof VariableAsFunctionResolvedCall) {
                        lookupReceivers(((VariableAsFunctionResolvedCall) call).getVariableCall());
                    }
                }
                else if (descriptor instanceof VariableDescriptor) {
                    DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
                    if (containingDeclaration instanceof ConstructorDescriptor) {
                        ClassDescriptor classDescriptor = ((ConstructorDescriptor) containingDeclaration).getConstructedClass();
                        if (classDescriptor == ImplementationBodyCodegen.this.descriptor) return;
                    }
                    if (lookupInContext(descriptor)) {
                        if (isDelegatedLocalVariable(descriptor)) {
                            VariableDescriptor metadata = getDelegatedLocalVariableMetadata((VariableDescriptor) descriptor, bindingContext);
                            lookupInContext(metadata);
                        }
                    }
                }
            }

            private void lookupReceivers(@NotNull ResolvedCall call) {
                lookupReceiver(call.getDispatchReceiver());
                lookupReceiver(call.getExtensionReceiver());
            }

            private void lookupReceiver(@Nullable ReceiverValue value) {
                if (value instanceof ImplicitReceiver) {
                    if (value instanceof ExtensionReceiver) {
                        ReceiverParameterDescriptor parameter =
                                ((ExtensionReceiver) value).getDeclarationDescriptor().getExtensionReceiverParameter();
                        assert parameter != null : "Extension receiver should exist: " + ((ExtensionReceiver) value).getDeclarationDescriptor();
                        lookupInContext(parameter);
                    }
                    else {
                        lookupInContext(((ImplicitReceiver) value).getDeclarationDescriptor());
                    }
                }
            }

            private boolean lookupInContext(@NotNull DeclarationDescriptor toLookup) {
                return context.lookupInContext(toLookup, StackValue.LOCAL_0, state, true) != null;
            }

            @Override
            public void visitThisExpression(@NotNull KtThisExpression expression) {
                DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expression.getInstanceReference());
                assert descriptor instanceof CallableDescriptor ||
                       descriptor instanceof ClassDescriptor : "'This' reference target should be class or callable descriptor but was " + descriptor;
                if (descriptor instanceof ClassDescriptor) {
                    lookupInContext(descriptor);
                }

                if (descriptor instanceof CallableDescriptor) {
                    ReceiverParameterDescriptor parameter = ((CallableDescriptor) descriptor).getExtensionReceiverParameter();
                    if (parameter != null) {
                        lookupInContext(parameter);
                    }
                }
            }

            @Override
            public void visitSuperExpression(@NotNull KtSuperExpression expression) {
                lookupInContext(ExpressionCodegen.getSuperCallLabelTarget(context, expression));
            }

            @Override
            public void visitArrayAccessExpression(@NotNull KtArrayAccessExpression expression) {
                ResolvedCall resolvedGetCall = bindingContext.get(INDEXED_LVALUE_GET, expression);
                if (resolvedGetCall != null) {
                    ReceiverValue receiver = resolvedGetCall.getDispatchReceiver();
                    lookupReceiver(receiver);
                }

                ResolvedCall resolvedSetCall = bindingContext.get(INDEXED_LVALUE_SET, expression);
                if (resolvedSetCall != null) {
                    ReceiverValue receiver = resolvedSetCall.getDispatchReceiver();
                    lookupReceiver(receiver);
                }
                super.visitArrayAccessExpression(expression);
            }
        };

        for (KtDeclaration declaration : myClass.getDeclarations()) {
            if (declaration instanceof KtProperty) {
                KtProperty property = (KtProperty) declaration;
                KtExpression initializer = property.getDelegateExpressionOrInitializer();
                if (initializer != null) {
                    initializer.accept(visitor);
                }
            }
            else if (declaration instanceof KtAnonymousInitializer) {
                KtAnonymousInitializer initializer = (KtAnonymousInitializer) declaration;
                initializer.accept(visitor);
            }
            else if (declaration instanceof KtSecondaryConstructor) {
                KtSecondaryConstructor constructor = (KtSecondaryConstructor) declaration;
                constructor.accept(visitor);
            }
        }

        for (KtSuperTypeListEntry specifier : myClass.getSuperTypeListEntries()) {
            if (specifier instanceof KtDelegatedSuperTypeEntry) {
                KtExpression delegateExpression = ((KtDelegatedSuperTypeEntry) specifier).getDelegateExpression();
                assert delegateExpression != null;
                delegateExpression.accept(visitor);
            }
            else if (specifier instanceof KtSuperTypeCallEntry) {
                specifier.accept(visitor);
            }
        }
    }

    private void generateEnumEntries() {
        if (descriptor.getKind() != ClassKind.ENUM_CLASS) return;

        List enumEntries = CollectionsKt.filterIsInstance(element.getDeclarations(), KtEnumEntry.class);

        for (KtEnumEntry enumEntry : enumEntries) {
            ClassDescriptor descriptor = getNotNull(bindingContext, BindingContext.CLASS, enumEntry);
            int isDeprecated = KotlinBuiltIns.isDeprecated(descriptor) ? ACC_DEPRECATED : 0;
            FieldVisitor fv = v.newField(JvmDeclarationOriginKt.OtherOrigin(enumEntry, descriptor), ACC_PUBLIC | ACC_ENUM | ACC_STATIC | ACC_FINAL | isDeprecated,
                                         descriptor.getName().asString(), classAsmType.getDescriptor(), null, null);
            AnnotationCodegen.forField(fv, this, state).genAnnotations(descriptor, null, null);
        }

        initializeEnumConstants(enumEntries);
    }

    private void initializeEnumConstants(@NotNull List enumEntries) {
        if (!state.getClassBuilderMode().generateBodies) return;

        ExpressionCodegen codegen = createOrGetClInitCodegen();
        InstructionAdapter iv = codegen.v;

        Type arrayAsmType = typeMapper.mapType(DescriptorUtilsKt.getBuiltIns(descriptor).getArrayType(INVARIANT, descriptor.getDefaultType()));
        v.newField(JvmDeclarationOriginKt.OtherOriginFromPure(myClass), ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, ENUM_VALUES_FIELD_NAME,
                   arrayAsmType.getDescriptor(), null, null);

        iv.iconst(enumEntries.size());
        iv.newarray(classAsmType);

        if (!enumEntries.isEmpty()) {
            iv.dup();
            for (int ordinal = 0, size = enumEntries.size(); ordinal < size; ordinal++) {
                initializeEnumConstant(enumEntries, ordinal);
            }
        }

        iv.putstatic(classAsmType.getInternalName(), ENUM_VALUES_FIELD_NAME, arrayAsmType.getDescriptor());
    }

    private void initializeEnumConstant(@NotNull List enumEntries, int ordinal) {
        ExpressionCodegen codegen = createOrGetClInitCodegen();
        InstructionAdapter iv = codegen.v;
        KtEnumEntry enumEntry = enumEntries.get(ordinal);

        iv.dup();
        iv.iconst(ordinal);

        ClassDescriptor classDescriptor = getNotNull(bindingContext, BindingContext.CLASS, enumEntry);
        Type implClass = typeMapper.mapClass(classDescriptor);

        iv.anew(implClass);
        iv.dup();

        iv.aconst(enumEntry.getName());
        iv.iconst(ordinal);

        List delegationSpecifiers = enumEntry.getSuperTypeListEntries();
        ResolvedCall defaultArgumentsConstructorCall = CallUtilKt.getResolvedCall(enumEntry, bindingContext);
        boolean enumEntryHasSubclass = CodegenBinding.enumEntryNeedSubclass(bindingContext, classDescriptor);
        if (delegationSpecifiers.size() == 1 && !enumEntryNeedSubclass(bindingContext, enumEntry)) {
            ResolvedCall resolvedCall = CallUtilKt.getResolvedCallWithAssert(delegationSpecifiers.get(0), bindingContext);

            CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor(), false);

            codegen.invokeMethodWithArguments(method, resolvedCall, StackValue.none());
        }
        else if (defaultArgumentsConstructorCall != null && !enumEntryHasSubclass) {
            codegen.invokeFunction(defaultArgumentsConstructorCall, StackValue.none()).put(Type.VOID_TYPE, iv);
        }
        else {
            iv.invokespecial(implClass.getInternalName(), "", "(Ljava/lang/String;I)V", false);
        }

        iv.dup();
        iv.putstatic(classAsmType.getInternalName(), enumEntry.getName(), classAsmType.getDescriptor());
        iv.astore(OBJECT_TYPE);
    }

    private void generateDelegates(DelegationFieldsInfo delegationFieldsInfo) {
        for (KtSuperTypeListEntry specifier : myClass.getSuperTypeListEntries()) {
            if (specifier instanceof KtDelegatedSuperTypeEntry) {
                DelegationFieldsInfo.Field field = delegationFieldsInfo.getInfo((KtDelegatedSuperTypeEntry) specifier);
                if (field == null) continue;

                generateDelegateField(field);
                KtExpression delegateExpression = ((KtDelegatedSuperTypeEntry) specifier).getDelegateExpression();
                KotlinType delegateExpressionType = delegateExpression != null ? bindingContext.getType(delegateExpression) : null;
                ClassDescriptor superClass = JvmCodegenUtil.getSuperClass(specifier, state, bindingContext);
                if (superClass == null) continue;

                generateDelegates(superClass, delegateExpressionType, field);
            }
        }
    }

    private void generateDelegateField(DelegationFieldsInfo.Field fieldInfo) {
        if (!fieldInfo.generateField) return;

        v.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_PRIVATE | ACC_FINAL | ACC_SYNTHETIC,
                   fieldInfo.name, fieldInfo.type.getDescriptor(), fieldInfo.genericSignature, null);
    }

    private void generateDelegates(
            @NotNull ClassDescriptor toInterface,
            @Nullable KotlinType delegateExpressionType,
            @NotNull DelegationFieldsInfo.Field field
    ) {
        for (Map.Entry entry : DelegationResolver.Companion.getDelegates(
                descriptor, toInterface, delegateExpressionType).entrySet()
        ) {
            CallableMemberDescriptor member = entry.getKey();
            CallableMemberDescriptor delegateTo = entry.getValue();
            if (member instanceof PropertyDescriptor) {
                propertyCodegen.genDelegate((PropertyDescriptor) member, (PropertyDescriptor) delegateTo, field.getStackValue());
            }
            else if (member instanceof FunctionDescriptor) {
                functionCodegen.genDelegate((FunctionDescriptor) member, (FunctionDescriptor) delegateTo, field.getStackValue());
            }
        }
    }

    public void addCompanionObjectPropertyToCopy(@NotNull PropertyDescriptor descriptor, Object defaultValue) {
        if (companionObjectPropertiesToCopy == null) {
            companionObjectPropertiesToCopy = new ArrayList<>();
        }
        companionObjectPropertiesToCopy.add(new PropertyAndDefaultValue(descriptor, defaultValue));
    }

    @Override
    protected void done() {
        for (Function2 task : additionalTasks) {
            task.invoke(this, v);
        }

        super.done();
    }

    private static class PropertyAndDefaultValue {
        public final PropertyDescriptor descriptor;
        public final Object defaultValue;

        public PropertyAndDefaultValue(@NotNull PropertyDescriptor descriptor, Object defaultValue) {
            this.descriptor = descriptor;
            this.defaultValue = defaultValue;
        }
    }

    public void addAdditionalTask(Function2 additionalTask) {
        additionalTasks.add(additionalTask);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy