/* * Copyright 2000-2016 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.settings.DebuggerSettings; import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; 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.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; 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.*; 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.Collections; import java.util.List; public class PopFrameAction extends DebuggerAction implements DumbAware { private static final Logger LOG = Logger.getInstance(PopFrameAction.class); public void actionPerformed(@NotNull AnActionEvent e) { final Project project = e.getData(CommonDataKeys.PROJECT); final JavaStackFrame stackFrame = getStackFrame(e); if (stackFrame == null || stackFrame.getStackFrameProxy().isBottom()) { return; } try { final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess(); if(debugProcess == null) { return; } debugProcess.getSession().setSteppingThrough(stackFrame.getStackFrameProxy().threadProxy()); if (evaluateFinallyBlocks(project, UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), stackFrame, new XDebuggerEvaluator.XEvaluationCallback() { @Override public void evaluated(@NotNull XValue result) { popFrame(debugProcess, debuggerContext, stackFrame); } @Override public void errorOccurred(@NotNull final String errorMessage) { ApplicationManager.getApplication().invokeLater(() -> Messages .showMessageDialog(project, DebuggerBundle.message("error.executing.finally", errorMessage), UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon())); } })) return; popFrame(debugProcess, debuggerContext, stackFrame); } catch (NativeMethodException e2){ Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon()); } catch (InvalidStackFrameException | VMDisconnectedException ignored) { } } static boolean evaluateFinallyBlocks(Project project, String title, JavaStackFrame stackFrame, XDebuggerEvaluator.XEvaluationCallback callback) { if (!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) { List<PsiStatement> statements = getFinallyStatements(project, stackFrame.getDescriptor().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)) { evaluateAndAct(project, stackFrame, sb, callback); return true; } else { int res = MessageDialogBuilder .yesNoCancel(title, 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 true; case Messages.NO: break; case Messages.YES: // evaluate finally evaluateAndAct(project, stackFrame, sb, callback); return true; } } } } return false; } private static void popFrame(DebugProcessImpl debugProcess, DebuggerContextImpl debuggerContext, JavaStackFrame stackFrame) { debugProcess.getManagerThread() .schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy())); } private static void evaluateAndAct(Project project, JavaStackFrame stackFrame, StringBuilder sb, XDebuggerEvaluator.XEvaluationCallback callback) { XDebuggerEvaluator evaluator = stackFrame.getEvaluator(); if (evaluator != null) { evaluator.evaluate(XExpressionImpl.fromText(sb.toString(), EvaluationMode.CODE_FRAGMENT), callback, 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(Project project, @Nullable SourcePosition position) { if (position == null) { return Collections.emptyList(); } List<PsiStatement> res = new ArrayList<>(); PsiElement element = position.getFile().findElementAt(position.getOffset()); PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class); while (tryStatement != null) { PsiResourceList resourceList = tryStatement.getResourceList(); if (resourceList != null) { PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); for (PsiResourceListElement listElement : resourceList) { String varName = getResourceName(listElement); if (varName != null) { res.add(factory.createStatementFromText("if (" + varName + " != null) " + varName + ".close();", tryStatement)); } } } PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); if (finallyBlock != null) { ContainerUtil.addAll(res, finallyBlock.getStatements()); } tryStatement = PsiTreeUtil.getParentOfType(tryStatement, PsiTryStatement.class); } return res; } private static String getResourceName(PsiResourceListElement resource) { if (resource instanceof PsiResourceVariable) { return ((PsiResourceVariable)resource).getName(); } else if (resource instanceof PsiResourceExpression) { return ((PsiResourceExpression)resource).getExpression().getText(); } LOG.error("Unknown PsiResourceListElement type: " + resource.getClass()); return null; } static JavaStackFrame getStackFrame(AnActionEvent e) { StackFrameDescriptorImpl descriptor = getSelectedStackFrameDescriptor(e); if (descriptor != null) { return new JavaStackFrame(descriptor, false); } return getSelectedStackFrame(e); } static StackFrameProxyImpl getStackFrameProxy(AnActionEvent e) { DebuggerTreeNodeImpl node = getSelectedNode(e.getDataContext()); if (node != null) { NodeDescriptorImpl descriptor = node.getDescriptor(); if (descriptor instanceof StackFrameDescriptorImpl) { return ((StackFrameDescriptorImpl)descriptor).getFrameProxy(); } } else { JavaStackFrame stackFrame = getSelectedStackFrame(e); if (stackFrame != null) { return stackFrame.getStackFrameProxy(); } } return null; } @Nullable private static StackFrameDescriptorImpl getSelectedStackFrameDescriptor(AnActionEvent e) { DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); if(selectedNode != null) { NodeDescriptorImpl descriptor = selectedNode.getDescriptor(); if(descriptor instanceof StackFrameDescriptorImpl) { return (StackFrameDescriptorImpl)descriptor; //if(selectedNode.getNextSibling() != null) { // return (StackFrameDescriptorImpl)descriptor; //} } } return null; } @Nullable private static JavaStackFrame getSelectedStackFrame(AnActionEvent e) { Project project = e.getProject(); if (project != null) { XDebugSession session = e.getData(XDebugSession.DATA_KEY); if (session == null) { session = XDebuggerManager.getInstance(project).getCurrentSession(); } if (session != null) { XStackFrame frame = session.getCurrentStackFrame(); if (frame instanceof JavaStackFrame) { return ((JavaStackFrame)frame); } } } //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(@NotNull AnActionEvent e) { boolean enable = false; StackFrameProxyImpl proxy = getStackFrameProxy(e); if (proxy != null && !proxy.isBottom() /*&& isAtBreakpoint(e)*/) { enable = proxy.getVirtualMachine().canPopFrames(); } if(ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) { e.getPresentation().setEnabled(enable); } else { e.getPresentation().setVisible(enable); } } }