diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 7372ba0a497..e41a79987a7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2793,7 +2793,7 @@ public class ExpressionCodegen extends JetVisitor implem }); } - private static class CallableReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased { + public static class CallableReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased { private final ResolvedCall resolvedCall; private final FunctionDescriptor referencedFunction; 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 c0a5d02b5a4..5459014ac22 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -24,7 +24,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.builtins.InlineStrategy; import org.jetbrains.kotlin.builtins.InlineUtil; -import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.codegen.*; import org.jetbrains.kotlin.codegen.context.CodegenContext; import org.jetbrains.kotlin.codegen.context.FieldOwnerContext; @@ -63,6 +62,7 @@ import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isFunctionExpression; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isFunctionLiteral; +import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCallWithAssert; public class InlineCodegen extends CallGenerator { private final GenerationState state; @@ -299,7 +299,7 @@ public class InlineCodegen extends CallGenerator { } private SMAPAndMethodNode generateLambdaBody(LambdaInfo info) { - JetFunctionLiteral declaration = info.getFunctionLiteral(); + JetExpression declaration = info.getFunctionLiteralOrCallableReference(); FunctionDescriptor descriptor = info.getFunctionDescriptor(); MethodContext parentContext = codegen.getContext(); @@ -321,27 +321,37 @@ public class InlineCodegen extends CallGenerator { @NotNull MethodVisitor adapter, @NotNull FunctionDescriptor descriptor, @NotNull MethodContext context, - @NotNull JetDeclarationWithBody declaration, + @NotNull JetExpression expression, @NotNull JvmMethodSignature jvmMethodSignature, boolean isLambda ) { FakeMemberCodegen parentCodegen = - new FakeMemberCodegen(codegen.getParentCodegen(), declaration, + new FakeMemberCodegen(codegen.getParentCodegen(), expression, (FieldOwnerContext) context.getParentContext(), isLambda ? codegen.getParentCodegen().getClassName() : typeMapper.mapOwner(descriptor, false).getInternalName()); + FunctionGenerationStrategy strategy = + expression instanceof JetCallableReferenceExpression ? + new ExpressionCodegen.CallableReferenceGenerationStrategy( + state, + descriptor, + getResolvedCallWithAssert(((JetCallableReferenceExpression) expression).getCallableReference(), + codegen.getBindingContext() + )) : + new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (JetDeclarationWithBody) expression); + FunctionCodegen.generateMethodBody( adapter, descriptor, context, jvmMethodSignature, - new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), + strategy, // Wrapping for preventing marking actual parent codegen as containing reifier markers parentCodegen ); - return createSMAPWithDefaultMapping(declaration, parentCodegen.getOrCreateSourceMapper().getResultMappings()); + return createSMAPWithDefaultMapping(expression, parentCodegen.getOrCreateSourceMapper().getResultMappings()); } private static SMAP createSMAPWithDefaultMapping( - @NotNull JetDeclarationWithBody declaration, + @NotNull JetExpression declaration, @NotNull List mappings ) { PsiFile containingFile = declaration.getContainingFile(); @@ -509,22 +519,28 @@ public class InlineCodegen extends CallGenerator { } } - public static boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptor) { + /*lambda or callable reference*/ + public static boolean isInliningParameter(JetExpression expression, ValueParameterDescriptor valueParameterDescriptor) { //TODO deparenthisise typed JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression); - return deparenthesized instanceof JetFunctionLiteralExpression && - InlineUtil.isInlineLambdaParameter(valueParameterDescriptor); + return InlineUtil.isInlineLambdaParameter(valueParameterDescriptor) && + (deparenthesized instanceof JetFunctionLiteralExpression || + deparenthesized instanceof JetCallableReferenceExpression); } public void rememberClosure(JetExpression expression, Type type) { - JetFunctionLiteralExpression lambda = (JetFunctionLiteralExpression) JetPsiUtil.deparenthesize(expression); - assert lambda != null : "Couldn't find lambda in " + expression.getText(); + JetExpression lambda = JetPsiUtil.deparenthesize(expression); + assert lambda instanceof JetCallableReferenceExpression || lambda instanceof JetFunctionLiteralExpression : + "Couldn't find inline expression in " + expression.getText(); String labelNameIfPresent = null; PsiElement parent = lambda.getParent(); if (parent instanceof JetLabeledExpression) { labelNameIfPresent = ((JetLabeledExpression) parent).getLabelName(); } + if (lambda instanceof JetFunctionLiteralExpression) { + lambda = ((JetFunctionLiteralExpression) lambda).getFunctionLiteral(); + } LambdaInfo info = new LambdaInfo(lambda, typeMapper, labelNameIfPresent); ParameterInfo closureInfo = invocationParamBuilder.addNextParameter(type, true, null); @@ -572,10 +588,10 @@ public class InlineCodegen extends CallGenerator { @NotNull JetExpression argumentExpression, @NotNull Type parameterType ) { - //TODO deparenthisise - if (isInliningClosure(argumentExpression, valueParameterDescriptor)) { + if (isInliningParameter(argumentExpression, valueParameterDescriptor)) { rememberClosure(argumentExpression, parameterType); - } else { + } + else { StackValue value = codegen.gen(argumentExpression); putValueIfNeeded(valueParameterDescriptor, parameterType, value); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java index 553c5901daf..8e18ba9928c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.java @@ -26,8 +26,7 @@ import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; -import org.jetbrains.kotlin.psi.JetFunctionLiteral; -import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression; +import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.org.objectweb.asm.Type; @@ -41,7 +40,7 @@ import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*; public class LambdaInfo implements CapturedParamOwner, LabelOwner { - public final JetFunctionLiteralExpression expression; + public final JetExpression expression; private final JetTypeMapper typeMapper; @@ -60,12 +59,12 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner { private final Type closureClassType; - LambdaInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper, @Nullable String labelName) { + LambdaInfo(@NotNull JetExpression expression, @NotNull JetTypeMapper typeMapper, @Nullable String labelName) { this.expression = expression; this.typeMapper = typeMapper; this.labelName = labelName; BindingContext bindingContext = typeMapper.getBindingContext(); - functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression.getFunctionLiteral()); + functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression); assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText(); classDescriptor = anonymousClassForFunction(bindingContext, functionDescriptor); @@ -87,8 +86,8 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner { return functionDescriptor; } - public JetFunctionLiteral getFunctionLiteral() { - return expression.getFunctionLiteral(); + public JetExpression getFunctionLiteralOrCallableReference() { + return expression; } public ClassDescriptor getClassDescriptor() { diff --git a/compiler/testData/codegen/boxInline/callableReference/classLevel.1.kt b/compiler/testData/codegen/boxInline/callableReference/classLevel.1.kt new file mode 100644 index 00000000000..be79b739172 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/classLevel.1.kt @@ -0,0 +1,7 @@ +import test.* + +fun box() : String { + val call = call(A(11), A::calc) + return if (call == 11) "OK" else "fail" +} + diff --git a/compiler/testData/codegen/boxInline/callableReference/classLevel.2.kt b/compiler/testData/codegen/boxInline/callableReference/classLevel.2.kt new file mode 100644 index 00000000000..5738150e894 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/classLevel.2.kt @@ -0,0 +1,9 @@ +package test + +class A(val z: Int) { + fun calc() = z +} + +inline fun call(p: A, s: A.() -> Int): Int { + return p.s() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/callableReference/classLevel2.1.kt b/compiler/testData/codegen/boxInline/callableReference/classLevel2.1.kt new file mode 100644 index 00000000000..44db4ed136e --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/classLevel2.1.kt @@ -0,0 +1,7 @@ +import test.* + +fun box() : String { + val call = A(11).test() + return if (call == 11) "OK" else "fail" +} + diff --git a/compiler/testData/codegen/boxInline/callableReference/classLevel2.2.kt b/compiler/testData/codegen/boxInline/callableReference/classLevel2.2.kt new file mode 100644 index 00000000000..8da891496ae --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/classLevel2.2.kt @@ -0,0 +1,11 @@ +package test + +class A(val z: Int) { + fun calc() = z + + fun test() = call(A(z), ::calc) +} + +inline fun call(p: A, s: A.() -> Int): Int { + return p.s() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/callableReference/constructor.1.kt b/compiler/testData/codegen/boxInline/callableReference/constructor.1.kt new file mode 100644 index 00000000000..9ab7ad48ca9 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/constructor.1.kt @@ -0,0 +1,7 @@ +import test.* + +fun box() : String { + val call = call(11, ::A) + return if (call == 11) "OK" else "fail" +} + diff --git a/compiler/testData/codegen/boxInline/callableReference/constructor.2.kt b/compiler/testData/codegen/boxInline/callableReference/constructor.2.kt new file mode 100644 index 00000000000..3c9678c5f27 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/constructor.2.kt @@ -0,0 +1,9 @@ +package test + +class A(val z: Int) { + fun calc() = z +} + +inline fun call(p: Int, s: (Int) -> A): Int { + return s(p).z +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/callableReference/intrinsic.1.kt b/compiler/testData/codegen/boxInline/callableReference/intrinsic.1.kt new file mode 100644 index 00000000000..ec51f1baa1a --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/intrinsic.1.kt @@ -0,0 +1,5 @@ +import test.* + +fun box() : String { + return if (call("123", String::length) == 3) "OK" else "fail" +} diff --git a/compiler/testData/codegen/boxInline/callableReference/intrinsic.2.kt b/compiler/testData/codegen/boxInline/callableReference/intrinsic.2.kt new file mode 100644 index 00000000000..a4e418a8804 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/intrinsic.2.kt @@ -0,0 +1,5 @@ +package test + +inline fun call(p: String, s: String.() -> Int): Int { + return p.s() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/callableReference/topLevel.1.kt b/compiler/testData/codegen/boxInline/callableReference/topLevel.1.kt new file mode 100644 index 00000000000..322598f48ee --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/topLevel.1.kt @@ -0,0 +1,9 @@ +import test.* + +fun box() : String { + return if (call(10, ::calc) == 5) "OK" else "fail" +} + +fun calc(p: Int) : Int { + return p / 2 +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/callableReference/topLevel.2.kt b/compiler/testData/codegen/boxInline/callableReference/topLevel.2.kt new file mode 100644 index 00000000000..0632650c47c --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/topLevel.2.kt @@ -0,0 +1,5 @@ +package test + +inline fun call(p: Int, s: (Int) -> Int): Int { + return s(p) +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/callableReference/topLevelExtension.1.kt b/compiler/testData/codegen/boxInline/callableReference/topLevelExtension.1.kt new file mode 100644 index 00000000000..2d76235bb00 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/topLevelExtension.1.kt @@ -0,0 +1,9 @@ +import test.* + +fun box() : String { + return if (call(10, Int::calc) == 100) "OK" else "fail" +} + +fun Int.calc(p: Int) : Int { + return p * this +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/callableReference/topLevelExtension.2.kt b/compiler/testData/codegen/boxInline/callableReference/topLevelExtension.2.kt new file mode 100644 index 00000000000..c4db2261ce7 --- /dev/null +++ b/compiler/testData/codegen/boxInline/callableReference/topLevelExtension.2.kt @@ -0,0 +1,5 @@ +package test + +inline fun call(p: Int, s: Int.(Int) -> Int): Int { + return p.s(p) +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/callableReferenceInline.kt b/compiler/testData/codegen/bytecodeText/callableReferenceInline.kt new file mode 100644 index 00000000000..5e137ea2b38 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/callableReferenceInline.kt @@ -0,0 +1,13 @@ +fun box(): String { + return if (call(10, ::calc) == 5) "OK" else "fail" +} + +fun calc(p: Int): Int { + return p / 2 +} + +inline fun call(p: Int, s: (Int) -> Int): Int { + return s(p) +} + +// 0 NEW \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index c5fe0edfca6..6a4201ba2e0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -65,6 +65,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("callableReferenceInline.kt") + public void testCallableReferenceInline() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/callableReferenceInline.kt"); + doTest(fileName); + } + @TestMetadata("componentEvaluatesOnlyOnce.kt") public void testComponentEvaluatesOnlyOnce() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/componentEvaluatesOnlyOnce.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java index 4a894df1773..09802dade73 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java @@ -89,6 +89,51 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo } } + @TestMetadata("compiler/testData/codegen/boxInline/callableReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CallableReference extends AbstractBlackBoxInlineCodegenTest { + public void testAllFilesPresentInCallableReference() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/callableReference"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("classLevel.1.kt") + public void testClassLevel() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("classLevel2.1.kt") + public void testClassLevel2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel2.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("constructor.1.kt") + public void testConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/constructor.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("intrinsic.1.kt") + public void testIntrinsic() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/intrinsic.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("topLevel.1.kt") + public void testTopLevel() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevel.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("topLevelExtension.1.kt") + public void testTopLevelExtension() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevelExtension.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/capture") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java index eca6cd37e88..27eac3d9103 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -89,6 +89,51 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi } } + @TestMetadata("compiler/testData/codegen/boxInline/callableReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CallableReference extends AbstractCompileKotlinAgainstInlineKotlinTest { + public void testAllFilesPresentInCallableReference() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/callableReference"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("classLevel.1.kt") + public void testClassLevel() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("classLevel2.1.kt") + public void testClassLevel2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/classLevel2.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("constructor.1.kt") + public void testConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/constructor.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("intrinsic.1.kt") + public void testIntrinsic() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/intrinsic.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("topLevel.1.kt") + public void testTopLevel() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevel.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("topLevelExtension.1.kt") + public void testTopLevelExtension() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/topLevelExtension.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/capture") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)