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 org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.codegen.state.GenerationState;
022 import org.jetbrains.kotlin.descriptors.*;
023 import org.jetbrains.kotlin.psi.Call;
024 import org.jetbrains.kotlin.psi.JetCallExpression;
025 import org.jetbrains.kotlin.psi.JetExpression;
026 import org.jetbrains.kotlin.psi.ValueArgument;
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.ArrayList;
039 import java.util.Iterator;
040 import java.util.List;
041
042 import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
043 import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
044
045 public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
046 private final ResolvedCall<?> resolvedCall;
047 private final FunctionDescriptor referencedFunction;
048
049 public FunctionReferenceGenerationStrategy(
050 @NotNull GenerationState state,
051 @NotNull FunctionDescriptor functionDescriptor,
052 @NotNull ResolvedCall<?> resolvedCall
053 ) {
054 super(state, functionDescriptor);
055 this.resolvedCall = resolvedCall;
056 this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
057 }
058
059 @Override
060 public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
061 /*
062 Here we need to put the arguments from our locals to the stack and invoke the referenced method. Since invocation
063 of methods is highly dependent on expressions, we create a fake call expression. Then we create a new instance of
064 ExpressionCodegen and, in order for it to generate code correctly, we save to its 'tempVariables' field every
065 argument of our fake expression, pointing it to the corresponding index in our locals. This way generation of
066 every argument boils down to calling LOAD with the corresponding index
067 */
068
069 JetCallExpression fakeExpression = constructFakeFunctionCall();
070 final List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
071
072 final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
073 final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
074 computeAndSaveArguments(fakeArguments, codegen);
075
076 ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
077 @NotNull
078 @Override
079 public ReceiverValue getExtensionReceiver() {
080 return extensionReceiver;
081 }
082
083 @NotNull
084 @Override
085 public ReceiverValue getDispatchReceiver() {
086 return dispatchReceiver;
087 }
088
089 @NotNull
090 @Override
091 public List<ResolvedValueArgument> getValueArgumentsByIndex() {
092 List<ResolvedValueArgument> result = new ArrayList<ResolvedValueArgument>(fakeArguments.size());
093 for (ValueArgument argument : fakeArguments) {
094 result.add(new ExpressionValueArgument(argument));
095 }
096 return result;
097 }
098 };
099
100 StackValue result;
101 Type returnType = codegen.getReturnType();
102 if (referencedFunction instanceof ConstructorDescriptor) {
103 if (returnType.getSort() == Type.ARRAY) {
104 //noinspection ConstantConditions
105 result = codegen.generateNewArray(fakeExpression, referencedFunction.getReturnType());
106 }
107 else {
108 result = codegen.generateConstructorCall(fakeResolvedCall, returnType);
109 }
110 }
111 else {
112 Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
113 result = codegen.invokeFunction(call, fakeResolvedCall, StackValue.none());
114 }
115
116 InstructionAdapter v = codegen.v;
117 result.put(returnType, v);
118 v.areturn(returnType);
119 }
120
121 @NotNull
122 private JetCallExpression constructFakeFunctionCall() {
123 StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall(");
124 for (Iterator<ValueParameterDescriptor> iterator = referencedFunction.getValueParameters().iterator(); iterator.hasNext(); ) {
125 ValueParameterDescriptor descriptor = iterator.next();
126 fakeFunctionCall.append("p").append(descriptor.getIndex());
127 if (iterator.hasNext()) {
128 fakeFunctionCall.append(", ");
129 }
130 }
131 fakeFunctionCall.append(")");
132 return (JetCallExpression) JetPsiFactory(state.getProject()).createExpression(fakeFunctionCall.toString());
133 }
134
135 private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
136 for (ValueParameterDescriptor parameter : callableDescriptor.getValueParameters()) {
137 ValueArgument fakeArgument = fakeArguments.get(parameter.getIndex());
138 Type type = state.getTypeMapper().mapType(parameter);
139 int localIndex = codegen.myFrameMap.getIndex(parameter);
140 codegen.tempVariables.put(fakeArgument.getArgumentExpression(), StackValue.local(localIndex, type));
141 }
142 }
143
144 @NotNull
145 private ReceiverValue computeAndSaveReceiver(
146 @NotNull JvmMethodSignature signature,
147 @NotNull ExpressionCodegen codegen,
148 @Nullable ReceiverParameterDescriptor receiver
149 ) {
150 if (receiver == null) return NO_RECEIVER;
151
152 JetExpression receiverExpression = JetPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
153 codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
154 return new ExpressionReceiver(receiverExpression, receiver.getType());
155 }
156
157 @NotNull
158 private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
159 // 0 is this (the callable reference class), 1 is the invoke() method's first parameter
160 return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
161 }
162 }