[debugger, kotlin] Smart step into: provide sync method for tests

* do not block DMT
* fix flaky tests

GitOrigin-RevId: d9f319d158d1f79f8fb1e664b43d72e7cf8403ab
This commit is contained in:
Maksim Zuev
2024-09-27 16:17:27 +02:00
committed by intellij-monorepo-bot
parent 436f5dd33f
commit 2fc7868ad6
3 changed files with 27 additions and 39 deletions

View File

@@ -25,6 +25,7 @@ import com.intellij.psi.util.parents
import com.intellij.util.Range
import com.intellij.util.containers.OrderedSet
import com.sun.jdi.Location
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.asPromise
import org.jetbrains.kotlin.idea.base.psi.getTopmostElementAtOffset
@@ -53,9 +54,12 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
findSmartStepTargetsInternal(position, session)
}.asPromise()
override fun findSmartStepTargets(position: SourcePosition): List<SmartStepTarget> =
override fun findSmartStepTargets(position: SourcePosition) = findSmartStepTargetsSync(position, null)
@ApiStatus.Internal
fun findSmartStepTargetsSync(position: SourcePosition, session: DebuggerSession?): List<SmartStepTarget> =
runBlockingCancellable {
findSmartStepTargetsInternal(position, null)
findSmartStepTargetsInternal(position, session)
}
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? =

View File

