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