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 }