diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 20b5a5a7494..057e661efd1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -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 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 implem }); } - public static class CallableReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased { - 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 fakeArguments = fakeExpression.getValueArguments(); - - final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter()); - final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter()); - computeAndSaveArguments(fakeArguments, codegen); - - ResolvedCall fakeResolvedCall = new DelegatingResolvedCall(resolvedCall) { - @NotNull - @Override - public ReceiverValue getExtensionReceiver() { - return extensionReceiver; - } - - @NotNull - @Override - public ReceiverValue getDispatchReceiver() { - return dispatchReceiver; - } - - @NotNull - @Override - public List getValueArgumentsByIndex() { - List result = new ArrayList(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 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 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 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() { @Override public Unit invoke(InstructionAdapter v) { @@ -3514,14 +3395,7 @@ public class ExpressionCodegen extends JetVisitor 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(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java new file mode 100644 index 00000000000..92a9f33c3a6 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java @@ -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 { + 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 fakeArguments = fakeExpression.getValueArguments(); + + final ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter()); + final ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter()); + computeAndSaveArguments(fakeArguments, codegen); + + ResolvedCall fakeResolvedCall = new DelegatingResolvedCall(resolvedCall) { + @NotNull + @Override + public ReceiverValue getExtensionReceiver() { + return extensionReceiver; + } + + @NotNull + @Override + public ReceiverValue getDispatchReceiver() { + return dispatchReceiver; + } + + @NotNull + @Override + public List getValueArgumentsByIndex() { + List result = new ArrayList(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 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 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]); + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index bdf85b4ae43..b96e2d8a3e7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -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(),