@@ -1,11 +1,11 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto
import com.intellij.debugger.engine.PositionManagerAsync
import com.intellij.debugger.PositionManager
import com.intellij.openapi.application.readAction
import com.intellij.psi.util.parentOfType
import com.sun.jdi.Location
import kotlinx.coroutines.future.await
import org.jetbrains.kotlin.idea.debugger.base.util.safeGetSourcePosition
import org.jetbrains.kotlin.idea.debugger.base.util.safeMethod
import org.jetbrains.kotlin.idea.debugger.core.getInlineFunctionAndArgumentVariablesToBordersMap
import org.jetbrains.kotlin.idea.debugger.core.isInlineFunctionMarkerVariableName
@@ -56,7 +56,7 @@ internal class KotlinSmartStepTargetFiltererAdapter(
suspend fun visitTrace(
targetFilterer: KotlinSmartStepTargetFilterer,
positionManager: PositionManagerAsync
positionManager: PositionManager
): Pair<List<KotlinMethodSmartStepTarget>, List<KotlinMethodSmartStepTarget>> {
for (element in visitedTrace) {
visitTraceElement(element, targetFilterer, positionManager)
@@ -72,7 +72,7 @@ internal class KotlinSmartStepTargetFiltererAdapter(
private suspend fun visitTraceElement(
element: BytecodeTraceElement,
targetFilterer: KotlinSmartStepTargetFilterer,
positionManager: PositionManagerAsync
positionManager: PositionManager
) {
when (element) {
is BytecodeTraceElement.InlineCall -> {
@@ -108,7 +108,7 @@ private fun extractInlineCalls(location: Location): List<InlineCallInfo> = locat
// Filter already visible variable to support smart-step-into while inside an inline function
.filterNot { location.codeIndex() in it.bciRange }
private suspend fun getCalledInlineFunction(positionManager: PositionManagerAsync, location: Location): KtNamedFunction? {
val sourcePosition = positionManager.getSourcePositionAsync(location).await()
return readAction { sourcePosition?.elementAt?.parentOfType<KtNamedFunction>() }
private suspend fun getCalledInlineFunction(positionManager: PositionManager, location: Location): KtNamedFunction? {
val sourcePosition = positionManager.safeGetSourcePosition(location) ?: return null
return readAction { sourcePosition.elementAt?.parentOfType<KtNamedFunction>() }
}

View File

@@ -21,9 +21,7 @@ import com.intellij.execution.process.ProcessOutputTypes
import com.intellij.jarRepository.JarRepositoryManager
import com.intellij.jarRepository.RemoteRepositoryDescription
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.components.ComponentManagerEx
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.libraries.ui.OrderRoot
@@ -32,11 +30,6 @@ import com.intellij.testFramework.runInEdtAndWait
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.impl.XSourcePositionImpl
import junit.framework.AssertionFailedError
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.concurrency.asDeferred
import org.jetbrains.idea.maven.aether.ArtifactKind
import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor
import org.jetbrains.kotlin.idea.base.test.InTextDirectivesUtils.areLogErrorsIgnored
@@ -189,9 +182,6 @@ abstract class KotlinDescriptorTestCaseWithStepping : KotlinDescriptorTestCase()
dp.managerThread.schedule(stepOverCommand)
}
private fun launch(block: suspend CoroutineScope.() -> Unit) =
(project as ComponentManagerEx).getCoroutineScope().launch(block = block)
private fun process(instruction: SteppingInstruction) {
fun loop(count: Int, block: SuspendContextImpl.() -> Unit) {
repeat(count) {
@@ -206,20 +196,18 @@ abstract class KotlinDescriptorTestCaseWithStepping : KotlinDescriptorTestCase()
SteppingInstructionKind.StepOut -> loop(instruction.arg) { doStepOut() }
SteppingInstructionKind.StepOver -> loop(instruction.arg) { doStepOver() }
SteppingInstructionKind.ForceStepOver -> loop(instruction.arg) { doStepOver(ignoreBreakpoints = true) }
SteppingInstructionKind.SmartStepInto -> loop(instruction.arg) { launch { doSmartStepInto() } }
SteppingInstructionKind.SmartStepIntoByIndex -> doOnBreakpoint { launch { doSmartStepInto(instruction.arg) } }
SteppingInstructionKind.SmartStepInto -> loop(instruction.arg) { doSmartStepInto() }
SteppingInstructionKind.SmartStepIntoByIndex -> doOnBreakpoint { doSmartStepInto(instruction.arg) }
SteppingInstructionKind.Resume -> loop(instruction.arg) { resume(this) }
SteppingInstructionKind.SmartStepTargetsExpectedNumber ->
doOnBreakpoint {
launch {
checkNumberOfSmartStepTargets(instruction.arg)
this@KotlinDescriptorTestCaseWithStepping.resume(this@doOnBreakpoint)
}
checkNumberOfSmartStepTargets(instruction.arg)
resume(this)
}
}
}
private suspend fun checkNumberOfSmartStepTargets(expectedNumber: Int) {
private fun checkNumberOfSmartStepTargets(expectedNumber: Int) {
val smartStepFilters = createSmartStepIntoFilters()
try {
assertEquals(
@@ -232,7 +220,7 @@ abstract class KotlinDescriptorTestCaseWithStepping : KotlinDescriptorTestCase()
}
}
private suspend fun SuspendContextImpl.doSmartStepInto(chooseFromList: Int = 0) {
private fun SuspendContextImpl.doSmartStepInto(chooseFromList: Int = 0) {
this.doSmartStepInto(chooseFromList, false)
}
@@ -280,7 +268,7 @@ abstract class KotlinDescriptorTestCaseWithStepping : KotlinDescriptorTestCase()
protected open fun extraPrintContext(context: SuspendContextImpl) {}
private suspend fun SuspendContextImpl.doSmartStepInto(chooseFromList: Int, ignoreFilters: Boolean) {
private fun SuspendContextImpl.doSmartStepInto(chooseFromList: Int, ignoreFilters: Boolean) {
val filters = createSmartStepIntoFilters()
if (chooseFromList == 0) {
if (filters.isEmpty()) {
@@ -303,26 +291,22 @@ abstract class KotlinDescriptorTestCaseWithStepping : KotlinDescriptorTestCase()
elementAt.getElementTextWithContext()
}
private suspend fun createSmartStepIntoFilters(): List<MethodFilter> {
val position = debuggerContext.sourcePosition
val stepTargets = withContext(Dispatchers.Default) {
KotlinSmartStepIntoHandler()
.findStepIntoTargets(position, debuggerSession)
.asDeferred().await()
}
private fun createSmartStepIntoFilters(): List<MethodFilter> {
val stepTargets = KotlinSmartStepIntoHandler()
.findSmartStepTargetsSync(debuggerContext.sourcePosition, debuggerSession)
// the resulting order is different from the order in code when stepping some methods are filtered
// due to de-prioritisation in JvmSmartStepIntoHandler.reorderWithSteppingFilters
if (readAction { stepTargets.none { DebugProcessImpl.isClassFiltered(it.className)} }) {
if (runReadAction { stepTargets.none { DebugProcessImpl.isClassFiltered(it.className)} }) {
try {
assertEquals("Smart step targets are not sorted by position in tree",
stepTargets.sortedByPositionInTree().map { readAction { it.presentation } },
stepTargets.map { readAction { it.presentation } })
stepTargets.sortedByPositionInTree().map { runReadAction { it.presentation } },
stepTargets.map { runReadAction { it.presentation } })
} catch (e: AssertionFailedError) {
thrownExceptions.add(e)
}
}
return readAction {
return runReadAction {
stepTargets.mapNotNull { stepTarget ->
when (stepTarget) {
is KotlinSmartStepTarget -> stepTarget.createMethodFilter()