mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[debugger] Fixed NPE in Coroutines View
IDEA-374561 (cherry picked from commit 37474b08ca8a3c0751b36fa883756c1dbf8b94dc) IJ-CR-167001 GitOrigin-RevId: 5b92085c4b025cd28b0c572ea79648b493c19cbc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f5b575c1b9
commit
03000b963a
@@ -177,7 +177,8 @@ public final class CoroutinesDebugHelper {
|
||||
for (int i = 0; i < coroutineInfos.length; i++) {
|
||||
lastObservedStackTraces[i] = lastObservedStackTrace(coroutineInfos[i]);
|
||||
}
|
||||
dump[3] = lastObservedStackTraces;
|
||||
dump = Arrays.copyOf(dump, dump.length + 1);
|
||||
dump[4] = lastObservedStackTraces;
|
||||
return dump;
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.debugger.impl.DebuggerUtilsImpl.logError
|
||||
import com.intellij.rt.debugger.coroutines.CoroutinesDebugHelper
|
||||
import com.sun.jdi.ArrayReference
|
||||
import com.sun.jdi.StringReference
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.kotlin.idea.debugger.base.util.evaluate.DefaultExecutionContext
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.callMethodFromHelper
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoCache
|
||||
@@ -51,7 +52,8 @@ class CoroutineDebugProbesProxy(val suspendContext: SuspendContextImpl) {
|
||||
*
|
||||
* The corresponding properties [CoroutineInfoData.job] and [CoroutineInfoData.parentJob] are set to the obtained values.
|
||||
*/
|
||||
internal fun fetchAndSetJobsAndParentsForCoroutines(infos: List<CoroutineInfoData>): Boolean {
|
||||
@ApiStatus.Internal
|
||||
fun fetchAndSetJobsAndParentsForCoroutines(infos: List<CoroutineInfoData>): Boolean {
|
||||
val executionContext = suspendContext.executionContext() ?: return false
|
||||
val debugCoroutineInfos = infos.map { it.debugCoroutineInfoRef }
|
||||
val array = callMethodFromHelper(CoroutinesDebugHelper::class.java, executionContext, "getJobsAndParentsForCoroutines", debugCoroutineInfos)
|
||||
|
||||
@@ -40,13 +40,13 @@ internal class CoroutinesInfoFromJsonAndReferencesProvider(
|
||||
}
|
||||
|
||||
val coroutinesInfoAsJsonString = arrayValues[0].safeAs<StringReference>()?.value()
|
||||
?: error("The first element of the result array must be a string")
|
||||
?: error("The 1st element of the result array must be a string")
|
||||
val lastObservedThreadRefs = arrayValues[1].safeAs<ArrayReference>()?.toTypedList<ThreadReference?>()
|
||||
?: error("The second element of the result array must be an array")
|
||||
?: error("The 2nd element of the result array must be an array")
|
||||
val lastObservedFrameRefs = arrayValues[2].safeAs<ArrayReference>()?.toTypedList<ObjectReference?>()
|
||||
?: error("The third element of the result array must be an array")
|
||||
?: error("The 3rd element of the result array must be an array")
|
||||
val coroutineInfoRefs = arrayValues[3].safeAs<ArrayReference>()?.toTypedList<ObjectReference>()
|
||||
?: error("The fourth element of the result array must be an array")
|
||||
?: error("The 4th element of the result array must be an array")
|
||||
val coroutinesInfo = Gson().fromJson(coroutinesInfoAsJsonString, Array<CoroutineInfoFromJson>::class.java)
|
||||
|
||||
if (coroutineInfoRefs.size != lastObservedFrameRefs.size ||
|
||||
@@ -55,7 +55,7 @@ internal class CoroutinesInfoFromJsonAndReferencesProvider(
|
||||
error("Arrays must have equal sizes")
|
||||
}
|
||||
|
||||
return calculateCoroutineInfoData(coroutinesInfo, coroutineInfoRefs, lastObservedThreadRefs, lastObservedFrameRefs)
|
||||
return calculateCoroutineInfoData(coroutinesInfo, coroutineInfoRefs, lastObservedThreadRefs, lastObservedFrameRefs, null)
|
||||
}
|
||||
|
||||
fun dumpCoroutinesWithStacktraces(): List<CoroutineInfoData>? {
|
||||
@@ -63,18 +63,20 @@ internal class CoroutinesInfoFromJsonAndReferencesProvider(
|
||||
|
||||
val arrayValues = (array as? ArrayReference)?.values ?: return null
|
||||
|
||||
if (arrayValues.size != 4) {
|
||||
error("The result array of 'dumpCoroutinesWithStacktracesAsJson' should be of size 4")
|
||||
if (arrayValues.size != 5) {
|
||||
error("The result array of 'dumpCoroutinesWithStacktracesAsJson' should be of size 5")
|
||||
}
|
||||
|
||||
val coroutinesInfoAsJsonString = arrayValues[0].safeAs<StringReference>()?.value()
|
||||
?: error("The first element of the result array must be a string")
|
||||
?: error("The 1st element of the result array must be a string")
|
||||
val lastObservedThreadRefs = arrayValues[1].safeAs<ArrayReference>()?.toTypedList<ThreadReference?>()
|
||||
?: error("The second element of the result array must be an array")
|
||||
?: error("The 2nd element of the result array must be an array")
|
||||
val lastObservedFrameRefs = arrayValues[2].safeAs<ArrayReference>()?.toTypedList<ObjectReference?>()
|
||||
?: error("The third element of the result array must be an array")
|
||||
val lastObservedStackTraceJsons = arrayValues[3].safeAs<ArrayReference>()?.toTypedList<StringReference>()
|
||||
?: error("The fourth element of the result array must be an array")
|
||||
?: error("The 3rd element of the result array must be an array")
|
||||
val coroutineInfoRefs = arrayValues[3].safeAs<ArrayReference>()?.toTypedList<ObjectReference>()
|
||||
?: error("The 4th element of the result array must be an array")
|
||||
val lastObservedStackTraceJsons = arrayValues[4].safeAs<ArrayReference>()?.toTypedList<StringReference>()
|
||||
?: error("The 5th element of the result array must be an array")
|
||||
|
||||
val coroutinesInfo = Gson().fromJson(coroutinesInfoAsJsonString, Array<CoroutineInfoFromJson>::class.java)
|
||||
val lastObservedStackTraces: List<List<Location>> = lastObservedStackTraceJsons.map {
|
||||
@@ -85,11 +87,12 @@ internal class CoroutinesInfoFromJsonAndReferencesProvider(
|
||||
|
||||
if (lastObservedStackTraces.size != lastObservedFrameRefs.size ||
|
||||
lastObservedFrameRefs.size != coroutinesInfo.size ||
|
||||
coroutineInfoRefs.size != coroutinesInfo.size ||
|
||||
coroutinesInfo.size != lastObservedThreadRefs.size) {
|
||||
error("Arrays must have equal sizes")
|
||||
}
|
||||
|
||||
return calculateCoroutineInfoDataWithStacktraces(coroutinesInfo, lastObservedThreadRefs, lastObservedFrameRefs, lastObservedStackTraces)
|
||||
return calculateCoroutineInfoData(coroutinesInfo, coroutineInfoRefs, lastObservedThreadRefs, lastObservedFrameRefs, lastObservedStackTraces)
|
||||
}
|
||||
|
||||
private fun fallbackToOldMirrorDump(executionContext: DefaultExecutionContext): ArrayReference? {
|
||||
@@ -103,7 +106,8 @@ internal class CoroutinesInfoFromJsonAndReferencesProvider(
|
||||
coroutineInfos: Array<CoroutineInfoFromJson>,
|
||||
coroutineInfoRefs: List<ObjectReference>,
|
||||
lastObservedThreadRefs: List<ThreadReference?>,
|
||||
lastObservedFrameRefs: List<ObjectReference?>
|
||||
lastObservedFrameRefs: List<ObjectReference?>,
|
||||
lastObservedStackTraces: List<List<Location>>?
|
||||
): List<CoroutineInfoData> {
|
||||
return coroutineInfoRefs.mapIndexed { i, ref ->
|
||||
val info = coroutineInfos[i]
|
||||
@@ -115,28 +119,8 @@ internal class CoroutinesInfoFromJsonAndReferencesProvider(
|
||||
lastObservedFrame = lastObservedFrameRefs[i],
|
||||
lastObservedThread = lastObservedThreadRefs[i],
|
||||
debugCoroutineInfoRef = ref,
|
||||
stackFrameProvider = stackFramesProvider
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateCoroutineInfoDataWithStacktraces(
|
||||
coroutineInfos: Array<CoroutineInfoFromJson>,
|
||||
lastObservedThreadRefs: List<ThreadReference?>,
|
||||
lastObservedFrameRefs: List<ObjectReference?>,
|
||||
lastObservedStackTraces: List<List<Location>>
|
||||
): List<CoroutineInfoData> {
|
||||
return coroutineInfos.mapIndexed { i, info ->
|
||||
CoroutineInfoData(
|
||||
name = info.name,
|
||||
id = info.sequenceNumber,
|
||||
state = info.state,
|
||||
dispatcher = info.dispatcher,
|
||||
lastObservedFrame = lastObservedFrameRefs[i],
|
||||
lastObservedThread = lastObservedThreadRefs[i],
|
||||
debugCoroutineInfoRef = null,
|
||||
stackFrameProvider = null,
|
||||
lastObservedStackTrace = lastObservedStackTraces[i]
|
||||
stackFrameProvider = stackFramesProvider,
|
||||
lastObservedStackTrace = lastObservedStackTraces?.get(i) ?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.k2.debugger.test.cases
|
||||
|
||||
import org.jetbrains.kotlin.config.JvmClosureGenerationScheme
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.idea.debugger.test.*
|
||||
import org.jetbrains.kotlin.idea.k2.debugger.test.K2DebuggerTestCompilerFacility
|
||||
|
||||
abstract class AbstractK2IdeK1CoroutineViewJobHierarchyTest : AbstractCoroutineViewJobHierarchyTest() {
|
||||
|
||||
override fun createDebuggerTestCompilerFacility(
|
||||
testFiles: TestFiles,
|
||||
jvmTarget: JvmTarget,
|
||||
compileConfig: TestCompileConfiguration
|
||||
): DebuggerTestCompilerFacility {
|
||||
return K2DebuggerTestCompilerFacility(project, testFiles, jvmTarget, compileConfig)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractK2IdeK2CoroutineViewJobHierarchyTest : AbstractK2IdeK1CoroutineViewJobHierarchyTest() {
|
||||
|
||||
override val compileWithK2 = true
|
||||
override fun lambdasGenerationScheme() = JvmClosureGenerationScheme.INDY
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.jetbrains.kotlin.idea.k2.debugger.test.cases;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
|
||||
import org.jetbrains.kotlin.idea.base.test.TestRoot;
|
||||
import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
|
||||
* DO NOT MODIFY MANUALLY.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
@TestRoot("jvm-debugger/test/k2")
|
||||
@TestDataPath("$CONTENT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/coroutinesView")
|
||||
public class K2IdeK1CoroutineViewJobHierarchyTestGenerated extends AbstractK2IdeK1CoroutineViewJobHierarchyTest {
|
||||
@java.lang.Override
|
||||
@org.jetbrains.annotations.NotNull
|
||||
public final KotlinPluginMode getPluginMode() {
|
||||
return KotlinPluginMode.K2;
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy2.kt")
|
||||
public void testCoroutinesHierarchy2() throws Exception {
|
||||
runTest("../testData/coroutinesView/coroutinesHierarchy2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy3.kt")
|
||||
public void testCoroutinesHierarchy3() throws Exception {
|
||||
runTest("../testData/coroutinesView/coroutinesHierarchy3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("oneCoroutine.kt")
|
||||
public void testOneCoroutine() throws Exception {
|
||||
runTest("../testData/coroutinesView/oneCoroutine.kt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.jetbrains.kotlin.idea.k2.debugger.test.cases;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
|
||||
import org.jetbrains.kotlin.idea.base.test.TestRoot;
|
||||
import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
|
||||
* DO NOT MODIFY MANUALLY.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
@TestRoot("jvm-debugger/test/k2")
|
||||
@TestDataPath("$CONTENT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/coroutinesView")
|
||||
public class K2IdeK2CoroutineViewJobHierarchyTestGenerated extends AbstractK2IdeK2CoroutineViewJobHierarchyTest {
|
||||
@java.lang.Override
|
||||
@org.jetbrains.annotations.NotNull
|
||||
public final KotlinPluginMode getPluginMode() {
|
||||
return KotlinPluginMode.K2;
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy2.kt")
|
||||
public void testCoroutinesHierarchy2() throws Exception {
|
||||
runTest("../testData/coroutinesView/coroutinesHierarchy2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy3.kt")
|
||||
public void testCoroutinesHierarchy3() throws Exception {
|
||||
runTest("../testData/coroutinesView/coroutinesHierarchy3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("oneCoroutine.kt")
|
||||
public void testOneCoroutine() throws Exception {
|
||||
runTest("../testData/coroutinesView/oneCoroutine.kt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.debugger.test
|
||||
|
||||
import com.intellij.debugger.engine.executeOnDMT
|
||||
import com.intellij.debugger.impl.PrioritizedTask
|
||||
import org.jetbrains.kotlin.config.JvmClosureGenerationScheme
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineDebugProbesProxy
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
|
||||
abstract class AbstractCoroutineViewJobHierarchyTest : KotlinDescriptorTestCaseWithStackFrames() {
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
doOnBreakpoint {
|
||||
val suspendContext = this
|
||||
try {
|
||||
executeOnDMT(suspendContext, PrioritizedTask.Priority.NORMAL) {
|
||||
val coroutineDebugProxy = CoroutineDebugProbesProxy(suspendContext)
|
||||
val coroutineCache = coroutineDebugProxy.dumpCoroutines()
|
||||
if (coroutineCache.isOk()) {
|
||||
val cache = coroutineCache.cache
|
||||
val isHierarchyBuilt = coroutineDebugProxy.fetchAndSetJobsAndParentsForCoroutines(cache)
|
||||
if (isHierarchyBuilt) {
|
||||
printCoroutinesJobHierarchy(cache)
|
||||
} else {
|
||||
printCoroutineInfos(cache)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
resume(suspendContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun printCoroutineInfos(infos: List<CoroutineInfoData>) {
|
||||
for (info in infos) {
|
||||
out(0, info.name + " " + info.state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun printCoroutinesJobHierarchy(infos: List<CoroutineInfoData>) {
|
||||
val parentJobToChildCoroutineInfos = infos.groupBy { it.parentJob }
|
||||
val jobToCoroutineInfo = infos.associateBy { it.job }
|
||||
val rootJobs = infos.filter { it.parentJob == null }.mapNotNull { it.job }
|
||||
|
||||
out(0, "==== Hierarchy of coroutines =====")
|
||||
for (root in rootJobs) {
|
||||
val rootCoroutineInfo = jobToCoroutineInfo[root]
|
||||
if (rootCoroutineInfo == null) {
|
||||
out(0, root)
|
||||
} else {
|
||||
printInfo(rootCoroutineInfo, parentJobToChildCoroutineInfos, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun printInfo(info: CoroutineInfoData, jobToChildCoroutineInfos: Map<String?, List<CoroutineInfoData>>, indent: Int) {
|
||||
out(indent, info.name + " " + info.state)
|
||||
val children = jobToChildCoroutineInfos[info.job] ?: emptyList()
|
||||
for (child in children) {
|
||||
printInfo(child, jobToChildCoroutineInfos, indent + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class AbstractK1IdeK2CoroutineViewTest : AbstractCoroutineViewJobHierarchyTest() {
|
||||
override val compileWithK2 = true
|
||||
|
||||
override fun lambdasGenerationScheme() = JvmClosureGenerationScheme.INDY
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
|
||||
import org.jetbrains.kotlin.idea.base.test.TestRoot;
|
||||
import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
|
||||
* DO NOT MODIFY MANUALLY.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
@TestRoot("jvm-debugger/test")
|
||||
@TestDataPath("$CONTENT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/coroutinesView")
|
||||
public class CoroutineViewJobHierarchyTestGenerated extends AbstractCoroutineViewJobHierarchyTest {
|
||||
@java.lang.Override
|
||||
@org.jetbrains.annotations.NotNull
|
||||
public final KotlinPluginMode getPluginMode() {
|
||||
return KotlinPluginMode.K1;
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy2.kt")
|
||||
public void testCoroutinesHierarchy2() throws Exception {
|
||||
runTest("testData/coroutinesView/coroutinesHierarchy2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy3.kt")
|
||||
public void testCoroutinesHierarchy3() throws Exception {
|
||||
runTest("testData/coroutinesView/coroutinesHierarchy3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("oneCoroutine.kt")
|
||||
public void testOneCoroutine() throws Exception {
|
||||
runTest("testData/coroutinesView/oneCoroutine.kt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
|
||||
import org.jetbrains.kotlin.idea.base.test.TestRoot;
|
||||
import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
|
||||
* DO NOT MODIFY MANUALLY.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
@TestRoot("jvm-debugger/test")
|
||||
@TestDataPath("$CONTENT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/coroutinesView")
|
||||
public class K1IdeK2CoroutineViewTestGenerated extends AbstractK1IdeK2CoroutineViewTest {
|
||||
@java.lang.Override
|
||||
@org.jetbrains.annotations.NotNull
|
||||
public final KotlinPluginMode getPluginMode() {
|
||||
return KotlinPluginMode.K1;
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy2.kt")
|
||||
public void testCoroutinesHierarchy2() throws Exception {
|
||||
runTest("testData/coroutinesView/coroutinesHierarchy2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("coroutinesHierarchy3.kt")
|
||||
public void testCoroutinesHierarchy3() throws Exception {
|
||||
runTest("testData/coroutinesView/coroutinesHierarchy3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("oneCoroutine.kt")
|
||||
public void testOneCoroutine() throws Exception {
|
||||
runTest("testData/coroutinesView/oneCoroutine.kt");
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,11 @@ abstract class KotlinDescriptorTestCaseWithStackFrames : KotlinDescriptorTestCas
|
||||
out(INDENT_VARIABLES, "(${variables.joinToString()})")
|
||||
}
|
||||
|
||||
private fun out(text: String) {
|
||||
protected fun out(text: String) {
|
||||
println(text, ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
|
||||
private fun out(indent: Int, text: String) {
|
||||
protected fun out(indent: Int, text: String) {
|
||||
println("\t".repeat(indent) + text, ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package coroutinesView
|
||||
|
||||
// ATTACH_LIBRARY: maven(org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2)-javaagent
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
fun main() {
|
||||
runBlocking(CoroutineName("root")) {
|
||||
println()
|
||||
//Breakpoint!
|
||||
delay(11L)
|
||||
println()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
LineBreakpoint created at coroutinesHierarchy2.kt:11
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
coroutinesHierarchy2.kt:11
|
||||
==== Hierarchy of coroutines =====
|
||||
root RUNNING
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,57 @@
|
||||
package coroutinesView
|
||||
|
||||
// ATTACH_LIBRARY: maven(org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2)-javaagent
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
fun main() {
|
||||
runBlocking(CoroutineName("root")) {
|
||||
test()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun test() {
|
||||
coroutineScope {
|
||||
launch(CoroutineName("launch1")) {
|
||||
delay(1)
|
||||
println()
|
||||
delay(1)
|
||||
}
|
||||
launch(CoroutineName("launch2")) {
|
||||
coroutineScope {
|
||||
val a = async(CoroutineName("childAsync1")) {
|
||||
val coroutineVar = 1
|
||||
delay(100)
|
||||
println("coroutineVar=$coroutineVar")
|
||||
6
|
||||
}
|
||||
|
||||
val b = async(CoroutineName("childAsync2")) {
|
||||
val coroutineVar = 2
|
||||
delay(100)
|
||||
println("coroutineVar=$coroutineVar")
|
||||
7
|
||||
}
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Default) {
|
||||
coroutineScope {
|
||||
val a = async(CoroutineName("async1")) {
|
||||
val coroutineVar = 1
|
||||
delay(100)
|
||||
println("coroutineVar=$coroutineVar") // ← breakpoint
|
||||
6
|
||||
}
|
||||
|
||||
val b = async(CoroutineName("async2")) {
|
||||
val coroutineVar = 2
|
||||
delay(100)
|
||||
println("coroutineVar=$coroutineVar")
|
||||
7
|
||||
}
|
||||
//Breakpoint!
|
||||
println("result: ${a.await() * b.await()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
LineBreakpoint created at coroutinesHierarchy3.kt:53
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
coroutinesHierarchy3.kt:53
|
||||
==== Hierarchy of coroutines =====
|
||||
root RUNNING
|
||||
launch2 SUSPENDED
|
||||
childAsync1 SUSPENDED
|
||||
childAsync2 SUSPENDED
|
||||
async1 SUSPENDED
|
||||
async2 SUSPENDED
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,30 @@
|
||||
package coroutinesView
|
||||
|
||||
// ATTACH_LIBRARY: maven(org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2)-javaagent
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
fun main() {
|
||||
runBlocking(CoroutineName("root")) {
|
||||
a()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun a() {
|
||||
val a = "a"
|
||||
b(a)
|
||||
val aLate = a
|
||||
}
|
||||
|
||||
suspend fun b(paramA: String) {
|
||||
yield()
|
||||
val b = "b"
|
||||
c(b)
|
||||
val dead = paramA
|
||||
}
|
||||
|
||||
suspend fun c(paramB: String) {
|
||||
val c = "c"
|
||||
//Breakpoint!
|
||||
val dead = paramB
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
LineBreakpoint created at oneCoroutine.kt:29
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
oneCoroutine.kt:29
|
||||
==== Hierarchy of coroutines =====
|
||||
root RUNNING
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -299,6 +299,12 @@ private fun assembleWorkspace(): TWorkspace = workspace(KotlinPluginMode.K1) {
|
||||
}
|
||||
}
|
||||
|
||||
listOf(AbstractCoroutineViewJobHierarchyTest::class, AbstractK1IdeK2CoroutineViewTest::class).forEach {
|
||||
testClass(it) {
|
||||
model("coroutinesView")
|
||||
}
|
||||
}
|
||||
|
||||
testClass<AbstractIrSequenceTraceTestCase> { // TODO: implement mapping logic for terminal operations
|
||||
model("sequence/streams/sequence", excludedDirectories = listOf("terminal"))
|
||||
}
|
||||
|
||||
@@ -98,6 +98,15 @@ internal fun MutableTWorkspace.generateK2DebuggerTests() {
|
||||
}
|
||||
}
|
||||
|
||||
listOf(
|
||||
AbstractK2IdeK2CoroutineViewJobHierarchyTest::class,
|
||||
AbstractK2IdeK1CoroutineViewJobHierarchyTest::class,
|
||||
).forEach {
|
||||
testClass(it) {
|
||||
model("coroutinesView")
|
||||
}
|
||||
}
|
||||
|
||||
//testClass<AbstractSequenceTraceTestCase> { // TODO: implement mapping logic for terminal operations
|
||||
// model("sequence/streams/sequence", excludedDirectories = listOf("terminal"))
|
||||
//}
|
||||
|
||||
Reference in New Issue
Block a user