From 243003c0b81f3a9cfcf32eec95c970887ef7d6e9 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Thu, 14 Jul 2016 14:25:52 +0300 Subject: [PATCH] IDEA-70251 smart step into displays in popup methods that have already been executed --- .../actions/JavaSmartStepIntoHandler.java | 88 +++++++++++++++++-- .../JvmSmartStepIntoActionHandler.java | 7 +- .../actions/JvmSmartStepIntoHandler.java | 27 +++--- .../debugger/impl/DebuggerUtilsEx.java | 17 ++++ resources/src/META-INF/IdeaPlugin.xml | 2 +- 5 files changed, 123 insertions(+), 18 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java index 6577be77132f..f5981e650af4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaSmartStepIntoHandler.java @@ -16,19 +16,34 @@ package com.intellij.debugger.actions; import com.intellij.debugger.SourcePosition; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.impl.DebuggerSession; import com.intellij.debugger.impl.DebuggerUtilsEx; +import com.intellij.debugger.jdi.MethodBytecodeUtil; +import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.util.DocumentUtil; import com.intellij.util.Range; import com.intellij.util.containers.OrderedSet; +import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; +import com.sun.jdi.Location; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.org.objectweb.asm.Label; +import org.jetbrains.org.objectweb.asm.MethodVisitor; +import org.jetbrains.org.objectweb.asm.Opcodes; import java.util.Collections; import java.util.List; @@ -39,6 +54,8 @@ import java.util.Stack; * Date: 22.11.11 */ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler { + private static final Logger LOG = Logger.getInstance(JavaSmartStepIntoHandler.class); + @Override public boolean isAvailable(final SourcePosition position) { final PsiFile file = position.getFile(); @@ -46,8 +63,37 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler { } @Override + public boolean doSmartStep(SourcePosition position, DebuggerSession session, TextEditor fileEditor) { + session.getProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(session.getContextManager().getContext()) { + @Override + public void threadAction(@NotNull SuspendContextImpl suspendContext) { + List targets = ApplicationManager.getApplication().runReadAction( + (Computable>)() -> findSmartStepTargets(position, suspendContext)); + DebuggerUIUtil.invokeLater(() -> { + if (targets.isEmpty()) { + doStepInto(session, Registry.is("debugger.single.smart.step.force"), null); + } + else { + handleTargets(position, session, fileEditor, targets); + } + }); + } + + @Override + public Priority getPriority() { + return Priority.NORMAL; + } + }); + return true; + } + @NotNull - public List findSmartStepTargets(final SourcePosition position) { + @Override + public List findSmartStepTargets(SourcePosition position) { + throw new IllegalStateException("Should not be used"); + } + + protected List findSmartStepTargets(final SourcePosition position, @Nullable SuspendContextImpl suspendContext) { final int line = position.getLine(); if (line < 0) { return Collections.emptyList(); // the document has been changed @@ -82,7 +128,7 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler { } element = parent; } - while(true); + while (true); //noinspection unchecked final List targets = new OrderedSet<>(); @@ -222,10 +268,42 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler { Range lines = new Range<>(doc.getLineNumber(textRange.get().getStartOffset()), doc.getLineNumber(textRange.get().getEndOffset())); - for (SmartStepTarget target : targets) { - target.setCallingExpressionLines(lines); + targets.forEach(t -> t.setCallingExpressionLines(lines)); + + if (!targets.isEmpty()) { + StackFrameProxyImpl frameProxy = suspendContext != null ? suspendContext.getFrameProxy() : null; + if (frameProxy != null) { + try { + Location location = frameProxy.location(); + MethodBytecodeUtil.visit(location.declaringType(), location.method(), location.codeIndex(), new MethodVisitor(Opcodes.ASM5) { + boolean myLineMatch = false; + + @Override + public void visitLineNumber(int line, Label start) { + myLineMatch = lines.isWithin(line - 1); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (myLineMatch) { + targets.removeIf(t -> { + if (t instanceof MethodSmartStepTarget) { + return DebuggerUtilsEx.methodMatches(((MethodSmartStepTarget)t).getMethod(), + owner.replace("/", "."), name, desc, suspendContext.getDebugProcess()); + } + return false; + }); + } + } + }); + } + catch (Exception e) { + LOG.info(e); + } + } + + return targets; } - return targets; } return Collections.emptyList(); } diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoActionHandler.java index e436fa0c2a95..4f8a20cf9190 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoActionHandler.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoActionHandler.java @@ -17,6 +17,7 @@ package com.intellij.debugger.actions; import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.SourcePosition; +import com.intellij.debugger.engine.MethodFilter; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.impl.DebuggerSession; @@ -51,8 +52,12 @@ public class JvmSmartStepIntoActionHandler extends DebuggerActionHandler { } } } + doStepInto(session, Registry.is("debugger.single.smart.step.force"), null); + } + + static void doStepInto(DebuggerSession session, boolean force, MethodFilter filter) { session.sessionResumed(); - session.stepInto(Registry.is("debugger.single.smart.step.force"), null); + session.stepInto(force, filter); } public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) { diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoHandler.java index f7925d52729c..2c09c96dffbc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoHandler.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/JvmSmartStepIntoHandler.java @@ -57,21 +57,22 @@ public abstract class JvmSmartStepIntoHandler { * @return false to continue for another handler or for default action (step into) */ public boolean doSmartStep(SourcePosition position, final DebuggerSession session, TextEditor fileEditor) { - final List targets = findSmartStepTargets(position); + return handleTargets(position, session, fileEditor, findSmartStepTargets(position)); + } + + protected final boolean handleTargets(SourcePosition position, + DebuggerSession session, + TextEditor fileEditor, + List targets) { if (!targets.isEmpty()) { - final SmartStepTarget firstTarget = targets.get(0); + SmartStepTarget firstTarget = targets.get(0); if (targets.size() == 1) { - session.sessionResumed(); - session.stepInto(Registry.is("debugger.single.smart.step.force"), createMethodFilter(firstTarget)); + doStepInto(session, Registry.is("debugger.single.smart.step.force"), firstTarget); } else { - final Editor editor = fileEditor.getEditor(); - final PsiMethodListPopupStep popupStep = new PsiMethodListPopupStep(editor, targets, new PsiMethodListPopupStep.OnChooseRunnable() { - public void execute(SmartStepTarget chosenTarget) { - session.sessionResumed(); - session.stepInto(true, createMethodFilter(chosenTarget)); - } - }); + Editor editor = fileEditor.getEditor(); + PsiMethodListPopupStep popupStep = + new PsiMethodListPopupStep(editor, targets, chosenTarget -> doStepInto(session, true, chosenTarget)); ListPopupImpl popup = new ListPopupImpl(popupStep); DebuggerUIUtil.registerExtraHandleShortcuts(popup, XDebuggerActions.STEP_INTO, XDebuggerActions.SMART_STEP_INTO); popup.setAdText(DebuggerUIUtil.getSelectionShortcutsAdText(XDebuggerActions.STEP_INTO, XDebuggerActions.SMART_STEP_INTO)); @@ -95,6 +96,10 @@ public abstract class JvmSmartStepIntoHandler { return false; } + protected void doStepInto(DebuggerSession session, boolean force, SmartStepTarget target) { + JvmSmartStepIntoActionHandler.doStepInto(session, force, createMethodFilter(target)); + } + private static void highlightTarget(PsiMethodListPopupStep popupStep, SmartStepTarget target) { final PsiElement highlightElement = target.getHighlightElement(); if (highlightElement != null) { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 7c271cec3d41..680bca874f00 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -918,6 +918,23 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { return false; } + public static boolean methodMatches(@NotNull PsiMethod psiMethod, + String className, + String name, + String signature, + DebugProcessImpl process) { + PsiClass containingClass = psiMethod.getContainingClass(); + try { + return containingClass != null && Objects.equals(containingClass.getQualifiedName(), className) && + JVMNameUtil.getJVMMethodName(psiMethod).equals(name) && + JVMNameUtil.getJVMSignature(psiMethod).getName(process).equals(signature); + } + catch (EvaluateException e) { + LOG.debug(e); + return false; + } + } + @Nullable public static PsiElement getContainingMethod(@Nullable PsiElement elem) { return PsiTreeUtil.getContextOfType(elem, PsiMethod.class, PsiLambdaExpression.class, PsiClassInitializer.class); diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index e7a7af05083c..cba0ef581b1a 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1616,7 +1616,7 @@ - +