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? functionReferenceReceiver: StackValue?
) )
fun markLineNumberAfterInlineIfNeeded() fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean)
fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker) fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker)
@@ -1499,10 +1499,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
v.visitLineNumber(lineNumber, label); v.visitLineNumber(lineNumber, label);
} }
//we should generate additional linenumber info after inline call only if it used as argument
@Override @Override
public void markLineNumberAfterInlineIfNeeded() { public void markLineNumberAfterInlineIfNeeded(boolean registerLineNumberAfterwards) {
if (!shouldMarkLineNumbers) { if (!shouldMarkLineNumbers || registerLineNumberAfterwards) {
//if it used as general argument //if it used as general argument
if (myLastLineNumber > -1) { if (myLastLineNumber > -1) {
Label label = new Label(); Label label = new Label();
@@ -1510,7 +1509,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
v.visitLineNumber(myLastLineNumber, label); v.visitLineNumber(myLastLineNumber, label);
} }
} else { } else {
//if it used as argument of infix call (in this case lineNumber for simple inlineCall also would be reset)
resetLastLineNumber(); resetLastLineNumber();
} }
} }
@@ -118,28 +118,29 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
AsmUtil.genThrow(codegen.v, "java/lang/UnsupportedOperationException", message) AsmUtil.genThrow(codegen.v, "java/lang/UnsupportedOperationException", message)
} }
protected fun endCall(result: InlineResult) { protected fun endCall(result: InlineResult, registerLineNumberAfterwards: Boolean) {
leaveTemps() leaveTemps()
codegen.propagateChildReifiedTypeParametersUsages(result.reifiedTypeParametersUsages) codegen.propagateChildReifiedTypeParametersUsages(result.reifiedTypeParametersUsages)
state.factory.removeClasses(result.calcClassesToRemove()) state.factory.removeClasses(result.calcClassesToRemove())
codegen.markLineNumberAfterInlineIfNeeded() codegen.markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards)
} }
fun performInline( fun performInline(
typeArguments: List<TypeParameterMarker>?, typeArguments: List<TypeParameterMarker>?,
inlineDefaultLambdas: Boolean, inlineDefaultLambdas: Boolean,
mapDefaultSignature: Boolean, mapDefaultSignature: Boolean,
typeSystem: TypeSystemCommonBackendContext typeSystem: TypeSystemCommonBackendContext,
registerLineNumberAfterwards: Boolean
) { ) {
var nodeAndSmap: SMAPAndMethodNode? = null var nodeAndSmap: SMAPAndMethodNode? = null
try { try {
nodeAndSmap = createInlineMethodNode( nodeAndSmap = createInlineMethodNode(
functionDescriptor, methodOwner, jvmSignature, mapDefaultSignature, typeArguments, typeSystem, state, sourceCompiler functionDescriptor, methodOwner, jvmSignature, mapDefaultSignature, typeArguments, typeSystem, state, sourceCompiler
) )
endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas)) endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas), registerLineNumberAfterwards)
} catch (e: CompilationException) { } catch (e: CompilationException) {
throw e throw e
} catch (e: InlineException) { } catch (e: InlineException) {
@@ -14,7 +14,10 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtIfExpression
import org.jetbrains.kotlin.psi.KtPsiUtil 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.callUtil.getResolvedCallWithAssert
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
@@ -66,12 +69,19 @@ class PsiInlineCodegen(
return return
} }
try { 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 { } finally {
state.globalInlineContext.exitFromInliningOf(inlineCall) 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) { override fun processAndPutHiddenParameters(justProcess: Boolean) {
if (getMethodAsmFlags(functionDescriptor, sourceCompiler.contextKind, state) and Opcodes.ACC_STATIC == 0) { if (getMethodAsmFlags(functionDescriptor, sourceCompiler.contextKind, state) and Opcodes.ACC_STATIC == 0) {
invocationParamBuilder.addNextParameter(methodOwner, false, actualDispatchReceiver) invocationParamBuilder.addNextParameter(methodOwner, false, actualDispatchReceiver)
@@ -1119,7 +1119,7 @@ class ExpressionCodegen(
//TODO //TODO
} }
override fun markLineNumberAfterInlineIfNeeded() { override fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean) {
// Inline function has its own line number which is in a separate instance of codegen, // 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. // therefore we need to reset lastLineNumber to force a line number generation after visiting inline function.
lastLineNumber = -1 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.dump
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
import org.jetbrains.kotlin.ir.util.isSuspend import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -187,7 +188,8 @@ class IrInlineCodegen(
expression.symbol.owner.typeParameters.map { it.symbol }, expression.symbol.owner.typeParameters.map { it.symbol },
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER,
false, false,
codegen.typeMapper.typeSystem codegen.typeMapper.typeSystem,
false
) )
} finally { } finally {
state.globalInlineContext.exitFromInliningOf(inlineCall) 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"); 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") @TestMetadata("singleThen.kt")
public void testSingleThen() throws Exception { public void testSingleThen() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt"); 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"); 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") @TestMetadata("singleThen.kt")
public void testSingleThen() throws Exception { public void testSingleThen() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt"); runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");