diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java index f5aa685c7994..665ac7ebf167 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java @@ -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()); diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java index bb3fafed746f..d18b0eb15fca 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java @@ -74,6 +74,8 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent myContentStates = new LinkedHashMap(); @@ -142,6 +144,7 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent { 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 { 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 { 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 { 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"); diff --git a/resources-en/src/messages/DebuggerBundle.properties b/resources-en/src/messages/DebuggerBundle.properties index ac623acc1fbe..ba40357f4709 100644 --- a/resources-en/src/messages/DebuggerBundle.properties +++ b/resources-en/src/messages/DebuggerBundle.properties @@ -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