Debugger: Cleanup stepping code

This commit is contained in:
Yan Zhulanow
2019-08-08 18:31:31 +09:00
parent 00a06ef9b9
commit d7eb0f4bb0
8 changed files with 51 additions and 53 deletions
@@ -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()) {
@@ -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
@@ -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
@@ -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()
@@ -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)
}
@@ -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
@@ -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
}
@@ -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