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

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

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 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 kotlin.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;

import java.util.*;

import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;

public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased {
    private final ResolvedCall resolvedCall;
    private final FunctionDescriptor referencedFunction;

    public FunctionReferenceGenerationStrategy(
            @NotNull GenerationState state,
            @NotNull FunctionDescriptor functionDescriptor,
            @NotNull ResolvedCall resolvedCall
    ) {
        super(state, functionDescriptor);
        this.resolvedCall = resolvedCall;
        this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
    }

    @Override
    public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
        /*
         Here we need to put the arguments from our locals to the stack and invoke the referenced method. Since invocation
         of methods is highly dependent on expressions, we create a fake call expression. Then we create a new instance of
         ExpressionCodegen and, in order for it to generate code correctly, we save to its 'tempVariables' field every
         argument of our fake expression, pointing it to the corresponding index in our locals. This way generation of
         every argument boils down to calling LOAD with the corresponding index
         */

        KtCallExpression fakeExpression = constructFakeFunctionCall();
        final List fakeArguments = fakeExpression.getValueArguments();

        final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
        final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
        computeAndSaveArguments(fakeArguments, codegen);

        ResolvedCall fakeResolvedCall = new DelegatingResolvedCall(resolvedCall) {

            private final Map argumentMap;
            {
                argumentMap = new LinkedHashMap(fakeArguments.size());
                int index = 0;
                List parameters = callableDescriptor.getValueParameters();
                for (ValueArgument argument : fakeArguments) {
                    argumentMap.put(parameters.get(index), new ExpressionValueArgument(argument));
                    index++;
                }
            }

            @NotNull
            @Override
            public ReceiverValue getExtensionReceiver() {
                return extensionReceiver;
            }

            @NotNull
            @Override
            public ReceiverValue getDispatchReceiver() {
                return dispatchReceiver;
            }

            @NotNull
            @Override
            public List getValueArgumentsByIndex() {
                return new ArrayList(argumentMap.values());
            }

            @NotNull
            @Override
            public Map getValueArguments() {
                return argumentMap;
            }
        };

        StackValue result;
        Type returnType = codegen.getReturnType();
        if (referencedFunction instanceof ConstructorDescriptor) {
            if (returnType.getSort() == Type.ARRAY) {
                //noinspection ConstantConditions
                result = codegen.generateNewArray(fakeExpression, referencedFunction.getReturnType());
            }
            else {
                result = codegen.generateConstructorCall(fakeResolvedCall, returnType);
            }
        }
        else {
            Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
            result = codegen.invokeFunction(call, fakeResolvedCall, StackValue.none());
        }

        InstructionAdapter v = codegen.v;
        result.put(returnType, v);
        v.areturn(returnType);
    }

    @NotNull
    private KtCallExpression constructFakeFunctionCall() {
        StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall(");
        for (Iterator iterator = referencedFunction.getValueParameters().iterator(); iterator.hasNext(); ) {
            ValueParameterDescriptor descriptor = iterator.next();
            fakeFunctionCall.append("p").append(descriptor.getIndex());
            if (iterator.hasNext()) {
                fakeFunctionCall.append(", ");
            }
        }
        fakeFunctionCall.append(")");
        return (KtCallExpression) KtPsiFactoryKt.KtPsiFactory(state.getProject()).createExpression(fakeFunctionCall.toString());
    }

    private void computeAndSaveArguments(@NotNull List fakeArguments, @NotNull ExpressionCodegen codegen) {
        int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) +
                        (referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0);

        List parameters = CollectionsKt.drop(callableDescriptor.getValueParameters(), receivers);
        for (int i = 0; i < parameters.size(); i++) {
            ValueParameterDescriptor parameter = parameters.get(i);
            ValueArgument fakeArgument = fakeArguments.get(i);

            Type type = state.getTypeMapper().mapType(parameter);
            int localIndex = codegen.myFrameMap.getIndex(parameter);
            codegen.tempVariables.put(fakeArgument.getArgumentExpression(), StackValue.local(localIndex, type));
        }
    }

    @NotNull
    private ReceiverValue computeAndSaveReceiver(
            @NotNull JvmMethodSignature signature,
            @NotNull ExpressionCodegen codegen,
            @Nullable ReceiverParameterDescriptor receiver
    ) {
        if (receiver == null) return NO_RECEIVER;

        KtExpression receiverExpression = KtPsiFactoryKt
                .KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
        codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
        return ExpressionReceiver.Companion.create(receiverExpression, receiver.getType(), BindingContext.EMPTY);
    }

    @NotNull
    private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
        // 0 is this (the callable reference class), 1 is the invoke() method's first parameter
        return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy