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.backend.common.CodegenUtil;
023    import org.jetbrains.kotlin.codegen.state.GenerationState;
024    import org.jetbrains.kotlin.descriptors.*;
025    import org.jetbrains.kotlin.psi.*;
026    import org.jetbrains.kotlin.resolve.BindingContext;
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.*;
039    
040    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
041    
042    public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased {
043        private final ResolvedCall<?> resolvedCall;
044        private final FunctionDescriptor referencedFunction;
045        private final FunctionDescriptor functionDescriptor;
046        private final Type receiverType; // non-null for bound references
047        private final StackValue receiverValue;
048        private final boolean isInliningStrategy;
049    
050        public FunctionReferenceGenerationStrategy(
051                @NotNull GenerationState state,
052                @NotNull FunctionDescriptor functionDescriptor,
053                @NotNull ResolvedCall<?> resolvedCall,
054                @Nullable Type receiverType,
055                @Nullable StackValue receiverValue,
056                boolean isInliningStrategy
057        ) {
058            super(state);
059            this.resolvedCall = resolvedCall;
060            this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
061            this.functionDescriptor = functionDescriptor;
062            this.receiverType = receiverType;
063            this.receiverValue = receiverValue;
064            this.isInliningStrategy = isInliningStrategy;
065            assert receiverType != null || receiverValue == null
066                    : "A receiver value is provided for unbound function reference. Either this is a bound reference and you forgot " +
067                      "to pass receiverType, or you accidentally passed some receiverValue for a reference without receiver";
068        }
069    
070        @Override
071        public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
072            /*
073             Here we need to put the arguments from our locals to the stack and invoke the referenced method. Since invocation
074             of methods is highly dependent on expressions, we create a fake call expression. Then we create a new instance of
075             ExpressionCodegen and, in order for it to generate code correctly, we save to its 'tempVariables' field every
076             argument of our fake expression, pointing it to the corresponding index in our locals. This way generation of
077             every argument boils down to calling LOAD with the corresponding index
078             */
079    
080            KtCallExpression fakeExpression = CodegenUtil.constructFakeFunctionCall(state.getProject(), referencedFunction);
081            final List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
082    
083            final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
084            final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
085            computeAndSaveArguments(fakeArguments, codegen);
086    
087            ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
088    
089                private final Map<ValueParameterDescriptor, ResolvedValueArgument> argumentMap;
090                {
091                    argumentMap = new LinkedHashMap<ValueParameterDescriptor, ResolvedValueArgument>(fakeArguments.size());
092                    int index = 0;
093                    List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
094                    for (ValueArgument argument : fakeArguments) {
095                        argumentMap.put(parameters.get(index), new ExpressionValueArgument(argument));
096                        index++;
097                    }
098                }
099    
100                @Nullable
101                @Override
102                public ReceiverValue getExtensionReceiver() {
103                    return extensionReceiver;
104                }
105    
106                @Nullable
107                @Override
108                public ReceiverValue getDispatchReceiver() {
109                    return dispatchReceiver;
110                }
111    
112                @NotNull
113                @Override
114                public List<ResolvedValueArgument> getValueArgumentsByIndex() {
115                    return new ArrayList<ResolvedValueArgument>(argumentMap.values());
116                }
117    
118                @NotNull
119                @Override
120                public Map<ValueParameterDescriptor, ResolvedValueArgument> getValueArguments() {
121                    return argumentMap;
122                }
123            };
124    
125            StackValue result;
126            Type returnType = codegen.getReturnType();
127            if (referencedFunction instanceof ConstructorDescriptor) {
128                if (returnType.getSort() == Type.ARRAY) {
129                    //noinspection ConstantConditions
130                    result = codegen.generateNewArray(fakeExpression, referencedFunction.getReturnType(), fakeResolvedCall);
131                }
132                else {
133                    result = codegen.generateConstructorCall(fakeResolvedCall, returnType);
134                }
135            }
136            else {
137                Call call = CallMaker.makeCall(fakeExpression, null, null, fakeExpression, fakeArguments);
138                result = codegen.invokeFunction(call, fakeResolvedCall, StackValue.none());
139            }
140    
141            InstructionAdapter v = codegen.v;
142            result.put(returnType, v);
143            v.areturn(returnType);
144        }
145    
146        private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
147            int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) +
148                            (referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0) -
149                            (receiverType != null ? 1 : 0);
150    
151            if (receivers < 0 &&  referencedFunction instanceof ConstructorDescriptor && isObject(referencedFunction.getContainingDeclaration().getContainingDeclaration())) {
152                //reference to object nested class
153                //TODO: seems problem should be fixed on frontend side (note that object instance are captured by generated class)
154                receivers = 0;
155            }
156    
157            List<ValueParameterDescriptor> parameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers);
158            for (int i = 0; i < parameters.size(); i++) {
159                ValueParameterDescriptor parameter = parameters.get(i);
160                ValueArgument fakeArgument = fakeArguments.get(i);
161    
162                Type type = state.getTypeMapper().mapType(parameter);
163                int localIndex = codegen.myFrameMap.getIndex(parameter);
164                codegen.tempVariables.put(fakeArgument.getArgumentExpression(), StackValue.local(localIndex, type));
165            }
166        }
167    
168        @Nullable
169        private ReceiverValue computeAndSaveReceiver(
170                @NotNull JvmMethodSignature signature,
171                @NotNull ExpressionCodegen codegen,
172                @Nullable ReceiverParameterDescriptor receiver
173        ) {
174            if (receiver == null) return null;
175    
176            KtExpression receiverExpression = KtPsiFactoryKt.KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
177            codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature, codegen));
178            return ExpressionReceiver.Companion.create(receiverExpression, receiver.getType(), BindingContext.EMPTY);
179        }
180    
181        @NotNull
182        private StackValue receiverParameterStackValue(@NotNull JvmMethodSignature signature, @NotNull ExpressionCodegen codegen) {
183            if (receiverValue != null) return receiverValue;
184    
185            if (receiverType != null) {
186                ClassDescriptor classDescriptor = (ClassDescriptor) codegen.getContext().getParentContext().getContextDescriptor();
187                Type asmType = codegen.getState().getTypeMapper().mapClass(classDescriptor);
188                return CallableReferenceUtilKt.capturedBoundReferenceReceiver(asmType, receiverType, isInliningStrategy);
189            }
190    
191            // 0 is this (the callable reference class), 1 is the invoke() method's first parameter
192            return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
193        }
194    }