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 cc5786fcbf7..e7eb4bb6bc0 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 @@ -401,6 +401,7 @@ class ExpressionCodegen( // IR constructors have no receiver and return the new instance, but on JVM they are void-returning // instance methods named . + markLineNumber(expression) mv.anew(asmType) mv.dup() } diff --git a/compiler/testData/debug/stepping/classObject.kt b/compiler/testData/debug/stepping/classObject.kt index f1d4022d147..3a93dc6653f 100644 --- a/compiler/testData/debug/stepping/classObject.kt +++ b/compiler/testData/debug/stepping/classObject.kt @@ -23,6 +23,8 @@ fun box() { // LINENUMBERS // test.kt:14 box +// test.kt:5 +// test.kt:6 // test.kt:5 getProp0 // LINENUMBERS JVM // test.kt:5 getProp0 diff --git a/compiler/testData/debug/stepping/constructorCall.kt b/compiler/testData/debug/stepping/constructorCall.kt new file mode 100644 index 00000000000..70249a32a2a --- /dev/null +++ b/compiler/testData/debug/stepping/constructorCall.kt @@ -0,0 +1,33 @@ +// FILE: test.kt + +class A(val x: Int) + +fun box() { + A(1) + A( + 2 + ) + A(3 + ) + A( + 4) +} + +// LINENUMBERS +// test.kt:6 box +// test.kt:3 +// test.kt:6 box +// test.kt:7 box +// test.kt:8 box +// test.kt:7 box +// test.kt:3 +// test.kt:7 box +// test.kt:10 box +// test.kt:3 +// test.kt:10 box +// test.kt:12 box +// test.kt:13 box +// test.kt:12 box +// test.kt:3 +// test.kt:12 box +// test.kt:14 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/enum.kt b/compiler/testData/debug/stepping/enum.kt index 070c0bfcef0..574713ebb37 100644 --- a/compiler/testData/debug/stepping/enum.kt +++ b/compiler/testData/debug/stepping/enum.kt @@ -1,7 +1,8 @@ // FILE: test.kt -enum class E { - E1; +enum class E() { + A, + B; fun foo() = { prop @@ -10,13 +11,43 @@ enum class E { val prop = 22 } -fun box() { - E.E1.foo() +enum class E2(val y : Int) { + C(1), + D( + 2 + ) } +fun box() { + E.A.foo() + E2.C; +} + +// JVM_IR maintains line number information in the class initializer for the +// initialization of the enum entries. There is line number information for +// the allocation of the object, for the evaluation of arguments to the +// constructor, and for the call to the constructor. This is consistent +// with the line number information generated for normal object allocation. + +// JVM has no line number information in if there are no arguments +// to the enum constructor. If there are arguments it has line number information +// for the evaluation of the arguments constructor and for the constructor call, +// but not for the allocation of the object. + // LINENUMBERS -// test.kt:14 box -// test.kt:6 foo -// test.kt:8 foo -// test.kt:14 box -// test.kt:15 box +// test.kt:22 box +// LINENUMBERS JVM_IR +// test.kt:4 +// test.kt:5 +// LINENUMBERS +// test.kt:7 foo +// test.kt:9 foo +// test.kt:22 box +// test.kt:23 box +// test.kt:15 +// LINENUMBERS JVM_IR +// test.kt:16 +// LINENUMBERS +// test.kt:17 +// test.kt:16 +// test.kt:24 box \ No newline at end of file diff --git a/compiler/testData/debug/stepping/initBlocksCompanion.kt b/compiler/testData/debug/stepping/initBlocksCompanion.kt index 4b0c529188f..5ad34211bfb 100644 --- a/compiler/testData/debug/stepping/initBlocksCompanion.kt +++ b/compiler/testData/debug/stepping/initBlocksCompanion.kt @@ -32,12 +32,19 @@ fun box() { // JVM backend has extra steps for getX and getS. -// TODO: since these tests operate as step-into, we do not get any steps -// in the for the companion object. Maybe extend the test infrastructure -// with directives to set break points in order to test this. - // LINENUMBERS // test.kt:29 box +// test.kt:7 +// test.kt:8 +// test.kt:9 +// test.kt:11 +// test.kt:13 +// test.kt:15 +// test.kt:16 +// test.kt:17 +// test.kt:19 +// test.kt:21 +// test.kt:22 // test.kt:11 getX // LINENUMBERS JVM // test.kt:11 getX @@ -49,4 +56,4 @@ fun box() { // test.kt:5 getS // LINENUMBERS // test.kt:30 box -// test.kt:31 box \ No newline at end of file +// test.kt:31 box diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractDebugTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractDebugTest.kt index 6f7adfa5bd0..5fef33f3a81 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractDebugTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractDebugTest.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen.debugInformation import com.intellij.openapi.util.SystemInfo import com.intellij.util.PathUtil import com.intellij.util.SystemProperties +import com.sun.jdi.AbsentInformationException import com.sun.jdi.VirtualMachine import com.sun.jdi.event.* import com.sun.jdi.request.EventRequest @@ -194,13 +195,22 @@ abstract class AbstractDebugTest : CodegenTestCase() { is MethodEntryEvent -> { if (!inBoxMethod && event.location().method().name() == BOX_METHOD) { if (manager.stepRequests().isEmpty()) { + // Create line stepping request to get all normal line steps starting now. val stepReq = manager.createStepRequest(event.thread(), StepRequest.STEP_LINE, StepRequest.STEP_INTO) stepReq.setSuspendPolicy(SUSPEND_ALL) stepReq.addClassExclusionFilter("java.*") stepReq.addClassExclusionFilter("sun.*") stepReq.addClassExclusionFilter("kotlin.*") + // Create class prepare request to be able to set breakpoints on class initializer lines. + // There are no line stepping events for class initializers, so we depend on breakpoints. + val prepareReq = manager.createClassPrepareRequest() + prepareReq.setSuspendPolicy(SUSPEND_ALL) + prepareReq.addClassExclusionFilter("java.*") + prepareReq.addClassExclusionFilter("sun.*") + prepareReq.addClassExclusionFilter("kotlin.*") } manager.stepRequests().map { it.enable() } + manager.classPrepareRequests().map { it.enable() } inBoxMethod = true storeStep(loggedItems, event) } @@ -209,6 +219,8 @@ abstract class AbstractDebugTest : CodegenTestCase() { // Handle the case where an Exception causing program to exit without MethodExitEvent. if (inBoxMethod && event.location().method().name() == "run") { manager.stepRequests().map { it.disable() } + manager.classPrepareRequests().map { it.disable() } + manager.breakpointRequests().map { it.disable() } break@vmLoop } if (inBoxMethod) { @@ -218,9 +230,28 @@ abstract class AbstractDebugTest : CodegenTestCase() { is MethodExitEvent -> { if (event.location().method().name() == BOX_METHOD) { manager.stepRequests().map { it.disable() } + manager.classPrepareRequests().map { it.disable() } + manager.breakpointRequests().map { it.disable() } break@vmLoop } } + is ClassPrepareEvent -> { + if (inBoxMethod) { + val initializer = event.referenceType().methods().find { it.isStaticInitializer } + try { + initializer?.allLineLocations()?.forEach { + manager.createBreakpointRequest(it).enable() + } + } catch (e: AbsentInformationException) { + // If there is no line information, do not set breakpoints. + } + } + } + is BreakpointEvent -> { + if (inBoxMethod) { + storeStep(loggedItems, event) + } + } else -> { throw IllegalStateException("event not handled: $event") } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java index 443199c20d3..d9dbfed815b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrSteppingTestGenerated.java @@ -97,6 +97,12 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest { runTest("compiler/testData/debug/stepping/conjunction.kt"); } + @Test + @TestMetadata("constructorCall.kt") + public void testConstructorCall() throws Exception { + runTest("compiler/testData/debug/stepping/constructorCall.kt"); + } + @Test @TestMetadata("defaultParameter.kt") public void testDefaultParameter() 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 0853eb0b9ee..cbac473d1c9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/SteppingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/SteppingTestGenerated.java @@ -97,6 +97,12 @@ public class SteppingTestGenerated extends AbstractSteppingTest { runTest("compiler/testData/debug/stepping/conjunction.kt"); } + @Test + @TestMetadata("constructorCall.kt") + public void testConstructorCall() throws Exception { + runTest("compiler/testData/debug/stepping/constructorCall.kt"); + } + @Test @TestMetadata("defaultParameter.kt") public void testDefaultParameter() throws Exception {