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

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

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2016 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.codegen;

import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.jvm.functions.Function1;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.bridges.Bridge;
import org.jetbrains.kotlin.backend.common.bridges.ImplKt;
import org.jetbrains.kotlin.codegen.annotation.AnnotatedWithOnlyTargetedAnnotations;
import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
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.AnnotationUseSiteTarget;
import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo;
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.kotlin.nativeDeclarations.NativeKt;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtNamedFunction;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.CallResolverUtilKt;
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.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
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.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
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.annotations.AnnotationUseSiteTarget.*;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.getSuperClassDescriptor;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
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 JetTypeMapper typeMapper;
    private final BindingContext bindingContext;
    private final CodegenContext owner;
    private final ClassBuilder v;
    private final MemberCodegen memberCodegen;

    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 (functionDescriptor == null) {
            throw ExceptionLogger.logDescriptorNotFound("No descriptor for function " + function.getName(), function);
        }

        if (owner.getContextKind() != OwnerKind.DEFAULT_IMPLS || function.hasBody()) {
            generateMethod(JvmDeclarationOriginKt.OtherOrigin(function, functionDescriptor), functionDescriptor,
                           new FunctionGenerationStrategy.FunctionDefault(state, functionDescriptor, function));
        }

        generateDefaultIfNeeded(owner.intoFunction(functionDescriptor), 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
        );
    }

    public void generateMethod(
            @NotNull JvmDeclarationOrigin origin,
            @NotNull FunctionDescriptor descriptor,
            @NotNull FunctionGenerationStrategy strategy
    ) {
        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();
        if (isInterface(functionDescriptor.getContainingDeclaration()) &&
            functionDescriptor.getVisibility() == Visibilities.PRIVATE &&
            contextKind != OwnerKind.DEFAULT_IMPLS) {
            return;
        }

        JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, contextKind);
        Method asmMethod = jvmSignature.getAsmMethod();

        int flags = getMethodAsmFlags(functionDescriptor, contextKind);
        boolean isNative = NativeKt.hasNativeAnnotation(functionDescriptor);

        if (isNative && owner instanceof MultifileClassFacadeContext) {
            // Native methods are only defined in facades and do not need package part implementations
            return;
        }
        MethodVisitor mv = v.newMethod(origin,
                                       flags,
                                       asmMethod.getName(),
                                       asmMethod.getDescriptor(),
                                       jvmSignature.getGenericsSignature(),
                                       getThrownExceptions(functionDescriptor, typeMapper));

        if (CodegenContextUtil.isImplClassOwner(owner)) {
            v.getSerializationBindings().put(METHOD_FOR_FUNCTION, functionDescriptor, asmMethod);
        }

        generateMethodAnnotations(functionDescriptor, asmMethod, mv);

        generateParameterAnnotations(functionDescriptor, mv, typeMapper.mapSignature(functionDescriptor));

        generateBridges(functionDescriptor);

        boolean staticInCompanionObject = AnnotationUtilKt.isPlatformStaticInCompanionObject(functionDescriptor);
        if (staticInCompanionObject) {
            ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
            parentBodyCodegen.addAdditionalTask(new JvmStaticGenerator(functionDescriptor, origin, state));
        }

        if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES || isAbstractMethod(functionDescriptor, contextKind)) {
            generateLocalVariableTable(
                    mv,
                    jvmSignature,
                    functionDescriptor,
                    getThisTypeForFunction(functionDescriptor, methodContext, typeMapper),
                    new Label(),
                    new Label(),
                    contextKind
            );

            mv.visitEnd();
            return;
        }

        if (!isNative) {
            generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen);
        }
        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 = JvmStaticGenerator.createStaticFunctionDescriptor(functionDescriptor);
            JvmMethodSignature jvmMethodSignature =
                    typeMapper.mapSignature(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null));
            Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration());
            generateDelegateToMethodBody(false, mv, jvmMethodSignature.getAsmMethod(), owningType.getInternalName());
        }

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

    private void generateMethodAnnotations(
            @NotNull FunctionDescriptor functionDescriptor,
            Method asmMethod,
            MethodVisitor mv
    ) {
        AnnotationCodegen annotationCodegen = AnnotationCodegen.forMethod(mv, typeMapper);

        if (functionDescriptor instanceof PropertyAccessorDescriptor) {
            AnnotationUseSiteTarget target = functionDescriptor instanceof PropertySetterDescriptor ? PROPERTY_SETTER : PROPERTY_GETTER;
            annotationCodegen.genAnnotations(functionDescriptor, asmMethod.getReturnType(), target);
        }
        else {
            annotationCodegen.genAnnotations(functionDescriptor, asmMethod.getReturnType());
        }
    }

    private void generateParameterAnnotations(
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmSignature
    ) {
        Iterator iterator = functionDescriptor.getValueParameters().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);
                continue;
            }

            if (kind == JvmMethodParameterKind.VALUE) {
                ValueParameterDescriptor parameter = iterator.next();
                AnnotationCodegen annotationCodegen = AnnotationCodegen.forParameter(i, mv, typeMapper);

                if (functionDescriptor instanceof PropertySetterDescriptor) {
                    PropertyDescriptor propertyDescriptor = ((PropertySetterDescriptor) functionDescriptor).getCorrespondingProperty();
                    Annotated targetedAnnotations = new AnnotatedWithOnlyTargetedAnnotations(propertyDescriptor);
                    annotationCodegen.genAnnotations(targetedAnnotations, parameterSignature.getAsmType(), SETTER_PARAMETER);
                }

                if (functionDescriptor instanceof ConstructorDescriptor) {
                    annotationCodegen.genAnnotations(parameter, parameterSignature.getAsmType(), CONSTRUCTOR_PARAMETER);
                }
                else {
                    annotationCodegen.genAnnotations(parameter, parameterSignature.getAsmType());
                }
            }
            else if (kind == JvmMethodParameterKind.RECEIVER) {
                ReceiverParameterDescriptor receiver = ((functionDescriptor instanceof PropertyAccessorDescriptor)
                                             ? ((PropertyAccessorDescriptor) functionDescriptor).getCorrespondingProperty()
                                             : functionDescriptor).getExtensionReceiverParameter();
                if (receiver != null) {
                    AnnotationCodegen annotationCodegen = AnnotationCodegen.forParameter(i, mv, typeMapper);
                    Annotated targetedAnnotations = new AnnotatedWithOnlyTargetedAnnotations(receiver.getType());
                    annotationCodegen.genAnnotations(targetedAnnotations, parameterSignature.getAsmType(), RECEIVER);
                }
            }
        }
    }

    private void markEnumOrInnerConstructorParameterAsSynthetic(MethodVisitor mv, int i) {
        // IDEA's ClsPsi builder fails to annotate synthetic parameters
        if (state.getClassBuilderMode() == 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 JetTypeMapper typeMapper
    ) {
        ReceiverParameterDescriptor dispatchReceiver = functionDescriptor.getDispatchReceiverParameter();
        if (functionDescriptor instanceof ConstructorDescriptor) {
            return typeMapper.mapType(functionDescriptor);
        }
        else if (dispatchReceiver != null) {
            return typeMapper.mapType(dispatchReceiver.getType());
        }
        else if (isFunctionLiteral(functionDescriptor) ||
                 isLocalFunction(functionDescriptor) ||
                 isFunctionExpression(functionDescriptor)) {
            return typeMapper.mapType(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
    ) {
        mv.visitCode();

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

        JetTypeMapper typeMapper = parentCodegen.typeMapper;
        if (BuiltinSpecialBridgesUtil.shouldHaveTypeSafeBarrier(functionDescriptor, getSignatureMapper(typeMapper))) {
            generateTypeCheckBarrierIfNeeded(
                    new InstructionAdapter(mv), functionDescriptor, signature.getReturnType(), /* delegateParameterType = */null);
        }

        Label methodEnd;

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

        if (context.getParentContext() instanceof MultifileClassFacadeContext) {
            generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
            methodEnd = new Label();
        }
        else {
            FrameMap frameMap = createFrameMap(parentCodegen.state, functionDescriptor, signature, 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 (!JetTypeMapper.isAccessor(functionDescriptor)) {
                genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap);
            }
            methodEnd = new Label();
            context.setMethodEndLabel(methodEnd);
            strategy.generateBody(mv, frameMap, signature, context, parentCodegen);
        }

        mv.visitLabel(methodEnd);

        Type thisType = getThisTypeForFunction(functionDescriptor, context, typeMapper);
        generateLocalVariableTable(mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind());

        if (context.isInlineMethodContext() && functionFakeIndex != -1) {
            mv.visitLocalVariable(
                    JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION + functionDescriptor.getName().asString(),
                    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));
                mv.visitLocalVariable(
                        JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + lambdaOrdinal,
                        Type.INT_TYPE.getDescriptor(), null,
                        methodBegin, methodEnd,
                        lambdaFakeIndex);
            }
        }
    }

    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
    ) {
        generateLocalVariablesForParameters(mv, jvmMethodSignature, thisType, methodBegin, methodEnd,
                                            functionDescriptor.getValueParameters(),
                                            AsmUtil.isStaticMethod(ownerKind, functionDescriptor));
    }

    public static void generateLocalVariablesForParameters(
            @NotNull MethodVisitor mv,
            @NotNull JvmMethodSignature jvmMethodSignature,
            @Nullable Type thisType,
            @NotNull Label methodBegin,
            @NotNull Label methodEnd,
            Collection valueParameters,
            boolean isStatic
    ) {
        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();
                parameterName = parameter.getName().asString();
            }
            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();
        }
    }

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

    private static void generateDelegateToMethodBody(
            boolean isStatic,
            @NotNull MethodVisitor mv,
            @NotNull Method asmMethod,
            @NotNull String classToDelegateTo
    ) {
        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 k = isStatic ? 0 : 1;
        for (Type argType : argTypes) {
            iv.load(k, argType);
            k += argType.getSize();
        }
        iv.invokestatic(classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), false);
        iv.areturn(asmMethod.getReturnType());
    }

    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, @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" +
                    (description != null ? " for " + description : "") +
                    t.getClass().getName() +
                    " " +
                    t.getMessage() +
                    (bytecode != null ? "\nbytecode:\n" + bytecode : ""),
                    t, method);
        }
    }

    private static String renderByteCodeIfAvailable(MethodVisitor mv) {
        String bytecode = null;

        if (mv instanceof OptimizationMethodVisitor) {
            mv = ((OptimizationMethodVisitor) 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;
    }

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

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

        // If the function doesn't have a physical declaration among super-functions, it's a SAM adapter or alike and doesn't need bridges
        if (CallResolverUtilKt.isOrOverridesSynthesized(descriptor)) return;

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

        Set> bridgesToGenerate;
        if (!isSpecial) {
            bridgesToGenerate = ImplKt.generateBridgesForFunctionDescriptor(
                    descriptor,
                    getSignatureMapper(typeMapper)
            );
            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,
                    getSignatureMapper(typeMapper)
            );

            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;

                Method method = typeMapper.mapSignature(descriptor).getAsmMethod();
                int flags = ACC_ABSTRACT | getVisibilityAccessFlag(descriptor);
                v.newMethod(JvmDeclarationOriginKt.OtherOrigin(overridden), flags, method.getName(), method.getDescriptor(), null, null);
            }
        }
    }

    @NotNull
    private static Function1 getSignatureMapper(final @NotNull  JetTypeMapper typeMapper) {
        return new Function1() {
            @Override
            public Method invoke(FunctionDescriptor descriptor) {
                return typeMapper.mapSignature(descriptor).getAsmMethod();
            }
        };
    }

    private 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 final JetTypeMapper 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 = ContainerUtil.mapNotNull(
                arrayValue.getValue(),
                new Function, String>() {
                    @Override
                    public String fun(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 (kind != OwnerKind.DEFAULT_IMPLS && isInterface(contextClass)) {
            return;
        }

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

        int flags = getVisibilityAccessFlag(functionDescriptor) |
                    getDeprecatedAccessFlag(functionDescriptor) |
                    ACC_SYNTHETIC;
        if (!(functionDescriptor instanceof ConstructorDescriptor)) {
            flags |= ACC_STATIC | ACC_BRIDGE;
        }
        // $default methods are never private to be accessible from other class files (e.g. inner) without the need of synthetic accessors
        flags &= ~ACC_PRIVATE;

        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, typeMapper).genAnnotations(functionDescriptor, defaultMethod.getReturnType());

        if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
            if (this.owner instanceof MultifileClassFacadeContext) {
                mv.visitCode();
                generateFacadeDelegateMethodBody(mv, defaultMethod, (MultifileClassFacadeContext) this.owner);
                endVisit(mv, "default method delegation", 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().mapSignature(functionDescriptor, methodContext.getContextKind());

        boolean isStatic = isStaticMethod(methodContext.getContextKind(), functionDescriptor);
        FrameMap frameMap = createFrameMap(state, functionDescriptor, signature, 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, defaultMethod);

        loadExplicitArgumentsOnStack(OBJECT_TYPE, isStatic, signature, generator);

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

        int maskIndex = 0;
        List valueParameters = functionDescriptor.getValueParameters();
        for (int index = 0; index < valueParameters.size(); index++) {
            if (index % Integer.SIZE == 0) {
                maskIndex = frameMap.enterTemp(Type.INT_TYPE);
            }
            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);
            }

            generator.putValueIfNeeded(type, StackValue.local(parameterIndex, type));
        }

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

        generator.genCallWithoutAssertions(method, codegen);

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

    private static void genDefaultSuperCallCheckIfNeeded(@NotNull InstructionAdapter iv, @NotNull Method defaultMethod) {
        String defaultMethodName = defaultMethod.getName();
        if ("".equals(defaultMethodName)) {
            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: " +
                         StringsKt.substringBeforeLast(defaultMethodName, JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, defaultMethodName));
        iv.visitLabel(end);
    }

    @NotNull
    private static FrameMap createFrameMap(
            @NotNull GenerationState state,
            @NotNull FunctionDescriptor function,
            @NotNull JvmMethodSignature signature,
            boolean isStatic
    ) {
        FrameMap frameMap = new FrameMap();
        if (!isStatic) {
            frameMap.enterTemp(OBJECT_TYPE);
        }

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

        for (ValueParameterDescriptor parameter : function.getValueParameters()) {
            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(ownerType, StackValue.local(var, ownerType));
            var += ownerType.getSize();
        }

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

    private static boolean isDefaultNeeded(FunctionDescriptor functionDescriptor) {
        boolean needed = false;
        if (functionDescriptor != null) {
            for (ValueParameterDescriptor parameterDescriptor : functionDescriptor.getValueParameters()) {
                if (parameterDescriptor.declaresDefaultValue()) {
                    needed = true;
                    break;
                }
            }
        }
        return needed;
    }

    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.

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

        mv.visitCode();

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

        InstructionAdapter iv = new InstructionAdapter(mv);
        MemberCodegen.markLineNumberForDescriptor(owner.getThisDescriptor(), iv);

        if (delegateTo.getArgumentTypes().length == 1 && isSpecialBridge) {
            generateTypeCheckBarrierIfNeeded(iv, descriptor, bridge.getReturnType(), delegateTo.getArgumentTypes()[0]);
        }

        iv.load(0, OBJECT_TYPE);
        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();
        }

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

        StackValue.coerce(delegateTo.getReturnType(), bridge.getReturnType(), iv);
        iv.areturn(bridge.getReturnType());

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

    private static void generateTypeCheckBarrierIfNeeded(
            @NotNull InstructionAdapter iv,
            @NotNull FunctionDescriptor descriptor,
            @NotNull Type returnType,
            @Nullable final Type delegateParameterType
    ) {
        BuiltinMethodsWithSpecialGenericSignature.DefaultValue defaultValue =
                BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor);
        if (defaultValue == null) return;

        assert descriptor.getValueParameters().size() == 1 : "Should be descriptor with one value parameter, but found: " + descriptor;

        boolean isCheckForAny = delegateParameterType == null || OBJECT_TYPE.equals(delegateParameterType);

        final KotlinType kotlinType = descriptor.getValueParameters().get(0).getType();

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

        iv.load(1, OBJECT_TYPE);

        Label afterBarrier = new Label();

        if (isCheckForAny) {
            assert !TypeUtils.isNullableType(kotlinType) : "Only bridges for not-nullable types are necessary";
            iv.ifnonnull(afterBarrier);
        }
        else {
            CodegenUtilKt.generateIsCheck(iv, kotlinType, boxType(delegateParameterType));
            iv.ifne(afterBarrier);
        }

        StackValue.constant(defaultValue.getValue(), returnType).put(returnType, iv);
        iv.areturn(returnType);

        iv.visitLabel(afterBarrier);
    }

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

    public void genDelegate(
            @NotNull final FunctionDescriptor delegateFunction,
            final FunctionDescriptor delegatedTo,
            final ClassDescriptor toClass,
            final StackValue field
    ) {
        generateMethod(
                JvmDeclarationOriginKt.Delegation(DescriptorToSourceUtils.descriptorToDeclaration(delegatedTo), delegateFunction), 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.mapSignature(delegateFunction).getAsmMethod();

                        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());
                        }

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

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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy