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 }