| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * @author Eugene Zhuravlev |
| */ |
| 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.DebuggerTreeNodeImpl; |
| import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl; |
| import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl; |
| 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.application.ApplicationManager; |
| 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.PsiStatement; |
| import com.intellij.psi.PsiTryStatement; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.ui.UIUtil; |
| import com.intellij.xdebugger.XDebugSession; |
| import com.intellij.xdebugger.XDebuggerBundle; |
| import com.intellij.xdebugger.XDebuggerManager; |
| import com.intellij.xdebugger.evaluation.EvaluationMode; |
| import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; |
| import com.intellij.xdebugger.frame.XStackFrame; |
| import com.intellij.xdebugger.frame.XValue; |
| import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; |
| 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; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| public class PopFrameAction extends DebuggerAction { |
| public void actionPerformed(AnActionEvent e) { |
| final Project project = e.getData(CommonDataKeys.PROJECT); |
| final JavaStackFrame stackFrame = getStackFrame(e); |
| if(stackFrame == null) { |
| return; |
| } |
| try { |
| final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); |
| final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess(); |
| if(debugProcess == null) { |
| return; |
| } |
| |
| 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()); |
| } |
| 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 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 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; |
| } |
| } |
| |
| @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.NO: |
| break; |
| case Messages.YES: // evaluate finally |
| evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb); |
| return; |
| } |
| } |
| } |
| } |
| debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy())); |
| } |
| catch (NativeMethodException e2){ |
| Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), |
| UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon()); |
| } |
| catch (InvalidStackFrameException ignored) { |
| } |
| catch(VMDisconnectedException ignored) { |
| } |
| } |
| |
| private static void evaluateAndPop(final Project project, |
| final JavaStackFrame stackFrame, |
| final DebuggerContextImpl debuggerContext, |
| final DebugProcessImpl debugProcess, StringBuilder sb) { |
| XDebuggerEvaluator evaluator = stackFrame.getEvaluator(); |
| if (evaluator != null) { |
| evaluator.evaluate(XExpressionImpl.fromText(sb.toString(), EvaluationMode.CODE_FRAGMENT), |
| 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()); |
| PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class); |
| while (tryStatement != null) { |
| PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); |
| if (finallyBlock != null) { |
| ContainerUtil.addAll(res, finallyBlock.getStatements()); |
| } |
| tryStatement = PsiTreeUtil.getParentOfType(tryStatement, PsiTryStatement.class); |
| } |
| return res; |
| } |
| |
| @Nullable |
| private static JavaStackFrame getStackFrame(AnActionEvent e) { |
| //DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); |
| //if(selectedNode != null) { |
| // NodeDescriptorImpl descriptor = selectedNode.getDescriptor(); |
| // if(descriptor instanceof StackFrameDescriptorImpl) { |
| // if(selectedNode.getNextSibling() != null) { |
| // StackFrameDescriptorImpl frameDescriptor = ((StackFrameDescriptorImpl)descriptor); |
| // return frameDescriptor.getFrameProxy(); |
| // } |
| // return null; |
| // } |
| // else if(descriptor instanceof ThreadDescriptorImpl || descriptor instanceof ThreadGroupDescriptorImpl) { |
| // return null; |
| // } |
| //} |
| |
| Project project = e.getProject(); |
| if (project != null) { |
| XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); |
| if (session != null) { |
| XStackFrame frame = session.getCurrentStackFrame(); |
| if (frame instanceof JavaStackFrame) { |
| StackFrameProxyImpl proxy = ((JavaStackFrame)frame).getStackFrameProxy(); |
| return !proxy.isBottom() ? ((JavaStackFrame)frame) : null; |
| } |
| } |
| } |
| |
| //DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); |
| //StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy(); |
| // |
| //if(frameProxy == null || frameProxy.isBottom()) { |
| // return null; |
| //} |
| //return frameProxy; |
| return null; |
| } |
| |
| private static boolean isAtBreakpoint(AnActionEvent e) { |
| DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); |
| if(selectedNode != null && selectedNode.getDescriptor() instanceof StackFrameDescriptorImpl) { |
| DebuggerTreeNodeImpl parent = selectedNode.getParent(); |
| if(parent != null) { |
| return ((ThreadDescriptorImpl)parent.getDescriptor()).isAtBreakpoint(); |
| } |
| } |
| DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); |
| SuspendContextImpl suspendContext = debuggerContext.getSuspendContext(); |
| return suspendContext != null && debuggerContext.getThreadProxy() == suspendContext.getThread(); |
| } |
| |
| public void update(AnActionEvent e) { |
| boolean enable = false; |
| JavaStackFrame stackFrame = getStackFrame(e); |
| |
| if(stackFrame != null && isAtBreakpoint(e)) { |
| VirtualMachineProxyImpl virtualMachineProxy = stackFrame.getStackFrameProxy().getVirtualMachine(); |
| enable = virtualMachineProxy.canPopFrames(); |
| } |
| |
| if(ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) { |
| e.getPresentation().setEnabled(enable); |
| } |
| else { |
| e.getPresentation().setVisible(enable); |
| } |
| } |
| } |