IDEA-129063 Show warning when 'Drop Frame' is invoked inside 'try' block with 'finally' section

This commit is contained in:
Egor.Ushakov
2014-09-01 19:13:15 +04:00
parent bed6d49984
commit 75bfa68a16
4 changed files with 81 additions and 1 deletions
@@ -19,26 +19,37 @@
*/
package com.intellij.debugger.actions;
import com.intellij.CommonBundle;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.JavaStackFrame;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiTryStatement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.frame.XStackFrame;
import com.sun.jdi.InvalidStackFrameException;
import com.sun.jdi.NativeMethodException;
import com.sun.jdi.VMDisconnectedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PopFrameAction extends DebuggerAction {
@@ -54,10 +65,53 @@ public class PopFrameAction extends DebuggerAction {
if(debugProcess == null) {
return;
}
if (DebuggerSettings.getInstance().CHECK_FINALLY_ON_POP_FRAME && isInTryWithFinally(debuggerContext.getSourcePosition())) {
int res = MessageDialogBuilder
.yesNo(UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
DebuggerBundle.message("warning.finally.block.detected"))
.project(project)
.icon(Messages.getWarningIcon())
.yesText(DebuggerBundle.message("button.drop.anyway"))
.noText(CommonBundle.message("button.cancel"))
.doNotAsk(
new DialogWrapper.DoNotAskOption() {
@Override
public boolean isToBeShown() {
return DebuggerSettings.getInstance().CHECK_FINALLY_ON_POP_FRAME;
}
@Override
public void setToBeShown(boolean value, int exitCode) {
DebuggerSettings.getInstance().CHECK_FINALLY_ON_POP_FRAME = false;
}
@Override
public boolean canBeHidden() {
return true;
}
@Override
public boolean shouldSaveOptionsOnCancel() {
return false;
}
@NotNull
@Override
public String getDoNotShowMessage() {
return CommonBundle.message("dialog.options.do.not.show");
}
})
.show();
if (res == Messages.NO) {
return;
}
}
debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame));
}
catch (NativeMethodException e2){
Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), UIUtil.removeMnemonic(
ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon());
}
catch (InvalidStackFrameException ignored) {
}
@@ -65,6 +119,19 @@ public class PopFrameAction extends DebuggerAction {
}
}
private static boolean isInTryWithFinally(SourcePosition position) {
PsiElement element = position.getFile().findElementAt(position.getOffset());
PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class);
while (tryStatement != null) {
PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
if (finallyBlock != null && finallyBlock.getStatements().length > 0) {
return true;
}
tryStatement = PsiTreeUtil.getParentOfType(tryStatement, PsiTryStatement.class);
}
return false;
}
@Nullable
private static StackFrameProxyImpl getStackFrameProxy(AnActionEvent e) {
DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
@@ -74,6 +74,8 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent<Ele
public volatile boolean AUTO_VARIABLES_MODE = false;
public volatile boolean SHOW_LIBRARY_STACKFRAMES = true;
public boolean CHECK_FINALLY_ON_POP_FRAME = true;
private ClassFilter[] mySteppingFilters = ClassFilter.EMPTY_ARRAY;
private Map<String, ContentState> myContentStates = new LinkedHashMap<String, ContentState>();
@@ -142,6 +144,7 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent<Ele
return
TRACING_FILTERS_ENABLED == secondSettings.TRACING_FILTERS_ENABLED &&
DEBUGGER_TRANSPORT == secondSettings.DEBUGGER_TRANSPORT &&
CHECK_FINALLY_ON_POP_FRAME == secondSettings.CHECK_FINALLY_ON_POP_FRAME &&
FORCE_CLASSIC_VM == secondSettings.FORCE_CLASSIC_VM &&
DISABLE_JIT == secondSettings.DISABLE_JIT &&
HOTSWAP_IN_BACKGROUND == secondSettings.HOTSWAP_IN_BACKGROUND &&
@@ -33,6 +33,7 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
private JCheckBox myCbSkipClassLoaders;
private ClassFilterEditor mySteppingFilterEditor;
private JCheckBox myCbSkipSimpleGetters;
private JCheckBox myCbCheckFinallyOnPopFrame;
@Override
public void reset(@NotNull DebuggerSettings settings) {
@@ -45,6 +46,8 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
mySteppingFilterEditor.setFilters(settings.getSteppingFilters());
mySteppingFilterEditor.setEnabled(settings.TRACING_FILTERS_ENABLED);
myCbCheckFinallyOnPopFrame.setSelected(settings.CHECK_FINALLY_ON_POP_FRAME);
}
@Override
@@ -59,6 +62,8 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
settings.SKIP_CLASSLOADERS = myCbSkipClassLoaders.isSelected();
settings.TRACING_FILTERS_ENABLED = myCbStepInfoFiltersEnabled.isSelected();
settings.CHECK_FINALLY_ON_POP_FRAME = myCbCheckFinallyOnPopFrame.isSelected();
mySteppingFilterEditor.stopEditing();
settings.setSteppingFilters(mySteppingFilterEditor.getFilters());
}
@@ -78,11 +83,13 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
myCbSkipConstructors = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.skip.constructors"));
myCbSkipClassLoaders = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.skip.classLoaders"));
myCbSkipSimpleGetters = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.skip.simple.getters"));
myCbCheckFinallyOnPopFrame = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.check.finally.on.pop"));
myCbStepInfoFiltersEnabled = new JCheckBox(DebuggerBundle.message("label.debugger.general.configurable.step.filters.list.header"));
panel.add(myCbSkipSyntheticMethods, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
panel.add(myCbSkipConstructors, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
panel.add(myCbSkipClassLoaders, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
panel.add(myCbSkipSimpleGetters, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
panel.add(myCbCheckFinallyOnPopFrame, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0),0, 0));
panel.add(myCbStepInfoFiltersEnabled, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(8, 0, 0, 0),0, 0));
mySteppingFilterEditor = new ClassFilterEditor(JavaDebuggerSupport.getContextProjectForEditorFieldsInDebuggerConfigurables(), null, "reference.viewBreakpoints.classFilters.newPattern");
@@ -27,6 +27,8 @@ title.customize.data.views=Customize Data Views
title.evaluating=Evaluating...
error.modification.watchpoints.not.supported=Target VM does not support modification watchpoints
error.native.method.exception=None of the frames through and including frame may be native
warning.finally.block.detected=Non-empty finally block detected
button.drop.anyway=Drop frame anyway
action.remove.watch.text={0,choice, 1#Remove Watch|2#Remove Watches}
progress.evaluating=Evaluating {0}
action.resume.thread.text.resume=Resume
@@ -221,6 +223,7 @@ label.debugger.general.configurable.skip.constructors=Skip &constructors
label.debugger.general.configurable.skip.classLoaders=Skip class l&oaders
label.debugger.general.configurable.skip.simple.getters=Skip simple &getters
label.debugger.general.configurable.step.filters.list.header=Do not step &into the classes
label.debugger.general.configurable.check.finally.on.pop=Ask if in finally block on pop frame
label.threads.view.configurable.show.source.file.name=Show source file name
label.threads.view.configurable.show.class.name=Show class name
label.threads.view.configurable.show.line.number=Show &line number