| /******************************************************************************* |
| * Copyright (c) 2000, 2005 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.jdt.internal.debug.core.breakpoints; |
| |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.model.IDebugTarget; |
| import org.eclipse.jdt.debug.core.IJavaWatchpoint; |
| import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget; |
| import com.sun.jdi.Field; |
| import com.sun.jdi.ObjectReference; |
| import com.sun.jdi.ReferenceType; |
| import com.sun.jdi.ThreadReference; |
| import com.sun.jdi.VMDisconnectedException; |
| import com.sun.jdi.event.AccessWatchpointEvent; |
| import com.sun.jdi.event.Event; |
| import com.sun.jdi.event.ModificationWatchpointEvent; |
| import com.sun.jdi.request.AccessWatchpointRequest; |
| import com.sun.jdi.request.EventRequest; |
| import com.sun.jdi.request.EventRequestManager; |
| import com.sun.jdi.request.ModificationWatchpointRequest; |
| import com.sun.jdi.request.WatchpointRequest; |
| |
| public class JavaWatchpoint extends JavaLineBreakpoint implements IJavaWatchpoint { |
| |
| private static final String JAVA_WATCHPOINT= "org.eclipse.jdt.debug.javaWatchpointMarker"; //$NON-NLS-1$ |
| /** |
| * Watchpoint attribute storing the access value (value <code>"org.eclipse.jdt.debug.core.access"</code>). |
| * This attribute is stored as a <code>boolean</code>, indicating whether a |
| * watchpoint is an access watchpoint. |
| */ |
| protected static final String ACCESS= "org.eclipse.jdt.debug.core.access"; //$NON-NLS-1$ |
| /** |
| * Watchpoint attribute storing the modification value (value <code>"org.eclipse.jdt.debug.core.modification"</code>). |
| * This attribute is stored as a <code>boolean</code>, indicating whether a |
| * watchpoint is a modification watchpoint. |
| */ |
| protected static final String MODIFICATION= "org.eclipse.jdt.debug.core.modification"; //$NON-NLS-1$ |
| /** |
| * Watchpoint attribute storing the auto_disabled value (value <code>"org.eclipse.jdt.debug.core.auto_disabled"</code>). |
| * This attribute is stored as a <code>boolean</code>, indicating whether a |
| * watchpoint has been auto-disabled (as opposed to being disabled explicitly by the user) |
| */ |
| protected static final String AUTO_DISABLED="org.eclipse.jdt.debug.core.auto_disabled"; //$NON-NLS-1$ |
| |
| /** |
| * Breakpoint attribute storing the name of the field |
| * on which a breakpoint is set. |
| * (value <code>"org.eclipse.jdt.debug.core.fieldName"</code>). This attribute is a <code>String</code>. |
| */ |
| protected static final String FIELD_NAME= "org.eclipse.jdt.debug.core.fieldName"; //$NON-NLS-1$ |
| /** |
| * Flag indicating that this breakpoint last suspended execution |
| * due to a field access |
| */ |
| protected static final Integer ACCESS_EVENT= new Integer(0); |
| /** |
| * Flag indicating that this breakpoint last suspended execution |
| * due to a field modification |
| */ |
| protected static final Integer MODIFICATION_EVENT= new Integer(1); |
| /** |
| * Maps each debug target that is suspended for this breakpiont to reason that |
| * this breakpoint suspended it. Reasons include: |
| * <ol> |
| * <li>Field access (value <code>ACCESS_EVENT</code>)</li> |
| * <li>Field modification (value <code>MODIFICATION_EVENT</code>)</li> |
| * </ol> |
| */ |
| private HashMap fLastEventTypes= new HashMap(10); |
| |
| public JavaWatchpoint() { |
| } |
| |
| /** |
| * @see JDIDebugModel#createWatchpoint(IResource, String, String, int, int, int, int, boolean, Map) |
| */ |
| public JavaWatchpoint(final IResource resource, final String typeName, final String fieldName, final int lineNumber, final int charStart, final int charEnd, final int hitCount, final boolean add, final Map attributes) throws DebugException { |
| IWorkspaceRunnable wr= new IWorkspaceRunnable() { |
| public void run(IProgressMonitor monitor) throws CoreException { |
| setMarker(resource.createMarker(JAVA_WATCHPOINT)); |
| |
| // add attributes |
| addLineBreakpointAttributes(attributes, getModelIdentifier(), true, lineNumber, charStart, charEnd); |
| addTypeNameAndHitCount(attributes, typeName, hitCount); |
| // configure the field handle |
| addFieldName(attributes, fieldName); |
| // configure the access and modification flags to defaults |
| addDefaultAccessAndModification(attributes); |
| |
| // set attributes |
| ensureMarker().setAttributes(attributes); |
| |
| register(add); |
| } |
| }; |
| run(getMarkerRule(resource), wr); |
| } |
| |
| /** |
| * @see JavaBreakpoint#createRequest(JDIDebugTarget, ReferenceType) |
| * |
| * Creates and installs an access and modification watchpoint request |
| * in the given reference type, configuring the requests as appropriate |
| * for this watchpoint. The requests are then enabled based on whether |
| * this watchpoint is an access watchpoint, modification watchpoint, or |
| * both. Finally, the requests are registered with the given target. |
| */ |
| protected boolean createRequest(JDIDebugTarget target, ReferenceType type) throws CoreException { |
| if (shouldSkipBreakpoint()) { |
| return false; |
| } |
| Field field= null; |
| |
| field= type.fieldByName(getFieldName()); |
| if (field == null) { |
| // error |
| return false; |
| } |
| AccessWatchpointRequest accessRequest= null; |
| ModificationWatchpointRequest modificationRequest= null; |
| if (target.supportsAccessWatchpoints()) { |
| accessRequest= createAccessWatchpoint(target, field); |
| registerRequest(accessRequest, target); |
| } else { |
| notSupported(JDIDebugBreakpointMessages.JavaWatchpoint_no_access_watchpoints); |
| } |
| if (target.supportsModificationWatchpoints()) { |
| modificationRequest= createModificationWatchpoint(target, field); |
| if (modificationRequest == null) { |
| return false; |
| } |
| registerRequest(modificationRequest, target); |
| return true; |
| } |
| notSupported(JDIDebugBreakpointMessages.JavaWatchpoint_no_modification_watchpoints); |
| return false; |
| } |
| |
| /** |
| * @see JavaBreakpoint#setRequestThreadFilter(EventRequest) |
| */ |
| protected void setRequestThreadFilter(EventRequest request, ThreadReference thread) { |
| ((WatchpointRequest)request).addThreadFilter(thread); |
| } |
| |
| /** |
| * Either access or modification watchpoints are not supported. Throw an appropriate exception. |
| * |
| * @param message the message that states that access or modification watchpoints |
| * are not supported |
| */ |
| protected void notSupported(String message) throws DebugException { |
| throw new DebugException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), |
| DebugException.NOT_SUPPORTED, message, null)); // |
| } |
| |
| /** |
| * Create an access watchpoint for the given breakpoint and associated field |
| */ |
| protected AccessWatchpointRequest createAccessWatchpoint(JDIDebugTarget target, Field field) throws CoreException { |
| return (AccessWatchpointRequest) createWatchpoint(target, field, true); |
| } |
| |
| /** |
| * Create a modification watchpoint for the given breakpoint and associated field |
| */ |
| protected ModificationWatchpointRequest createModificationWatchpoint(JDIDebugTarget target, Field field) throws CoreException { |
| return (ModificationWatchpointRequest) createWatchpoint(target, field, false); |
| } |
| |
| /** |
| * Create a watchpoint for the given breakpoint and associated field. |
| * |
| * @param target the target in which the request will be installed |
| * @param field the field on which the request will be set |
| * @param access <code>true</code> if an access watchpoint will be |
| * created. <code>false</code> if a modification watchpoint will |
| * be created. |
| * |
| * @return an WatchpointRequest (AccessWatchpointRequest if access is |
| * <code>true</code>; ModificationWatchpointRequest if access is <code>false</code>). |
| */ |
| protected WatchpointRequest createWatchpoint(JDIDebugTarget target, Field field, boolean access) throws CoreException { |
| WatchpointRequest request= null; |
| EventRequestManager manager = target.getEventRequestManager(); |
| if (manager == null) { |
| target.requestFailed(JDIDebugBreakpointMessages.JavaWatchpoint_Unable_to_create_breakpoint_request___VM_disconnected__1, null); |
| } |
| try { |
| if (access) { |
| request= manager.createAccessWatchpointRequest(field); |
| } else { |
| request= manager.createModificationWatchpointRequest(field); |
| } |
| configureRequest(request, target); |
| } catch (VMDisconnectedException e) { |
| if (!target.isAvailable()) { |
| return null; |
| } |
| target.internalError(e); |
| return null; |
| } catch (RuntimeException e) { |
| target.internalError(e); |
| return null; |
| } |
| return request; |
| } |
| |
| /** |
| * @see JavaBreakpoint#recreateRequest(EventRequest, JDIDebugTarget) |
| */ |
| protected EventRequest recreateRequest(EventRequest request, JDIDebugTarget target) throws CoreException { |
| try { |
| Field field= ((WatchpointRequest) request).field(); |
| if (request instanceof AccessWatchpointRequest) { |
| request= createAccessWatchpoint(target, field); |
| } else if (request instanceof ModificationWatchpointRequest) { |
| request= createModificationWatchpoint(target, field); |
| } |
| } catch (VMDisconnectedException e) { |
| if (!target.isAvailable()) { |
| return request; |
| } |
| target.internalError(e); |
| return request; |
| } catch (RuntimeException e) { |
| target.internalError(e); |
| } |
| return request; |
| } |
| |
| /** |
| * @see IBreakpoint#setEnabled(boolean) |
| * |
| * If the watchpoint is not watching access or modification, |
| * set the default values. If this isn't done, the resulting |
| * state (enabled with access and modification both disabled) |
| * is ambiguous. |
| */ |
| public void setEnabled(boolean enabled) throws CoreException { |
| if (enabled) { |
| if (!(isAccess() || isModification())) { |
| setDefaultAccessAndModification(); |
| } |
| } |
| super.setEnabled(enabled); |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IWatchpoint#isAccess() |
| */ |
| public boolean isAccess() throws CoreException { |
| return ensureMarker().getAttribute(ACCESS, false); |
| } |
| |
| /** |
| * Sets whether this breakpoint will suspend execution when its associated |
| * field is accessed. If true and this watchpoint is disabled, this watchpoint |
| * is automatically enabled. If both access and modification are false, |
| * this watchpoint is automatically disabled. |
| * |
| * @param access whether to suspend on field access |
| * @exception CoreException if unable to set the property |
| * on this breakpoint's underlying marker |
| * @see org.eclipse.debug.core.model.IWatchpoint#setAccess(boolean) |
| */ |
| public void setAccess(boolean access) throws CoreException { |
| if (access == isAccess()) { |
| return; |
| } |
| setAttribute(ACCESS, access); |
| if (access && !isEnabled()) { |
| setEnabled(true); |
| } else if (!(access || isModification())) { |
| setEnabled(false); |
| } |
| recreate(); |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IWatchpoint#isModification() |
| */ |
| public boolean isModification() throws CoreException { |
| return ensureMarker().getAttribute(MODIFICATION, false); |
| } |
| |
| /** |
| * Sets whether this breakpoint will suspend execution when its associated |
| * field is modified. If true and this watchpoint is disabled, this watchpoint |
| * is automatically enabled. If both access and modification are false, |
| * this watchpoint is automatically disabled. |
| * |
| * @param modification whether to suspend on field modification |
| * @exception CoreException if unable to set the property on |
| * this breakpoint's underlying marker |
| * @see org.eclipse.debug.core.model.IWatchpoint#setModification(boolean) |
| */ |
| public void setModification(boolean modification) throws CoreException { |
| if (modification == isModification()) { |
| return; |
| } |
| setAttribute(MODIFICATION, modification); |
| if (modification && !isEnabled()) { |
| setEnabled(true); |
| } else if (!(modification || isAccess())) { |
| setEnabled(false); |
| } |
| recreate(); |
| } |
| |
| /** |
| * Sets the default access and modification attributes of the watchpoint. |
| * The default values are: |
| * <ul> |
| * <li>access = <code>false</code> |
| * <li>modification = <code>true</code> |
| * <ul> |
| */ |
| protected void setDefaultAccessAndModification() throws CoreException { |
| Object[] values= new Object[]{Boolean.TRUE, Boolean.TRUE}; |
| String[] attributes= new String[]{ACCESS, MODIFICATION}; |
| setAttributes(attributes, values); |
| } |
| |
| |
| /** |
| * Adds the default access and modification attributes of |
| * the watchpoint to the given map |
| * <ul> |
| * <li>access = true |
| * <li>modification = true |
| * <li>auto disabled = false |
| * <ul> |
| */ |
| protected void addDefaultAccessAndModification(Map attributes) { |
| attributes.put(ACCESS, Boolean.TRUE); |
| attributes.put(MODIFICATION, Boolean.TRUE); |
| attributes.put(AUTO_DISABLED, Boolean.FALSE); |
| } |
| |
| /** |
| * Adds the field name to the given attribute map |
| */ |
| protected void addFieldName(Map attributes, String fieldName) { |
| attributes.put(FIELD_NAME, fieldName); |
| } |
| |
| /** |
| * @see IJavaWatchpoint#getFieldName() |
| */ |
| public String getFieldName() throws CoreException { |
| return ensureMarker().getAttribute(FIELD_NAME, null); |
| } |
| |
| /** |
| * Store the type of the event, then handle it as specified in |
| * the superclass. This is useful for correctly generating the |
| * thread text when asked (assumes thread text is requested after |
| * the event is passed to this breakpoint. |
| * |
| * Also, @see JavaBreakpoint#handleEvent(Event, JDIDebugTarget) |
| */ |
| public boolean handleEvent(Event event, JDIDebugTarget target) { |
| if (event instanceof AccessWatchpointEvent) { |
| fLastEventTypes.put(target, ACCESS_EVENT); |
| } else if (event instanceof ModificationWatchpointEvent) { |
| fLastEventTypes.put(target, MODIFICATION_EVENT); |
| } |
| return super.handleEvent(event, target); |
| } |
| |
| |
| /** |
| * @see JavaBreakpoint#updateEnabledState(EventRequest, JDIDebugTarget) |
| */ |
| protected void updateEnabledState(EventRequest request, JDIDebugTarget target) throws CoreException { |
| boolean enabled = isEnabled(); |
| if (request instanceof AccessWatchpointRequest) { |
| if (isAccess()) { |
| if (enabled != request.isEnabled()) { |
| internalUpdateEnabledState(request, enabled, target); |
| } |
| } else { |
| if (request.isEnabled()) { |
| internalUpdateEnabledState(request, false, target); |
| } |
| } |
| } |
| if (request instanceof ModificationWatchpointRequest) { |
| if (isModification()) { |
| if (enabled != request.isEnabled()) { |
| internalUpdateEnabledState(request, enabled, target); |
| } |
| } else { |
| if (request.isEnabled()) { |
| internalUpdateEnabledState(request, false, target); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @see IJavaWatchpoint#isAccessSuspend(IDebugTarget) |
| */ |
| public boolean isAccessSuspend(IDebugTarget target) { |
| Integer lastEventType= (Integer) fLastEventTypes.get(target); |
| if (lastEventType == null) { |
| return false; |
| } |
| return lastEventType.equals(ACCESS_EVENT); |
| } |
| /** |
| * @see IJavaLineBreakpoint#supportsCondition() |
| */ |
| public boolean supportsCondition() { |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#removeFromTarget(JDIDebugTarget) |
| */ |
| public void removeFromTarget(JDIDebugTarget target) throws CoreException { |
| fLastEventTypes.remove(target); |
| super.removeFromTarget(target); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#addInstanceFilter(EventRequest, ObjectReference) |
| */ |
| protected void addInstanceFilter(EventRequest request, ObjectReference object) { |
| if (request instanceof WatchpointRequest) { |
| ((WatchpointRequest)request).addInstanceFilter(object); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IWatchpoint#supportsAccess() |
| */ |
| public boolean supportsAccess() { |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IWatchpoint#supportsModification() |
| */ |
| public boolean supportsModification() { |
| return true; |
| } |
| |
| } |