From dd71643f8edf146337a375b23e08da6f286c352d Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 29 Jan 2013 21:52:32 +0100 Subject: [PATCH] IDEA-100057 Show exception object in debugger Variables when stopped on exception breakpoint --- .../debugger/engine/SuspendContextImpl.java | 2 + .../debugger/impl/DebuggerUtilsEx.java | 16 +++-- .../data/ThrownExceptionValueData.java | 56 ++++++++++++++++ .../debugger/ui/impl/watch/DebuggerTree.java | 17 +++++ .../impl/watch/NodeDescriptorFactoryImpl.java | 4 ++ .../ThrownExceptionValueDescriptorImpl.java | 67 +++++++++++++++++++ 6 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThrownExceptionValueDescriptorImpl.java diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java index 4ce8162fcd66..c8536bef1702 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java @@ -29,6 +29,7 @@ import com.sun.jdi.ThreadReference; import com.sun.jdi.event.EventSet; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; @@ -108,6 +109,7 @@ public abstract class SuspendContextImpl implements SuspendContext { } + @Nullable public EventSet getEventSet() { assertNotResumed(); return myEventSet; diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 88c3f50b3d86..908798e7dfbc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -40,11 +40,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.ui.classFilter.ClassFilter; +import com.intellij.util.SmartList; import com.sun.jdi.*; import com.sun.jdi.event.Event; +import com.sun.jdi.event.EventSet; import org.jdom.Attribute; import org.jdom.Element; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -332,16 +335,21 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { return elementsEqual(root1, root2); } + @NotNull public static List> getEventDescriptors(SuspendContextImpl suspendContext) { DebuggerManagerThreadImpl.assertIsManagerThread(); - if(suspendContext == null || suspendContext.getEventSet() == null) { + if(suspendContext == null) { return Collections.emptyList(); } - final List> eventDescriptors = new ArrayList>(); + final EventSet events = suspendContext.getEventSet(); + if(events == null) { + return Collections.emptyList(); + } + final List> eventDescriptors = new SmartList>(); final RequestManagerImpl requestManager = suspendContext.getDebugProcess().getRequestsManager(); - for (final Event event : suspendContext.getEventSet()) { - Requestor requestor = requestManager.findRequestor(event.request()); + for (final Event event : events) { + final Requestor requestor = requestManager.findRequestor(event.request()); if (requestor instanceof Breakpoint) { eventDescriptors.add(new Pair((Breakpoint)requestor, event)); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java new file mode 100644 index 000000000000..5c8610255f22 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/impl/descriptors/data/ThrownExceptionValueData.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2013 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.impl.descriptors.data; + +import com.intellij.debugger.ui.impl.watch.ThrownExceptionValueDescriptorImpl; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +public final class ThrownExceptionValueData extends DescriptorData{ + @NotNull + private final ObjectReference myExceptionObj; + + public ThrownExceptionValueData(@NotNull ObjectReference exceptionObj) { + myExceptionObj = exceptionObj; + } + + protected ThrownExceptionValueDescriptorImpl createDescriptorImpl(Project project) { + return new ThrownExceptionValueDescriptorImpl(project, myExceptionObj); + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ThrownExceptionValueData data = (ThrownExceptionValueData)o; + + if (!myExceptionObj.equals(data.myExceptionObj)) return false; + + return true; + } + + @Override + public int hashCode() { + return myExceptionObj.hashCode(); + } + + public DisplayKey getDisplayKey() { + return new SimpleDisplayKey(myExceptionObj); + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index e80bf60ebad0..06ca84038da2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -31,12 +31,14 @@ import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.impl.DebuggerSession; +import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.LocalVariableProxyImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.settings.ThreadsViewSettings; +import com.intellij.debugger.ui.breakpoints.Breakpoint; import com.intellij.debugger.ui.impl.DebuggerTreeBase; import com.intellij.debugger.ui.impl.tree.TreeBuilder; import com.intellij.debugger.ui.impl.tree.TreeBuilderNode; @@ -56,6 +58,8 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeSpeedSearch; import com.sun.jdi.*; +import com.sun.jdi.event.Event; +import com.sun.jdi.event.ExceptionEvent; import javax.swing.*; import javax.swing.event.TreeModelEvent; @@ -482,6 +486,19 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi final DebuggerTreeNodeImpl methodReturnValueNode = myNodeManager.createNode(returnValueDescriptor, evaluationContext); myChildren.add(1, methodReturnValueNode); } + // add context exceptions + for (Pair pair : DebuggerUtilsEx.getEventDescriptors(getSuspendContext())) { + final Event debugEvent = pair.getSecond(); + if (debugEvent instanceof ExceptionEvent) { + final ObjectReference exception = ((ExceptionEvent)debugEvent).exception(); + if (exception != null) { + final ValueDescriptorImpl exceptionDescriptor = myNodeManager.getThrownExceptionObjectDescriptor(stackDescriptor, exception); + final DebuggerTreeNodeImpl exceptionNode = myNodeManager.createNode(exceptionDescriptor, evaluationContext); + myChildren.add(1, exceptionNode); + } + } + } + } catch (EvaluateException e) { myChildren.clear(); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java index 9596b397b222..ba0c43f4c3e4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java @@ -172,6 +172,10 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { return getDescriptor(parent, new MethodReturnValueData(method, value)); } + public ValueDescriptorImpl getThrownExceptionObjectDescriptor(NodeDescriptorImpl parent, ObjectReference exceptionObject) { + return getDescriptor(parent, new ThrownExceptionValueData(exceptionObject)); + } + public ThreadDescriptorImpl getThreadDescriptor(NodeDescriptorImpl parent, ThreadReferenceProxyImpl thread) { return getDescriptor(parent, new ThreadData(thread)); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThrownExceptionValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThrownExceptionValueDescriptorImpl.java new file mode 100644 index 000000000000..40b5c3300468 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThrownExceptionValueDescriptorImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2013 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.ui.impl.watch; + +import com.intellij.debugger.DebuggerContext; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiExpression; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Type; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; + +/** + * User: lex + * Date: Oct 8, 2003 + * Time: 5:08:07 PM + */ +public class ThrownExceptionValueDescriptorImpl extends ValueDescriptorImpl{ + @NotNull + private final ObjectReference myExceptionObj; + + public ThrownExceptionValueDescriptorImpl(Project project, @NotNull ObjectReference exceptionObj) { + super(project); + myExceptionObj = exceptionObj; + } + + public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException { + return myExceptionObj; + } + + public String getName() { + return "Exception"; + } + + @NotNull + @Override + public Type getType() { + return myExceptionObj.referenceType(); + } + + public String calcValueName() { + return getName(); + } + + public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException { + throw new EvaluateException("Evaluation not supported for thrown exception object"); + } + + public boolean canSetValue() { + return false; + } +}