Reformat: debugger.stepping
This commit is contained in:
+2
-1
@@ -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
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+8
-9
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -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
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+16
-12
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-11
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -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 {
|
||||
|
||||
+9
-14
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+104
-97
@@ -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
|
||||
}
|
||||
|
||||
+7
-7
@@ -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
|
||||
|
||||
+12
-11
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user