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