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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.codegen.state.GenerationState;
022    import org.jetbrains.kotlin.descriptors.*;
023    import org.jetbrains.kotlin.psi.Call;
024    import org.jetbrains.kotlin.psi.JetCallExpression;
025    import org.jetbrains.kotlin.psi.JetExpression;
026    import org.jetbrains.kotlin.psi.ValueArgument;
027    import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall;
028    import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
029    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
030    import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
031    import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
032    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
033    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
035    import org.jetbrains.org.objectweb.asm.Type;
036    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
037    
038    import java.util.ArrayList;
039    import java.util.Iterator;
040    import java.util.List;
041    
042    import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
043    import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
044    
045    public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
046        private final ResolvedCall<?> resolvedCall;
047        private final FunctionDescriptor referencedFunction;
048    
049        public FunctionReferenceGenerationStrategy(
050                @NotNull GenerationState state,
051                @NotNull FunctionDescriptor functionDescriptor,
052                @NotNull ResolvedCall<?> resolvedCall
053        ) {
054            super(state, functionDescriptor);
055            this.resolvedCall = resolvedCall;
056            this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
057        }
058    
059        @Override
060        public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
061            /*
062             Here we need to put the arguments from our locals to the stack and invoke the referenced method. Since invocation
063             of methods is highly dependent on expressions, we create a fake call expression. Then we create a new instance of
064             ExpressionCodegen and, in order for it to generate code correctly, we save to its 'tempVariables' field every
065             argument of our fake expression, pointing it to the corresponding index in our locals. This way generation of
066             every argument boils down to calling LOAD with the corresponding index
067             */
068    
069            JetCallExpression fakeExpression = constructFakeFunctionCall();
070            final List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
071    
072            final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
073            final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
074            computeAndSaveArguments(fakeArguments, codegen);
075    
076            ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
077                @NotNull
078                @Override
079                public ReceiverValue getExtensionReceiver() {
080                    return extensionReceiver;
081                }
082    
083                @NotNull
084                @Override
085                public ReceiverValue getDispatchReceiver() {
086                    return dispatchReceiver;
087                }
088    
089                @NotNull
090                @Override
091                public List<ResolvedValueArgument> getValueArgumentsByIndex() {
092                    List<ResolvedValueArgument> result = new ArrayList<ResolvedValueArgument>(fakeArguments.size());
093                    for (ValueArgument argument : fakeArguments) {
094                        result.add(new ExpressionValueArgument(argument));
095                    }
096                    return result;
097                }
098            };
099    
100            StackValue result;
101            Type returnType = codegen.getReturnType();
102            if (referencedFunction instanceof ConstructorDescriptor) {
103                if (returnType.getSort() == Type.ARRAY) {
104                    //noinspection ConstantConditions
105                    result = codegen.generateNewArray(fakeExpression, referencedFunction.getReturnType());
106                }
107                else {
108                    result = codegen.generateConstructorCall(fakeResolvedCall, returnType);
109                }
110            }
111            else {
112                Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
113                result = codegen.invokeFunction(call, fakeResolvedCall, StackValue.none());
114            }
115    
116            InstructionAdapter v = codegen.v;
117            result.put(returnType, v);
118            v.areturn(returnType);
119        }
120    
121        @NotNull
122        private JetCallExpression constructFakeFunctionCall() {
123            StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall(");
124            for (Iterator<ValueParameterDescriptor> iterator = referencedFunction.getValueParameters().iterator(); iterator.hasNext(); ) {
125                ValueParameterDescriptor descriptor = iterator.next();
126                fakeFunctionCall.append("p").append(descriptor.getIndex());
127                if (iterator.hasNext()) {
128                    fakeFunctionCall.append(", ");
129                }
130            }
131            fakeFunctionCall.append(")");
132            return (JetCallExpression) JetPsiFactory(state.getProject()).createExpression(fakeFunctionCall.toString());
133        }
134    
135        private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
136            for (ValueParameterDescriptor parameter : callableDescriptor.getValueParameters()) {
137                ValueArgument fakeArgument = fakeArguments.get(parameter.getIndex());
138                Type type = state.getTypeMapper().mapType(parameter);
139                int localIndex = codegen.myFrameMap.getIndex(parameter);
140                codegen.tempVariables.put(fakeArgument.getArgumentExpression(), StackValue.local(localIndex, type));
141            }
142        }
143    
144        @NotNull
145        private ReceiverValue computeAndSaveReceiver(
146                @NotNull JvmMethodSignature signature,
147                @NotNull ExpressionCodegen codegen,
148                @Nullable ReceiverParameterDescriptor receiver
149        ) {
150            if (receiver == null) return NO_RECEIVER;
151    
152            JetExpression receiverExpression = JetPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
153            codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
154            return new ExpressionReceiver(receiverExpression, receiver.getType());
155        }
156    
157        @NotNull
158        private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
159            // 0 is this (the callable reference class), 1 is the invoke() method's first parameter
160            return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
161        }
162    }