blob: 58d28632efb59635f4eca48578fcce0f58fa2bd0 [file] [log] [blame]
package org.eclipse.jdi.internal.request;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import com.sun.jdi.*;
import com.sun.jdi.request.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.*;
import org.eclipse.jdi.internal.*;
import org.eclipse.jdi.internal.jdwp.*;
import org.eclipse.jdi.internal.connect.*;
import org.eclipse.jdi.internal.event.*;
import java.io.*;
import java.util.*;
/**
* this class implements the corresponding interfaces
* declared by the JDI specification. See the com.sun.jdi package
* for more information.
*
*/
public abstract class EventRequestImpl extends MirrorImpl implements EventRequest {
/** Jdwp constants for StepRequests. */
public static final byte STEP_SIZE_MIN_JDWP = 0;
public static final byte STEP_SIZE_LINE_JDWP = 1;
public static final byte STEP_DEPTH_INTO_JDWP = 0;
public static final byte STEP_DEPTH_OVER_JDWP = 1;
public static final byte STEP_DEPTH_OUT_JDWP = 2;
public static final byte STEP_DEPTH_REENTER_JDWP_HCR = 3; // OTI specific for Hot Code Replacement.
/** Jdwp constants for SuspendPolicy. */
public static final byte SUSPENDPOL_NONE_JDWP = 0;
public static final byte SUSPENDPOL_EVENT_THREAD_JDWP = 1;
public static final byte SUSPENDPOL_ALL_JDWP = 2;
/** Constants for ModifierKind. */
public static final byte MODIF_KIND_COUNT = 1;
public static final byte MODIF_KIND_CONDITIONAL = 2;
public static final byte MODIF_KIND_THREADONLY = 3;
public static final byte MODIF_KIND_CLASSONLY = 4;
public static final byte MODIF_KIND_CLASSMATCH = 5;
public static final byte MODIF_KIND_CLASSEXCLUDE = 6;
public static final byte MODIF_KIND_LOCATIONONLY = 7;
public static final byte MODIF_KIND_EXCEPTIONONLY = 8;
public static final byte MODIF_KIND_FIELDONLY = 9;
public static final byte MODIF_KIND_STEP = 10;
/** Mapping of command codes to strings. */
private static HashMap fStepSizeMap = null;
private static HashMap fStepDepthMap = null;
private static HashMap fSuspendPolicyMap = null;
private static HashMap fModifierKindMap = null;
/** Value for counfilter if not set. */
protected static final int COUNTFILTER_NONE = -1;
/** Flag that indicates the request was generated from inside of this JDI implementation. */
private boolean fGeneratedInside = false;
/** User property map. */
private HashMap fPropertyMap;
/** RequestId of EventRequest, assigned by the reply data of the JDWP Event Reuqest Set command, null if request had not yet been enabled. */
protected RequestID fRequestID = null;
/** Determines the threads to suspend when the requested event occurs in the target VM. */
private byte fSuspendPolicy = SUSPEND_ALL; // Default is as specified bu JDI spec.
/**
* Modifiers.
*/
/** Limits the requested event to be reported at most once after a given number of occurrences. */
protected int fCountFilter = COUNTFILTER_NONE;
/** Thread filter. */
protected ThreadReferenceImpl fThreadFilter = null;
/** Class filter. */
protected String fClassFilter = null;
/** Class filter. */
protected ReferenceTypeImpl fClassFilterRef = null;
/** Class Exclusion filter. */
protected String fClassExclusionFilter = null;
/** Location filter. */
protected LocationImpl fLocationFilter = null;
/** If non-null, specifies that exceptions which are instances of fExceptionFilterRef will be reported. */
protected boolean fHasExceptionFilter = false;
/** If non-null, specifies that exceptions which are instances of fExceptionFilterRef will be reported. */
protected ReferenceTypeImpl fExceptionFilter = null;
/** If true, caught exceptions will be reported. */
protected boolean fNotifyCaught = false;
/** If true, uncaught exceptions will be reported. */
protected boolean fNotifyUncaught = false;
/** Restricts reported events to those that occur for a given field. */
protected FieldImpl fFieldFilter = null;
/** ThreadReference of thread in which to step. */
protected ThreadReferenceImpl fThreadStepFilter = null;
/** Size of each step. */
protected int fThreadStepSize;
/** Relative call stack limit. */
protected int fThreadStepDepth;
/**
* Creates new EventRequest.
*/
protected EventRequestImpl(String description, VirtualMachineImpl vmImpl) {
super(description, vmImpl);
}
/**
* @return Returns string representation.
*/
public String toString() {
return super.toString() + (fRequestID == null ? " (not enabled)" : ": " + fRequestID);
}
/**
* @return Returns the value of the property with the specified key.
*/
public Object getProperty(Object key) {
if (fPropertyMap == null)
return null;
else
return fPropertyMap.get(key);
}
/**
* Add an arbitrary key/value "property" to this request.
*/
public void putProperty(Object key, Object value) {
if (fPropertyMap == null)
fPropertyMap = new HashMap();
if (key == null)
fPropertyMap.remove(key);
else
fPropertyMap.put(key, value);
}
/**
* Sets the generated inside flag. Used for requests that are not generated by JDI requests from outside.
*/
public void setGeneratedInside() {
fGeneratedInside = true;
}
/**
* @return Returns whether the event request was generated from inside of this JDI implementation.
*/
public final boolean isGeneratedInside() {
return fGeneratedInside;
}
/**
* Disables event request.
*/
public void disable() {
if (!isEnabled())
return;
initJdwpRequest();
try {
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
DataOutputStream outData = new DataOutputStream(outBytes);
writeByte(eventKind(), "event kind", EventImpl.eventKindMap(), outData);
fRequestID.write(this, outData);
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.ER_CLEAR, outBytes);
defaultReplyErrorHandler(replyPacket.errorCode());
fRequestID = null;
} catch (IOException e) {
defaultIOExceptionHandler(e);
} finally {
handledJdwpRequest();
}
}
/**
* Enables event request.
*/
public void enable() {
if (isEnabled())
return;
initJdwpRequest();
try {
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
DataOutputStream outData = new DataOutputStream(outBytes);
writeByte(eventKind(), "event kind", EventImpl.eventKindMap(), outData);
writeByte(suspendPolicyJDWP(), "suspend policy", EventRequestImpl.suspendPolicyMap(), outData);
writeInt(modifierCount(), "modifiers", outData);
writeModifiers(outData);
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.ER_SET, outBytes);
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
fRequestID = RequestID.read(this, replyData);
virtualMachineImpl().eventRequestManagerImpl().addRequestIDMapping(this);
} catch (IOException e) {
defaultIOExceptionHandler(e);
} finally {
handledJdwpRequest();
}
}
/**
* Clear all breakpoints (used by EventRequestManager).
*/
public static void clearAllBreakpoints(MirrorImpl mirror) {
mirror.initJdwpRequest();
try {
JdwpReplyPacket replyPacket = mirror.requestVM(JdwpCommandPacket.ER_CLEAR_ALL_BREAKPOINTS);
mirror.defaultReplyErrorHandler(replyPacket.errorCode());
} finally {
mirror.handledJdwpRequest();
}
}
/**
* @return Returns whether event request is enabled.
*/
public final boolean isEnabled() {
return fRequestID != null;
}
/**
* Disables or enables event request.
*/
public void setEnabled(boolean enable) {
if (enable)
enable();
else
disable();
}
/**
* @exception InvalidRequestStateException is thrown if this request is enabled.
*/
public void checkDisabled() throws InvalidRequestStateException {
if (isEnabled())
throw new InvalidRequestStateException();
}
/**
* Sets suspend policy.
*/
public void setSuspendPolicy(int suspendPolicy) {
fSuspendPolicy = (byte)suspendPolicy;
if (isEnabled()) {
disable();
enable();
}
}
/**
* @return Returns suspend policy.
*/
public int suspendPolicy() {
return fSuspendPolicy;
}
/**
* @return Returns requestID, or null if request ID is not (yet) assigned.
*/
public final RequestID requestID() {
return fRequestID;
}
/**
* Sets countfilter.
*/
public void addCountFilter(int count) throws InvalidRequestStateException {
checkDisabled();
fCountFilter = count;
}
/**
* Restricts reported events to those in the given thread.
*/
public void addThreadFilter(ThreadReference threadFilter) throws ObjectCollectedException, VMMismatchException, InvalidRequestStateException {
checkVM(threadFilter);
checkDisabled();
if (threadFilter.isCollected())
throw new ObjectCollectedException();
fThreadFilter = (ThreadReferenceImpl)threadFilter;
}
/**
* Restricts the events generated by this request to the preparation of reference types whose name matches this restricted regular expression.
*/
public void addClassFilter(ReferenceType filter) throws VMMismatchException, InvalidRequestStateException {
checkVM(filter);
checkDisabled();
fClassFilterRef = (ReferenceTypeImpl)filter;
}
/**
* Restricts the events generated by this request to be the preparation of the given reference type and any subtypes.
*/
public void addClassFilter(String filter) throws InvalidRequestStateException {
checkDisabled();
fClassFilter = filter;
}
/**
* Restricts the events generated by this request to the preparation of reference types whose name does not match this restricted regular expression.
*/
public void addClassExclusionFilter(String filter) throws InvalidRequestStateException {
checkDisabled();
fClassExclusionFilter = filter;
}
/**
* Restricts the events generated by this request to those that occur at the given location.
*/
public void addLocationFilter(LocationImpl location) throws VMMismatchException {
// Used in createBreakpointRequest.
checkVM(location);
fLocationFilter = location;
}
/**
* Restricts reported exceptions by their class and whether they are caught or uncaught.
*/
public void addExceptionFilter(ReferenceTypeImpl refType, boolean notifyCaught, boolean notifyUncaught) throws VMMismatchException {
// Used in createExceptionRequest.
fHasExceptionFilter = true;
// refType Null means report exceptions of all types.
if (refType != null)
checkVM(refType);
fExceptionFilter = refType;
fNotifyCaught = notifyCaught;
fNotifyUncaught = notifyUncaught;
}
/**
* Restricts reported events to those that occur for a given field.
*/
public void addFieldFilter(FieldImpl field) throws VMMismatchException {
// Used in createXWatchpointRequest methods.
checkVM(field);
fFieldFilter = field;
}
/**
* Restricts reported step events to those which satisfy depth and size constraints.
*/
public void addStepFilter(ThreadReferenceImpl thread, int size, int depth) throws VMMismatchException, ObjectCollectedException {
// Used in createStepRequest.
checkVM(thread);
if (thread.isCollected())
throw new ObjectCollectedException();
fThreadStepFilter = thread;
fThreadStepSize = size;
fThreadStepDepth = depth;
}
/**
* From here on JDWP functionality of EventRequest is implemented.
*/
/**
* @return Returns JDWP constant for suspend policy.
*/
public byte suspendPolicyJDWP() {
switch(fSuspendPolicy) {
case SUSPEND_NONE:
return SUSPENDPOL_NONE_JDWP;
case SUSPEND_EVENT_THREAD:
return SUSPENDPOL_EVENT_THREAD_JDWP;
case SUSPEND_ALL:
return SUSPENDPOL_ALL_JDWP;
default:
throw new InternalException("Invalid suspend policy encountered: " + fSuspendPolicy);
}
}
/**
* @return Returns JDWP constant for step size.
*/
public int threadStepSizeJDWP() {
switch (fThreadStepSize) {
case StepRequest.STEP_MIN:
return STEP_SIZE_MIN_JDWP;
case StepRequest.STEP_LINE:
return STEP_SIZE_LINE_JDWP;
default:
throw new InternalException("Invalid step size encountered: " + fThreadStepSize);
}
}
/**
* @return Returns JDWP constant for step depth.
*/
public int threadStepDepthJDWP() {
switch (fThreadStepDepth) {
case StepRequest.STEP_INTO:
return STEP_DEPTH_INTO_JDWP;
case StepRequest.STEP_OVER:
return STEP_DEPTH_OVER_JDWP;
case StepRequest.STEP_OUT:
return STEP_DEPTH_OUT_JDWP;
default:
throw new InternalException("Invalid step depth encountered: " + fThreadStepDepth);
}
}
/**
* @return Returns JDWP EventKind.
*/
protected abstract byte eventKind();
/**
* @return Returns true if modifier count is set.
*/
private boolean modifierCountIsSet() {
return (fCountFilter >= 0 );
}
/**
* @return Returns number of modifiers.
*/
protected int modifierCount() {
int count = 0;
if (modifierCountIsSet())
count++;
if (fThreadFilter != null)
count++;
if (fClassFilterRef != null)
count++;
if (fClassFilter != null)
count++;
if (fClassExclusionFilter != null)
count++;
if (fLocationFilter != null)
count++;
if (fHasExceptionFilter)
count++;
if (fFieldFilter != null)
count++;
if (fThreadStepFilter != null)
count++;
return count;
}
/**
* Writes JDWP bytestream representation of modifiers.
*/
protected void writeModifiers(DataOutputStream outData) throws IOException {
// Note: for some reason the order of these modifiers matters when communicating with SUN's VM.
// It seems to expect them 'the wrong way around'.
if (fThreadStepFilter != null) {
writeByte(MODIF_KIND_STEP, "modifier", modifierKindMap(), outData);
fThreadStepFilter.write(this, outData);
writeInt(threadStepSizeJDWP(), "step size", outData);
writeInt(threadStepDepthJDWP(), "step depth", outData);
}
if (fFieldFilter != null) {
writeByte(MODIF_KIND_FIELDONLY, "modifier", modifierKindMap(), outData);
fFieldFilter.writeWithReferenceType(this, outData);
}
if (fHasExceptionFilter) {
writeByte(MODIF_KIND_EXCEPTIONONLY, "modifier", modifierKindMap(), outData);
if (fExceptionFilter != null)
fExceptionFilter.write(this, outData);
else
ReferenceTypeImpl.writeNull(this, outData);
writeBoolean(fNotifyCaught, "notify caught", outData);
writeBoolean(fNotifyUncaught, "notify uncaught", outData);
}
if (fLocationFilter != null) {
writeByte(MODIF_KIND_LOCATIONONLY, "modifier", modifierKindMap(), outData);
fLocationFilter.write(this, outData);
}
if (fClassExclusionFilter != null) {
writeByte(MODIF_KIND_CLASSEXCLUDE, "modifier", modifierKindMap(), outData);
writeString(fClassExclusionFilter, "class excl. filter", outData);
}
if (fClassFilter != null) {
writeByte(MODIF_KIND_CLASSMATCH, "modifier", modifierKindMap(), outData);
writeString(fClassFilter, "class filter", outData);
}
if (fClassFilterRef != null) {
writeByte(MODIF_KIND_CLASSONLY, "modifier", modifierKindMap(), outData);
fClassFilterRef.write(this, outData);
}
if (fThreadFilter != null) {
writeByte(MODIF_KIND_THREADONLY, "modifier", modifierKindMap(), outData);
fThreadFilter.write(this, outData);
}
if (modifierCountIsSet()) {
writeByte(MODIF_KIND_COUNT, "modifier", modifierKindMap(), outData);
writeInt(fCountFilter, "count filter", outData);
}
}
/**
* Retrieves constant mappings.
*/
public static void getConstantMaps() {
if (fStepSizeMap != null)
return;
java.lang.reflect.Field[] fields = EventRequestImpl.class.getDeclaredFields();
fStepSizeMap = new HashMap();
fStepDepthMap = new HashMap();
fSuspendPolicyMap = new HashMap();
fModifierKindMap = new HashMap();
for (int i = 0; i < fields.length; i++) {
java.lang.reflect.Field field = fields[i];
if ((field.getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0 || (field.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0 || (field.getModifiers() & java.lang.reflect.Modifier.FINAL) == 0)
continue;
try {
String name = field.getName();
Integer intValue = new Integer(field.getInt(null));
if (name.startsWith("STEP_SIZE_")) {
name = name.substring(10);
fStepSizeMap.put(intValue, name);
} else if (name.startsWith("STEP_DEPTH_")) {
name = name.substring(11);
fStepDepthMap.put(intValue, name);
} else if (name.startsWith("SUSPENDPOL_")) {
name = name.substring(11);
fSuspendPolicyMap.put(intValue, name);
} else if (name.startsWith("MODIF_KIND_")) {
name = name.substring(11);
fModifierKindMap.put(intValue, name);
}
} catch (IllegalAccessException e) {
// Will not occur for own class.
} catch (IllegalArgumentException e) {
// Should not occur.
// We should take care that all public static final constants
// in this class are numbers that are convertible to int.
}
}
}
/**
* @return Returns a map with string representations of tags.
*/
public static Map stepSizeMap() {
getConstantMaps();
return fStepSizeMap;
}
/**
* @return Returns a map with string representations of tags.
*/
public static Map stepDepthMap() {
getConstantMaps();
return fStepDepthMap;
}
/**
* @return Returns a map with string representations of type tags.
*/
public static Map suspendPolicyMap() {
getConstantMaps();
return fSuspendPolicyMap;
}
/**
* @return Returns a map with string representations of type tags.
*/
public static Map modifierKindMap() {
getConstantMaps();
return fModifierKindMap;
}
}