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 }