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 }