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 7d3d159e3c9..149507390cb 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 @@ -127,6 +127,7 @@ class ExpressionCodegen( get() = typeMapper.typeSystem override var lastLineNumber: Int = -1 + var noLineNumberScope: Boolean = false private val closureReifiedMarkers = hashMapOf() @@ -151,6 +152,7 @@ class ExpressionCodegen( private fun getLineNumberForOffset(offset: Int): Int = fileEntry?.getLineNumber(offset)?.plus(1) ?: -1 private fun IrElement.markLineNumber(startOffset: Boolean) { + if (noLineNumberScope) return val offset = if (startOffset) this.startOffset else endOffset if (offset < 0) { return @@ -167,6 +169,13 @@ class ExpressionCodegen( fun markLineNumber(element: IrElement) = element.markLineNumber(true) + fun noLineNumberScope(block: () -> Unit) { + val previousState = noLineNumberScope + noLineNumberScope = true + block() + noLineNumberScope = previousState + } + // TODO remove fun gen(expression: IrExpression, type: Type, irType: IrType, data: BlockInfo): StackValue { if (expression.attributeOwnerId === context.fakeContinuation) { @@ -365,7 +374,7 @@ class ExpressionCodegen( private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) = container.statements.fold(unitValue) { prev, exp -> prev.discard() - exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() } + exp.accept(this, data) } override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): PromisedValue { @@ -767,7 +776,18 @@ class ExpressionCodegen( override fun visitWhen(expression: IrWhen, data: BlockInfo): PromisedValue { expression.markLineNumber(startOffset = true) SwitchGenerator(expression, data, this).generate()?.let { return it } - + // When a lookup/table switch instruction is not generate, output a nop + // for the line number of the when itself. Otherwise, there will be + // no option of breaking on the line of the `when` if there is no + // subject: + // + // when { + // cond1 -> exp1 + // else -> exp2 + // } + if (expression.origin == IrStatementOrigin.WHEN) { + mv.nop() + } val endLabel = Label() val exhaustive = expression.branches.any { it.condition.isTrueConst() } && !expression.type.isUnit() assert(exhaustive || expression.type.isUnit()) { @@ -849,19 +869,6 @@ class ExpressionCodegen( } } - private fun IrExpression.markEndOfStatementIfNeeded() { - when (this) { - is IrWhen -> if (this.branches.size > 1) { - this.markLineNumber(false) - } - is IrTry -> this.markLineNumber(false) - is IrContainerExpression -> when (this.origin) { - IrStatementOrigin.WHEN, IrStatementOrigin.IF -> - this.markLineNumber(false) - } - } - } - override fun visitWhileLoop(loop: IrWhileLoop, data: BlockInfo): PromisedValue { val continueLabel = markNewLabel() val endLabel = Label() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt index 3dd3b57a181..1d5f54635bb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt @@ -348,7 +348,27 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf override fun shouldOptimize() = cases.size > 1 override fun genSwitch() { - subject.accept(codegen, data).materialize() + // Do not generate line numbers for the table switching. In particular, + // the subject is extracted from the condition of the first branch which + // will give the wrong stepping behavior for code such as: + // + // when { + // x == 42 -> 1 + // x == 32 -> 2 + // x == 24 -> 3 + // ... + // } + // + // If the subject line number is generated, we will not stop on the line + // of the `when` but instead stop on the `x == 42` line. When x is 24, + // we would stop on the line `x == 42` and then step to the line `x == 24`. + // That is confusing and we prefer to stop on the `when` line and then step + // to the `x == 24` line. This is accomplished by ignoring the line number + // information for the subject as the `when` line number has already been + // emitted. + codegen.noLineNumberScope { + subject.accept(codegen, data).materialize() + } genIntSwitch(cases) } } @@ -419,13 +439,33 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf override fun genSwitch() { with(codegen) { - if (subject.type.isNullableString()) { + // Do not generate line numbers for the table switching. In particular, + // the subject is extracted from the condition of the first branch which + // will give the wrong stepping behavior for code such as: + // + // when { + // x == "x" -> 1 + // x == "y" -> 2 + // x == "z" -> 3 + // ... + // } + // + // If the subject line number is generated, we will not stop on the line + // of the `when` but instead stop on the `x == "x"` line. When x is "z", + // we would stop on the line `x == "x"` and then step to the line `x == "z"`. + // That is confusing and we prefer to stop on the `when` line and then step + // to the `x == "z"` line. This is accomplished by ignoring the line number + // information for the subject as the `when` line number has already been + // emitted. + noLineNumberScope { + if (subject.type.isNullableString()) { + subject.accept(codegen, data).materialize() + mv.ifnull(cases.find { it.value == null }?.label ?: defaultLabel) + } + // Reevaluating the subject is fine here because it is a read of a temporary. subject.accept(codegen, data).materialize() - mv.ifnull(cases.find { it.value == null }?.label ?: defaultLabel) + mv.invokevirtual("java/lang/String", "hashCode", "()I", false) } - // Reevaluating the subject is fine here because it is a read of a temporary. - subject.accept(codegen, data).materialize() - mv.invokevirtual("java/lang/String", "hashCode", "()I", false) genIntSwitch(hashAndSwitchLabels) // Multiple strings can be hashed into the same bucket. @@ -433,7 +473,9 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf for ((hash, switchLabel) in hashAndSwitchLabels) { mv.visitLabel(switchLabel) for ((string, label) in hashToStringAndExprLabels[hash]!!) { - subject.accept(codegen, data).materialize() + noLineNumberScope { + subject.accept(codegen, data).materialize() + } mv.aconst(string) mv.invokevirtual("java/lang/String", "equals", "(Ljava/lang/Object;)Z", false) mv.ifne(label) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt index f64052dd0fd..728a29acdb7 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt @@ -88,7 +88,8 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo val superMethod = firstSuperMethodFromKotlin(irFunction, interfaceImplementation).owner val defaultImplFun = context.cachedDeclarations.getDefaultImplsFunction(superMethod) - context.createIrBuilder(irFunction.symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply { + val classStartOffset = classOverride.parentAsClass.startOffset + context.createIrBuilder(irFunction.symbol, classStartOffset, classStartOffset).apply { irFunction.body = irBlockBody { +irReturn( irCall(defaultImplFun.symbol, irFunction.returnType).apply { diff --git a/compiler/testData/codegen/bytecodeText/controlStructures/kt17110.kt b/compiler/testData/codegen/bytecodeText/controlStructures/kt17110.kt index 8d2527c6fd4..bb3125d0fe9 100644 --- a/compiler/testData/codegen/bytecodeText/controlStructures/kt17110.kt +++ b/compiler/testData/codegen/bytecodeText/controlStructures/kt17110.kt @@ -21,6 +21,5 @@ fun infiniteLoop() { // 1 GOTO L1 // JVM_IR_TEMPLATES -// 1 GOTO L6 -// 1 GOTO L7 +// 2 GOTO L6 // 1 GOTO L0 \ No newline at end of file diff --git a/compiler/testData/debug/stepping/stringSwitches.kt b/compiler/testData/debug/stepping/stringSwitches.kt new file mode 100644 index 00000000000..edbb32e6c0f --- /dev/null +++ b/compiler/testData/debug/stepping/stringSwitches.kt @@ -0,0 +1,113 @@ +// FILE: test.kt + +fun stringSwitch(x: String) { + val l = when { + x == "x" -> 1 + x == "xy" -> 2 + x == "xyz" -> 3 + else -> -1 + } + + val l2 = when (x) { + "x" -> 1 + "xy" -> 2 + "xyz" -> 3 + else -> -1 + } + + val l3 = when + (x) + { + "x" -> 1 + "xy" -> 2 + "xyz" -> 3 + else -> -1 + } +} + +fun box() { + stringSwitch("x") + stringSwitch("xy") + stringSwitch("xyz") + stringSwitch("nope") +} + +// JVM_IR uses the line number of the start of the `when` as the line number +// for the lookup/table switch. Therefore when the subject and the when is +// on separate lines the first step is on the subject, then steop to the when, +// then to the right branch. + +// JVM_IR uses optimized lookup/table switches for all these cases. JVM +// does not. So on JVM there are steps on each condition evaluation for +// the first `when`. + +// LINENUMBERS +// test.kt:29 box +// test.kt:4 stringSwitch +// test.kt:5 stringSwitch +// test.kt:4 stringSwitch +// test.kt:11 stringSwitch +// test.kt:12 stringSwitch +// test.kt:11 stringSwitch +// test.kt:19 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:18 stringSwitch +// LINENUMBERS +// test.kt:21 stringSwitch +// test.kt:18 stringSwitch +// test.kt:26 stringSwitch +// test.kt:30 box +// test.kt:4 stringSwitch +// LINENUMBERS JVM +// test.kt:5 stringSwitch +// LINENUMBERS +// test.kt:6 stringSwitch +// test.kt:4 stringSwitch +// test.kt:11 stringSwitch +// test.kt:13 stringSwitch +// test.kt:11 stringSwitch +// test.kt:19 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:18 stringSwitch +// LINENUMBERS +// test.kt:22 stringSwitch +// test.kt:18 stringSwitch +// test.kt:26 stringSwitch +// test.kt:31 box +// test.kt:4 stringSwitch +// LINENUMBERS JVM +// test.kt:5 stringSwitch +// test.kt:6 stringSwitch +// LINENUMBERS +// test.kt:7 stringSwitch +// test.kt:4 stringSwitch +// test.kt:11 stringSwitch +// test.kt:14 stringSwitch +// test.kt:11 stringSwitch +// test.kt:19 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:18 stringSwitch +// LINENUMBERS +// test.kt:23 stringSwitch +// test.kt:18 stringSwitch +// test.kt:26 stringSwitch +// test.kt:32 box +// test.kt:4 stringSwitch +// LINENUMBERS JVM +// test.kt:5 stringSwitch +// test.kt:6 stringSwitch +// test.kt:7 stringSwitch +// LINENUMBERS +// test.kt:8 stringSwitch +// test.kt:4 stringSwitch +// test.kt:11 stringSwitch +// test.kt:15 stringSwitch +// test.kt:11 stringSwitch +// test.kt:19 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:18 stringSwitch +// LINENUMBERS +// test.kt:24 stringSwitch +// test.kt:18 stringSwitch +// test.kt:26 stringSwitch +// test.kt:33 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/stringSwitchesSmall.kt b/compiler/testData/debug/stepping/stringSwitchesSmall.kt new file mode 100644 index 00000000000..9ce227760d4 --- /dev/null +++ b/compiler/testData/debug/stepping/stringSwitchesSmall.kt @@ -0,0 +1,96 @@ +// FILE: test.kt + +fun stringSwitch(x: String) { + val l = when { + x == "x" -> 1 + x == "xy" -> 2 + else -> -1 + } + + val l2 = when (x) { + "x" -> 1 + "xy" -> 2 + else -> -1 + } + + val l3 = when + (x) + { + "x" -> 1 + "xy" -> 2 + else -> -1 + } +} + +fun box() { + stringSwitch("x") + stringSwitch("xy") + stringSwitch("nope") +} + +// JVM_IR uses the line number of the start of the `when` as the line number +// for the lookup/table switch. Therefore when the subject and the when is +// on separate lines the first step is on the subject, then steop to the when, +// then to the right branch. + +// JVM_IR uses unoptimized lookup/table switches for all these cases. JVM +// does not. So on JVM there are direct jumps to the right branch for the +// last two whens. + +// LINENUMBERS +// test.kt:26 box +// test.kt:4 stringSwitch +// test.kt:5 stringSwitch +// test.kt:4 stringSwitch +// test.kt:10 stringSwitch +// test.kt:11 stringSwitch +// test.kt:10 stringSwitch +// test.kt:17 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:16 stringSwitch +// LINENUMBERS +// test.kt:19 stringSwitch +// test.kt:16 stringSwitch +// test.kt:23 stringSwitch +// test.kt:27 box +// test.kt:4 stringSwitch +// test.kt:5 stringSwitch +// test.kt:6 stringSwitch +// test.kt:4 stringSwitch +// test.kt:10 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:11 stringSwitch +// LINENUMBERS +// test.kt:12 stringSwitch +// test.kt:10 stringSwitch +// test.kt:17 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:16 stringSwitch +// test.kt:19 stringSwitch +// LINENUMBERS +// test.kt:20 stringSwitch +// test.kt:16 stringSwitch +// test.kt:23 stringSwitch +// test.kt:28 box +// test.kt:4 stringSwitch +// test.kt:5 stringSwitch +// test.kt:6 stringSwitch +// test.kt:7 stringSwitch +// test.kt:4 stringSwitch +// test.kt:10 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:11 stringSwitch +// test.kt:12 stringSwitch +// LINENUMBERS +// test.kt:13 stringSwitch +// test.kt:10 stringSwitch +// test.kt:17 stringSwitch +// LINENUMBERS JVM_IR +// test.kt:16 stringSwitch +// test.kt:19 stringSwitch +// test.kt:20 stringSwitch +// LINENUMBERS +// test.kt:21 stringSwitch +// test.kt:16 stringSwitch +// test.kt:23 stringSwitch +// test.kt:29 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/trait.kt b/compiler/testData/debug/stepping/trait.kt index bb56e290cec..1293dada46d 100644 --- a/compiler/testData/debug/stepping/trait.kt +++ b/compiler/testData/debug/stepping/trait.kt @@ -8,34 +8,38 @@ interface A { } } +class B : A + fun box() { (object : A {}).bar() + B().bar() } -// The JVM backend generates non-synthetic overrides of foo and bar -// in the object both with line number 12. That means that there will -// be steps on line number 12 on entry and exit to both bar and foo. - -// TODO: Is this what we want? Should they be marked as bridges instead? -// Doesn't look like the intellij debugger skips non-synthetic bridges? -// There seems to be some heuristics in intellij dealing with this as -// the stepping behavior with repeated step-into is mostly OK. - -// IGNORE_BACKEND: JVM_IR -// The JVM_IR backend generates non-synthetic overrides of foo and bar -// with no line numbers. That leads to steps on line -1 but only on -// exit from bar and foo. +// The dispatch methods added to classes directly implementing +// interfaces with default methods (forwarding to the actual implementation +// on A$DefaultImpls) have the line number of the class declaration. // LINENUMBERS -// test.kt:12 box -// test.kt:12 -// test.kt:12 box -// test.kt:12 bar +// test.kt:14 box +// test.kt:14 +// test.kt:14 box +// test.kt:14 bar // test.kt:7 bar -// test.kt:12 foo +// test.kt:14 foo // test.kt:4 foo -// test.kt:12 foo +// test.kt:14 foo // test.kt:7 bar -// test.kt:12 bar -// test.kt:12 box -// test.kt:13 box +// test.kt:14 bar +// test.kt:14 box +// test.kt:15 box +// test.kt:11 +// test.kt:15 box +// test.kt:11 bar +// test.kt:7 bar +// test.kt:11 foo +// test.kt:4 foo +// test.kt:11 foo +// test.kt:7 bar +// test.kt:11 bar +// test.kt:15 box +// test.kt:16 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/when.kt b/compiler/testData/debug/stepping/when.kt index 16eb234d4d5..1c2c94557ed 100644 --- a/compiler/testData/debug/stepping/when.kt +++ b/compiler/testData/debug/stepping/when.kt @@ -18,41 +18,51 @@ fun box() { foo(21) } -// IGNORE_BACKEND: JVM_IR - -// The JVM_IR backend has line number 8 when leaving the first -// when. Also, the stepping is different, probably because the when -// is compiled to a switch which it isn't with JVM? The stepping -// behavior is likely OK, but should be double checked. +// JVM_IR backend optimized the when to a switch in the java bytecode. +// Therefore, the stepping for JVM_IR does not step through the evaluation +// of each of the conditions, but goes directly to the right body. The +// JVM_IR stepping behavior here is the same as for `whenSubject.kt`. // LINENUMBERS // test.kt:18 box // test.kt:4 foo // test.kt:5 foo // test.kt:4 foo +// LINENUMBERS JVM // test.kt:5 foo +// LINENUMBERS // test.kt:6 foo // test.kt:4 foo +// LINENUMBERS JVM // test.kt:5 foo // test.kt:6 foo +// LINENUMBERS // test.kt:7 foo // test.kt:10 foo +// LINENUMBERS JVM // test.kt:11 foo // test.kt:12 foo +// LINENUMBERS // test.kt:13 foo // test.kt:10 foo // test.kt:15 foo // test.kt:6 foo // test.kt:10 foo +// LINENUMBERS JVM // test.kt:11 foo +// LINENUMBERS // test.kt:12 foo // test.kt:4 foo +// LINENUMBERS JVM // test.kt:5 foo // test.kt:6 foo +// LINENUMBERS // test.kt:7 foo // test.kt:10 foo +// LINENUMBERS JVM // test.kt:11 foo // test.kt:12 foo +// LINENUMBERS // test.kt:13 foo // test.kt:10 foo // test.kt:15 foo @@ -63,29 +73,41 @@ fun box() { // test.kt:10 foo // test.kt:11 foo // test.kt:4 foo +// LINENUMBERS JVM // test.kt:5 foo +// LINENUMBERS // test.kt:6 foo // test.kt:4 foo +// LINENUMBERS JVM // test.kt:5 foo // test.kt:6 foo +// LINENUMBERS // test.kt:7 foo // test.kt:10 foo +// LINENUMBERS JVM // test.kt:11 foo // test.kt:12 foo +// LINENUMBERS // test.kt:13 foo // test.kt:10 foo // test.kt:15 foo // test.kt:6 foo // test.kt:10 foo +// LINENUMBERS JVM // test.kt:11 foo +// LINENUMBERS // test.kt:12 foo // test.kt:4 foo +// LINENUMBERS JVM // test.kt:5 foo // test.kt:6 foo +// LINENUMBERS // test.kt:7 foo // test.kt:10 foo +// LINENUMBERS JVM // test.kt:11 foo // test.kt:12 foo +// LINENUMBERS // test.kt:13 foo // test.kt:10 foo // test.kt:15 foo @@ -95,4 +117,4 @@ fun box() { // test.kt:11 foo // test.kt:10 foo // test.kt:15 foo -// test.kt:19 box +// test.kt:19 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/whenComplicatedSubject.kt b/compiler/testData/debug/stepping/whenComplicatedSubject.kt new file mode 100644 index 00000000000..912b177be15 --- /dev/null +++ b/compiler/testData/debug/stepping/whenComplicatedSubject.kt @@ -0,0 +1,49 @@ +// FILE: test.kt + +fun foo(x: Int) { + when (val y = + when { + x == 0 -> 1 + x == 1 -> 2 + else -> 0 + }) { + 0 -> 3 + 1 -> 4 + } +} + +fun box() { + foo(0) + foo(1) + foo(2) +} + +// The JVM_IR backend optimizes the inner when to a switch and therefore goes directly to the +// right case without stepping through evaluation of each condition. + +// LINENUMBERS +// test.kt:16 box +// test.kt:5 foo +// test.kt:6 foo +// test.kt:4 foo +// test.kt:11 foo +// test.kt:13 foo +// test.kt:17 box +// test.kt:5 foo +// LINENUMBERS JVM +// test.kt:6 foo +// LINENUMBERS +// test.kt:7 foo +// test.kt:4 foo +// test.kt:13 foo +// test.kt:18 box +// test.kt:5 foo +// LINENUMBERS JVM +// test.kt:6 foo +// test.kt:7 foo +// LINENUMBERS +// test.kt:8 foo +// test.kt:4 foo +// test.kt:10 foo +// test.kt:13 foo +// test.kt:19 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/whenMultiLine.kt b/compiler/testData/debug/stepping/whenMultiLine.kt new file mode 100644 index 00000000000..bd792eb5b41 --- /dev/null +++ b/compiler/testData/debug/stepping/whenMultiLine.kt @@ -0,0 +1,83 @@ +// FILE: test.kt + +fun foo(x: Int): Int { + when { + x == 21 -> + 1 + x == 42 -> + 2 + else -> + 3 + } + + val t = when { + x == 21 -> + 1 + x == 42 -> + 2 + else -> + 3 + } + + return t +} + +fun box() { + foo(21) + foo(42) + foo(63) +} + +// JVM_IR backend optimized the when to a switch in the java bytecode. +// Therefore, the stepping for JVM_IR does not step through the evaluation +// of each of the conditions, but goes directly to the right body. +// JVM_IR stepping behavior here is the same as for `whenMultiLineSubject.kt`. + +// LINENUMBERS +// test.kt:26 box +// test.kt:4 foo +// LINENUMBERS JVM +// test.kt:5 foo +// LINENUMBERS +// test.kt:6 foo +// test.kt:13 foo +// LINENUMBERS JVM +// test.kt:14 foo +// LINENUMBERS +// test.kt:15 foo +// test.kt:13 foo +// test.kt:22 foo +// test.kt:26 box +// test.kt:27 box +// test.kt:4 foo +// LINENUMBERS JVM +// test.kt:5 foo +// test.kt:7 foo +// LINENUMBERS +// test.kt:8 foo +// test.kt:13 foo +// LINENUMBERS JVM +// test.kt:14 foo +// test.kt:16 foo +// LINENUMBERS +// test.kt:17 foo +// test.kt:13 foo +// test.kt:22 foo +// test.kt:27 box +// test.kt:28 box +// test.kt:4 foo +// LINENUMBERS JVM +// test.kt:5 foo +// test.kt:7 foo +// LINENUMBERS +// test.kt:10 foo +// test.kt:13 foo +// LINENUMBERS JVM +// test.kt:14 foo +// test.kt:16 foo +// LINENUMBERS +// test.kt:19 foo +// test.kt:13 foo +// test.kt:22 foo +// test.kt:28 box +// test.kt:29 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/whenMultiLineSubject.kt b/compiler/testData/debug/stepping/whenMultiLineSubject.kt new file mode 100644 index 00000000000..d672f8a1b3f --- /dev/null +++ b/compiler/testData/debug/stepping/whenMultiLineSubject.kt @@ -0,0 +1,56 @@ +// FILE: test.kt + +fun foo(x: Int): Int { + when (x) { + 21 -> + 1 + 42 -> + 2 + else -> + 3 + } + + val t = when (x) { + 21 -> + 1 + 42 -> + 2 + else -> + 3 + } + + return t +} + +fun box() { + foo(21) + foo(42) + foo(63) +} + +// LINENUMBERS +// test.kt:26 box +// test.kt:4 foo +// test.kt:6 foo +// test.kt:13 foo +// test.kt:15 foo +// test.kt:13 foo +// test.kt:22 foo +// test.kt:26 box +// test.kt:27 box +// test.kt:4 foo +// test.kt:8 foo +// test.kt:13 foo +// test.kt:17 foo +// test.kt:13 foo +// test.kt:22 foo +// test.kt:27 box +// test.kt:28 box +// test.kt:4 foo +// test.kt:10 foo +// test.kt:13 foo +// test.kt:19 foo +// test.kt:13 foo +// test.kt:22 foo +// test.kt:28 box +// test.kt:29 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/whenSubject.kt b/compiler/testData/debug/stepping/whenSubject.kt index 10d08c8f366..972dfebee6e 100644 --- a/compiler/testData/debug/stepping/whenSubject.kt +++ b/compiler/testData/debug/stepping/whenSubject.kt @@ -18,8 +18,6 @@ fun box() { foo(21) } -// JVM_IR stops on line 8 when exiting the first when. - // LINENUMBERS // test.kt:18 box // test.kt:4 foo @@ -28,24 +26,15 @@ fun box() { // test.kt:6 foo // test.kt:4 foo // test.kt:7 foo -// LINENUMBERS JVM_IR -// test.kt:8 foo -// LINENUMBERS // test.kt:10 foo // test.kt:13 foo // test.kt:10 foo // test.kt:15 foo // test.kt:6 foo -// LINENUMBERS JVM_IR -// test.kt:8 foo -// LINENUMBERS // test.kt:10 foo // test.kt:12 foo // test.kt:4 foo // test.kt:7 foo -// LINENUMBERS JVM_IR -// test.kt:8 foo -// LINENUMBERS // test.kt:10 foo // test.kt:13 foo // test.kt:10 foo @@ -54,33 +43,21 @@ fun box() { // test.kt:10 foo // test.kt:15 foo // test.kt:5 foo -// LINENUMBERS JVM_IR -// test.kt:8 foo -// LINENUMBERS // test.kt:10 foo // test.kt:11 foo // test.kt:4 foo // test.kt:6 foo // test.kt:4 foo // test.kt:7 foo -// LINENUMBERS JVM_IR -// test.kt:8 foo -// LINENUMBERS // test.kt:10 foo // test.kt:13 foo // test.kt:10 foo // test.kt:15 foo // test.kt:6 foo -// LINENUMBERS JVM_IR -// test.kt:8 foo -// LINENUMBERS // test.kt:10 foo // test.kt:12 foo // test.kt:4 foo // test.kt:7 foo -// LINENUMBERS JVM_IR -// test.kt:8 foo -// LINENUMBERS // test.kt:10 foo // test.kt:13 foo // test.kt:10 foo diff --git a/compiler/testData/debug/stepping/whenSubject2.kt b/compiler/testData/debug/stepping/whenSubject2.kt new file mode 100644 index 00000000000..d164d802564 --- /dev/null +++ b/compiler/testData/debug/stepping/whenSubject2.kt @@ -0,0 +1,121 @@ +// FILE: test.kt + +fun foo(x: Int) { + when + (x) + { + 21 -> foo(42) + 42 -> foo(63) + else -> 1 + } + + val t = when + (x) + { + 21 -> foo(42) + 42 -> foo(63) + else -> 2 + } +} + +fun box() { + foo(21) +} + +// JVM_IR uses the line number of the when for the table switch and therefore, +// it stops on the subject line first, then on the when line (line 4 and 12), and +// then goes to the right branch. + +// LINENUMBERS +// test.kt:22 box +// test.kt:5 foo +// LINENUMBERS JVM_IR +// test.kt:4 foo +// LINENUMBERS +// test.kt:7 foo +// test.kt:5 foo +// LINENUMBERS JVM_IR +// test.kt:4 foo +// LINENUMBERS +// test.kt:8 foo +// test.kt:5 foo +// LINENUMBERS JVM_IR +// test.kt:4 foo +// LINENUMBERS +// test.kt:9 foo +// test.kt:13 foo +// LINENUMBERS JVM_IR +// test.kt:12 foo +// LINENUMBERS +// test.kt:17 foo +// test.kt:12 foo +// test.kt:19 foo +// test.kt:8 foo +// test.kt:13 foo +// LINENUMBERS JVM_IR +// test.kt:12 foo +// LINENUMBERS +// test.kt:16 foo +// test.kt:5 foo +// LINENUMBERS JVM_IR +// test.kt:4 foo +// LINENUMBERS +// test.kt:9 foo +// test.kt:13 foo +// LINENUMBERS JVM_IR +// test.kt:12 foo +// LINENUMBERS +// test.kt:17 foo +// test.kt:12 foo +// test.kt:19 foo +// test.kt:16 foo +// test.kt:12 foo +// test.kt:19 foo +// test.kt:7 foo +// test.kt:13 foo +// LINENUMBERS JVM_IR +// test.kt:12 foo +// LINENUMBERS +// test.kt:15 foo +// test.kt:5 foo +// LINENUMBERS JVM_IR +// test.kt:4 foo +// LINENUMBERS +// test.kt:8 foo +// test.kt:5 foo +// LINENUMBERS JVM_IR +// test.kt:4 foo +// LINENUMBERS +// test.kt:9 foo +// test.kt:13 foo +// LINENUMBERS JVM_IR +// test.kt:12 foo +// LINENUMBERS +// test.kt:17 foo +// test.kt:12 foo +// test.kt:19 foo +// test.kt:8 foo +// test.kt:13 foo +// LINENUMBERS JVM_IR +// test.kt:12 foo +// LINENUMBERS +// test.kt:16 foo +// test.kt:5 foo +// LINENUMBERS JVM_IR +// test.kt:4 foo +// LINENUMBERS +// test.kt:9 foo +// test.kt:13 foo +// LINENUMBERS JVM_IR +// test.kt:12 foo +// LINENUMBERS +// test.kt:17 foo +// test.kt:12 foo +// test.kt:19 foo +// test.kt:16 foo +// test.kt:12 foo +// test.kt:19 foo +// test.kt:15 foo +// test.kt:12 foo +// test.kt:19 foo +// test.kt:23 box diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractSteppingTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractSteppingTest.kt index 878fbbc2e27..e10e061b6e5 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractSteppingTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractSteppingTest.kt @@ -85,7 +85,7 @@ abstract class AbstractSteppingTest : AbstractDebugTest() { LINENUMBERS_MARKER -> TargetBackend.ANY JVM_LINENUMBER_MARKER -> TargetBackend.JVM JVM_IR_LINENUMBER_MARKER -> TargetBackend.JVM_IR - else -> error("Expected JVM backend") + else -> error("Expected JVM backend: $line") } continue } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java index f9f42064f67..9924d704a2f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java @@ -349,6 +349,18 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest { runTest("compiler/testData/debug/stepping/smapInlineInIntrinsicArgument.kt"); } + @Test + @TestMetadata("stringSwitches.kt") + public void testStringSwitches() throws Exception { + runTest("compiler/testData/debug/stepping/stringSwitches.kt"); + } + + @Test + @TestMetadata("stringSwitchesSmall.kt") + public void testStringSwitchesSmall() throws Exception { + runTest("compiler/testData/debug/stepping/stringSwitchesSmall.kt"); + } + @Test @TestMetadata("throwException.kt") public void testThrowException() throws Exception { @@ -409,12 +421,36 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest { runTest("compiler/testData/debug/stepping/when.kt"); } + @Test + @TestMetadata("whenComplicatedSubject.kt") + public void testWhenComplicatedSubject() throws Exception { + runTest("compiler/testData/debug/stepping/whenComplicatedSubject.kt"); + } + + @Test + @TestMetadata("whenMultiLine.kt") + public void testWhenMultiLine() throws Exception { + runTest("compiler/testData/debug/stepping/whenMultiLine.kt"); + } + + @Test + @TestMetadata("whenMultiLineSubject.kt") + public void testWhenMultiLineSubject() throws Exception { + runTest("compiler/testData/debug/stepping/whenMultiLineSubject.kt"); + } + @Test @TestMetadata("whenSubject.kt") public void testWhenSubject() throws Exception { runTest("compiler/testData/debug/stepping/whenSubject.kt"); } + @Test + @TestMetadata("whenSubject2.kt") + public void testWhenSubject2() throws Exception { + runTest("compiler/testData/debug/stepping/whenSubject2.kt"); + } + @Test @TestMetadata("while.kt") public void testWhile() throws Exception { diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/SteppingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/SteppingTestGenerated.java index 445546ea7e9..7ce96d3c1a4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/SteppingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/SteppingTestGenerated.java @@ -349,6 +349,18 @@ public class SteppingTestGenerated extends AbstractSteppingTest { runTest("compiler/testData/debug/stepping/smapInlineInIntrinsicArgument.kt"); } + @Test + @TestMetadata("stringSwitches.kt") + public void testStringSwitches() throws Exception { + runTest("compiler/testData/debug/stepping/stringSwitches.kt"); + } + + @Test + @TestMetadata("stringSwitchesSmall.kt") + public void testStringSwitchesSmall() throws Exception { + runTest("compiler/testData/debug/stepping/stringSwitchesSmall.kt"); + } + @Test @TestMetadata("throwException.kt") public void testThrowException() throws Exception { @@ -409,12 +421,36 @@ public class SteppingTestGenerated extends AbstractSteppingTest { runTest("compiler/testData/debug/stepping/when.kt"); } + @Test + @TestMetadata("whenComplicatedSubject.kt") + public void testWhenComplicatedSubject() throws Exception { + runTest("compiler/testData/debug/stepping/whenComplicatedSubject.kt"); + } + + @Test + @TestMetadata("whenMultiLine.kt") + public void testWhenMultiLine() throws Exception { + runTest("compiler/testData/debug/stepping/whenMultiLine.kt"); + } + + @Test + @TestMetadata("whenMultiLineSubject.kt") + public void testWhenMultiLineSubject() throws Exception { + runTest("compiler/testData/debug/stepping/whenMultiLineSubject.kt"); + } + @Test @TestMetadata("whenSubject.kt") public void testWhenSubject() throws Exception { runTest("compiler/testData/debug/stepping/whenSubject.kt"); } + @Test + @TestMetadata("whenSubject2.kt") + public void testWhenSubject2() throws Exception { + runTest("compiler/testData/debug/stepping/whenSubject2.kt"); + } + @Test @TestMetadata("while.kt") public void testWhile() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt index 19dc50f7fa1..87553282beb 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.ir.txt @@ -113,8 +113,8 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa LABEL (L4) ICONST_0 ISTORE (3) - GOTO (L5) - LABEL (L6) + GOTO (L2) + LABEL (L5) ALOAD (7) ALOAD (2) ICONST_0 @@ -131,21 +131,16 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa ICONST_1 IOR ISTORE (5) - GOTO (L5) - LABEL (L7) + GOTO (L2) + LABEL (L6) NEW DUP ILOAD (4) INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, , (I)V) ATHROW - LABEL (L5) - LINENUMBER (13) - GOTO (L2) LABEL (L3) ALOAD (7) ALOAD (2) - LABEL (L8) - LINENUMBER (12) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V) NEW DUP @@ -156,7 +151,7 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa CHECKCAST INVOKESPECIAL (ListOfUsers, , (ILjava/util/List;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V) ARETURN - LABEL (L9) + LABEL (L7) } public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder decoder) { @@ -295,10 +290,8 @@ public final class ListOfUsers : java/lang/Object { ALOAD (0) ALOAD (2) PUTFIELD (list, Ljava/util/List;) - LABEL (L2) - LINENUMBER (13) RETURN - LABEL (L3) + LABEL (L2) } public final java.util.List getList() @@ -411,8 +404,8 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ LABEL (L4) ICONST_0 ISTORE (3) - GOTO (L5) - LABEL (L6) + GOTO (L2) + LABEL (L5) ALOAD (7) ALOAD (2) ICONST_0 @@ -425,21 +418,16 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ ICONST_1 IOR ISTORE (5) - GOTO (L5) - LABEL (L7) + GOTO (L2) + LABEL (L6) NEW DUP ILOAD (4) INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, , (I)V) ATHROW - LABEL (L5) - LINENUMBER (10) - GOTO (L2) LABEL (L3) ALOAD (7) ALOAD (2) - LABEL (L8) - LINENUMBER (9) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V) NEW DUP @@ -450,7 +438,7 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ CHECKCAST INVOKESPECIAL (OptionalUser, , (ILUser;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V) ARETURN - LABEL (L9) + LABEL (L7) } public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder decoder) { @@ -636,7 +624,6 @@ public final class OptionalUser : java/lang/Object { ALOAD (2) PUTFIELD (user, LUser;) LABEL (L4) - LINENUMBER (10) RETURN LABEL (L5) } @@ -779,8 +766,8 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in LABEL (L4) ICONST_0 ISTORE (3) - GOTO (L5) - LABEL (L6) + GOTO (L2) + LABEL (L5) ALOAD (8) ALOAD (2) ICONST_0 @@ -790,8 +777,8 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in ICONST_1 IOR ISTORE (5) - GOTO (L5) - LABEL (L7) + GOTO (L2) + LABEL (L6) ALOAD (8) ALOAD (2) ICONST_1 @@ -801,21 +788,16 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in ICONST_2 IOR ISTORE (5) - GOTO (L5) - LABEL (L8) + GOTO (L2) + LABEL (L7) NEW DUP ILOAD (4) INVOKESPECIAL (kotlinx/serialization/UnknownFieldException, , (I)V) ATHROW - LABEL (L5) - LINENUMBER (7) - GOTO (L2) LABEL (L3) ALOAD (8) ALOAD (2) - LABEL (L9) - LINENUMBER (6) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeDecoder, endStructure, (Lkotlinx/serialization/descriptors/SerialDescriptor;)V) NEW DUP @@ -826,7 +808,7 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in CHECKCAST INVOKESPECIAL (User, , (ILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V) ARETURN - LABEL (L10) + LABEL (L8) } public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder decoder) { @@ -973,29 +955,24 @@ public final class User : java/lang/Object { ALOAD (0) ALOAD (2) PUTFIELD (firstName, Ljava/lang/String;) - LABEL (L2) - LINENUMBER (7) - LINENUMBER (6) ILOAD (1) ICONST_2 IAND - IFNE (L3) + IFNE (L2) NEW DUP LDC (lastName) INVOKESPECIAL (kotlinx/serialization/MissingFieldException, , (Ljava/lang/String;)V) ATHROW - LABEL (L3) + LABEL (L2) ALOAD (0) ALOAD (3) PUTFIELD (lastName, Ljava/lang/String;) - LABEL (L4) - LINENUMBER (7) RETURN - LABEL (L5) + LABEL (L3) } public final java.lang.String getFirstName() public final java.lang.String getLastName() -} +} \ No newline at end of file