Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2000-2009 JetBrains s.r.o. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | /* |
| 18 | * @author Eugene Zhuravlev |
| 19 | */ |
| 20 | package com.intellij.debugger.actions; |
| 21 | |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 22 | import com.intellij.CommonBundle; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 23 | import com.intellij.debugger.DebuggerBundle; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 24 | import com.intellij.debugger.SourcePosition; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 25 | import com.intellij.debugger.engine.DebugProcessImpl; |
Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 26 | import com.intellij.debugger.engine.JavaStackFrame; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 27 | import com.intellij.debugger.engine.SuspendContextImpl; |
| 28 | import com.intellij.debugger.impl.DebuggerContextImpl; |
| 29 | import com.intellij.debugger.jdi.StackFrameProxyImpl; |
| 30 | import com.intellij.debugger.jdi.VirtualMachineProxyImpl; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 31 | import com.intellij.debugger.settings.DebuggerSettings; |
| 32 | import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; |
| 33 | import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl; |
| 34 | import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 35 | import com.intellij.idea.ActionsBundle; |
| 36 | import com.intellij.openapi.actionSystem.ActionPlaces; |
| 37 | import com.intellij.openapi.actionSystem.AnActionEvent; |
Tor Norbye | 32218cc | 2013-10-08 09:48:09 -0700 | [diff] [blame] | 38 | import com.intellij.openapi.actionSystem.CommonDataKeys; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 39 | import com.intellij.openapi.application.ApplicationManager; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 40 | import com.intellij.openapi.project.Project; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 41 | import com.intellij.openapi.ui.DialogWrapper; |
| 42 | import com.intellij.openapi.ui.MessageDialogBuilder; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 43 | import com.intellij.openapi.ui.Messages; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 44 | import com.intellij.psi.PsiCodeBlock; |
| 45 | import com.intellij.psi.PsiElement; |
| 46 | import com.intellij.psi.PsiStatement; |
| 47 | import com.intellij.psi.PsiTryStatement; |
| 48 | import com.intellij.psi.util.PsiTreeUtil; |
| 49 | import com.intellij.util.containers.ContainerUtil; |
| 50 | import com.intellij.util.ui.UIUtil; |
Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 51 | import com.intellij.xdebugger.XDebugSession; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 52 | import com.intellij.xdebugger.XDebuggerBundle; |
Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 53 | import com.intellij.xdebugger.XDebuggerManager; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 54 | import com.intellij.xdebugger.evaluation.EvaluationMode; |
| 55 | import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; |
Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 56 | import com.intellij.xdebugger.frame.XStackFrame; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 57 | import com.intellij.xdebugger.frame.XValue; |
| 58 | import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 59 | import com.sun.jdi.InvalidStackFrameException; |
| 60 | import com.sun.jdi.NativeMethodException; |
| 61 | import com.sun.jdi.VMDisconnectedException; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 62 | import org.jetbrains.annotations.NotNull; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 63 | import org.jetbrains.annotations.Nullable; |
| 64 | |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 65 | import java.util.ArrayList; |
| 66 | import java.util.List; |
| 67 | |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 68 | public class PopFrameAction extends DebuggerAction { |
| 69 | public void actionPerformed(AnActionEvent e) { |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 70 | final Project project = e.getData(CommonDataKeys.PROJECT); |
| 71 | final JavaStackFrame stackFrame = getStackFrame(e); |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 72 | if(stackFrame == null) { |
| 73 | return; |
| 74 | } |
| 75 | try { |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 76 | final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); |
| 77 | final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess(); |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 78 | if(debugProcess == null) { |
| 79 | return; |
| 80 | } |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 81 | |
| 82 | if (!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) { |
| 83 | List<PsiStatement> statements = getFinallyStatements(debuggerContext.getSourcePosition()); |
| 84 | if (!statements.isEmpty()) { |
| 85 | StringBuilder sb = new StringBuilder(); |
| 86 | for (PsiStatement statement : statements) { |
| 87 | sb.append("\n").append(statement.getText()); |
| 88 | } |
| 89 | if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) { |
| 90 | evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb); |
| 91 | return; |
| 92 | } |
| 93 | else { |
| 94 | int res = MessageDialogBuilder |
| 95 | .yesNoCancel(UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), |
| 96 | DebuggerBundle.message("warning.finally.block.detected") + sb) |
| 97 | .project(project) |
| 98 | .icon(Messages.getWarningIcon()) |
| 99 | .yesText(DebuggerBundle.message("button.execute.finally")) |
| 100 | .noText(DebuggerBundle.message("button.drop.anyway")) |
| 101 | .cancelText(CommonBundle.message("button.cancel")) |
| 102 | .doNotAsk( |
| 103 | new DialogWrapper.DoNotAskOption() { |
| 104 | @Override |
| 105 | public boolean isToBeShown() { |
| 106 | return !DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME) && |
| 107 | !DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME); |
| 108 | } |
| 109 | |
| 110 | @Override |
| 111 | public void setToBeShown(boolean value, int exitCode) { |
| 112 | if (!value) { |
| 113 | DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME = |
| 114 | exitCode == Messages.YES ? DebuggerSettings.EVALUATE_FINALLY_ALWAYS : DebuggerSettings.EVALUATE_FINALLY_NEVER; |
| 115 | } |
| 116 | else { |
| 117 | DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | @Override |
| 122 | public boolean canBeHidden() { |
| 123 | return true; |
| 124 | } |
| 125 | |
| 126 | @Override |
| 127 | public boolean shouldSaveOptionsOnCancel() { |
| 128 | return false; |
| 129 | } |
| 130 | |
| 131 | @NotNull |
| 132 | @Override |
| 133 | public String getDoNotShowMessage() { |
| 134 | return CommonBundle.message("dialog.options.do.not.show"); |
| 135 | } |
| 136 | }) |
| 137 | .show(); |
| 138 | |
| 139 | switch (res) { |
| 140 | case Messages.CANCEL: |
| 141 | return; |
| 142 | case Messages.NO: |
| 143 | break; |
| 144 | case Messages.YES: // evaluate finally |
| 145 | evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb); |
| 146 | return; |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy())); |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 152 | } |
| 153 | catch (NativeMethodException e2){ |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 154 | Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), |
| 155 | UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon()); |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 156 | } |
| 157 | catch (InvalidStackFrameException ignored) { |
| 158 | } |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 159 | catch(VMDisconnectedException ignored) { |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 160 | } |
| 161 | } |
| 162 | |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 163 | private static void evaluateAndPop(final Project project, |
| 164 | final JavaStackFrame stackFrame, |
| 165 | final DebuggerContextImpl debuggerContext, |
| 166 | final DebugProcessImpl debugProcess, StringBuilder sb) { |
| 167 | XDebuggerEvaluator evaluator = stackFrame.getEvaluator(); |
| 168 | if (evaluator != null) { |
| 169 | evaluator.evaluate(XExpressionImpl.fromText(sb.toString(), EvaluationMode.CODE_FRAGMENT), |
| 170 | new XDebuggerEvaluator.XEvaluationCallback() { |
| 171 | @Override |
| 172 | public void evaluated(@NotNull XValue result) { |
| 173 | debugProcess.getManagerThread() |
| 174 | .schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy())); |
| 175 | } |
| 176 | |
| 177 | @Override |
| 178 | public void errorOccurred(@NotNull final String errorMessage) { |
| 179 | ApplicationManager.getApplication().invokeLater(new Runnable() { |
| 180 | @Override |
| 181 | public void run() { |
| 182 | Messages |
| 183 | .showMessageDialog(project, DebuggerBundle.message("error.executing.finally", errorMessage), |
| 184 | UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), |
| 185 | Messages.getErrorIcon()); |
| 186 | } |
| 187 | }); |
| 188 | } |
| 189 | }, stackFrame.getSourcePosition()); |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 190 | } |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 191 | else { |
| 192 | Messages.showMessageDialog(project, XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator"), |
| 193 | UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), |
| 194 | Messages.getErrorIcon()); |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | private static List<PsiStatement> getFinallyStatements(SourcePosition position) { |
| 199 | List<PsiStatement> res = new ArrayList<PsiStatement>(); |
| 200 | PsiElement element = position.getFile().findElementAt(position.getOffset()); |
| 201 | PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class); |
| 202 | while (tryStatement != null) { |
| 203 | PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); |
| 204 | if (finallyBlock != null) { |
| 205 | ContainerUtil.addAll(res, finallyBlock.getStatements()); |
| 206 | } |
| 207 | tryStatement = PsiTreeUtil.getParentOfType(tryStatement, PsiTryStatement.class); |
| 208 | } |
| 209 | return res; |
| 210 | } |
| 211 | |
| 212 | @Nullable |
| 213 | private static JavaStackFrame getStackFrame(AnActionEvent e) { |
| 214 | //DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); |
| 215 | //if(selectedNode != null) { |
| 216 | // NodeDescriptorImpl descriptor = selectedNode.getDescriptor(); |
| 217 | // if(descriptor instanceof StackFrameDescriptorImpl) { |
| 218 | // if(selectedNode.getNextSibling() != null) { |
| 219 | // StackFrameDescriptorImpl frameDescriptor = ((StackFrameDescriptorImpl)descriptor); |
| 220 | // return frameDescriptor.getFrameProxy(); |
| 221 | // } |
| 222 | // return null; |
| 223 | // } |
| 224 | // else if(descriptor instanceof ThreadDescriptorImpl || descriptor instanceof ThreadGroupDescriptorImpl) { |
| 225 | // return null; |
| 226 | // } |
| 227 | //} |
Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 228 | |
| 229 | Project project = e.getProject(); |
| 230 | if (project != null) { |
| 231 | XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); |
| 232 | if (session != null) { |
| 233 | XStackFrame frame = session.getCurrentStackFrame(); |
| 234 | if (frame instanceof JavaStackFrame) { |
| 235 | StackFrameProxyImpl proxy = ((JavaStackFrame)frame).getStackFrameProxy(); |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 236 | return !proxy.isBottom() ? ((JavaStackFrame)frame) : null; |
Tor Norbye | c667c1f | 2014-05-28 17:06:51 -0700 | [diff] [blame] | 237 | } |
| 238 | } |
| 239 | } |
| 240 | |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 241 | //DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); |
| 242 | //StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy(); |
| 243 | // |
| 244 | //if(frameProxy == null || frameProxy.isBottom()) { |
| 245 | // return null; |
| 246 | //} |
| 247 | //return frameProxy; |
| 248 | return null; |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | private static boolean isAtBreakpoint(AnActionEvent e) { |
| 252 | DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); |
| 253 | if(selectedNode != null && selectedNode.getDescriptor() instanceof StackFrameDescriptorImpl) { |
| 254 | DebuggerTreeNodeImpl parent = selectedNode.getParent(); |
| 255 | if(parent != null) { |
| 256 | return ((ThreadDescriptorImpl)parent.getDescriptor()).isAtBreakpoint(); |
| 257 | } |
| 258 | } |
| 259 | DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); |
| 260 | SuspendContextImpl suspendContext = debuggerContext.getSuspendContext(); |
| 261 | return suspendContext != null && debuggerContext.getThreadProxy() == suspendContext.getThread(); |
| 262 | } |
| 263 | |
| 264 | public void update(AnActionEvent e) { |
| 265 | boolean enable = false; |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 266 | JavaStackFrame stackFrame = getStackFrame(e); |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 267 | |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 268 | if(stackFrame != null && isAtBreakpoint(e)) { |
| 269 | VirtualMachineProxyImpl virtualMachineProxy = stackFrame.getStackFrameProxy().getVirtualMachine(); |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 270 | enable = virtualMachineProxy.canPopFrames(); |
| 271 | } |
| 272 | |
Tor Norbye | e782c57 | 2014-09-18 11:43:07 -0700 | [diff] [blame^] | 273 | if(ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) { |
Jean-Baptiste Queru | b56ea2a | 2013-01-08 11:11:20 -0800 | [diff] [blame] | 274 | e.getPresentation().setEnabled(enable); |
| 275 | } |
| 276 | else { |
| 277 | e.getPresentation().setVisible(enable); |
| 278 | } |
| 279 | } |
| 280 | } |