Debugger, BE: Generate original line number after inlining if call is used in an if condition

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.
This commit is contained in:
Yan Zhulanow
2019-12-23 18:08:13 +09:00
parent f115bde682
commit cc2fe6b0c6
10 changed files with 76 additions and 12 deletions
@@ -47,7 +47,7 @@ interface BaseExpressionCodegen {
functionReferenceReceiver: StackValue?
)
fun markLineNumberAfterInlineIfNeeded()
fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean)
fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker)
@@ -1499,10 +1499,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> 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<StackValue, StackValue> 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();
}
}
@@ -118,28 +118,29 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
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<TypeParameterMarker>?,
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) {
@@ -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<KtIfExpression>(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)
@@ -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
@@ -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)
@@ -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 <T> Iterable<T>.myAny(predicate: (T) -> Boolean): Boolean {
for (element in this) {
if (predicate(element)) return true
}
return false
}
// 3 LINENUMBER 5
@@ -0,0 +1,16 @@
//FILE: test.kt
fun box() {
if (listOf(1, 2, 3).myAny { it > 2 } == true) {
println("foo")
}
}
public inline fun <T> Iterable<T>.myAny(predicate: (T) -> Boolean): Boolean {
for (element in this) {
if (predicate(element)) return true
}
return false
}
// 3 LINENUMBER 4
@@ -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");
@@ -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");