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 }