Debugger: Cleanup stepping code
This commit is contained in:
+13
-12
@@ -45,14 +45,14 @@ import java.util.List;
|
||||
|
||||
|
||||
public class DebuggerSteppingHelper {
|
||||
private static Logger LOG = Logger.getInstance(DebuggerSteppingHelper.class);
|
||||
private static final Logger LOG = Logger.getInstance(DebuggerSteppingHelper.class);
|
||||
|
||||
public static DebugProcessImpl.ResumeCommand createStepOverCommand(
|
||||
final SuspendContextImpl suspendContext,
|
||||
final boolean ignoreBreakpoints,
|
||||
final KotlinSteppingCommandProvider.KotlinSourcePosition kotlinSourcePosition
|
||||
SuspendContextImpl suspendContext,
|
||||
boolean ignoreBreakpoints,
|
||||
KotlinSteppingCommandProvider.KotlinSourcePosition kotlinSourcePosition
|
||||
) {
|
||||
final DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||
|
||||
return debugProcess.new ResumeCommand(suspendContext) {
|
||||
@Override
|
||||
@@ -88,12 +88,12 @@ public class DebuggerSteppingHelper {
|
||||
}
|
||||
|
||||
public static DebugProcessImpl.ResumeCommand createStepOutCommand(
|
||||
final SuspendContextImpl suspendContext,
|
||||
final boolean ignoreBreakpoints,
|
||||
final List<KtNamedFunction> inlineFunctions,
|
||||
final KtFunctionLiteral inlineArgument
|
||||
SuspendContextImpl suspendContext,
|
||||
boolean ignoreBreakpoints,
|
||||
List<KtNamedFunction> inlineFunctions,
|
||||
KtFunctionLiteral inlineArgument
|
||||
) {
|
||||
final DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||
return debugProcess.new ResumeCommand(suspendContext) {
|
||||
@Override
|
||||
public void contextAction() {
|
||||
@@ -129,7 +129,8 @@ public class DebuggerSteppingHelper {
|
||||
@NotNull SuspendContextImpl suspendContext,
|
||||
@Nullable ThreadReferenceProxyImpl stepThread,
|
||||
@NotNull EventRequestManager requestManager,
|
||||
int size, int depth
|
||||
@SuppressWarnings("SameParameterValue") int size,
|
||||
@SuppressWarnings("SameParameterValue") int depth
|
||||
) {
|
||||
if (stepThread == null) {
|
||||
return;
|
||||
@@ -170,7 +171,7 @@ public class DebuggerSteppingHelper {
|
||||
// copied from DebugProcessImpl.getActiveFilters
|
||||
@NotNull
|
||||
private static List<ClassFilter> getActiveFilters() {
|
||||
List<ClassFilter> activeFilters = new ArrayList<ClassFilter>();
|
||||
List<ClassFilter> activeFilters = new ArrayList<>();
|
||||
DebuggerSettings settings = DebuggerSettings.getInstance();
|
||||
if (settings.TRACING_FILTERS_ENABLED) {
|
||||
for (ClassFilter filter : settings.getSteppingFilters()) {
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ class KotlinBasicStepMethodFilter(
|
||||
}
|
||||
|
||||
val declaration = declarationPtr?.element
|
||||
?: return true // Element is lost. But we know that name is matches, so stop.
|
||||
?: return true // Element is lost. But we know that name is matches, so stop.
|
||||
|
||||
if (currentDeclaration.isEquivalentTo(declaration)) {
|
||||
return true
|
||||
|
||||
+1
-2
@@ -30,8 +30,7 @@ class KotlinSimpleGetterProvider : SimplePropertyGetterProvider {
|
||||
|
||||
val accessor = PsiTreeUtil.getParentOfType(element, KtPropertyAccessor::class.java)
|
||||
if (accessor != null && accessor.isGetter) {
|
||||
val body = accessor.bodyExpression
|
||||
return when (body) {
|
||||
return when (val body = accessor.bodyExpression) {
|
||||
is KtBlockExpression -> {
|
||||
// val a: Int get() { return field }
|
||||
val returnedExpression = (body.statements.singleOrNull() as? KtReturnExpression)?.returnedExpression ?: return false
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
val elementAtOffset = position.elementAt ?: return emptyList()
|
||||
|
||||
val element = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, elementAtOffset.textRange.startOffset) as? KtElement
|
||||
?: return emptyList()
|
||||
?: return emptyList()
|
||||
|
||||
val elementTextRange = element.textRange ?: return emptyList()
|
||||
|
||||
|
||||
+5
-3
@@ -63,7 +63,9 @@ class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) {
|
||||
private fun doStep(
|
||||
suspendContext: SuspendContextImpl,
|
||||
stepThread: ThreadReferenceProxyImpl,
|
||||
size: Int, depth: Int, hint: RequestHint
|
||||
size: Int,
|
||||
@Suppress("SameParameterValue") depth: Int,
|
||||
hint: RequestHint
|
||||
) {
|
||||
val doStepMethod = DebugProcessImpl::class.java.getDeclaredMethod(
|
||||
"doStep",
|
||||
@@ -80,7 +82,7 @@ class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) {
|
||||
return getFromField(DebugProcessImpl::class.java.declaredFields.single { it.type == fieldType })
|
||||
}
|
||||
|
||||
private fun <T> getFromField(fieldName: String): T {
|
||||
private fun <T> getFromField(@Suppress("SameParameterValue") fieldName: String): T {
|
||||
return getFromField(DebugProcessImpl::class.java.getDeclaredField(fieldName))
|
||||
}
|
||||
|
||||
@@ -104,7 +106,7 @@ class KotlinStepActionFactory(private val debuggerProcess: DebugProcessImpl) {
|
||||
// there could be explicit resume as a result of call to voteSuspend()
|
||||
// e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_
|
||||
// resuming and all breakpoints in other threads will be ignored.
|
||||
// As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens
|
||||
// As resume() implicitly clears the filter, the filter must be always applied _before_ any resume() action happens
|
||||
val breakpointManager = DebuggerManagerEx.getInstanceEx(project).breakpointManager
|
||||
breakpointManager.applyThreadFilter(debuggerProcess, thread.threadReference)
|
||||
}
|
||||
|
||||
+3
-2
@@ -30,8 +30,9 @@ class KotlinStepOverInlinedLinesHint(
|
||||
suspendContext: SuspendContextImpl,
|
||||
methodFilter: KotlinMethodFilter
|
||||
) : RequestHint(stepThread, suspendContext, methodFilter) {
|
||||
|
||||
private val LOG = Logger.getInstance(KotlinStepOverInlinedLinesHint::class.java)
|
||||
private companion object {
|
||||
private val LOG = Logger.getInstance(KotlinStepOverInlinedLinesHint::class.java)
|
||||
}
|
||||
|
||||
private val filter = methodFilter
|
||||
|
||||
|
||||
+25
-24
@@ -259,7 +259,7 @@ private fun findCallsOnPosition(sourcePosition: SourcePosition, filter: (KtCallE
|
||||
return allFilteredCalls.filter {
|
||||
val shouldInclude = it.getLineNumber() in linesRange
|
||||
if (shouldInclude) {
|
||||
linesRange = min(linesRange.start, it.getLineNumber())..max(linesRange.endInclusive, it.getLineNumber(false))
|
||||
linesRange = min(linesRange.first, it.getLineNumber())..max(linesRange.last, it.getLineNumber(false))
|
||||
}
|
||||
shouldInclude
|
||||
}
|
||||
@@ -269,17 +269,17 @@ sealed class Action(
|
||||
val position: XSourcePositionImpl? = null,
|
||||
val stepOverInlineData: StepOverFilterData? = null
|
||||
) {
|
||||
class STEP_OVER : Action() {
|
||||
class StepOver : Action() {
|
||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) =
|
||||
debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction(suspendContext)
|
||||
}
|
||||
|
||||
class STEP_OUT : Action() {
|
||||
class StepOut : Action() {
|
||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) =
|
||||
debugProcess.createStepOutCommand(suspendContext).contextAction(suspendContext)
|
||||
}
|
||||
|
||||
class RUN_TO_CURSOR(position: XSourcePositionImpl) : Action(position) {
|
||||
class RunToCursor(position: XSourcePositionImpl) : Action(position) {
|
||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
||||
return runReadAction {
|
||||
debugProcess.createRunToCursorCommand(suspendContext, position!!, ignoreBreakpoints)
|
||||
@@ -287,7 +287,7 @@ sealed class Action(
|
||||
}
|
||||
}
|
||||
|
||||
class STEP_OVER_INLINED(stepOverInlineData: StepOverFilterData) : Action(stepOverInlineData = stepOverInlineData) {
|
||||
class StepOverInlined(stepOverInlineData: StepOverFilterData) : Action(stepOverInlineData = stepOverInlineData) {
|
||||
override fun apply(debugProcess: DebugProcessImpl, suspendContext: SuspendContextImpl, ignoreBreakpoints: Boolean) {
|
||||
return KotlinStepActionFactory(debugProcess).createKotlinStepOverInlineAction(
|
||||
KotlinStepOverInlineFilter(debugProcess.project, stepOverInlineData!!)
|
||||
@@ -326,13 +326,13 @@ fun getStepOverAction(
|
||||
frameProxy: StackFrameProxyImpl,
|
||||
isDexDebug: Boolean
|
||||
): Action {
|
||||
location.declaringType() ?: return Action.STEP_OVER()
|
||||
location.declaringType() ?: return Action.StepOver()
|
||||
|
||||
val project = sourceFile.project
|
||||
|
||||
val methodLocations = location.method().safeAllLineLocations()
|
||||
if (methodLocations.isEmpty()) {
|
||||
return Action.STEP_OVER()
|
||||
return Action.StepOver()
|
||||
}
|
||||
|
||||
val locationsLineAndFile = methodLocations.keysToMap { ktLocationInfo(it, isDexDebug, project, true) }
|
||||
@@ -354,15 +354,16 @@ fun getStepOverAction(
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
return nextLocation.ktFileName() == sourceFile.name
|
||||
return try {
|
||||
nextLocation.ktFileName() == sourceFile.name
|
||||
} catch (e: AbsentInformationException) {
|
||||
return true
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun isBackEdgeLocation(): Boolean {
|
||||
val previousSuitableLocation = methodLocations.reversed()
|
||||
.asSequence()
|
||||
.dropWhile { it != location }
|
||||
.drop(1)
|
||||
.filter(::isThisMethodLocation)
|
||||
@@ -409,8 +410,8 @@ fun getStepOverAction(
|
||||
!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.
|
||||
if (stepOverLocations.isNotEmpty()) {
|
||||
// Some Kotlin inlined methods with 'for' (and maybe others) generates bytecode that, being dex-processed, have a strange artifact.
|
||||
// GOTO instructions are moved to the end of method and as they don't have proper line, line is obtained from the previous
|
||||
// instruction. It might be method return or previous GOTO from the inlining. Simple stepping over such function is really
|
||||
// terrible. On each iteration position jumps to the method end or some previous inline call and then returns back. To prevent
|
||||
@@ -425,7 +426,7 @@ fun getStepOverAction(
|
||||
}
|
||||
} else -1L
|
||||
|
||||
return Action.STEP_OVER_INLINED(
|
||||
return Action.StepOverInlined(
|
||||
StepOverFilterData(
|
||||
patchedLineNumber,
|
||||
stepOverLocations.map { it.ktLineNumber() }.toSet(),
|
||||
@@ -436,7 +437,7 @@ fun getStepOverAction(
|
||||
)
|
||||
}
|
||||
|
||||
return Action.STEP_OVER()
|
||||
return Action.StepOver()
|
||||
}
|
||||
|
||||
fun getStepOutAction(
|
||||
@@ -445,7 +446,7 @@ fun getStepOutAction(
|
||||
inlineFunctions: List<KtNamedFunction>,
|
||||
inlinedArgument: KtFunctionLiteral?
|
||||
): Action {
|
||||
val computedReferenceType = location.declaringType() ?: return Action.STEP_OUT()
|
||||
val computedReferenceType = location.declaringType() ?: return Action.StepOut()
|
||||
|
||||
val locations = computedReferenceType.safeAllLineLocations()
|
||||
val nextLineLocations = locations
|
||||
@@ -456,15 +457,15 @@ fun getStepOutAction(
|
||||
|
||||
if (inlineFunctions.isNotEmpty()) {
|
||||
val position = suspendContext.getXPositionForStepOutFromInlineFunction(nextLineLocations, inlineFunctions)
|
||||
return position?.let { Action.RUN_TO_CURSOR(it) } ?: Action.STEP_OVER()
|
||||
return position?.let { Action.RunToCursor(it) } ?: Action.StepOver()
|
||||
}
|
||||
|
||||
if (inlinedArgument != null) {
|
||||
val position = suspendContext.getXPositionForStepOutFromInlinedArgument(nextLineLocations, inlinedArgument)
|
||||
return position?.let { Action.RUN_TO_CURSOR(it) } ?: Action.STEP_OVER()
|
||||
return position?.let { Action.RunToCursor(it) } ?: Action.StepOver()
|
||||
}
|
||||
|
||||
return Action.STEP_OVER()
|
||||
return Action.StepOver()
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.getXPositionForStepOutFromInlineFunction(
|
||||
@@ -578,11 +579,11 @@ private fun findReturnFromDexBytecode(method: Method): Long {
|
||||
}
|
||||
|
||||
object DexBytecode {
|
||||
val RETURN_VOID = 0x0e
|
||||
val RETURN = 0x0f
|
||||
val RETURN_WIDE = 0x10
|
||||
val RETURN_OBJECT = 0x11
|
||||
const val RETURN_VOID = 0x0e
|
||||
const val RETURN = 0x0f
|
||||
const val RETURN_WIDE = 0x10
|
||||
const val RETURN_OBJECT = 0x11
|
||||
|
||||
val GOTO = 0x28
|
||||
val MOVE = 0x01
|
||||
const val GOTO = 0x28
|
||||
const val MOVE = 0x01
|
||||
}
|
||||
|
||||
+2
-8
@@ -31,13 +31,7 @@ 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,
|
||||
@MagicConstant(intValues = [StepRequest.STEP_INTO.toLong(), StepRequest.STEP_OVER.toLong(), StepRequest.STEP_OUT.toLong()]) depth: Int,
|
||||
methodFilter: MethodFilter
|
||||
) : RequestHint(stepThread, suspendContext, methodFilter) {
|
||||
private var targetMethodMatched = false
|
||||
@@ -49,7 +43,7 @@ internal class RequestHintWithMethodFilter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun findFieldWithValue(value: Int, type: Class<*>): Field? {
|
||||
private fun findFieldWithValue(@Suppress("SameParameterValue") value: Int, @Suppress("SameParameterValue") type: Class<*>): Field? {
|
||||
return RequestHint::class.java.declaredFields.firstOrNull { field ->
|
||||
if (field.type == type) {
|
||||
field.isAccessible = true
|
||||
|
||||
Reference in New Issue
Block a user