From 938fd1a57e4d3a603cef61a4f56f76ccaba73414 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 22 Nov 2017 18:28:21 +0100 Subject: [PATCH] Use ResolvedCall for callable reference in KCallableNameProperty intrinsic Instead of manually inspecting the DOUBLE_COLON_LHS slice, which is a bit more error-prone. Note that new tests were passing before this change --- .../intrinsics/KCallableNameProperty.kt | 19 +++++++------ .../bound/kCallableNameIntrinsic.kt | 10 ++++++- .../inline.kt} | 0 .../nameIntrinsicWithImplicitThis.kt | 9 +++++++ .../codegen/BytecodeTextTestGenerated.java | 27 ++++++++++++++----- 5 files changed, 48 insertions(+), 17 deletions(-) rename compiler/testData/codegen/bytecodeText/{callableReferenceInline.kt => callableReference/inline.kt} (100%) create mode 100644 compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KCallableNameProperty.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KCallableNameProperty.kt index 65e24973a95..deaec63e447 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KCallableNameProperty.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KCallableNameProperty.kt @@ -17,14 +17,14 @@ package org.jetbrains.kotlin.codegen.intrinsics import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.psi.KtCallableReferenceExpression -import org.jetbrains.kotlin.resolve.BindingContext.DOUBLE_COLON_LHS import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.types.expressions.DoubleColonLHS +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type.VOID_TYPE @@ -33,18 +33,17 @@ class KCallableNameProperty : IntrinsicPropertyGetter() { val expressionReceiver = resolvedCall!!.dispatchReceiver as? ExpressionReceiver ?: return null val expression = expressionReceiver.expression as? KtCallableReferenceExpression ?: return null - val receiverExpression = expression.receiverExpression - val lhs = receiverExpression?.let { codegen.bindingContext.get(DOUBLE_COLON_LHS, it) } - - val callableReference = expression.callableReference - val descriptor = callableReference.getResolvedCall(codegen.bindingContext)?.resultingDescriptor ?: return null + val referenceResolvedCall = expression.callableReference.getResolvedCall(codegen.bindingContext) ?: return null + val callableReferenceReceiver = JvmCodegenUtil.getBoundCallableReferenceReceiver(referenceResolvedCall) return StackValue.operation(returnType) { iv -> // Generate the left-hand side of a bound callable reference expression - if (lhs is DoubleColonLHS.Expression) { - codegen.gen(receiverExpression, VOID_TYPE) + if (callableReferenceReceiver != null && callableReferenceReceiver !is ImplicitClassReceiver) { + val stackValue = codegen.generateReceiverValue(callableReferenceReceiver, false) + StackValue.coercion(stackValue, codegen.asmType(callableReferenceReceiver.type)).put(VOID_TYPE, iv) } - iv.aconst(descriptor.name.asString()) + + iv.aconst(referenceResolvedCall.resultingDescriptor.name.asString()) StackValue.coerce(JAVA_STRING_TYPE, returnType, iv) } } diff --git a/compiler/testData/codegen/box/callableReference/bound/kCallableNameIntrinsic.kt b/compiler/testData/codegen/box/callableReference/bound/kCallableNameIntrinsic.kt index f03245fd2f5..0a9f2d370bf 100644 --- a/compiler/testData/codegen/box/callableReference/bound/kCallableNameIntrinsic.kt +++ b/compiler/testData/codegen/box/callableReference/bound/kCallableNameIntrinsic.kt @@ -2,7 +2,15 @@ fun box(): String { var state = 0 val name = (state++)::toString.name if (name != "toString") return "Fail 1: $name" - if (state != 1) return "Fail 2: $state" + + val name2 = with(state++) { + ::toString.name + ::toString.name + ::toString.name + } + if (name2 != "toString") return "Fail 2: $name2" + + if (state != 2) return "Fail 3: $state" return "OK" } diff --git a/compiler/testData/codegen/bytecodeText/callableReferenceInline.kt b/compiler/testData/codegen/bytecodeText/callableReference/inline.kt similarity index 100% rename from compiler/testData/codegen/bytecodeText/callableReferenceInline.kt rename to compiler/testData/codegen/bytecodeText/callableReference/inline.kt diff --git a/compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt b/compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt new file mode 100644 index 00000000000..668c5602362 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt @@ -0,0 +1,9 @@ +class Foo { + fun bar(): String { + return ::bar.name + } +} + +// We can avoid loading (and then immediately popping) implicit "this" when generating intrinsified bytecode for KCallable.name +// because this can't have any side effects +// 0 POP diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 4403fad3f29..68a6af8b76f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -96,12 +96,6 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } - @TestMetadata("callableReferenceInline.kt") - public void testCallableReferenceInline() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/callableReferenceInline.kt"); - doTest(fileName); - } - @TestMetadata("charConstant.kt") public void testCharConstant() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/charConstant.kt"); @@ -690,6 +684,27 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/callableReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CallableReference extends AbstractBytecodeTextTest { + public void testAllFilesPresentInCallableReference() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/callableReference"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("inline.kt") + public void testInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/callableReference/inline.kt"); + doTest(fileName); + } + + @TestMetadata("nameIntrinsicWithImplicitThis.kt") + public void testNameIntrinsicWithImplicitThis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)