diff --git a/compiler/testData/debug/localVariables/copyFunction.kt b/compiler/testData/debug/localVariables/copyFunction.kt new file mode 100644 index 00000000000..149581dfd70 --- /dev/null +++ b/compiler/testData/debug/localVariables/copyFunction.kt @@ -0,0 +1,20 @@ +//FILE: test.kt + +data class someClass(val a: Double, val b: Double) + +fun box() { + val a = someClass(1.0, 2.0) + val b = a.copy(b = 3.0) +} + +// IGNORE_BACKEND: JVM_IR +// LOCAL VARIABLES +// TestKt:6: +// someClass:3: a:double, b:double +// TestKt:6: +// TestKt:7: a:someClass +// someClass:3: a:double, b:double +// someClass.copy(double, double)+9: a:double, b:double +// someClass.copy$default(someClass, double, double, int, java.lang.Object)+30: +// TestKt:7: a:someClass +// TestKt:8: a:someClass, b:someClass \ No newline at end of file diff --git a/compiler/testData/debug/localVariables/destructuringInFor.kt b/compiler/testData/debug/localVariables/destructuringInFor.kt new file mode 100644 index 00000000000..a7528b12d5a --- /dev/null +++ b/compiler/testData/debug/localVariables/destructuringInFor.kt @@ -0,0 +1,20 @@ +//FILE: test.kt + +// WITH_RUNTIME + +fun box() { + val map: Map = mapOf("1" to "23") + for ((a, b) + in map) { + a + b + } +} +// IGNORE_BACKEND: JVM_IR +// LOCAL VARIABLES +// TestKt:6: +// TestKt:8: map:java.util.Collections$SingletonMap +// TestKt:7: map:java.util.Collections$SingletonMap +// TestKt:9: map:java.util.Collections$SingletonMap, a:java.lang.String, b:java.lang.String +// TestKt:7: map:java.util.Collections$SingletonMap +// TestKt:8: map:java.util.Collections$SingletonMap +// TestKt:11: map:java.util.Collections$SingletonMap diff --git a/compiler/testData/debug/localVariables/destructuringInLambdas.kt b/compiler/testData/debug/localVariables/destructuringInLambdas.kt new file mode 100644 index 00000000000..ca1c252bc6c --- /dev/null +++ b/compiler/testData/debug/localVariables/destructuringInLambdas.kt @@ -0,0 +1,20 @@ +//FILE: test.kt + +data class A(val x: String, val y: Int) + +fun foo(a: A, block: (A) -> String): String = block(a) + +fun box() { + foo(A("O", 123)) { (x, y) -> x + y } +} +// IGNORE_BACKEND: JVM_IR +// LOCAL VARIABLES +// TestKt:8: +// A:3: x:java.lang.String, y:int +// TestKt:8: +// TestKt:5: $dstr$x$y:A +// TestKt$box$1:8: +// TestKt$box$1.invoke(java.lang.Object)+8: a:A, block:TestKt$box$1 +// TestKt:5: a:A, block:TestKt$box$1 +// TestKt:8: +// TestKt:9: \ No newline at end of file diff --git a/compiler/testData/debug/localVariables/localFun.kt b/compiler/testData/debug/localVariables/localFun.kt new file mode 100644 index 00000000000..d74602f1f24 --- /dev/null +++ b/compiler/testData/debug/localVariables/localFun.kt @@ -0,0 +1,16 @@ +//FILE: test.kt + +fun foo() { + fun bar() { + } +} + +fun box() { + foo() +} +// IGNORE_BACKEND: JVM_IR +// LOCAL VARIABLES +// TestKt:9: +// TestKt:4: $fun$bar$1:TestKt$foo$1 +// TestKt:6: $fun$bar$1:TestKt$foo$1 +// TestKt:10: \ No newline at end of file diff --git a/compiler/testData/debug/localVariables/underscoreNames.kt b/compiler/testData/debug/localVariables/underscoreNames.kt new file mode 100644 index 00000000000..4eeb479658d --- /dev/null +++ b/compiler/testData/debug/localVariables/underscoreNames.kt @@ -0,0 +1,49 @@ +//FILE: test.kt + +data class A(val x: Double = 1.0, val y: String = "", val z: Char = '0') + +fun foo(a: A, block: (A, String, Int) -> String): String = block(a, "", 1) + +val arrayOfA: Array = Array(1) { A() } + +fun box() { + + foo(A()) { + (x, _, y), _, w -> + + val (a, _, c) = A() + val (_, `_`, d) = A() + + for ((_, q) in arrayOfA) { + + } + + "" + } +} +// IGNORE_BACKEND: JVM_IR +// LOCAL VARIABLES +// TestKt:11: +// A:3: +// A:3: x:double, y:java.lang.String, z:char +// A:3: +// TestKt:11: +// TestKt:5: $dstr$x$_u24__u24$y:A, $noName_1:java.lang.String, w:int +// TestKt$box$1:14: +// A:3: x:double, y:java.lang.String, z:char +// A:3: +// A:3: $dstr$x$_u24__u24$y:A, $noName_1:java.lang.String, w:int, x:double, y:char +// TestKt$box$1:14: $dstr$x$_u24__u24$y:A, $noName_1:java.lang.String, w:int, x:double, y:char, a:double, c:char +// TestKt$box$1:15: +// A:3: x:double, y:java.lang.String, z:char +// A:3: +// A:3: $dstr$x$_u24__u24$y:A, $noName_1:java.lang.String, w:int, x:double, y:char, a:double, c:char +// TestKt$box$1:15: $dstr$x$_u24__u24$y:A, $noName_1:java.lang.String, w:int, x:double, y:char, a:double, c:char, _:java.lang.String, d:char +// TestKt$box$1:17: +// TestKt:7: $dstr$x$_u24__u24$y:A, $noName_1:java.lang.String, w:int, x:double, y:char, a:double, c:char, _:java.lang.String, d:char +// TestKt$box$1:17: $dstr$x$_u24__u24$y:A, $noName_1:java.lang.String, w:int, x:double, y:char, a:double, c:char, _:java.lang.String, d:char +// TestKt$box$1:21: +// TestKt$box$1.invoke(java.lang.Object, java.lang.Object, java.lang.Object)+19: a:A, block:TestKt$box$1 +// TestKt:5: a:A, block:TestKt$box$1 +// TestKt:11: +// TestKt:23: \ No newline at end of file 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 5032359f7a3..613adbd8841 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 @@ -11,7 +11,7 @@ import com.intellij.util.SystemProperties import com.sun.jdi.VirtualMachine import com.sun.jdi.event.* import com.sun.jdi.request.EventRequest -import com.sun.jdi.request.EventRequest.SUSPEND_NONE +import com.sun.jdi.request.EventRequest.SUSPEND_ALL import com.sun.jdi.request.StepRequest import com.sun.tools.jdi.SocketAttachingConnector import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection @@ -53,7 +53,7 @@ abstract class AbstractDebugTest : CodegenTestCase() { val methodExitReq = manager.createMethodExitRequest() methodExitReq.addClassFilter(TEST_CLASS) - methodExitReq.setSuspendPolicy(EventRequest.SUSPEND_NONE) + methodExitReq.setSuspendPolicy(EventRequest.SUSPEND_ALL) methodExitReq.enable() } @@ -178,7 +178,7 @@ abstract class AbstractDebugTest : CodegenTestCase() { var inBoxMethod = false vmLoop@ while (true) { - val eventSet = virtualMachine.eventQueue().remove() + val eventSet = virtualMachine.eventQueue().remove(100) for (event in eventSet) { when (event) { is VMDeathEvent, is VMDisconnectEvent -> { @@ -192,7 +192,7 @@ abstract class AbstractDebugTest : CodegenTestCase() { if (!inBoxMethod && event.location().method().name() == BOX_METHOD) { if (manager.stepRequests().isEmpty()) { val stepReq = manager.createStepRequest(event.thread(), StepRequest.STEP_LINE, StepRequest.STEP_INTO) - stepReq.setSuspendPolicy(SUSPEND_NONE) + stepReq.setSuspendPolicy(SUSPEND_ALL) stepReq.addClassExclusionFilter("java.*") stepReq.addClassExclusionFilter("sun.*") stepReq.addClassExclusionFilter("kotlin.*") @@ -206,17 +206,21 @@ abstract class AbstractDebugTest : CodegenTestCase() { is StepEvent -> { // Handle the case where an Exception causing program to exit without MethodExitEvent. if (inBoxMethod && event.location().method().name() == "run") { + virtualMachine.resume() break@vmLoop } if (inBoxMethod) { storeStep(loggedItems, event) } + virtualMachine.resume() } is MethodExitEvent -> { if (event.location().method().name() == BOX_METHOD) { manager.stepRequests().map { it.disable() } + virtualMachine.resume() break@vmLoop } + virtualMachine.resume() } else -> { throw IllegalStateException("event not handled: $event") diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractIrLocalVariableTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractIrLocalVariableTest.kt new file mode 100644 index 00000000000..24302350b63 --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractIrLocalVariableTest.kt @@ -0,0 +1,12 @@ +package org.jetbrains.kotlin.codegen.debugInformation + +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JVMConfigurationKeys + +abstract class AbstractIrLocalVariableTest : AbstractLocalVariableTest() { + + override fun updateConfiguration(configuration: CompilerConfiguration) { + super.updateConfiguration(configuration) + configuration.put(JVMConfigurationKeys.IR, true) + } +} \ No newline at end of file diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractLocalVariableTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractLocalVariableTest.kt new file mode 100644 index 00000000000..ec6e5704545 --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/debugInformation/AbstractLocalVariableTest.kt @@ -0,0 +1,74 @@ +package org.jetbrains.kotlin.codegen.debugInformation + +import com.sun.jdi.LocalVariable +import com.sun.jdi.StackFrame +import com.sun.jdi.VirtualMachine +import com.sun.jdi.event.Event +import com.sun.jdi.event.LocatableEvent +import junit.framework.TestCase +import org.junit.AfterClass +import org.junit.BeforeClass +import java.io.File + +abstract class AbstractLocalVariableTest : AbstractDebugTest() { + + override val virtualMachine: VirtualMachine = Companion.virtualMachine + override val proxyPort: Int = Companion.proxyPort + + companion object { + const val LOCAL_VARIABLES = "// LOCAL VARIABLES" + var proxyPort = 0 + lateinit var process: Process + lateinit var virtualMachine: VirtualMachine + + @BeforeClass + @JvmStatic + fun setUpTest() { + val (process, port) = startDebuggeeProcess() + this.process = process + virtualMachine = attachDebugger(port) + setUpVM(virtualMachine) + + proxyPort = getProxyPort(process) + } + + @AfterClass + @JvmStatic + fun tearDownTest() { + process.destroy() + } + } + + override fun storeStep(loggedItems: ArrayList, event: Event) { + waitUntil { (event as LocatableEvent).thread().isSuspended } + val visibleVars = (event as LocatableEvent) + .thread() + .frame(0) + .visibleVariables() + .map { variable -> toRecord(event.thread().frame(0), variable) } + .joinToString(", ") + loggedItems.add("${event.location()}: $visibleVars".trim()) + } + + override fun checkResult(wholeFile: File, loggedItems: List) { + val expectedLocalVariables = wholeFile + .readLines() + .dropWhile { !it.startsWith(LOCAL_VARIABLES) } + .drop(1) + .map { it.drop(3) } + .joinToString("\n") + val actualLocalVariables = loggedItems.joinToString("\n") + + TestCase.assertEquals(expectedLocalVariables, actualLocalVariables) + } + + private fun toRecord(frame: StackFrame, variable: LocalVariable): String { + return "${variable.name()}:${frame.getValue(variable)?.type()?.name() ?: "null"}" + } + + private fun waitUntil(condition: () -> Boolean) { + while (!condition()) { + Thread.sleep(10) + } + } +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrLocalVariableTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrLocalVariableTestGenerated.java new file mode 100644 index 00000000000..a42dfc9658a --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/IrLocalVariableTestGenerated.java @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.debugInformation; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/debug/localVariables") +@TestDataPath("$PROJECT_ROOT") +@RunWith(BlockJUnit4ClassRunner.class) +public class IrLocalVariableTestGenerated extends AbstractIrLocalVariableTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + @Test + public void testAllFilesPresentInLocalVariables() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/debug/localVariables"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("copyFunction.kt") + public void testCopyFunction() throws Exception { + runTest("compiler/testData/debug/localVariables/copyFunction.kt"); + } + + @Test + @TestMetadata("destructuringInFor.kt") + public void testDestructuringInFor() throws Exception { + runTest("compiler/testData/debug/localVariables/destructuringInFor.kt"); + } + + @Test + @TestMetadata("destructuringInLambdas.kt") + public void testDestructuringInLambdas() throws Exception { + runTest("compiler/testData/debug/localVariables/destructuringInLambdas.kt"); + } + + @Test + @TestMetadata("localFun.kt") + public void testLocalFun() throws Exception { + runTest("compiler/testData/debug/localVariables/localFun.kt"); + } + + @Test + @TestMetadata("underscoreNames.kt") + public void testUnderscoreNames() throws Exception { + runTest("compiler/testData/debug/localVariables/underscoreNames.kt"); + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/LocalVariableTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/LocalVariableTestGenerated.java new file mode 100644 index 00000000000..6f5da91a360 --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/codegen/debugInformation/LocalVariableTestGenerated.java @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.debugInformation; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/debug/localVariables") +@TestDataPath("$PROJECT_ROOT") +@RunWith(BlockJUnit4ClassRunner.class) +public class LocalVariableTestGenerated extends AbstractLocalVariableTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + @Test + public void testAllFilesPresentInLocalVariables() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/debug/localVariables"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @Test + @TestMetadata("copyFunction.kt") + public void testCopyFunction() throws Exception { + runTest("compiler/testData/debug/localVariables/copyFunction.kt"); + } + + @Test + @TestMetadata("destructuringInFor.kt") + public void testDestructuringInFor() throws Exception { + runTest("compiler/testData/debug/localVariables/destructuringInFor.kt"); + } + + @Test + @TestMetadata("destructuringInLambdas.kt") + public void testDestructuringInLambdas() throws Exception { + runTest("compiler/testData/debug/localVariables/destructuringInLambdas.kt"); + } + + @Test + @TestMetadata("localFun.kt") + public void testLocalFun() throws Exception { + runTest("compiler/testData/debug/localVariables/localFun.kt"); + } + + @Test + @TestMetadata("underscoreNames.kt") + public void testUnderscoreNames() throws Exception { + runTest("compiler/testData/debug/localVariables/underscoreNames.kt"); + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt b/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt index 35ddb85c96c..c8886ab8a07 100644 --- a/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt +++ b/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt @@ -14,7 +14,9 @@ import org.jetbrains.kotlin.checkers.* import org.jetbrains.kotlin.checkers.javac.* import org.jetbrains.kotlin.cli.AbstractCliTest import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.codegen.debugInformation.AbstractIrLocalVariableTest import org.jetbrains.kotlin.codegen.debugInformation.AbstractIrSteppingTest +import org.jetbrains.kotlin.codegen.debugInformation.AbstractLocalVariableTest import org.jetbrains.kotlin.codegen.debugInformation.AbstractSteppingTest import org.jetbrains.kotlin.codegen.defaultConstructor.AbstractDefaultArgumentsReflectionTest import org.jetbrains.kotlin.codegen.flags.AbstractWriteFlagsTest @@ -373,6 +375,10 @@ fun main(args: Array) { model("debug/stepping", targetBackend = TargetBackend.JVM) } + testClass(useJunit4 = true) { + model("debug/localVariables", targetBackend = TargetBackend.JVM) + } + testClass { model("serialization/local") } @@ -466,6 +472,10 @@ fun main(args: Array) { model("debug/stepping", targetBackend = TargetBackend.JVM_IR) } + testClass(useJunit4 = true) { + model("debug/localVariables", targetBackend = TargetBackend.JVM_IR) + } + testClass { model("codegen/boxInline", targetBackend = TargetBackend.JVM_IR) }