From cc2fe6b0c602c41ca9deda9ed7fab22c278b1902 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Mon, 23 Dec 2019 18:08:13 +0900 Subject: [PATCH] Debugger, BE: Generate original line number after inlining if call is used in an if condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a part of an 'if' condition is an inline function call, we need to insert the original condition line after it. Otherwise, the debugger will think it is inside the inline function implementation. Obviously, this breaks stepping – instead of the 'if' body, we go on stepping through the inline function. This commit fixes 'KotlinSteppingTestGenerated.StepOver#testSoInlineLibFun' test. --- .../kotlin/codegen/BaseExpressionCodegen.kt | 2 +- .../kotlin/codegen/ExpressionCodegen.java | 6 ++---- .../kotlin/codegen/inline/InlineCodegen.kt | 9 +++++---- .../kotlin/codegen/inline/PsiInlineCodegen.kt | 12 +++++++++++- .../backend/jvm/codegen/ExpressionCodegen.kt | 2 +- .../backend/jvm/codegen/IrInlineCodegen.kt | 4 +++- .../bytecodeText/lineNumbers/inlineCondition.kt | 17 +++++++++++++++++ .../lineNumbers/inlineCondition2.kt | 16 ++++++++++++++++ .../codegen/BytecodeTextTestGenerated.java | 10 ++++++++++ .../codegen/ir/IrBytecodeTextTestGenerated.java | 10 ++++++++++ 10 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition.kt create mode 100644 compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition2.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/BaseExpressionCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/BaseExpressionCodegen.kt index e5acd00721c..7d3f01ec4b9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/BaseExpressionCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/BaseExpressionCodegen.kt @@ -47,7 +47,7 @@ interface BaseExpressionCodegen { functionReferenceReceiver: StackValue? ) - fun markLineNumberAfterInlineIfNeeded() + fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean) fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 5a8295b8a83..aa0651709ee 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1499,10 +1499,9 @@ public class ExpressionCodegen extends KtVisitor impleme v.visitLineNumber(lineNumber, label); } - //we should generate additional linenumber info after inline call only if it used as argument @Override - public void markLineNumberAfterInlineIfNeeded() { - if (!shouldMarkLineNumbers) { + public void markLineNumberAfterInlineIfNeeded(boolean registerLineNumberAfterwards) { + if (!shouldMarkLineNumbers || registerLineNumberAfterwards) { //if it used as general argument if (myLastLineNumber > -1) { Label label = new Label(); @@ -1510,7 +1509,6 @@ public class ExpressionCodegen extends KtVisitor impleme v.visitLineNumber(myLastLineNumber, label); } } else { - //if it used as argument of infix call (in this case lineNumber for simple inlineCall also would be reset) resetLastLineNumber(); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index b3f25b8d7b9..ea369419f42 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -118,28 +118,29 @@ abstract class InlineCodegen( AsmUtil.genThrow(codegen.v, "java/lang/UnsupportedOperationException", message) } - protected fun endCall(result: InlineResult) { + protected fun endCall(result: InlineResult, registerLineNumberAfterwards: Boolean) { leaveTemps() codegen.propagateChildReifiedTypeParametersUsages(result.reifiedTypeParametersUsages) state.factory.removeClasses(result.calcClassesToRemove()) - codegen.markLineNumberAfterInlineIfNeeded() + codegen.markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards) } fun performInline( typeArguments: List?, inlineDefaultLambdas: Boolean, mapDefaultSignature: Boolean, - typeSystem: TypeSystemCommonBackendContext + typeSystem: TypeSystemCommonBackendContext, + registerLineNumberAfterwards: Boolean ) { var nodeAndSmap: SMAPAndMethodNode? = null try { nodeAndSmap = createInlineMethodNode( functionDescriptor, methodOwner, jvmSignature, mapDefaultSignature, typeArguments, typeSystem, state, sourceCompiler ) - endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas)) + endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas), registerLineNumberAfterwards) } catch (e: CompilationException) { throw e } catch (e: InlineException) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt index 2ab8b66df9a..df0f924ccb5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt @@ -14,7 +14,10 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.inline.InlineUtil @@ -66,12 +69,19 @@ class PsiInlineCodegen( return } try { - performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem) + val registerLineNumber = registerLineNumberAfterwards(resolvedCall) + performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem, registerLineNumber) } finally { state.globalInlineContext.exitFromInliningOf(inlineCall) } } + private fun registerLineNumberAfterwards(resolvedCall: ResolvedCall<*>?): Boolean { + val callElement = resolvedCall?.call?.callElement ?: return false + val parentIfCondition = callElement.getParentOfType(true)?.condition ?: return false + return parentIfCondition.isAncestor(callElement, false) + } + override fun processAndPutHiddenParameters(justProcess: Boolean) { if (getMethodAsmFlags(functionDescriptor, sourceCompiler.contextKind, state) and Opcodes.ACC_STATIC == 0) { invocationParamBuilder.addNextParameter(methodOwner, false, actualDispatchReceiver) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 74e689dd571..0f7acd23ac2 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -1119,7 +1119,7 @@ class ExpressionCodegen( //TODO } - override fun markLineNumberAfterInlineIfNeeded() { + override fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean) { // Inline function has its own line number which is in a separate instance of codegen, // therefore we need to reset lastLineNumber to force a line number generation after visiting inline function. lastLineNumber = -1 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt index c2cf9be8fc0..930f8d5510e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.getArgumentsWithIr import org.jetbrains.kotlin.ir.util.isSuspend +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature @@ -187,7 +188,8 @@ class IrInlineCodegen( expression.symbol.owner.typeParameters.map { it.symbol }, function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, false, - codegen.typeMapper.typeSystem + codegen.typeMapper.typeSystem, + false ) } finally { state.globalInlineContext.exitFromInliningOf(inlineCall) diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition.kt new file mode 100644 index 00000000000..4abd4ae5805 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition.kt @@ -0,0 +1,17 @@ +//FILE: test.kt +// IGNORE_BACKEND: JVM_IR + +fun box() { + if (listOf(1, 2, 3).myAny { it > 2 }) { + println("foo") + } +} + +public inline fun Iterable.myAny(predicate: (T) -> Boolean): Boolean { + for (element in this) { + if (predicate(element)) return true + } + return false +} + +// 3 LINENUMBER 5 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition2.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition2.kt new file mode 100644 index 00000000000..902a94d2767 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition2.kt @@ -0,0 +1,16 @@ +//FILE: test.kt + +fun box() { + if (listOf(1, 2, 3).myAny { it > 2 } == true) { + println("foo") + } +} + +public inline fun Iterable.myAny(predicate: (T) -> Boolean): Boolean { + for (element in this) { + if (predicate(element)) return true + } + return false +} + +// 3 LINENUMBER 4 \ 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 bf67a54a256..c17d56f4788 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -3253,6 +3253,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifTrueElse.kt"); } + @TestMetadata("inlineCondition.kt") + public void testInlineCondition() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition.kt"); + } + + @TestMetadata("inlineCondition2.kt") + public void testInlineCondition2() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition2.kt"); + } + @TestMetadata("singleThen.kt") public void testSingleThen() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index 20129099942..1fefea29ab3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -3298,6 +3298,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/ifTrueElse.kt"); } + @TestMetadata("inlineCondition.kt") + public void testInlineCondition() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition.kt"); + } + + @TestMetadata("inlineCondition2.kt") + public void testInlineCondition2() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/inlineCondition2.kt"); + } + @TestMetadata("singleThen.kt") public void testSingleThen() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");