001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.codegen;
018    
019    import kotlin.KotlinPackage;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.codegen.state.GenerationState;
023    import org.jetbrains.kotlin.descriptors.*;
024    import org.jetbrains.kotlin.psi.Call;
025    import org.jetbrains.kotlin.psi.JetCallExpression;
026    import org.jetbrains.kotlin.psi.JetExpression;
027    import org.jetbrains.kotlin.psi.ValueArgument;
028    import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall;
029    import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
030    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
031    import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
032    import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
033    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
035    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
036    import org.jetbrains.org.objectweb.asm.Type;
037    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
038    
039    import java.util.ArrayList;
040    import java.util.Iterator;
041    import java.util.List;
042    
043    import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
044    import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
045    
046    public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
047        private final ResolvedCall<?> resolvedCall;
048        private final FunctionDescriptor referencedFunction;
049    
050        public FunctionReferenceGenerationStrategy(
051                @NotNull GenerationState state,
052                @NotNull FunctionDescriptor functionDescriptor,
053                @NotNull ResolvedCall<?> resolvedCall
054        ) {
055            super(state, functionDescriptor);
056            this.resolvedCall = resolvedCall;
057            this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
058        }
059    
060        @Override
061        public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
062            /*
063             Here we need to put the arguments from our locals to the stack and invoke the referenced method. Since invocation
064             of methods is highly dependent on expressions, we create a fake call expression. Then we create a new instance of
065             ExpressionCodegen and, in order for it to generate code correctly, we save to its 'tempVariables' field every
066             argument of our fake expression, pointing it to the corresponding index in our locals. This way generation of
067             every argument boils down to calling LOAD with the corresponding index
068             */
069    
070            JetCallExpression fakeExpression = constructFakeFunctionCall();
071            final List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
072    
073            final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
074            final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
075            computeAndSaveArguments(fakeArguments, codegen);
076    
077            ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
078                @NotNull
079                @Override
080                public ReceiverValue getExtensionReceiver() {
081                    return extensionReceiver;
082                }
083    
084                @NotNull
085                @Override
086                public ReceiverValue getDispatchReceiver() {
087                    return dispatchReceiver;
088                }
089    
090                @NotNull
091                @Override
092                public List<ResolvedValueArgument> getValueArgumentsByIndex() {
093                    List<ResolvedValueArgument> result = new ArrayList<ResolvedValueArgument>(fakeArguments.size());
094                    for (ValueArgument argument : fakeArguments) {
095                        result.add(new ExpressionValueArgument(argument));
096                    }
097                    return result;
098                }
099            };
100    
101            StackValue result;
102            Type returnType = codegen.getReturnType();
103            if (referencedFunction instanceof ConstructorDescriptor) {
104                if (returnType.getSort() == Type.ARRAY) {
105                    //noinspection ConstantConditions
106                    result = codegen.generateNewArray(fakeExpression, referencedFunction.getReturnType());
107                }
108                else {
109                    result = codegen.generateConstructorCall(fakeResolvedCall, returnType);
110                }
111            }
112            else {
113                Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
114                result = codegen.invokeFunction(call, fakeResolvedCall, StackValue.none());
115            }
116    
117            InstructionAdapter v = codegen.v;
118            result.put(returnType, v);
119            v.areturn(returnType);
120        }
121    
122        @NotNull
123        private JetCallExpression constructFakeFunctionCall() {
124            StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall(");
125            for (Iterator<ValueParameterDescriptor> iterator = referencedFunction.getValueParameters().iterator(); iterator.hasNext(); ) {
126                ValueParameterDescriptor descriptor = iterator.next();
127                fakeFunctionCall.append("p").append(descriptor.getIndex());
128                if (iterator.hasNext()) {
129                    fakeFunctionCall.append(", ");
130                }
131            }
132            fakeFunctionCall.append(")");
133            return (JetCallExpression) JetPsiFactory(state.getProject()).createExpression(fakeFunctionCall.toString());
134        }
135    
136        private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
137            int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) +
138                            (referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0);
139    
140            List<ValueParameterDescriptor> parameters = KotlinPackage.drop(callableDescriptor.getValueParameters(), receivers);
141            for (int i = 0; i < parameters.size(); i++) {
142                ValueParameterDescriptor parameter = parameters.get(i);
143                ValueArgument fakeArgument = fakeArguments.get(i);
144    
145                Type type = state.getTypeMapper().mapType(parameter);
146                int localIndex = codegen.myFrameMap.getIndex(parameter);
147                codegen.tempVariables.put(fakeArgument.getArgumentExpression(), StackValue.local(localIndex, type));
148            }
149        }
150    
151        @NotNull
152        private ReceiverValue computeAndSaveReceiver(
153                @NotNull JvmMethodSignature signature,
154                @NotNull ExpressionCodegen codegen,
155                @Nullable ReceiverParameterDescriptor receiver
156        ) {
157            if (receiver == null) return NO_RECEIVER;
158    
159            JetExpression receiverExpression = JetPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
160            codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
161            return new ExpressionReceiver(receiverExpression, receiver.getType());
162        }
163    
164        @NotNull
165        private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
166            // 0 is this (the callable reference class), 1 is the invoke() method's first parameter
167            return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
168        }
169    }