Reformat: debugger.stepping

This commit is contained in:
Nikolay Krasko
2018-06-13 17:20:35 +03:00
parent 89f0b11765
commit 64f14ad89b
13 changed files with 191 additions and 184 deletions
@@ -27,7 +27,8 @@ public class DebugProcessImplHelper {
public static DebugProcessImpl.StepOverCommand createStepOverCommandWithCustomFilter(
SuspendContextImpl suspendContext,
boolean ignoreBreakpoints,
KotlinSuspendCallStepOverFilter methodFilter) {
KotlinSuspendCallStepOverFilter methodFilter
) {
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
return debugProcess.new StepOverCommand(suspendContext, ignoreBreakpoints, StepRequest.STEP_LINE) {
@NotNull
@@ -36,8 +36,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
import org.jetbrains.kotlin.resolve.DescriptorUtils
class KotlinBasicStepMethodFilter(
targetDescriptor: CallableMemberDescriptor,
private val myCallingExpressionLines: Range<Int>
targetDescriptor: CallableMemberDescriptor,
private val myCallingExpressionLines: Range<Int>
) : NamedMethodFilter {
private val myTargetMethodName: String = when (targetDescriptor) {
is ClassDescriptor, is ConstructorDescriptor -> "<init>"
@@ -46,7 +46,7 @@ class KotlinBasicStepMethodFilter(
}
private val _targetDescriptor = SofterReference(
(targetDescriptor as? FunctionDescriptor)?.let { SamCodegenUtil.getOriginalIfSamAdapter(it) } ?: targetDescriptor
(targetDescriptor as? FunctionDescriptor)?.let { SamCodegenUtil.getOriginalIfSamAdapter(it) } ?: targetDescriptor
)
override fun getCallingExpressionLines() = myCallingExpressionLines
@@ -22,18 +22,18 @@ import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.util.Range
import com.sun.jdi.Location
import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME
import org.jetbrains.kotlin.idea.refactoring.isMultiLine
import org.jetbrains.kotlin.idea.debugger.isInsideInlineArgument
import org.jetbrains.kotlin.idea.refactoring.isMultiLine
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.util.OperatorNameConventions
class KotlinLambdaMethodFilter(
private val lambda: KtFunction,
private val myCallingExpressionLines: Range<Int>,
private val isInline: Boolean,
private val isSuspend: Boolean
): BreakpointStepMethodFilter {
private val lambda: KtFunction,
private val myCallingExpressionLines: Range<Int>,
private val isInline: Boolean,
private val isSuspend: Boolean
) : BreakpointStepMethodFilter {
private val myFirstStatementPosition: SourcePosition?
private val myLastStatementLine: Int
@@ -52,8 +52,7 @@ class KotlinLambdaMethodFilter(
}
myFirstStatementPosition = firstStatementPosition
myLastStatementLine = if (lastStatementPosition != null) lastStatementPosition.line else -1
}
else {
} else {
myFirstStatementPosition = SourcePosition.createFromElement(lambda)
myLastStatementLine = myFirstStatementPosition!!.line
}
@@ -78,7 +77,7 @@ class KotlinLambdaMethodFilter(
if (isSuspend) {
return name == DO_RESUME_METHOD_NAME
}
return name == OperatorNameConventions.INVOKE.asString()
}
@@ -26,12 +26,12 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
import javax.swing.Icon
class KotlinLambdaSmartStepTarget(
label: String,
highlightElement: KtFunction,
lines: Range<Int>,
val isInline: Boolean,
val isSuspend: Boolean
): SmartStepTarget(label, highlightElement, true, lines) {
label: String,
highlightElement: KtFunction,
lines: Range<Int>,
val isInline: Boolean,
val isSuspend: Boolean
) : SmartStepTarget(label, highlightElement, true, lines) {
override fun getIcon(): Icon = KotlinIcons.LAMBDA
fun getLambda() = highlightElement as KtFunction
@@ -13,11 +13,11 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import javax.swing.Icon
class KotlinMethodSmartStepTarget(
val descriptor: CallableMemberDescriptor,
label: String,
highlightElement: PsiElement,
lines: Range<Int>
): SmartStepTarget(label, highlightElement, false, lines) {
val descriptor: CallableMemberDescriptor,
label: String,
highlightElement: PsiElement,
lines: Range<Int>
) : SmartStepTarget(label, highlightElement, false, lines) {
override fun getIcon(): Icon? {
return when {
descriptor.isExtension -> KotlinIcons.EXTENSION_FUNCTION
@@ -32,13 +32,12 @@ class KotlinSimpleGetterProvider : SimplePropertyGetterProvider {
if (accessor != null && accessor.isGetter) {
val body = accessor.bodyExpression
return when (body) {
// val a: Int get() { return field }
is KtBlockExpression -> {
// val a: Int get() { return field }
val returnedExpression = (body.statements.singleOrNull() as? KtReturnExpression)?.returnedExpression ?: return false
returnedExpression.textMatches("field")
}
// val a: Int get() = field
is KtExpression -> body.textMatches("field")
is KtExpression -> body.textMatches("field") // val a: Int get() = field
else -> false
}
}
@@ -54,8 +54,8 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
val elementAtOffset = position.elementAt ?: return emptyList()
val element = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, elementAtOffset.textRange.startOffset) as? KtElement ?:
return emptyList()
val element = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, elementAtOffset.textRange.startOffset) as? KtElement
?: return emptyList()
val elementTextRange = element.textRange ?: return emptyList()
@@ -67,7 +67,7 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
val result = OrderedSet<SmartStepTarget>()
// TODO support class initializers, local functions, delegated properties with specified type, setter for properties
element.accept(object: KtTreeVisitorVoid() {
element.accept(object : KtTreeVisitorVoid() {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
recordFunctionLiteral(lambdaExpression.functionLiteral)
}
@@ -87,8 +87,11 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
if (argument.arguments.any { getArgumentExpression(it) == function }) {
val resultingDescriptor = resolvedCall.resultingDescriptor
val label = KotlinLambdaSmartStepTarget.calcLabel(resultingDescriptor, param.name)
result.add(KotlinLambdaSmartStepTarget(
label, function, lines, InlineUtil.isInline(resultingDescriptor), param.type.isSuspendFunctionType))
result.add(
KotlinLambdaSmartStepTarget(
label, function, lines, InlineUtil.isInline(resultingDescriptor), param.type.isSuspendFunctionType
)
)
return true
}
}
@@ -96,7 +99,8 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
return false
}
private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
private fun getArgumentExpression(it: ValueArgument) =
(it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) {
// skip calls in object declarations
@@ -159,10 +163,10 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
val label = KotlinMethodSmartStepTarget.calcLabel(getterDescriptor)
result.add(KotlinMethodSmartStepTarget(getterDescriptor, label, expression, lines))
}
}
else {
} else {
val delegatedPropertyGetterDescriptor = delegatedResolvedCall.resultingDescriptor
val label = "${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
val label =
"${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
result.add(KotlinMethodSmartStepTarget(delegatedPropertyGetterDescriptor, label, expression, lines))
}
}
@@ -180,8 +184,7 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
(DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor) as? PsiMethod)?.let {
result.add(MethodSmartStepTarget(it, null, expression, false, lines))
}
}
else {
} else {
if (descriptor is ConstructorDescriptor && descriptor.isPrimary) {
val psiElement = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
if (psiElement is KtClass && psiElement.getAnonymousInitializers().isEmpty()) {
@@ -216,7 +219,8 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.callingExpressionLines!!)
is KotlinLambdaSmartStepTarget ->
KotlinLambdaMethodFilter(
stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline, stepTarget.isSuspend)
stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline, stepTarget.isSuspend
)
else -> super.createMethodFilter(stepTarget)
}
}
@@ -61,13 +61,15 @@ class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) {
// TODO: ask for better API
// Should be safe to use reflection as method is protected and not obfuscated
private fun doStep(
suspendContext: SuspendContextImpl,
stepThread: ThreadReferenceProxyImpl,
size: Int, depth: Int, hint: RequestHint) {
suspendContext: SuspendContextImpl,
stepThread: ThreadReferenceProxyImpl,
size: Int, depth: Int, hint: RequestHint
) {
val doStepMethod = DebugProcessImpl::class.java.getDeclaredMethod(
"doStep",
SuspendContextImpl::class.java, ThreadReferenceProxyImpl::class.java,
Integer.TYPE, Integer.TYPE, RequestHint::class.java)
"doStep",
SuspendContextImpl::class.java, ThreadReferenceProxyImpl::class.java,
Integer.TYPE, Integer.TYPE, RequestHint::class.java
)
doStepMethod.isAccessible = true
@@ -89,7 +91,8 @@ class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) {
return field.get(debuggerProcess) as T
}
private inner class StepOverInlineCommand(private val mySmartStepFilter: KotlinMethodFilter, private val myStepSize: Int) : KotlinStepAction() {
private inner class StepOverInlineCommand(private val mySmartStepFilter: KotlinMethodFilter, private val myStepSize: Int) :
KotlinStepAction() {
private fun getContextThread(suspendContext: SuspendContextImpl): ThreadReferenceProxyImpl? {
val contextThread = debuggerContext.threadProxy
return contextThread ?: suspendContext.thread
@@ -114,8 +117,7 @@ class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) {
}
if (isResumeOnlyCurrentThread && suspendContext.suspendPolicy == EventRequest.SUSPEND_ALL) {
suspendManager.resumeThread(suspendContext, thread)
}
else {
} else {
suspendManager.resume(suspendContext)
}
}
@@ -136,8 +138,7 @@ class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) {
try {
session.setIgnoreStepFiltersFlag(stepThread.frameCount())
}
catch (e: EvaluateException) {
} catch (e: EvaluateException) {
LOG.info(e)
}
@@ -25,11 +25,11 @@ import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.ktLocationInfo
class StepOverFilterData(
val lineNumber: Int,
val stepOverLines: Set<Int>,
val inlineRangeVariables: List<LocalVariable>,
val isDexDebug: Boolean,
val skipAfterCodeIndex: Long = -1
val lineNumber: Int,
val stepOverLines: Set<Int>,
val inlineRangeVariables: List<LocalVariable>,
val isDexDebug: Boolean,
val skipAfterCodeIndex: Long = -1
)
class KotlinStepOverInlineFilter(val project: Project, val data: StepOverFilterData) : KotlinMethodFilter {
@@ -16,22 +16,20 @@
package org.jetbrains.kotlin.idea.debugger.stepping
import com.intellij.debugger.SourcePosition
import com.intellij.debugger.engine.*
import com.intellij.debugger.engine.RequestHint
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.jdi.StackFrameProxy
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Computable
import com.sun.jdi.VMDisconnectedException
import com.sun.jdi.request.StepRequest
// Originally copied from RequestHint
class KotlinStepOverInlinedLinesHint(
stepThread: ThreadReferenceProxyImpl,
suspendContext: SuspendContextImpl,
methodFilter: KotlinMethodFilter) : RequestHint(stepThread, suspendContext, methodFilter) {
stepThread: ThreadReferenceProxyImpl,
suspendContext: SuspendContextImpl,
methodFilter: KotlinMethodFilter
) : RequestHint(stepThread, suspendContext, methodFilter) {
private val LOG = Logger.getInstance(KotlinStepOverInlinedLinesHint::class.java)
@@ -46,8 +44,7 @@ class KotlinStepOverInlinedLinesHint(
if (isTheSameFrame(context)) {
return if (filter.locationMatches(context, frameProxy.location())) {
STOP
}
else {
} else {
StepRequest.STEP_OVER
}
}
@@ -58,10 +55,8 @@ class KotlinStepOverInlinedLinesHint(
return StepRequest.STEP_OUT
}
}
catch (ignored: VMDisconnectedException) {
}
catch (e: EvaluateException) {
} catch (ignored: VMDisconnectedException) {
} catch (e: EvaluateException) {
LOG.error(e)
}
@@ -62,9 +62,9 @@ import org.jetbrains.kotlin.utils.keysToMap
class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
override fun getStepOverCommand(
suspendContext: SuspendContextImpl?,
ignoreBreakpoints: Boolean,
stepSize: Int
suspendContext: SuspendContextImpl?,
ignoreBreakpoints: Boolean,
stepSize: Int
): DebugProcessImpl.ResumeCommand? {
if (suspendContext == null || suspendContext.isResumed) return null
@@ -74,17 +74,18 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
@TestOnly
fun getStepOverCommand(
suspendContext: SuspendContextImpl,
ignoreBreakpoints: Boolean,
debuggerContext: DebuggerContextImpl
suspendContext: SuspendContextImpl,
ignoreBreakpoints: Boolean,
debuggerContext: DebuggerContextImpl
): DebugProcessImpl.ResumeCommand? {
return getStepOverCommand(suspendContext, ignoreBreakpoints, debuggerContext.sourcePosition)
}
private fun getStepOverCommand(
suspendContext: SuspendContextImpl,
ignoreBreakpoints: Boolean,
sourcePosition: SourcePosition): DebugProcessImpl.ResumeCommand? {
suspendContext: SuspendContextImpl,
ignoreBreakpoints: Boolean,
sourcePosition: SourcePosition
): DebugProcessImpl.ResumeCommand? {
val kotlinSourcePosition = KotlinSourcePosition.create(sourcePosition) ?: return null
if (isSpecialStepOverNeeded(kotlinSourcePosition)) {
@@ -95,14 +96,17 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
val location = suspendContext.debugProcess.invokeInManagerThread { suspendContext.frameProxy?.location() } ?: return null
if (isInSuspendMethod(location) && !isOnSuspendReturnOrReenter(location) && !isLastLineLocationInMethod(location)) {
return DebugProcessImplHelper.createStepOverCommandWithCustomFilter(
suspendContext, ignoreBreakpoints, KotlinSuspendCallStepOverFilter(sourcePosition.line, file, ignoreBreakpoints))
suspendContext, ignoreBreakpoints, KotlinSuspendCallStepOverFilter(sourcePosition.line, file, ignoreBreakpoints)
)
}
return null
}
data class KotlinSourcePosition(val file: KtFile, val function: KtNamedFunction,
val linesRange: IntRange, val sourcePosition: SourcePosition) {
data class KotlinSourcePosition(
val file: KtFile, val function: KtNamedFunction,
val linesRange: IntRange, val sourcePosition: SourcePosition
) {
companion object {
fun create(sourcePosition: SourcePosition): KotlinSourcePosition? {
val file = sourcePosition.file as? KtFile ?: return null
@@ -110,8 +114,8 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
val elementAt = sourcePosition.elementAt ?: return null
val containingFunction = elementAt.parents
.filterIsInstance<KtNamedFunction>()
.firstOrNull { !it.isLocal } ?: return null
.filterIsInstance<KtNamedFunction>()
.firstOrNull { !it.isLocal } ?: return null
val startLineNumber = containingFunction.getLineNumber(true) + 1
val endLineNumber = containingFunction.getLineNumber(false) + 1
@@ -193,14 +197,18 @@ private fun getInlineFunctionsIfAny(file: KtFile, offset: Int): List<KtNamedFunc
private fun getInlineArgumentsIfAny(inlineFunctionCalls: List<KtCallExpression>): List<KtFunction> {
return inlineFunctionCalls.flatMap {
it.valueArguments
.map(::getArgumentExpression)
.filterIsInstance<KtFunction>()
.map(::getArgumentExpression)
.filterIsInstance<KtFunction>()
}
}
private fun getArgumentExpression(it: ValueArgument) = (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
private fun getArgumentExpression(it: ValueArgument) =
(it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
private fun getInlineArgumentsCallsIfAny(sourcePosition: SourcePosition, declarationDescriptor: DeclarationDescriptor): List<KtCallExpression>? {
private fun getInlineArgumentsCallsIfAny(
sourcePosition: SourcePosition,
declarationDescriptor: DeclarationDescriptor
): List<KtCallExpression>? {
if (declarationDescriptor !is CallableDescriptor) return null
val valueParameters = declarationDescriptor.valueParameters.filter { it.type.isFunctionType }.toSet()
@@ -249,12 +257,11 @@ private fun findCallsOnPosition(sourcePosition: SourcePosition, filter: (KtCallE
val start = lineElement.startOffset
val end = lineElement.endOffset
val allFilteredCalls = CodeInsightUtils.
findElementsOfClassInRange(file, start, end, KtExpression::class.java)
.map { KtPsiUtil.getParentCallIfPresent(it as KtExpression) }
.filterIsInstance<KtCallExpression>()
.filter { filter(it) }
.toSet()
val allFilteredCalls = CodeInsightUtils.findElementsOfClassInRange(file, start, end, KtExpression::class.java)
.map { KtPsiUtil.getParentCallIfPresent(it as KtExpression) }
.filterIsInstance<KtCallExpression>()
.filter { filter(it) }
.toSet()
// It is necessary to check range because of multiline assign
var linesRange = lineNumber..lineNumber
@@ -267,16 +274,20 @@ private fun findCallsOnPosition(sourcePosition: SourcePosition, filter: (KtCallE
}
}
sealed class Action(val position: XSourcePositionImpl? = null,
val stepOverInlineData: StepOverFilterData? = null) {
sealed class Action(
val position: XSourcePositionImpl? = null,
val stepOverInlineData: StepOverFilterData? = null
) {
class STEP_OVER : Action() {
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) =
debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction(suspendContext)
debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction(suspendContext)
}
class STEP_OUT : Action() {
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) =
debugProcess.createStepOutCommand(suspendContext).contextAction(suspendContext)
}
class RUN_TO_CURSOR(position: XSourcePositionImpl) : Action(position) {
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
return runReadAction {
@@ -284,10 +295,12 @@ sealed class Action(val position: XSourcePositionImpl? = null,
}.contextAction(suspendContext)
}
}
class STEP_OVER_INLINED(stepOverInlineData: StepOverFilterData) : Action(stepOverInlineData = stepOverInlineData) {
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
return KotlinStepActionFactory(debugProcess).createKotlinStepOverInlineAction(
KotlinStepOverInlineFilter(debugProcess.project, stepOverInlineData!!)).contextAction(suspendContext)
KotlinStepOverInlineFilter(debugProcess.project, stepOverInlineData!!)
).contextAction(suspendContext)
}
}
@@ -299,26 +312,28 @@ interface KotlinMethodFilter : MethodFilter {
}
fun getStepOverAction(
location: Location,
kotlinSourcePosition: KotlinSteppingCommandProvider.KotlinSourcePosition,
frameProxy: StackFrameProxyImpl,
isDexDebug: Boolean
location: Location,
kotlinSourcePosition: KotlinSteppingCommandProvider.KotlinSourcePosition,
frameProxy: StackFrameProxyImpl,
isDexDebug: Boolean
): Action {
val inlineArgumentsToSkip = runReadAction {
getInlineCallFunctionArgumentsIfAny(kotlinSourcePosition.sourcePosition)
}
return getStepOverAction(location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange,
inlineArgumentsToSkip, frameProxy, isDexDebug)
return getStepOverAction(
location, kotlinSourcePosition.file, kotlinSourcePosition.linesRange,
inlineArgumentsToSkip, frameProxy, isDexDebug
)
}
fun getStepOverAction(
location: Location,
sourceFile: KtFile,
range: IntRange,
inlineFunctionArguments: List<KtElement>,
frameProxy: StackFrameProxyImpl,
isDexDebug: Boolean
location: Location,
sourceFile: KtFile,
range: IntRange,
inlineFunctionArguments: List<KtElement>,
frameProxy: StackFrameProxyImpl,
isDexDebug: Boolean
): Action {
location.declaringType() ?: return Action.STEP_OVER()
@@ -346,19 +361,18 @@ fun getStepOverAction(
try {
return nextLocation.ktFileName() == sourceFile.name
}
catch(e: AbsentInformationException) {
} catch (e: AbsentInformationException) {
return true
}
}
fun isBackEdgeLocation(): Boolean {
val previousSuitableLocation = methodLocations.reversed()
.dropWhile { it != location }
.drop(1)
.filter(::isThisMethodLocation)
.dropWhile { it.ktLineNumber() == location.ktLineNumber() }
.firstOrNull()
.dropWhile { it != location }
.drop(1)
.filter(::isThisMethodLocation)
.dropWhile { it.ktLineNumber() == location.ktLineNumber() }
.firstOrNull()
return previousSuitableLocation != null && previousSuitableLocation.ktLineNumber() > location.ktLineNumber()
}
@@ -366,10 +380,9 @@ fun getStepOverAction(
val patchedLocation = if (isBackEdgeLocation()) {
// Pretend we had already done a backing step
methodLocations
.filter(::isThisMethodLocation)
.firstOrNull { it.ktLineNumber() == location.ktLineNumber() } ?: location
}
else {
.filter(::isThisMethodLocation)
.firstOrNull { it.ktLineNumber() == location.ktLineNumber() } ?: location
} else {
location
}
@@ -394,12 +407,12 @@ fun getStepOverAction(
// We might erroneously extend this range too much when there's a call of function argument or other
// inline function in last statement of inline function. The list of inlineRangeVariables will be used later to overcome it.
val stepOverLocations = methodLocations
.dropWhile { it != patchedLocation }
.drop(1)
.dropWhile { it.ktLineNumber() == patchedLineNumber }
.takeWhile { loc ->
!isThisMethodLocation(loc) || lambdaArgumentRanges.any { loc.ktLineNumber() in it } || loc.ktLineNumber() == patchedLineNumber
}
.dropWhile { it != patchedLocation }
.drop(1)
.dropWhile { it.ktLineNumber() == patchedLineNumber }
.takeWhile { loc ->
!isThisMethodLocation(loc) || lambdaArgumentRanges.any { loc.ktLineNumber() in it } || loc.ktLineNumber() == patchedLineNumber
}
if (!stepOverLocations.isEmpty()) {
// Some Kotlin inlined methods with 'for' (and maybe others) generates bytecode that after dexing have a strange artifact.
@@ -412,39 +425,39 @@ fun getStepOverAction(
val locationsOfLine = method.safeLocationsOfLine(range.last)
if (locationsOfLine.isNotEmpty()) {
locationsOfLine.map { it.codeIndex() }.max() ?: -1L
}
else {
} else {
findReturnFromDexBytecode(location.method())
}
}
else -1L
} else -1L
return Action.STEP_OVER_INLINED(StepOverFilterData(
return Action.STEP_OVER_INLINED(
StepOverFilterData(
patchedLineNumber,
stepOverLocations.map { it.ktLineNumber() }.toSet(),
inlineRangeVariables,
isDexDebug,
returnCodeIndex
))
)
)
}
return Action.STEP_OVER()
}
fun getStepOutAction(
location: Location,
suspendContext: SuspendContextImpl,
inlineFunctions: List<KtNamedFunction>,
inlinedArgument: KtFunctionLiteral?
location: Location,
suspendContext: SuspendContextImpl,
inlineFunctions: List<KtNamedFunction>,
inlinedArgument: KtFunctionLiteral?
): Action {
val computedReferenceType = location.declaringType() ?: return Action.STEP_OUT()
val locations = computedReferenceType.safeAllLineLocations()
val nextLineLocations = locations
.dropWhile { it != location }
.drop(1)
.filter { it.method() == location.method() }
.dropWhile { it.lineNumber() == location.lineNumber() }
.dropWhile { it != location }
.drop(1)
.filter { it.method() == location.method() }
.dropWhile { it.lineNumber() == location.lineNumber() }
if (inlineFunctions.isNotEmpty()) {
val position = suspendContext.getXPositionForStepOutFromInlineFunction(nextLineLocations, inlineFunctions)
@@ -460,11 +473,10 @@ fun getStepOutAction(
}
private fun SuspendContextImpl.getXPositionForStepOutFromInlineFunction(
locations: List<Location>,
inlineFunctionsToSkip: List<KtNamedFunction>
locations: List<Location>,
inlineFunctionsToSkip: List<KtNamedFunction>
): XSourcePositionImpl? {
return getNextPositionWithFilter(locations) {
offset, elementAt ->
return getNextPositionWithFilter(locations) { offset, elementAt ->
if (inlineFunctionsToSkip.any { it.textRange.contains(offset) }) {
return@getNextPositionWithFilter true
}
@@ -474,27 +486,25 @@ private fun SuspendContextImpl.getXPositionForStepOutFromInlineFunction(
}
private fun SuspendContextImpl.getXPositionForStepOutFromInlinedArgument(
locations: List<Location>,
inlinedArgumentToSkip: KtFunctionLiteral
locations: List<Location>,
inlinedArgumentToSkip: KtFunctionLiteral
): XSourcePositionImpl? {
return getNextPositionWithFilter(locations) {
offset, _ ->
return getNextPositionWithFilter(locations) { offset, _ ->
inlinedArgumentToSkip.textRange.contains(offset)
}
}
private fun SuspendContextImpl.getNextPositionWithFilter(
locations: List<Location>,
skip: (Int, PsiElement) -> Boolean
locations: List<Location>,
skip: (Int, PsiElement) -> Boolean
): XSourcePositionImpl? {
for (location in locations) {
val position = runReadAction l@ {
val position = runReadAction l@{
val sourcePosition = try {
this.debugProcess.positionManager.getSourcePosition(location)
}
catch(e: NoDataException) {
null
} ?: return@l null
} catch (e: NoDataException) {
null
} ?: return@l null
val file = sourcePosition.file as? KtFile ?: return@l null
val elementAt = sourcePosition.elementAt ?: return@l null
@@ -514,11 +524,11 @@ private fun SuspendContextImpl.getNextPositionWithFilter(
fun getInlineRangeLocalVariables(stackFrame: StackFrameProxyImpl): List<LocalVariable> {
return stackFrame.visibleVariables()
.filter {
val name = it.name()
name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION)
}
.map { it.variable }
.filter {
val name = it.name()
name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION)
}
.map { it.variable }
}
private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? {
@@ -541,8 +551,7 @@ private fun findReturnFromDexBytecode(method: Method): Long {
while (true) {
if (method.locationOfCodeIndex(lastMethodCodeIndex + 1) != null) {
lastMethodCodeIndex++
}
else {
} else {
break
}
}
@@ -562,11 +571,9 @@ private fun findReturnFromDexBytecode(method: Method): Long {
if (instruction == RETURN_VOID || instruction == RETURN || instruction == RETURN_WIDE || instruction == RETURN_OBJECT) {
// Instruction found
return returnIndex
}
else if (instruction == MOVE || instruction == GOTO) {
} else if (instruction == MOVE || instruction == GOTO) {
// proceed
}
else {
} else {
// Don't know the instruction and it's length. Abort.
break
}
@@ -33,9 +33,10 @@ import org.jetbrains.kotlin.idea.debugger.suspendFunctionFirstLineLocation
import org.jetbrains.kotlin.idea.util.application.runReadAction
class KotlinSuspendCallStepOverFilter(
private val line: Int,
private val file: PsiFile,
private val ignoreBreakpoints: Boolean) : MethodFilter {
private val line: Int,
private val file: PsiFile,
private val ignoreBreakpoints: Boolean
) : MethodFilter {
override fun getCallingExpressionLines(): Range<Int>? = Range(line, line)
override fun locationMatches(process: DebugProcessImpl, location: Location?): Boolean {
@@ -66,10 +67,9 @@ private fun createRunToCursorBreakpoint(context: SuspendContextImpl, line: Int,
}
val runToCursorBreakpoint =
runReadAction {
DebuggerManagerEx.getInstanceEx(process.project).breakpointManager.addRunToCursorBreakpoint(position, ignoreBreakpoints)
} ?:
return
runReadAction {
DebuggerManagerEx.getInstanceEx(process.project).breakpointManager.addRunToCursorBreakpoint(position, ignoreBreakpoints)
} ?: return
runToCursorBreakpoint.suspendPolicy = when {
context.suspendPolicy == EventRequest.SUSPEND_EVENT_THREAD -> DebuggerSettings.SUSPEND_THREAD
@@ -29,13 +29,16 @@ import org.intellij.lang.annotations.MagicConstant
import java.lang.reflect.Field
internal class RequestHintWithMethodFilter(
stepThread: ThreadReferenceProxyImpl,
suspendContext: SuspendContextImpl,
@MagicConstant(intValues = longArrayOf(
StepRequest.STEP_INTO.toLong(),
StepRequest.STEP_OVER.toLong(),
StepRequest.STEP_OUT.toLong())) depth: Int,
methodFilter: MethodFilter
stepThread: ThreadReferenceProxyImpl,
suspendContext: SuspendContextImpl,
@MagicConstant(
intValues = longArrayOf(
StepRequest.STEP_INTO.toLong(),
StepRequest.STEP_OVER.toLong(),
StepRequest.STEP_OUT.toLong()
)
) depth: Int,
methodFilter: MethodFilter
) : RequestHint(stepThread, suspendContext, methodFilter) {
private var targetMethodMatched = false
@@ -71,11 +74,9 @@ internal class RequestHintWithMethodFilter(
return filter.onReached(context, this)
}
}
}
catch (ignored: VMDisconnectedException) {
} catch (ignored: VMDisconnectedException) {
return STOP
}
catch (e: EvaluateException) {
} catch (e: EvaluateException) {
LOG.error(e)
return STOP
}