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