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

This commit is contained in:
Egor.Ushakov
2014-09-01 20:52:47 +04:00
parent f51fa4dfb6
commit da029c12a8
4 changed files with 149 additions and 76 deletions
@@ -23,7 +23,6 @@ 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.JavaDebugProcess;
import com.intellij.debugger.engine.JavaStackFrame;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
@@ -79,85 +78,73 @@ public class PopFrameAction extends DebuggerAction {
if(debugProcess == null) {
return;
}
if (DebuggerSettings.getInstance().CHECK_FINALLY_ON_POP_FRAME) {
if (!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) {
List<PsiStatement> statements = getFinallyStatements(debuggerContext.getSourcePosition());
if (!statements.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (PsiStatement statement : statements) {
sb.append("\n").append(statement.getText());
}
int res = MessageDialogBuilder
.yesNoCancel(UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
DebuggerBundle.message("warning.finally.block.detected") + sb)
.project(project)
.icon(Messages.getWarningIcon())
.yesText(DebuggerBundle.message("button.drop.anyway"))
.noText(DebuggerBundle.message("button.execute.finally"))
.cancelText(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 = value;
}
@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();
switch (res) {
case Messages.CANCEL : return;
case Messages.OK : break; // drop frame
case Messages.NO : // evaluate
JavaDebugProcess process = debugProcess.getXdebugProcess();
XExpressionImpl expression = XExpressionImpl.fromText(sb.toString());
expression = XExpressionImpl.changeMode(expression, EvaluationMode.CODE_FRAGMENT);
XDebuggerEvaluator evaluator = stackFrame.getEvaluator();
if (evaluator != null) {
evaluator.evaluate(expression, new XDebuggerEvaluator.XEvaluationCallback() {
if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) {
evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb);
return;
}
else {
int res = MessageDialogBuilder
.yesNoCancel(UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
DebuggerBundle.message("warning.finally.block.detected") + sb)
.project(project)
.icon(Messages.getWarningIcon())
.yesText(DebuggerBundle.message("button.execute.finally"))
.noText(DebuggerBundle.message("button.drop.anyway"))
.cancelText(CommonBundle.message("button.cancel"))
.doNotAsk(
new DialogWrapper.DoNotAskOption() {
@Override
public void evaluated(@NotNull XValue result) {
debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy()));
public boolean isToBeShown() {
return !DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME) &&
!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME);
}
@Override
public void errorOccurred(@NotNull final String errorMessage) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
Messages
.showMessageDialog(project, DebuggerBundle.message("error.executing.finally", errorMessage),
UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon());
}
});
public void setToBeShown(boolean value, int exitCode) {
if (!value) {
DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME =
exitCode == Messages.YES ? DebuggerSettings.EVALUATE_FINALLY_ALWAYS : DebuggerSettings.EVALUATE_FINALLY_NEVER;
}
else {
DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK;
}
}
}, stackFrame.getSourcePosition());
@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();
switch (res) {
case Messages.CANCEL:
return;
}
else {
Messages.showMessageDialog(project, XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator"),
UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
Messages.getErrorIcon());
}
case Messages.NO:
break;
case Messages.YES: // evaluate finally
evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb);
return;
}
}
}
}
@@ -173,6 +160,40 @@ public class PopFrameAction extends DebuggerAction {
}
}
private static void evaluateAndPop(final Project project,
final JavaStackFrame stackFrame,
final DebuggerContextImpl debuggerContext,
final DebugProcessImpl debugProcess, StringBuilder sb) {
XExpressionImpl expression = XExpressionImpl.fromText(sb.toString());
expression = XExpressionImpl.changeMode(expression, EvaluationMode.CODE_FRAGMENT);
XDebuggerEvaluator evaluator = stackFrame.getEvaluator();
if (evaluator != null) {
evaluator.evaluate(expression, new XDebuggerEvaluator.XEvaluationCallback() {
@Override
public void evaluated(@NotNull XValue result) {
debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy()));
}
@Override
public void errorOccurred(@NotNull final String errorMessage) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
Messages
.showMessageDialog(project, DebuggerBundle.message("error.executing.finally", errorMessage),
UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon());
}
});
}
}, stackFrame.getSourcePosition());
}
else {
Messages.showMessageDialog(project, XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator"),
UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
Messages.getErrorIcon());
}
}
private static List<PsiStatement> getFinallyStatements(SourcePosition position) {
List<PsiStatement> res = new ArrayList<PsiStatement>();
PsiElement element = position.getFile().findElementAt(position.getOffset());
@@ -20,6 +20,7 @@ import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.classFilter.ClassFilter;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
@@ -55,6 +56,10 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent<Ele
@NonNls public static final String RUN_HOTSWAP_NEVER = "RunHotswapNever";
@NonNls public static final String RUN_HOTSWAP_ASK = "RunHotswapAsk";
@NonNls public static final String EVALUATE_FINALLY_ALWAYS = "EvaluateFinallyAlways";
@NonNls public static final String EVALUATE_FINALLY_NEVER = "EvaluateFinallyNever";
@NonNls public static final String EVALUATE_FINALLY_ASK = "EvaluateFinallyAsk";
public boolean TRACING_FILTERS_ENABLED;
public int DEBUGGER_TRANSPORT;
public boolean FORCE_CLASSIC_VM;
@@ -74,7 +79,7 @@ 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;
public String EVALUATE_FINALLY_ON_POP_FRAME = EVALUATE_FINALLY_ASK;
private ClassFilter[] mySteppingFilters = ClassFilter.EMPTY_ARRAY;
@@ -144,7 +149,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 &&
StringUtil.equals(EVALUATE_FINALLY_ON_POP_FRAME, secondSettings.EVALUATE_FINALLY_ON_POP_FRAME) &&
FORCE_CLASSIC_VM == secondSettings.FORCE_CLASSIC_VM &&
DISABLE_JIT == secondSettings.DISABLE_JIT &&
HOTSWAP_IN_BACKGROUND == secondSettings.HOTSWAP_IN_BACKGROUND &&
@@ -22,6 +22,7 @@ import com.intellij.ui.classFilter.ClassFilterEditor;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -33,7 +34,9 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
private JCheckBox myCbSkipClassLoaders;
private ClassFilterEditor mySteppingFilterEditor;
private JCheckBox myCbSkipSimpleGetters;
private JCheckBox myCbCheckFinallyOnPopFrame;
private JRadioButton myRbEvaluateFinallyAlways;
private JRadioButton myRbEvaluateFinallyNever;
private JRadioButton myRbEvaluateFinallyAsk;
@Override
public void reset(@NotNull DebuggerSettings settings) {
@@ -47,7 +50,15 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
mySteppingFilterEditor.setFilters(settings.getSteppingFilters());
mySteppingFilterEditor.setEnabled(settings.TRACING_FILTERS_ENABLED);
myCbCheckFinallyOnPopFrame.setSelected(settings.CHECK_FINALLY_ON_POP_FRAME);
if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(settings.EVALUATE_FINALLY_ON_POP_FRAME)) {
myRbEvaluateFinallyAlways.setSelected(true);
}
else if (DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(settings.EVALUATE_FINALLY_ON_POP_FRAME)) {
myRbEvaluateFinallyNever.setSelected(true);
}
else {
myRbEvaluateFinallyAsk.setSelected(true);
}
}
@Override
@@ -62,7 +73,15 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
settings.SKIP_CLASSLOADERS = myCbSkipClassLoaders.isSelected();
settings.TRACING_FILTERS_ENABLED = myCbStepInfoFiltersEnabled.isSelected();
settings.CHECK_FINALLY_ON_POP_FRAME = myCbCheckFinallyOnPopFrame.isSelected();
if (myRbEvaluateFinallyAlways.isSelected()) {
settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ALWAYS;
}
else if (myRbEvaluateFinallyNever.isSelected()) {
settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_NEVER;
}
else {
settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK;
}
mySteppingFilterEditor.stopEditing();
settings.setSteppingFilters(mySteppingFilterEditor.getFilters());
@@ -83,13 +102,11 @@ 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");
@@ -101,6 +118,33 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
mySteppingFilterEditor.setEnabled(myCbStepInfoFiltersEnabled.isSelected());
}
});
myRbEvaluateFinallyAlways = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.always"));
myRbEvaluateFinallyNever = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.never"));
myRbEvaluateFinallyAsk = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.ask"));
int cbLeftOffset = 0;
final Border border = myCbSkipSimpleGetters.getBorder();
if (border != null) {
final Insets insets = border.getBorderInsets(myCbSkipSimpleGetters);
if (insets != null) {
cbLeftOffset = insets.left;
}
}
final ButtonGroup group = new ButtonGroup();
group.add(myRbEvaluateFinallyAlways);
group.add(myRbEvaluateFinallyNever);
group.add(myRbEvaluateFinallyAsk);
final Box box = Box.createHorizontalBox();
box.add(myRbEvaluateFinallyAlways);
box.add(myRbEvaluateFinallyNever);
box.add(myRbEvaluateFinallyAsk);
final JPanel evalFinallyPanel = new JPanel(new BorderLayout());
evalFinallyPanel.add(box, BorderLayout.CENTER);
evalFinallyPanel.add(new JLabel(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.on.pop")), BorderLayout.WEST);
panel.add(evalFinallyPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, cbLeftOffset, 0, 0), 0, 0));
return panel;
}
}
@@ -225,7 +225,10 @@ 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.debugger.general.configurable.evaluate.finally.on.pop=Evaluate finally blocks on pop frame:
label.debugger.general.configurable.evaluate.finally.always=&Always
label.debugger.general.configurable.evaluate.finally.never=N&ever
label.debugger.general.configurable.evaluate.finally.ask=As&k
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