Bug 284158 - Dismiss exception breakpoint per instance
- ask user once per breakpoint and store answer in the breakpoint
Change-Id: I02d2e206e046d25bf0bfbcc27e173cf018a8abec
Signed-off-by: Stephan Herrmann <stephan.herrmann@berlin.de>
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java
index 628eec6..18f6620 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java
@@ -179,6 +179,7 @@
public static String thread_suspended_linebreakpoint;
public static String thread_suspended_methodentry;
public static String thread_suspended_exception;
+ public static String thread_suspended_exception_uncaught;
public static String thread_suspended_methodexit;
public static String thread_suspended_fieldmodification;
public static String thread_suspended_runtoline;
@@ -311,7 +312,15 @@
public static String JavaDebugOptionsManager_Method_breakpoint___2;
public static String JavaDebugOptionsManager_Watchpoint___3;
public static String JavaDebugOptionsManager_0;
+
+ public static String JavaDebugOptionsManager_exceptionRecurrence_dialogTitle;
+ public static String JavaDebugOptionsManager_exceptionRecurrence_dialogMessage;
+ public static String JavaDebugOptionsManager_cancel_buttonLabel;
+ public static String JavaDebugOptionsManager_skip_buttonLabel;
+ public static String JavaDebugOptionsManager_suspend_buttonLabel;
+
public static String JavaDebugOptionsManager_Line_breakpoint___4;
+
public static String JavaBreakpointWorkbenchAdapterFactory_1;
public static String HotCodeReplaceErrorDialog_0;
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties
index 861d79c..03b9ef4 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties
@@ -128,6 +128,7 @@
thread_suspended_linebreakpoint=Thread [{0}] (Suspended (breakpoint at line {1} in {2}))
thread_suspended_methodentry=Thread [{0}] (Suspended (entry into method {1} in {2}))
thread_suspended_exception=Thread [{0}] (Suspended (exception {1}))
+thread_suspended_exception_uncaught=Thread [{0}] (Suspended (uncaught exception {1}))
thread_suspended_methodexit=Thread [{0}] (Suspended (exit of method {1} in {2}))
thread_suspended_fieldmodification=Thread [{0}] (Suspended (modification of field {1} in {2}))
thread_suspended_runtoline=Thread [{0}] (Suspended (run to line {1} in {2}))
@@ -267,7 +268,20 @@
JavaDebugOptionsManager_Method_breakpoint___2=Method breakpoint:
JavaDebugOptionsManager_Watchpoint___3=Watchpoint:
JavaDebugOptionsManager_0=Initialize Java Debug Options
+JavaDebugOptionsManager_exceptionRecurrence_dialogTitle=Repeated exception occurrence
+JavaDebugOptionsManager_exceptionRecurrence_dialogMessage=The debugger is about to suspend on the exception breakpoint for {0}, \
+but the current exception instance has already caused suspending before.\n\
+\n\
+Please choose how this breakpoint should react to repeated occurrences of the same exception instance:\n\
+ \u2022 Skip:\t\t fire only once per exception instance\n\
+ \u2022 Suspend:\t fire always\n\
+ \u2022 Cancel:\t suspend now, but don't remember this decision\n\n\
+This choice does not affect the global preference regarding uncaught exceptions.
JavaDebugOptionsManager_Line_breakpoint___4=Line breakpoint:
+JavaDebugOptionsManager_skip_buttonLabel=Skip
+JavaDebugOptionsManager_suspend_buttonLabel=Suspend
+JavaDebugOptionsManager_cancel_buttonLabel=Cancel
+
JavaBreakpointWorkbenchAdapterFactory_1=\ [line:
HotCodeReplaceErrorDialog_0=&Continue
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java
index f243689..49f06cf 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -77,6 +77,7 @@
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceListValue;
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceListVariable;
import org.eclipse.jdt.internal.debug.core.model.JDIThread;
+import org.eclipse.jdt.internal.debug.ui.breakpoints.SuspendOnUncaughtExceptionListener;
import org.eclipse.jdt.internal.debug.ui.display.JavaInspectExpression;
import org.eclipse.jdt.internal.debug.ui.monitors.JavaContendedMonitor;
import org.eclipse.jdt.internal.debug.ui.monitors.JavaOwnedMonitor;
@@ -368,6 +369,9 @@
// check args == null in case the exception is a compilation error
if (breakpoint instanceof IJavaExceptionBreakpoint && args == null) {
key.append("_exception"); //$NON-NLS-1$
+ if (isUncaughtExceptionsBreakpoint(breakpoint)) {
+ key.append("_uncaught"); //$NON-NLS-1$
+ }
String exName = ((IJavaExceptionBreakpoint)breakpoint).getExceptionTypeName();
if (exName == null) {
exName = typeName;
@@ -432,6 +436,19 @@
return DebugUIMessages.JDIModelPresentation_unknown_name__1;
}
+ private boolean isUncaughtExceptionsBreakpoint(IJavaBreakpoint breakpoint) {
+ try {
+ for (String id : breakpoint.getBreakpointListeners()) {
+ if (SuspendOnUncaughtExceptionListener.ID_UNCAUGHT_EXCEPTION_LISTENER.equals(id)) {
+ return true;
+ }
+ }
+ } catch (CoreException e) {
+ DebugUIPlugin.log(e);
+ }
+ return false;
+ }
+
/**
* Build the text for an IJavaDebugTarget.
*/
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java
index 93d63bf..541ee0c 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -55,6 +55,7 @@
import org.eclipse.jdt.debug.core.IJavaBreakpointListener;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint.SuspendOnRecurrenceStrategy;
import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
import org.eclipse.jdt.debug.core.IJavaMethodBreakpoint;
import org.eclipse.jdt.debug.core.IJavaMethodEntryBreakpoint;
@@ -67,10 +68,12 @@
import org.eclipse.jdt.internal.debug.core.breakpoints.JavaExceptionBreakpoint;
import org.eclipse.jdt.internal.debug.core.logicalstructures.IJavaStructuresListener;
import org.eclipse.jdt.internal.debug.core.logicalstructures.JavaLogicalStructures;
+import org.eclipse.jdt.internal.debug.core.model.JDIThread;
import org.eclipse.jdt.internal.debug.ui.actions.JavaBreakpointPropertiesAction;
import org.eclipse.jdt.internal.debug.ui.breakpoints.SuspendOnCompilationErrorListener;
import org.eclipse.jdt.internal.debug.ui.breakpoints.SuspendOnUncaughtExceptionListener;
import org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookLauncher;
+import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
@@ -81,6 +84,7 @@
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import com.ibm.icu.text.MessageFormat;
import com.sun.jdi.InvocationException;
import com.sun.jdi.ObjectReference;
@@ -567,10 +571,74 @@
*/
@Override
public int breakpointHit(IJavaThread thread, IJavaBreakpoint breakpoint) {
+ if (thread instanceof JDIThread && breakpoint instanceof IJavaExceptionBreakpoint) {
+ if (shouldSkipSubsequentOccurrence((JDIThread) thread, (IJavaExceptionBreakpoint) breakpoint)) {
+ return DONT_SUSPEND;
+ }
+ }
return DONT_CARE;
}
/**
+ * Figure out whether suspending on an exceptionBreakpoint should be skipped due to the user's choice regarding
+ * {@link IJavaExceptionBreakpoint#setSuspendOnRecurrenceStrategy(int)}.
+ * <p>
+ * If the current hit is indeed recurrence of an already-seen exception instance, and if the user has not yet made a choice for this breakpoint,
+ * then a question dialog will be opened to request the user's choice.
+ * </p>
+ *
+ * @param thread
+ * the thread where an exception occurred
+ * @param exceptionBreakpoint
+ * the exceptionBreakpoint that just fired
+ * @return {@code true} if the current breakpoint hit should be skipped.
+ */
+ public boolean shouldSkipSubsequentOccurrence(JDIThread thread, IJavaExceptionBreakpoint exceptionBreakpoint) {
+ if (exceptionBreakpoint == fSuspendOnExceptionBreakpoint) {
+ // this breakpoint doesn't have the recurrence property, wait until we are called from SuspendOnUncaughtExceptionListener itself
+ return false;
+ }
+ SuspendOnRecurrenceStrategy skip = thread.shouldSkipExceptionRecurrence(exceptionBreakpoint);
+ if (skip == null) {
+ return false; // not a recurrence
+ }
+ if (skip == SuspendOnRecurrenceStrategy.RECURRENCE_UNCONFIGURED) {
+ skip = askUserExceptionRecurrence(exceptionBreakpoint);
+ try {
+ exceptionBreakpoint.setSuspendOnRecurrenceStrategy(skip);
+ } catch (CoreException e) {
+ JDIDebugUIPlugin.log(e);
+ }
+ }
+ switch (skip) {
+ case SKIP_RECURRENCES:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static SuspendOnRecurrenceStrategy askUserExceptionRecurrence(IJavaExceptionBreakpoint breakpoint) {
+ Shell shell = JDIDebugUIPlugin.getShell();
+ MessageDialog question = new MessageDialog(shell, DebugUIMessages.JavaDebugOptionsManager_exceptionRecurrence_dialogTitle, null, //
+ MessageFormat.format(DebugUIMessages.JavaDebugOptionsManager_exceptionRecurrence_dialogMessage, breakpoint.getExceptionTypeName()), //
+ MessageDialog.QUESTION, 0, //
+ DebugUIMessages.JavaDebugOptionsManager_skip_buttonLabel, //
+ DebugUIMessages.JavaDebugOptionsManager_suspend_buttonLabel, //
+ DebugUIMessages.JavaDebugOptionsManager_cancel_buttonLabel);
+ int answer[] = { -1 };
+ shell.getDisplay().syncExec(() -> answer[0] = question.open());
+ switch (answer[0]) {
+ case 0:
+ return SuspendOnRecurrenceStrategy.SKIP_RECURRENCES;
+ case 1:
+ return SuspendOnRecurrenceStrategy.SUSPEND_ALWAYS;
+ default:
+ return SuspendOnRecurrenceStrategy.RECURRENCE_UNCONFIGURED;
+ }
+ }
+
+ /**
* @see IJavaBreakpointListener#breakpointInstalled(IJavaDebugTarget, IJavaBreakpoint)
*/
@Override
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/breakpoints/ExceptionBreakpointEditor.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/breakpoints/ExceptionBreakpointEditor.java
index 8de056b..ccf78ee 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/breakpoints/ExceptionBreakpointEditor.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/breakpoints/ExceptionBreakpointEditor.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2009, 2011 IBM Corporation and others.
+ * Copyright (c) 2009, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,13 +13,23 @@
*******************************************************************************/
package org.eclipse.jdt.internal.debug.ui.breakpoints;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.jdt.debug.core.IJavaBreakpoint;
import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint.SuspendOnRecurrenceStrategy;
import org.eclipse.jdt.internal.debug.core.breakpoints.JavaExceptionBreakpoint;
import org.eclipse.jdt.internal.debug.ui.propertypages.PropertyPageMessages;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
@@ -34,11 +44,14 @@
public static final int PROP_CAUGHT = 0x1020;
public static final int PROP_UNCAUGHT = 0x1021;
public static final int PROP_SUBCLASSES = 0x1022;
+ public static final int PROP_RECURRENCE = 0x1023;
// editors
private Button fCaught;
private Button fUncaught;
private Button fSubclasses;
+ private Combo fRecurrence;
+ private Map<SuspendOnRecurrenceStrategy, String> fRecurrenceOptions;
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.debug.ui.breakpoints.StandardJavaBreakpointEditor#createControl(org.eclipse.swt.widgets.Composite)
@@ -53,9 +66,27 @@
fCaught = createSusupendPropertyEditor(composite, processMnemonics(PropertyPageMessages.ExceptionBreakpointEditor_1), PROP_CAUGHT);
fUncaught = createSusupendPropertyEditor(composite, processMnemonics(PropertyPageMessages.ExceptionBreakpointEditor_2), PROP_UNCAUGHT);
fSubclasses = createSusupendPropertyEditor(composite, processMnemonics(PropertyPageMessages.ExceptionBreakpointEditor_3), PROP_SUBCLASSES);
+ composite = SWTFactory.createComposite(container, parent.getFont(), 2, 1, 0, 0, 0);
+ fRecurrence = createRecurrenceEditor(composite, processMnemonics(PropertyPageMessages.ExceptionBreakpointEditor_recurrence_label), PROP_RECURRENCE);
return container;
}
+ private Combo createRecurrenceEditor(Composite parent, String labelText, int propId) {
+ fRecurrenceOptions = new HashMap<>();
+ fRecurrenceOptions.put(SuspendOnRecurrenceStrategy.RECURRENCE_UNCONFIGURED, PropertyPageMessages.ExceptionBreakpointEditor_recurrence_unconfigured);
+ fRecurrenceOptions.put(SuspendOnRecurrenceStrategy.SUSPEND_ALWAYS, PropertyPageMessages.ExceptionBreakpointEditor_recurrence_always);
+ fRecurrenceOptions.put(SuspendOnRecurrenceStrategy.SKIP_RECURRENCES, PropertyPageMessages.ExceptionBreakpointEditor_recurrence_onlyOnce);
+ SWTFactory.createLabel(parent, labelText, 1);
+ Combo box = SWTFactory.createCombo(parent, SWT.READ_ONLY, 1, GridData.FILL_HORIZONTAL, fRecurrenceOptions.values().toArray(new String[fRecurrenceOptions.size()]));
+ box.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setDirty(propId);
+ }
+ });
+ return box;
+ }
+
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.debug.ui.breakpoints.StandardJavaBreakpointEditor#setBreakpoint(org.eclipse.jdt.debug.core.IJavaBreakpoint)
*/
@@ -67,13 +98,16 @@
fCaught.setEnabled(true);
fUncaught.setEnabled(true);
fSubclasses.setEnabled(true);
+ fRecurrence.setEnabled(true);
fCaught.setSelection(ex.isCaught());
fUncaught.setSelection(ex.isUncaught());
fSubclasses.setSelection(((JavaExceptionBreakpoint)ex).isSuspendOnSubclasses());
+ fRecurrence.setText(fRecurrenceOptions.get(ex.getSuspendOnRecurrenceStrategy()));
} else {
fCaught.setEnabled(false);
fUncaught.setEnabled(false);
fSubclasses.setEnabled(false);
+ fRecurrence.setEnabled(false);
}
}
@@ -89,6 +123,16 @@
ex.setCaught(fCaught.getSelection());
ex.setUncaught(fUncaught.getSelection());
((JavaExceptionBreakpoint)ex).setSuspendOnSubclasses(fSubclasses.getSelection());
+ ex.setSuspendOnRecurrenceStrategy(stringToRecurrence(fRecurrence.getText()));
}
}
+
+ private SuspendOnRecurrenceStrategy stringToRecurrence(String text) {
+ for (Entry<SuspendOnRecurrenceStrategy, String> entry : fRecurrenceOptions.entrySet()) {
+ if (entry.getValue().equals(text)) {
+ return entry.getKey();
+ }
+ }
+ return SuspendOnRecurrenceStrategy.RECURRENCE_UNCONFIGURED;
+ }
}
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java
index 2c8c3b1..5f9e88f 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.java
@@ -23,6 +23,11 @@
public static String ExceptionBreakpointEditor_2;
public static String ExceptionBreakpointEditor_3;
+ public static String ExceptionBreakpointEditor_recurrence_label;
+ public static String ExceptionBreakpointEditor_recurrence_always;
+ public static String ExceptionBreakpointEditor_recurrence_onlyOnce;
+ public static String ExceptionBreakpointEditor_recurrence_unconfigured;
+
public static String ExceptionFilterEditor_5;
public static String ExceptionFilterEditor_6;
public static String ExceptionFilterEditor_7;
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties
index 904b158..b3f1417 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/propertypages/PropertyPageMessages.properties
@@ -15,6 +15,10 @@
ExceptionBreakpointEditor_1=&Caught locations
ExceptionBreakpointEditor_2=&Uncaught locations
ExceptionBreakpointEditor_3=Subclasses of this e&xception
+ExceptionBreakpointEditor_recurrence_label=Suspend on &recurrence per instance:
+ExceptionBreakpointEditor_recurrence_always=always
+ExceptionBreakpointEditor_recurrence_onlyOnce=only once
+ExceptionBreakpointEditor_recurrence_unconfigured=unconfigured
ExceptionFilterEditor_5=Re&strict to Selected Location(s):\nChecked locations are inclusive (stop in the specified location)\nUnchecked locations are exclusive (do not stop in the specified location)
ExceptionFilterEditor_6=&Add
ExceptionFilterEditor_7=Add a fully qualified type name or package name expression as a filter pattern for this breakpoint
diff --git a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
index 6911b7a..dd3ebe3 100644
--- a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jdt.debug; singleton:=true
-Bundle-Version: 3.13.200.qualifier
+Bundle-Version: 3.14.0.qualifier
Bundle-ClassPath: jdi.jar,
jdimodel.jar,
tools.jar
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaExceptionBreakpoint.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaExceptionBreakpoint.java
index fd11cf7..93c19c2 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaExceptionBreakpoint.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/IJavaExceptionBreakpoint.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -196,4 +196,53 @@
*/
@Deprecated
public boolean isInclusiveFiltered() throws CoreException;
+
+ /**
+ * Constants for telling the debugger for each exception breakpoint how to handle multiple occurrences of the same exception instance, which can
+ * happen via re-throwing or multiple finally clauses in the call stack.
+ *
+ * @since 3.14
+ * @see IJavaExceptionBreakpoint#getSuspendOnRecurrenceStrategy()
+ * @see IJavaExceptionBreakpoint#setSuspendOnRecurrenceStrategy(SuspendOnRecurrenceStrategy)
+ */
+ enum SuspendOnRecurrenceStrategy {
+ /**
+ * Signals that this setting has not yet been configured for a given breakpoint
+ */
+ RECURRENCE_UNCONFIGURED,
+ /**
+ * Signals that the exception breakpoint should always cause suspending.
+ */
+ SUSPEND_ALWAYS,
+ /**
+ * Signals that the breakpoint should not cause suspending more than once. This does not influence the way how the debugger reacts to uncaught
+ * exceptions.
+ */
+ SKIP_RECURRENCES
+ }
+
+ /**
+ * Define this breakpoint's {@link SuspendOnRecurrenceStrategy strategy} for suspending on recurrences of the same exception instance.
+ *
+ * @param strategy
+ * the new strategy
+ *
+ * @throws CoreException
+ * if accessing the breakpoint's marker failed
+ * @since 3.14
+ * @see #getSuspendOnRecurrenceStrategy()
+ */
+ void setSuspendOnRecurrenceStrategy(SuspendOnRecurrenceStrategy strategy) throws CoreException;
+
+ /**
+ * Answer this breakpoint's {@link SuspendOnRecurrenceStrategy strategy} for suspending on recurrences of the same exception instance.
+ *
+ * @return the strategy
+ *
+ * @throws CoreException
+ * if accessing the breakpoint's marker failed
+ * @since 3.14
+ * @see #setSuspendOnRecurrenceStrategy(SuspendOnRecurrenceStrategy)
+ */
+ SuspendOnRecurrenceStrategy getSuspendOnRecurrenceStrategy() throws CoreException;
}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaExceptionBreakpoint.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaExceptionBreakpoint.java
index bb2139e..9b1b622 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaExceptionBreakpoint.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaExceptionBreakpoint.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -105,6 +105,14 @@
protected static final String SUSPEND_ON_SUBCLASSES = "org.eclipse.jdt.debug.core.suspend_on_subclasses"; //$NON-NLS-1$
/**
+ * Allows the user to specify that each exception instance matching this breakpoint should suspend only once, i.e., re-throws and finally clauses
+ * will not again suspend on an exception instance that had already caused a suspend.
+ *
+ * @since 3.14
+ */
+ protected static final String SUSPEND_ON_RECURRENCE = "org.eclipse.jdt.debug.core.suspend_on_recurrence"; //$NON-NLS-1$
+
+ /**
* Name of the exception that was actually hit (could be a sub-type of the
* type that is being caught).
*/
@@ -322,6 +330,21 @@
recreate();
}
+ @Override
+ public SuspendOnRecurrenceStrategy getSuspendOnRecurrenceStrategy() throws CoreException {
+ int valueIndex = ensureMarker().getAttribute(SUSPEND_ON_RECURRENCE, SuspendOnRecurrenceStrategy.RECURRENCE_UNCONFIGURED.ordinal());
+ return SuspendOnRecurrenceStrategy.values()[valueIndex];
+ }
+
+ @Override
+ public void setSuspendOnRecurrenceStrategy(SuspendOnRecurrenceStrategy strategy) throws CoreException {
+ if (strategy == getSuspendOnRecurrenceStrategy()) {
+ return;
+ }
+ setAttribute(SUSPEND_ON_RECURRENCE, strategy.ordinal());
+ // don't re-create, the change only affects the debugger, not the target
+ }
+
/**
* @see IJavaExceptionBreakpoint#isChecked()
*/
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
index 45ee0ad..2e9a6a7 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -47,6 +47,8 @@
import org.eclipse.jdt.debug.core.IEvaluationRunnable;
import org.eclipse.jdt.debug.core.IJavaBreakpoint;
import org.eclipse.jdt.debug.core.IJavaBreakpointListener;
+import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint.SuspendOnRecurrenceStrategy;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
@@ -58,6 +60,7 @@
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.core.breakpoints.ConditionalBreakpointHandler;
import org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint;
+import org.eclipse.jdt.internal.debug.core.breakpoints.JavaExceptionBreakpoint;
import org.eclipse.jdt.internal.debug.core.breakpoints.JavaLineBreakpoint;
import org.eclipse.jdt.internal.debug.core.model.MethodResult.ResultType;
@@ -307,6 +310,11 @@
private MethodResult fMethodResult;
/**
+ * If previous suspend was on an exception breakpoint, this variable holds that Java exception instance, else {@code null}.
+ */
+ private IJavaObject fPreviousException;
+
+ /**
* Creates a new thread on the underlying thread reference in the given
* debug target.
*
@@ -1506,6 +1514,33 @@
}
/**
+ * To be called whenever a Java exception breakpoint is called. This thread will remember the Java exception instance associated with this event
+ * and will answer if suspending should be skipped as per the breakpoint's {@link IJavaExceptionBreakpoint.SuspendOnRecurrenceStrategy suspend on
+ * recurrence} strategy.
+ *
+ * @param breakpoint
+ * the breakpoint about to trigger
+ * @return either {@code null} to signal that this breakpoint hit is not a recurrence, or one of
+ * {@link SuspendOnRecurrenceStrategy#RECURRENCE_UNCONFIGURED}, {@link SuspendOnRecurrenceStrategy#SKIP_RECURRENCES}, or
+ * {@link SuspendOnRecurrenceStrategy#SUSPEND_ALWAYS}.
+ */
+ public SuspendOnRecurrenceStrategy shouldSkipExceptionRecurrence(IJavaExceptionBreakpoint breakpoint) {
+ if (breakpoint instanceof JavaExceptionBreakpoint) {
+ JavaExceptionBreakpoint exceptionBreakpoint = (JavaExceptionBreakpoint) breakpoint;
+ try {
+ IJavaObject lastException = exceptionBreakpoint.getLastException();
+ if (fPreviousException != null && fPreviousException.equals(lastException)) {
+ return exceptionBreakpoint.getSuspendOnRecurrenceStrategy();
+ }
+ fPreviousException = lastException;
+ } catch (CoreException e) {
+ // ignore
+ }
+ }
+ return null; // skipping not applicable, since this is no recurrence
+ }
+
+ /**
* Called after an event set with a breakpoint is done being processed.
* Updates thread state based on the result of handling the event set.
* Aborts any step in progress and fires a suspend event is suspending.
diff --git a/org.eclipse.jdt.debug/pom.xml b/org.eclipse.jdt.debug/pom.xml
index c14300e..7193858 100644
--- a/org.eclipse.jdt.debug/pom.xml
+++ b/org.eclipse.jdt.debug/pom.xml
@@ -18,6 +18,6 @@
</parent>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.debug</artifactId>
- <version>3.13.200-SNAPSHOT</version>
+ <version>3.14.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>