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 }