Permalink
Browse files

Bug 304383 - Support suspending on JavaScript exceptions

  • Loading branch information...
1 parent ad76568 commit 0645100b73daceb16f7703f0e91bbc4860b7d290 skaegi committed May 11, 2010
View
8 ...g.eclipse.wst.jsdt.debug.core/src/org/eclipse/wst/jsdt/debug/internal/core/Constants.java
@@ -78,5 +78,11 @@
/**
* Preference to suspend execution when any script loads, i.e. for every script load event
*/
- public static final String SUSPEND_ON_ALL_SCRIPT_LOADS = JavaScriptDebugPlugin.PLUGIN_ID + ".suspend_on_all_script_loads"; //$NON-NLS-1$
+ public static final String SUSPEND_ON_ALL_SCRIPT_LOADS = JavaScriptDebugPlugin.PLUGIN_ID + ".suspend_on_all_script_loads"; //$NON-NLS-1$
+
+ /**
+ * Preference to suspend execution when a thrown exception is encountered
+ * @since 1.1
+ */
+ public static final String SUSPEN_ON_THROWN_EXCEPTION = JavaScriptDebugPlugin.PLUGIN_ID + ".suspend_on_thrown_exceptions"; //$NON-NLS-1$
}
View
73 ...debug.core/src/org/eclipse/wst/jsdt/debug/internal/core/JavaScriptPreferencesManager.java
@@ -25,6 +25,7 @@
import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptBreakpoint;
import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptLoadBreakpoint;
import org.eclipse.wst.jsdt.debug.core.model.JavaScriptDebugModel;
+import org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptExceptionBreakpoint;
import org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptLoadBreakpoint;
import org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget;
@@ -46,6 +47,12 @@
private static IJavaScriptLoadBreakpoint allLoadsBreakpoint = null;
/**
+ * The invisible "suspend on exception" breakpoint
+ * @since 1.1
+ */
+ private static JavaScriptExceptionBreakpoint allExceptions = null;
+
+ /**
* Starts the manager
*/
public void start() {
@@ -54,6 +61,9 @@ public void start() {
if(node.getBoolean(Constants.SUSPEND_ON_ALL_SCRIPT_LOADS, false)) {
allLoadsBreakpoint = createSuspendOnAllLoads();
}
+ if(node.getBoolean(Constants.SUSPEN_ON_THROWN_EXCEPTION, false)) {
+ allExceptions = createSuspendOnException();
+ }
}
/**
@@ -68,6 +78,9 @@ public void stop() {
if(allLoadsBreakpoint != null) {
allLoadsBreakpoint.delete();
}
+ if(allExceptions != null) {
+ allExceptions.delete();
+ }
} catch (CoreException e) {
JavaScriptDebugPlugin.log(e);
}
@@ -86,6 +99,63 @@ public void preferenceChange(PreferenceChangeEvent event) {
deleteSuspendOnAllLoads();
}
}
+ if(event.getKey().equals(Constants.SUSPEN_ON_THROWN_EXCEPTION)) {
+ if(event.getNewValue().equals(Boolean.TRUE.toString())) {
+ //create it
+ allExceptions = createSuspendOnException();
+ }
+ else {
+ deleteSuspendOnException();
+ }
+ }
+ }
+
+ /**
+ * Creates the singleton exception breakpoint
+ *
+ * @return the new {@link JavaScriptExceptionBreakpoint}
+ * @since 1.1
+ */
+ private JavaScriptExceptionBreakpoint createSuspendOnException() {
+ try {
+ JavaScriptExceptionBreakpoint breakpoint = new JavaScriptExceptionBreakpoint(new HashMap());
+ IDebugTarget[] targets = DebugPlugin.getDefault().getLaunchManager().getDebugTargets();
+ for (int i = 0; i < targets.length; i++) {
+ if(targets[i] instanceof JavaScriptDebugTarget) {
+ ((JavaScriptDebugTarget)targets[i]).breakpointAdded(breakpoint);
+ }
+ }
+ return breakpoint;
+ }
+ catch(DebugException de) {
+ JavaScriptDebugPlugin.log(de);
+ }
+ return null;
+ }
+
+ /**
+ * Deletes any set exception breakpoints
+ *
+ * @since 1.1
+ */
+ private void deleteSuspendOnException() {
+ if(allExceptions != null) {
+ //notify all the targets
+ IDebugTarget[] targets = DebugPlugin.getDefault().getLaunchManager().getDebugTargets();
+ for (int i = 0; i < targets.length; i++) {
+ if(targets[i] instanceof JavaScriptDebugTarget) {
+ ((JavaScriptDebugTarget)targets[i]).breakpointRemoved(allExceptions, null);
+ }
+ }
+ try {
+ allExceptions.delete();
+ } catch (CoreException e) {
+ JavaScriptDebugPlugin.log(e);
+ }
+ finally {
+ allExceptions = null;
+ }
+ }
}
/**
@@ -166,6 +236,9 @@ public static void setGlobalSuspendOn(String scriptpath) {
if(allLoadsBreakpoint != null) {
breakpoints.add(allLoadsBreakpoint);
}
+ if(allExceptions != null) {
+ breakpoints.add(allExceptions);
+ }
return (IJavaScriptBreakpoint[]) breakpoints.toArray(new IJavaScriptBreakpoint[breakpoints.size()]);
}
View
100 ...c/org/eclipse/wst/jsdt/debug/internal/core/breakpoints/JavaScriptExceptionBreakpoint.java
@@ -10,12 +10,26 @@
*******************************************************************************/
package org.eclipse.wst.jsdt.debug.internal.core.breakpoints;
+import java.util.Map;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptLoadBreakpoint;
import org.eclipse.wst.jsdt.debug.core.jsdi.ScriptReference;
import org.eclipse.wst.jsdt.debug.core.jsdi.event.Event;
import org.eclipse.wst.jsdt.debug.core.jsdi.event.EventSet;
+import org.eclipse.wst.jsdt.debug.core.jsdi.event.ExceptionEvent;
+import org.eclipse.wst.jsdt.debug.core.jsdi.request.EventRequest;
+import org.eclipse.wst.jsdt.debug.core.jsdi.request.ExceptionRequest;
import org.eclipse.wst.jsdt.debug.core.model.JavaScriptDebugModel;
+import org.eclipse.wst.jsdt.debug.internal.core.JavaScriptDebugPlugin;
import org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget;
+import org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptThread;
/**
@@ -24,13 +38,46 @@
* @since 1.0
*/
public class JavaScriptExceptionBreakpoint extends JavaScriptBreakpoint {
-
+
+ public static final String MESSAGE = JavaScriptDebugPlugin.PLUGIN_ID + ".exception_message"; //$NON-NLS-1$
+
+ private ExceptionRequest request = null;
+
/**
* Constructor
*
* Required for persistence / restore
*/
public JavaScriptExceptionBreakpoint() {}
+
+ /**
+ * Constructor
+ *
+ * @param attributes
+ * @throws DebugException
+ */
+ public JavaScriptExceptionBreakpoint(final Map attributes) throws DebugException {
+ IWorkspaceRunnable wr = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ IMarker marker = ResourcesPlugin.getWorkspace().getRoot().createMarker(IJavaScriptLoadBreakpoint.MARKER_ID);
+ // create the marker
+ setMarker(marker);
+
+ // add attributes
+ attributes.put(IBreakpoint.ID, getModelIdentifier());
+ attributes.put(IBreakpoint.ENABLED, Boolean.valueOf(true));
+ Integer nochar = new Integer(-1);
+ attributes.put(IMarker.CHAR_START, nochar);
+ attributes.put(IMarker.CHAR_END, nochar);
+
+ ensureMarker().setAttributes(attributes);
+
+ // add to breakpoint manager if requested
+ register(false);
+ }
+ };
+ run(getMarkerRule(ResourcesPlugin.getWorkspace().getRoot()), wr);
+ }
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IBreakpoint#getModelIdentifier()
@@ -40,16 +87,63 @@ public String getModelIdentifier() {
}
/* (non-Javadoc)
+ * @see org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptBreakpoint#addToTarget(org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget)
+ */
+ public void addToTarget(JavaScriptDebugTarget target) throws CoreException {
+ if (target.isTerminated() || shouldSkipBreakpoint()) {
+ return;
+ }
+ if(request == null) {
+ request = target.getVM().eventRequestManager().createExceptionRequest();
+ request.setEnabled(true);
+ addRequestForTarget(target, request);
+ }
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptBreakpoint#deregisterRequest(org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget, org.eclipse.wst.jsdt.debug.core.jsdi.request.EventRequest)
+ */
+ protected void deregisterRequest(JavaScriptDebugTarget target, EventRequest request) {
+ target.removeJSDIEventListener(this, request);
+ this.request = null;
+ }
+
+ /* (non-Javadoc)
* @see org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptBreakpoint#createRequest(org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget, org.eclipse.wst.jsdt.debug.core.jsdi.ScriptReference)
*/
protected boolean createRequest(JavaScriptDebugTarget target, ScriptReference script) throws CoreException {
- return false;
+ return true;
}
/* (non-Javadoc)
* @see org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptBreakpoint#handleEvent(org.eclipse.wst.jsdt.debug.core.jsdi.event.Event, org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget, boolean, org.eclipse.wst.jsdt.debug.core.jsdi.event.EventSet)
*/
public boolean handleEvent(Event event, JavaScriptDebugTarget target, boolean suspendVote, EventSet eventSet) {
- return false;
+ if(event instanceof ExceptionEvent) {
+ try {
+ ExceptionEvent eevent = (ExceptionEvent) event;
+ setAttribute(MESSAGE, eevent.message());
+ JavaScriptThread thread = target.findThread(eevent.thread());
+ if (thread != null) {
+ thread.addBreakpoint(this);
+ return false;
+ }
+ }
+ catch(CoreException ce) {
+ JavaScriptDebugPlugin.log(ce);
+ }
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptBreakpoint#eventSetComplete(org.eclipse.wst.jsdt.debug.core.jsdi.event.Event, org.eclipse.wst.jsdt.debug.internal.core.model.JavaScriptDebugTarget, boolean, org.eclipse.wst.jsdt.debug.core.jsdi.event.EventSet)
+ */
+ public void eventSetComplete(Event event, JavaScriptDebugTarget target, boolean suspend, EventSet eventSet) {
+ if(event instanceof ExceptionEvent) {
+ JavaScriptThread thread = target.findThread(((ExceptionEvent) event).thread());
+ if (thread != null) {
+ thread.suspendForException(this);
+ }
+ }
}
}
View
82 ....jsdt.debug.core/src/org/eclipse/wst/jsdt/debug/internal/core/model/JavaScriptThread.java
@@ -44,6 +44,7 @@
import org.eclipse.wst.jsdt.debug.internal.core.Constants;
import org.eclipse.wst.jsdt.debug.internal.core.JavaScriptDebugPlugin;
import org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptBreakpoint;
+import org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptExceptionBreakpoint;
import org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptLoadBreakpoint;
/**
@@ -247,44 +248,47 @@ public JavaScriptThread(JavaScriptDebugTarget target, ThreadReference thread) {
*/
private synchronized String statusText() {
switch (state) {
- case SUSPENDED: {
- if (this.breakpoints.size() > 0) {
- try {
- JavaScriptBreakpoint breakpoint = (JavaScriptBreakpoint) breakpoints.get(0);
- if (breakpoint instanceof JavaScriptLoadBreakpoint) {
- String name = breakpoint.getScriptPath();
- if (Constants.EMPTY_STRING.equals(name)) {
- name = getSourceName();
+ case SUSPENDED: {
+ if (this.breakpoints.size() > 0) {
+ try {
+ JavaScriptBreakpoint breakpoint = (JavaScriptBreakpoint) breakpoints.get(0);
+ if (breakpoint instanceof JavaScriptLoadBreakpoint) {
+ String name = breakpoint.getScriptPath();
+ if (Constants.EMPTY_STRING.equals(name)) {
+ name = getSourceName();
+ }
+ return NLS.bind(ModelMessages.JSDIThread_suspended_loading_script, name);
}
- return NLS.bind(ModelMessages.JSDIThread_suspended_loading_script, name);
- }
- // TODO support function breakpoints here
- if (breakpoint instanceof IJavaScriptLineBreakpoint) {
- IJavaScriptLineBreakpoint bp = (IJavaScriptLineBreakpoint) breakpoint;
- return NLS.bind(ModelMessages.breakpoint_at_line_location, new String[] { Integer.toString(bp.getLineNumber()), getSourceName() });
+ if(breakpoint instanceof JavaScriptExceptionBreakpoint) {
+ return NLS.bind(ModelMessages.JavaScriptThread_suspended_on_exception, breakpoint.getMarker().getAttribute(JavaScriptExceptionBreakpoint.MESSAGE));
+ }
+ // TODO support function breakpoints here
+ if (breakpoint instanceof IJavaScriptLineBreakpoint) {
+ IJavaScriptLineBreakpoint bp = (IJavaScriptLineBreakpoint) breakpoint;
+ return NLS.bind(ModelMessages.breakpoint_at_line_location, new String[] { Integer.toString(bp.getLineNumber()), getSourceName() });
+ }
+ // TODO also need to report stopped at debugger; statement
+ } catch (CoreException ce) {
+ JavaScriptDebugPlugin.log(ce);
}
- // TODO also need to report stopped at debugger; statement
- } catch (CoreException ce) {
- JavaScriptDebugPlugin.log(ce);
}
+ return ModelMessages.thread_suspended;
}
- return ModelMessages.thread_suspended;
- }
- case RUNNING: {
- if (pendingstep != null) {
- return ModelMessages.thread_stepping;
+ case RUNNING: {
+ if (pendingstep != null) {
+ return ModelMessages.thread_stepping;
+ }
+ return ModelMessages.thread_running;
+ }
+ case TERMINATED: {
+ return ModelMessages.thread_terminated;
+ }
+ case ThreadReference.THREAD_STATUS_ZOMBIE: {
+ return ModelMessages.thread_zombie;
+ }
+ default: {
+ return ModelMessages.thread_state_unknown;
}
- return ModelMessages.thread_running;
- }
- case TERMINATED: {
- return ModelMessages.thread_terminated;
- }
- case ThreadReference.THREAD_STATUS_ZOMBIE: {
- return ModelMessages.thread_zombie;
- }
- default: {
- return ModelMessages.thread_state_unknown;
- }
}
}
@@ -530,6 +534,18 @@ public void run() {
}
/**
+ * Suspend the thread because an exception has been caught
+ *
+ * @param breakpoint
+ * @since 1.1
+ */
+ public void suspendForException(JavaScriptExceptionBreakpoint breakpoint) {
+ addBreakpoint(breakpoint);
+ markSuspended();
+ fireSuspendEvent(DebugEvent.BREAKPOINT);
+ }
+
+ /**
* Call-back from a breakpoint that has been hit
*
* @param breakpoint
View
1 ...wst.jsdt.debug.core/src/org/eclipse/wst/jsdt/debug/internal/core/model/ModelMessages.java
@@ -21,6 +21,7 @@
private static final String BUNDLE_NAME = "org.eclipse.wst.jsdt.debug.internal.core.model.modelmessages"; //$NON-NLS-1$
public static String breakpoint_at_line_location;
public static String JavaScriptThread_evaluated_script;
+ public static String JavaScriptThread_suspended_on_exception;
public static String JavaScriptValue_object_value_label;
public static String JSDIDebugTarget_jsdi_debug_target;
public static String JSDIDebugTarget_not_support_disconnect;
View
1 ...dt.debug.core/src/org/eclipse/wst/jsdt/debug/internal/core/model/modelmessages.properties
@@ -10,6 +10,7 @@
###############################################################################
breakpoint_at_line_location=suspended at breakpoint on line {0} in {1}
JavaScriptThread_evaluated_script=<evaluated script>
+JavaScriptThread_suspended_on_exception=suspended on exception: {0}
JavaScriptValue_object_value_label={0} (id={1})
JSDIDebugTarget_jsdi_debug_target=JavaScript Debug Target
JSDIDebugTarget_not_support_disconnect=JavaScript debug target {0} does not support being disconnected
View
2 bundles/org.eclipse.wst.jsdt.debug.ui/OSGI-INF/l10n/bundle.properties
@@ -31,6 +31,8 @@ toggleFunctionBreakpointAction.name = Toggle Function Breakpoint
toggleFunctionBreakpointAction.tooltip = Toggle a breakpoint on the selected function
showAllScripts.name = Show All Scripts
showAllScripts.tooltip = Shows or hides all scripts loaded in the visible targets
+suspendOnExceptions.name = Suspend On JavaScript Exceptions
+suspendOnExceptions.tooltip = Suspend on all JavaScript exceptions
suspendForAllScriptLoadsAction.name = Suspend For All Script Loads
suspendForAllScriptLoadsAction.tooltip = Suspends when any script is loaded
javascriptMenu.name = JavaScript
View
9 bundles/org.eclipse.wst.jsdt.debug.ui/plugin.xml
@@ -310,6 +310,15 @@
style="toggle"
tooltip="%suspendForAllScriptLoadsAction.tooltip">
</action>
+ <action
+ class="org.eclipse.wst.jsdt.debug.internal.ui.actions.SuspendOnExceptionsAction"
+ helpContextId="suspend_on_javascript_exceptions"
+ id="org.eclipse.wst.jsdt.debug.ui.suspend.on.exceptions"
+ label="%suspendOnExceptions.name"
+ menubarPath="javascriptActions"
+ style="toggle"
+ tooltip="%suspendOnExceptions.tooltip">
+ </action>
</viewContribution>
<viewContribution
id="debugViewActions"
View
68 ...ebug.ui/src/org/eclipse/wst/jsdt/debug/internal/ui/actions/SuspendOnExceptionsAction.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.debug.internal.ui.actions;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IViewActionDelegate;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.wst.jsdt.debug.internal.core.Constants;
+import org.eclipse.wst.jsdt.debug.internal.core.JavaScriptDebugPlugin;
+import org.eclipse.wst.jsdt.debug.internal.ui.JavaScriptDebugUIPlugin;
+import org.osgi.service.prefs.BackingStoreException;
+
+/**
+ * Handles suspending the debugger when an exception is thrown
+ *
+ * @since 1.1
+ */
+public class SuspendOnExceptionsAction implements IViewActionDelegate {
+
+ boolean initialized = false;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+ */
+ public void run(IAction action) {
+ IEclipsePreferences prefs = new InstanceScope().getNode(JavaScriptDebugPlugin.PLUGIN_ID);
+ if(prefs != null) {
+ prefs.putBoolean(Constants.SUSPEN_ON_THROWN_EXCEPTION, action.isChecked());
+ try {
+ prefs.flush();
+ } catch (BackingStoreException e) {
+ JavaScriptDebugUIPlugin.log(e);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ if(!initialized) {
+ boolean checked = Platform.getPreferencesService().getBoolean(
+ JavaScriptDebugPlugin.PLUGIN_ID,
+ Constants.SUSPEN_ON_THROWN_EXCEPTION,
+ false,
+ null);
+ action.setChecked(checked);
+ initialized = true;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart)
+ */
+ public void init(IViewPart view) {}
+}

0 comments on commit 0645100

Please sign in to comment.