Extract FunctionReferenceGenerationStrategy to top level

This commit is contained in:
Alexander Udalov
2015-05-20 21:16:26 +03:00
parent d0bb06dbb0
commit 9aeae7ddc6
3 changed files with 168 additions and 132 deletions
@@ -103,7 +103,6 @@ import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.TraitImpl;
import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
import static org.jetbrains.kotlin.serialization.deserialization.DeserializationPackage.findClassAcrossModuleDependencies;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
@@ -2702,7 +2701,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
ResolvedCall<?> resolvedCall = getResolvedCallWithAssert(expression.getCallableReference(), bindingContext);
FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
if (functionDescriptor != null) {
CallableReferenceGenerationStrategy strategy = new CallableReferenceGenerationStrategy(state, functionDescriptor, resolvedCall);
FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall);
return genClosure(expression, functionDescriptor, strategy, null, KotlinSyntheticClass.Kind.CALLABLE_REFERENCE_WRAPPER);
}
@@ -2817,125 +2816,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
});
}
public static class CallableReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
private final ResolvedCall<?> resolvedCall;
private final FunctionDescriptor referencedFunction;
public CallableReferenceGenerationStrategy(
@NotNull GenerationState state,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull ResolvedCall<?> resolvedCall
) {
super(state, functionDescriptor);
this.resolvedCall = resolvedCall;
this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
}
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
/*
Here we need to put the arguments from our locals to the stack and invoke the referenced method. Since invocation
of methods is highly dependent on expressions, we create a fake call expression. Then we create a new instance of
ExpressionCodegen and, in order for it to generate code correctly, we save to its 'tempVariables' field every
argument of our fake expression, pointing it to the corresponding index in our locals. This way generation of
every argument boils down to calling LOAD with the corresponding index
*/
JetCallExpression fakeExpression = constructFakeFunctionCall();
final List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
computeAndSaveArguments(fakeArguments, codegen);
ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
@NotNull
@Override
public ReceiverValue getExtensionReceiver() {
return extensionReceiver;
}
@NotNull
@Override
public ReceiverValue getDispatchReceiver() {
return dispatchReceiver;
}
@NotNull
@Override
public List<ResolvedValueArgument> getValueArgumentsByIndex() {
List<ResolvedValueArgument> result = new ArrayList<ResolvedValueArgument>(fakeArguments.size());
for (ValueArgument argument : fakeArguments) {
result.add(new ExpressionValueArgument(argument));
}
return result;
}
};
StackValue result;
Type returnType = codegen.returnType;
if (referencedFunction instanceof ConstructorDescriptor) {
if (returnType.getSort() == Type.ARRAY) {
//noinspection ConstantConditions
result = codegen.generateNewArray(fakeExpression, referencedFunction.getReturnType());
}
else {
result = codegen.generateConstructorCall(fakeResolvedCall, returnType);
}
}
else {
Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
result = codegen.invokeFunction(call, fakeResolvedCall, StackValue.none());
}
InstructionAdapter v = codegen.v;
result.put(returnType, v);
v.areturn(returnType);
}
@NotNull
private JetCallExpression constructFakeFunctionCall() {
StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall(");
for (Iterator<ValueParameterDescriptor> iterator = referencedFunction.getValueParameters().iterator(); iterator.hasNext(); ) {
ValueParameterDescriptor descriptor = iterator.next();
fakeFunctionCall.append("p").append(descriptor.getIndex());
if (iterator.hasNext()) {
fakeFunctionCall.append(", ");
}
}
fakeFunctionCall.append(")");
return (JetCallExpression) JetPsiFactory(state.getProject()).createExpression(fakeFunctionCall.toString());
}
private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
for (ValueParameterDescriptor parameter : callableDescriptor.getValueParameters()) {
ValueArgument fakeArgument = fakeArguments.get(parameter.getIndex());
Type type = state.getTypeMapper().mapType(parameter);
int localIndex = codegen.myFrameMap.getIndex(parameter);
codegen.tempVariables.put(fakeArgument.getArgumentExpression(), StackValue.local(localIndex, type));
}
}
@NotNull
private ReceiverValue computeAndSaveReceiver(
@NotNull JvmMethodSignature signature,
@NotNull ExpressionCodegen codegen,
@Nullable ReceiverParameterDescriptor receiver
) {
if (receiver == null) return NO_RECEIVER;
JetExpression receiverExpression = JetPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
return new ExpressionReceiver(receiverExpression, receiver.getType());
}
@NotNull
private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
// 0 is this (the callable reference class), 1 is the invoke() method's first parameter
return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
}
}
@Override
public StackValue visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, StackValue receiver) {
StackValue receiverValue = StackValue.none(); //gen(expression.getReceiverExpression())
@@ -3479,14 +3359,15 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
private StackValue generateNewCall(@NotNull JetCallExpression expression, @NotNull ResolvedCall<?> resolvedCall) {
Type type = expressionType(expression);
if (type.getSort() == Type.ARRAY) {
return generateNewArray(expression);
//noinspection ConstantConditions
return generateNewArray(expression, bindingContext.getType(expression));
}
return generateConstructorCall(resolvedCall, type);
}
@NotNull
private StackValue generateConstructorCall(@NotNull final ResolvedCall<?> resolvedCall, @NotNull final Type objectType) {
public StackValue generateConstructorCall(@NotNull final ResolvedCall<?> resolvedCall, @NotNull final Type objectType) {
return StackValue.functionCall(objectType, new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter v) {
@@ -3514,14 +3395,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
});
}
public StackValue generateNewArray(@NotNull JetCallExpression expression) {
JetType arrayType = bindingContext.getType(expression);
assert arrayType != null : "Array instantiation isn't type checked: " + expression.getText();
return generateNewArray(expression, arrayType);
}
private StackValue generateNewArray(@NotNull JetCallExpression expression, @NotNull final JetType arrayType) {
public StackValue generateNewArray(@NotNull JetCallExpression expression, @NotNull final JetType arrayType) {
assert expression.getValueArguments().size() == 1 : "Size argument expected";
final JetExpression sizeExpression = expression.getValueArguments().get(0).getArgumentExpression();
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.psi.Call;
import org.jetbrains.kotlin.psi.JetCallExpression;
import org.jetbrains.kotlin.psi.JetExpression;
import org.jetbrains.kotlin.psi.ValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
import static org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
private final ResolvedCall<?> resolvedCall;
private final FunctionDescriptor referencedFunction;
public FunctionReferenceGenerationStrategy(
@NotNull GenerationState state,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull ResolvedCall<?> resolvedCall
) {
super(state, functionDescriptor);
this.resolvedCall = resolvedCall;
this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
}
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
/*
Here we need to put the arguments from our locals to the stack and invoke the referenced method. Since invocation
of methods is highly dependent on expressions, we create a fake call expression. Then we create a new instance of
ExpressionCodegen and, in order for it to generate code correctly, we save to its 'tempVariables' field every
argument of our fake expression, pointing it to the corresponding index in our locals. This way generation of
every argument boils down to calling LOAD with the corresponding index
*/
JetCallExpression fakeExpression = constructFakeFunctionCall();
final List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
computeAndSaveArguments(fakeArguments, codegen);
ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
@NotNull
@Override
public ReceiverValue getExtensionReceiver() {
return extensionReceiver;
}
@NotNull
@Override
public ReceiverValue getDispatchReceiver() {
return dispatchReceiver;
}
@NotNull
@Override
public List<ResolvedValueArgument> getValueArgumentsByIndex() {
List<ResolvedValueArgument> result = new ArrayList<ResolvedValueArgument>(fakeArguments.size());
for (ValueArgument argument : fakeArguments) {
result.add(new ExpressionValueArgument(argument));
}
return result;
}
};
StackValue result;
Type returnType = codegen.getReturnType();
if (referencedFunction instanceof ConstructorDescriptor) {
if (returnType.getSort() == Type.ARRAY) {
//noinspection ConstantConditions
result = codegen.generateNewArray(fakeExpression, referencedFunction.getReturnType());
}
else {
result = codegen.generateConstructorCall(fakeResolvedCall, returnType);
}
}
else {
Call call = CallMaker.makeCall(fakeExpression, NO_RECEIVER, null, fakeExpression, fakeArguments);
result = codegen.invokeFunction(call, fakeResolvedCall, StackValue.none());
}
InstructionAdapter v = codegen.v;
result.put(returnType, v);
v.areturn(returnType);
}
@NotNull
private JetCallExpression constructFakeFunctionCall() {
StringBuilder fakeFunctionCall = new StringBuilder("callableReferenceFakeCall(");
for (Iterator<ValueParameterDescriptor> iterator = referencedFunction.getValueParameters().iterator(); iterator.hasNext(); ) {
ValueParameterDescriptor descriptor = iterator.next();
fakeFunctionCall.append("p").append(descriptor.getIndex());
if (iterator.hasNext()) {
fakeFunctionCall.append(", ");
}
}
fakeFunctionCall.append(")");
return (JetCallExpression) JetPsiFactory(state.getProject()).createExpression(fakeFunctionCall.toString());
}
private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
for (ValueParameterDescriptor parameter : callableDescriptor.getValueParameters()) {
ValueArgument fakeArgument = fakeArguments.get(parameter.getIndex());
Type type = state.getTypeMapper().mapType(parameter);
int localIndex = codegen.myFrameMap.getIndex(parameter);
codegen.tempVariables.put(fakeArgument.getArgumentExpression(), StackValue.local(localIndex, type));
}
}
@NotNull
private ReceiverValue computeAndSaveReceiver(
@NotNull JvmMethodSignature signature,
@NotNull ExpressionCodegen codegen,
@Nullable ReceiverParameterDescriptor receiver
) {
if (receiver == null) return NO_RECEIVER;
JetExpression receiverExpression = JetPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
return new ExpressionReceiver(receiverExpression, receiver.getType());
}
@NotNull
private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
// 0 is this (the callable reference class), 1 is the invoke() method's first parameter
return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
}
}
@@ -336,7 +336,7 @@ public class InlineCodegen extends CallGenerator {
FunctionGenerationStrategy strategy =
expression instanceof JetCallableReferenceExpression ?
new ExpressionCodegen.CallableReferenceGenerationStrategy(
new FunctionReferenceGenerationStrategy(
state,
descriptor,
getResolvedCallWithAssert(((JetCallableReferenceExpression) expression).getCallableReference(),