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 }