From b47ec5cc86fc2bf30a31e3c336df6cd5e0e83b9f Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Mon, 21 Sep 2015 16:20:05 +0300 Subject: [PATCH] Force early return initial --- .../actions/ForceEarlyReturnAction.java | 216 ++++++++++++++++++ .../debugger/actions/PopFrameAction.java | 15 +- .../debugger/engine/JavaStackFrame.java | 1 + .../jdi/ThreadReferenceProxyImpl.java | 11 + .../debugger/jdi/VirtualMachineProxyImpl.java | 11 +- .../src/messages/ActionsBundle.properties | 2 + .../src/messages/DebuggerBundle.properties | 3 +- resources/src/idea/JavaActions.xml | 4 + 8 files changed, 249 insertions(+), 14 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/actions/ForceEarlyReturnAction.java diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ForceEarlyReturnAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ForceEarlyReturnAction.java new file mode 100644 index 000000000000..0545dd5a1ce9 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ForceEarlyReturnAction.java @@ -0,0 +1,216 @@ +/* + * Copyright 2000-2015 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. + */ + +package com.intellij.debugger.actions; + +import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.JavaStackFrame; +import com.intellij.debugger.engine.JavaValue; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.impl.DebuggerContextImpl; +import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; +import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.util.ui.UIUtil; +import com.intellij.xdebugger.XDebuggerBundle; +import com.intellij.xdebugger.XExpression; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; +import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; +import com.sun.jdi.Method; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class ForceEarlyReturnAction extends DebuggerAction { + public void actionPerformed(@NotNull AnActionEvent e) { + final Project project = e.getProject(); + final JavaStackFrame stackFrame = PopFrameAction.getStackFrame(e); + if (stackFrame == null || project == null) { + return; + } + final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); + final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess(); + if (debugProcess == null) { + return; + } + + final StackFrameProxyImpl proxy = stackFrame.getStackFrameProxy(); + final ThreadReferenceProxyImpl thread = proxy.threadProxy(); + + debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debuggerContext, thread) { + @Override + public void threadAction() { + Method method; + try { + method = proxy.location().method(); + } + catch (EvaluateException e) { + showError(project, DebuggerBundle.message("error.early.return", e.getLocalizedMessage())); + return; + } + + if ("void".equals(method.returnTypeName())) { + forceEarlyReturn(thread.getVirtualMachine().mirrorOf(), thread, debugProcess, null); + } + else { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new ReturnExpressionDialog(project, debugProcess.getXdebugProcess().getEditorsProvider(), debugProcess, stackFrame).show(); + } + }); + } + } + }); + } + + private static void forceEarlyReturn(Value value, + ThreadReferenceProxyImpl thread, + final DebugProcessImpl debugProcess, + @Nullable final DialogWrapper dialog) { + try { + thread.forceEarlyReturn(value); + } + catch (Exception e) { + showError(debugProcess.getProject(), DebuggerBundle.message("error.early.return", e.getLocalizedMessage())); + return; + } + //noinspection SSBasedInspection + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (dialog != null) { + dialog.close(DialogWrapper.OK_EXIT_CODE); + } + debugProcess.getSession().stepInto(true, null); + } + }); + } + + private static void evaluateAndReturn(final Project project, + final JavaStackFrame stackFrame, + final DebugProcessImpl debugProcess, + XExpression expression, + final DialogWrapper dialog) { + XDebuggerEvaluator evaluator = stackFrame.getEvaluator(); + if (evaluator != null) { + evaluator.evaluate(expression, + new XDebuggerEvaluator.XEvaluationCallback() { + @Override + public void evaluated(@NotNull XValue result) { + if (result instanceof JavaValue) { + forceEarlyReturn(((JavaValue)result).getDescriptor().getValue(), + stackFrame.getDescriptor().getFrameProxy().threadProxy(), + debugProcess, + dialog); + } + } + + @Override + public void errorOccurred(@NotNull final String errorMessage) { + showError(project, DebuggerBundle.message("error.unable.to.evaluate.expression") + ": " + errorMessage); + } + }, stackFrame.getSourcePosition()); + } + else { + showError(project, XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator")); + } + } + + private static void showError(final Project project, final String message) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + Messages.showMessageDialog(project, + message, + UIUtil.removeMnemonic(ActionsBundle.actionText("Debugger.ForceEarlyReturn")), + Messages.getErrorIcon()); + } + }, ModalityState.any()); + } + + public void update(@NotNull AnActionEvent e) { + boolean enable = false; + + JavaStackFrame stackFrame = PopFrameAction.getStackFrame(e); + if (stackFrame != null && stackFrame.getDescriptor().getUiIndex() == 0) { + enable = stackFrame.getStackFrameProxy().getVirtualMachine().canForceEarlyReturn(); + } + + if (ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) { + e.getPresentation().setEnabled(enable); + } + else { + e.getPresentation().setVisible(enable); + } + } + + private static class ReturnExpressionDialog extends DialogWrapper { + private final Project myProject; + private final XDebuggerEditorsProvider myEditorsProvider; + private final DebugProcessImpl myProcess; + private final JavaStackFrame myFrame; + private final XDebuggerExpressionEditor myEditor; + + public ReturnExpressionDialog(@NotNull Project project, + XDebuggerEditorsProvider provider, + DebugProcessImpl process, + JavaStackFrame frame) { + super(project); + myProject = project; + myEditorsProvider = provider; + myProcess = process; + myFrame = frame; + myEditor = new XDebuggerExpressionEditor(myProject, myEditorsProvider, "forceReturnValue", myFrame.getSourcePosition(), + XExpressionImpl.EMPTY_EXPRESSION, false); + + setTitle("Return Value"); + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myEditor.getComponent(); + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myEditor.getPreferredFocusedComponent(); + } + + @Override + protected void doOKAction() { + evaluateAndReturn(myProject, myFrame, myProcess, myEditor.getExpression(), this); + } + } +} 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 5b4048129cba..f18200161a4d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java @@ -70,7 +70,7 @@ public class PopFrameAction extends DebuggerAction { public void actionPerformed(@NotNull AnActionEvent e) { final Project project = e.getData(CommonDataKeys.PROJECT); final JavaStackFrame stackFrame = getStackFrame(e); - if(stackFrame == null) { + if (stackFrame == null || stackFrame.getStackFrameProxy().isBottom()) { return; } try { @@ -214,21 +214,12 @@ public class PopFrameAction extends DebuggerAction { return res; } - private static JavaStackFrame getStackFrame(AnActionEvent e) { + static JavaStackFrame getStackFrame(AnActionEvent e) { StackFrameDescriptorImpl descriptor = getSelectedStackFrameDescriptor(e); if (descriptor != null) { - if (descriptor.getFrameProxy().isBottom()) { - return null; - } return new JavaStackFrame(descriptor, false); } - JavaStackFrame frame = getSelectedStackFrame(e); - if (frame != null) { - StackFrameProxyImpl proxy = frame.getStackFrameProxy(); - if (proxy == null || proxy.isBottom()) return null; - return frame; - } - return null; + return getSelectedStackFrame(e); } static StackFrameProxyImpl getStackFrameProxy(AnActionEvent e) { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java index b4cfc1680d26..3a8c2b6bac48 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java @@ -397,6 +397,7 @@ public class JavaStackFrame extends XStackFrame { } } + @NotNull public StackFrameProxyImpl getStackFrameProxy() { return myDescriptor.getFrameProxy(); } diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java index 7b8b1d1f9df0..b9b4e57b24f2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/ThreadReferenceProxyImpl.java @@ -309,6 +309,17 @@ public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl imp } } + public void forceEarlyReturn(Value value) throws ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException { + DebuggerManagerThreadImpl.assertIsManagerThread(); + try { + getThreadReference().forceEarlyReturn(value); + } + finally { + clearCaches(); + getVirtualMachineProxy().clearCaches(); + } + } + public boolean isSuspended() throws ObjectCollectedException { try { DebuggerManagerThreadImpl.assertIsManagerThread(); diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java index c2211fe1c648..982964678413 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java @@ -278,7 +278,7 @@ public class VirtualMachineProxyImpl implements JdiTimer, VirtualMachineProxy { return myVirtualMachine.eventRequestManager(); } - public VoidValue mirrorOf() throws EvaluateException { + public VoidValue mirrorOf() { return myVirtualMachine.mirrorOfVoid(); } @@ -461,6 +461,15 @@ public class VirtualMachineProxyImpl implements JdiTimer, VirtualMachineProxy { return myPopFrames.isAvailable(); } + private final Capability myForceEarlyReturn = new Capability() { + protected boolean calcValue() { + return myVirtualMachine.canForceEarlyReturn(); + } + }; + public boolean canForceEarlyReturn() { + return myForceEarlyReturn.isAvailable(); + } + private final Capability myCanGetInstanceInfo = new Capability() { protected boolean calcValue() { if (!myVersionHigher_15) { diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 35af1b7ecf06..cd52bace6af4 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -709,6 +709,8 @@ action.ForceRunToCursor.text=Force Run to Cur_sor action.ForceRunToCursor.description=Run to the line where the caret is, ignoring any breakpoints action.Debugger.PopFrame.text=Drop _Frame action.Debugger.PopFrame.description=Moves execution point back to the method call dropping current method frames from the stack +action.Debugger.ForceEarlyReturn.text=Force Early Return +action.Debugger.ForceEarlyReturn.description=Force a method to return before it reaches a return statement action.Debugger.ShowLibraryFrames.text=Show/Hide Library Frames action.Pause.text=_Pause Program action.Pause.description=Suspend program execution and enable debugging diff --git a/resources-en/src/messages/DebuggerBundle.properties b/resources-en/src/messages/DebuggerBundle.properties index 0ccde1aa3a48..d0c2d8834f89 100644 --- a/resources-en/src/messages/DebuggerBundle.properties +++ b/resources-en/src/messages/DebuggerBundle.properties @@ -28,6 +28,7 @@ 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 error.executing.finally=Error while executing finally blocks: {0} +error.early.return=Error while doing early return: {0} warning.finally.block.detected=Non-empty finally blocks detected: button.drop.anyway=Drop frame anyway button.execute.finally=Execute finally blocks and drop frame @@ -335,7 +336,7 @@ method.breakpoints.tab.title=Java Method Breakpoints title.error.evaluating.breakpoint.condition=Breakpoint Condition Error title.error.evaluating.breakpoint.action=Breakpoint Action Error error.evaluating.breakpoint.condition.or.action=Problem processing VM event:\nBreakpoint: ''{0}''\nError: {1}\nWould you like to stop at the breakpoint? -error.unable.to.evaluate.expression=unable to evaluate the expression +error.unable.to.evaluate.expression=Unable to evaluate the expression breakpoint.properties.panel.option.pass.count=&Pass count: breakpoint.properties.panel.option.class.filters=C&lass filters: breakpoint.properties.panel.option.instance.filters=&Instance filters: diff --git a/resources/src/idea/JavaActions.xml b/resources/src/idea/JavaActions.xml index 2f511f16f876..6f675109b883 100644 --- a/resources/src/idea/JavaActions.xml +++ b/resources/src/idea/JavaActions.xml @@ -110,6 +110,10 @@ + + + +