blob: 37309af3faf4729b990d1e91e68e13fbeccf118d [file] [log] [blame]
package org.eclipse.jdi.internal.request;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdi.internal.FieldImpl;
import org.eclipse.jdi.internal.LocationImpl;
import org.eclipse.jdi.internal.MirrorImpl;
import org.eclipse.jdi.internal.ObjectReferenceImpl;
import org.eclipse.jdi.internal.ReferenceTypeImpl;
import org.eclipse.jdi.internal.ThreadReferenceImpl;
import org.eclipse.jdi.internal.VirtualMachineImpl;
import org.eclipse.jdi.internal.event.EventImpl;
import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket;
import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
import com.sun.jdi.InternalException;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VMMismatchException;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.InvalidRequestStateException;
import com.sun.jdi.request.StepRequest;
/**
* 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;
public static final byte MODIF_KIND_INSTANCE = 11;
/** 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;
/** 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 by JDI spec.
/**
* Modifiers.
*/
/** Count filters. */
protected ArrayList fCountFilters;
/** Thread filters. */
protected ArrayList fThreadFilters = null;
/** Class filters. */
protected ArrayList fClassFilters = null;
/** Class filters. */
protected ArrayList fClassFilterRefs = null;
/** Class Exclusion filters. */
protected ArrayList fClassExclusionFilters = null;
/** Location filters. */
protected ArrayList fLocationFilters = null;
/** Exception filters. */
protected ArrayList fExceptionFilters = null;
/** Field filters. */
protected ArrayList fFieldFilters = null;
/** Thread step filters. */
protected ArrayList fThreadStepFilters = null;
/** Instance filters. */
protected ArrayList fInstanceFilters = null;
/**
* Creates new EventRequest.
*/
protected EventRequestImpl(String description, VirtualMachineImpl vmImpl) {
super(description, vmImpl);
}
/**
* @return Returns string representation.
*/
public String toString() {
return super.toString() + (fRequestID == null ? RequestMessages.getString("EventRequestImpl._(not_enabled)_1") : RequestMessages.getString("EventRequestImpl.___2") + fRequestID); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* @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 (value == 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); //$NON-NLS-1$
fRequestID.write(this, outData);
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.ER_CLEAR, outBytes);
switch (replyPacket.errorCode()) {
case JdwpReplyPacket.NOT_FOUND:
throw new InvalidRequestStateException();
}
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); //$NON-NLS-1$
writeByte(suspendPolicyJDWP(), "suspend policy", EventRequestImpl.suspendPolicyMap(), outData); //$NON-NLS-1$
writeInt(modifierCount(), "modifiers", outData); //$NON-NLS-1$
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();
if (fCountFilters == null)
fCountFilters = new ArrayList();
fCountFilters.add(new Integer(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();
if (fThreadFilters == null)
fThreadFilters = new ArrayList();
fThreadFilters.add(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();
if (fClassFilterRefs == null)
fClassFilterRefs = new ArrayList();
fClassFilterRefs.add(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();
if (fClassFilters == null)
fClassFilters = new ArrayList();
fClassFilters.add(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();
if (fClassExclusionFilters == null)
fClassExclusionFilters = new ArrayList();
fClassExclusionFilters.add(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);
if (fLocationFilters == null)
fLocationFilters = new ArrayList();
fLocationFilters.add(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 {
// refType Null means report exceptions of all types.
if (refType != null)
checkVM(refType);
if (fExceptionFilters == null)
fExceptionFilters = new ArrayList();
ExceptionFilter filter = new ExceptionFilter();
filter.fException = refType;
filter.fNotifyCaught = notifyCaught;
filter.fNotifyUncaught = notifyUncaught;
fExceptionFilters.add(filter);
}
/**
* Restricts reported events to those that occur for a given field.
*/
public void addFieldFilter(FieldImpl field) throws VMMismatchException {
// Used in createXWatchpointRequest methods.
checkVM(field);
if (fFieldFilters == null)
fFieldFilters = new ArrayList();
fFieldFilters.add(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();
if (fThreadStepFilters == null)
fThreadStepFilters = new ArrayList();
ThreadStepFilter filter = new ThreadStepFilter();
filter.fThread = thread;
filter.fThreadStepSize = size;
filter.fThreadStepDepth = depth;
fThreadStepFilters.add(filter);
}
public void addInstanceFilter(ObjectReference instance) {
checkVM(instance);
if (fInstanceFilters == null) {
fInstanceFilters = new ArrayList();
}
fInstanceFilters.add(instance);
}
/**
* 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(RequestMessages.getString("EventRequestImpl.Invalid_suspend_policy_encountered___3") + fSuspendPolicy); //$NON-NLS-1$
}
}
/**
* @return Returns JDWP constant for step size.
*/
public int threadStepSizeJDWP(int threadStepSize) {
switch (threadStepSize) {
case StepRequest.STEP_MIN:
return STEP_SIZE_MIN_JDWP;
case StepRequest.STEP_LINE:
return STEP_SIZE_LINE_JDWP;
default:
throw new InternalException(RequestMessages.getString("EventRequestImpl.Invalid_step_size_encountered___4") + threadStepSize); //$NON-NLS-1$
}
}
/**
* @return Returns JDWP constant for step depth.
*/
public int threadStepDepthJDWP(int threadStepDepth) {
switch (threadStepDepth) {
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(RequestMessages.getString("EventRequestImpl.Invalid_step_depth_encountered___5") + threadStepDepth); //$NON-NLS-1$
}
}
/**
* @return Returns JDWP EventKind.
*/
protected abstract byte eventKind();
/**
* @return Returns true if modifier count is set.
*/
private boolean modifierCountIsSet() {
return (fCountFilters != null);
}
/**
* @return Returns number of modifiers.
*/
protected int modifierCount() {
int count = 0;
if (fCountFilters != null)
count += fCountFilters.size();
if (fThreadFilters != null)
count += fThreadFilters.size();
if (fClassFilterRefs != null)
count += fClassFilterRefs.size();
if (fClassFilters != null)
count += fClassFilters.size();
if (fClassExclusionFilters != null)
count += fClassExclusionFilters.size();
if (fLocationFilters != null)
count += fLocationFilters.size();
if (fExceptionFilters != null)
count += fExceptionFilters.size();
if (fFieldFilters != null)
count += fFieldFilters.size();
if (fThreadStepFilters != null)
count += fThreadStepFilters.size();
if (fInstanceFilters != null)
count += fInstanceFilters.size();
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 (fThreadStepFilters != null) {
for (int i = 0; i < fThreadStepFilters.size(); i++) {
ThreadStepFilter filter = (ThreadStepFilter)fThreadStepFilters.get(i);
writeByte(MODIF_KIND_STEP, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
filter.fThread.write(this, outData);
writeInt(threadStepSizeJDWP(filter.fThreadStepSize), "step size", outData); //$NON-NLS-1$
writeInt(threadStepDepthJDWP(filter.fThreadStepDepth), "step depth", outData); //$NON-NLS-1$
}
}
if (fFieldFilters != null) {
for (int i = 0; i < fFieldFilters.size(); i++) {
writeByte(MODIF_KIND_FIELDONLY, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
((FieldImpl)fFieldFilters.get(i)).writeWithReferenceType(this, outData);
}
}
if (fExceptionFilters != null) {
for (int i = 0; i < fExceptionFilters.size(); i++) {
ExceptionFilter filter = (ExceptionFilter)fExceptionFilters.get(i);
writeByte(MODIF_KIND_EXCEPTIONONLY, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
if (filter.fException != null)
filter.fException.write(this, outData);
else
ReferenceTypeImpl.writeNull(this, outData);
writeBoolean(filter.fNotifyCaught, "notify caught", outData); //$NON-NLS-1$
writeBoolean(filter.fNotifyUncaught, "notify uncaught", outData); //$NON-NLS-1$
}
}
if (fLocationFilters != null) {
for (int i = 0; i < fLocationFilters.size(); i++) {
writeByte(MODIF_KIND_LOCATIONONLY, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
((LocationImpl)fLocationFilters.get(i)).write(this, outData);
}
}
if (fClassExclusionFilters != null) {
for (int i = 0; i < fClassExclusionFilters.size(); i++) {
writeByte(MODIF_KIND_CLASSEXCLUDE, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
writeString((String)fClassExclusionFilters.get(i), "class excl. filter", outData); //$NON-NLS-1$
}
}
if (fClassFilters != null) {
for (int i = 0; i < fClassFilters.size(); i++) {
writeByte(MODIF_KIND_CLASSMATCH, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
writeString((String)fClassFilters.get(i), "class filter", outData); //$NON-NLS-1$
}
}
if (fClassFilterRefs != null) {
for (int i = 0; i < fClassFilterRefs.size(); i++) {
writeByte(MODIF_KIND_CLASSONLY, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
((ReferenceTypeImpl)fClassFilterRefs.get(i)).write(this, outData);
}
}
if (fThreadFilters != null) {
for (int i = 0; i < fThreadFilters.size(); i++) {
writeByte(MODIF_KIND_THREADONLY, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
((ThreadReferenceImpl)fThreadFilters.get(i)).write(this, outData);
}
}
if (fCountFilters != null) {
for (int i = 0; i < fCountFilters.size(); i++) {
writeByte(MODIF_KIND_COUNT, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
writeInt(((Integer)fCountFilters.get(i)).intValue(), "count filter", outData); //$NON-NLS-1$
}
}
if (fInstanceFilters != null) {
for (int i = 0; i < fInstanceFilters.size(); i++) {
writeByte(MODIF_KIND_INSTANCE, "modifier", modifierKindMap(), outData); //$NON-NLS-1$
((ObjectReferenceImpl)fInstanceFilters.get(i)).write(this, 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_")) { //$NON-NLS-1$
name = name.substring(10);
fStepSizeMap.put(intValue, name);
} else if (name.startsWith("STEP_DEPTH_")) { //$NON-NLS-1$
name = name.substring(11);
fStepDepthMap.put(intValue, name);
} else if (name.startsWith("SUSPENDPOL_")) { //$NON-NLS-1$
name = name.substring(11);
fSuspendPolicyMap.put(intValue, name);
} else if (name.startsWith("MODIF_KIND_")) { //$NON-NLS-1$
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;
}
class ExceptionFilter {
/** If non-null, specifies that exceptions which are instances of fExceptionFilterRef will be reported. */
ReferenceTypeImpl fException = null;
/** If true, caught exceptions will be reported. */
boolean fNotifyCaught = false;
/** If true, uncaught exceptions will be reported. */
boolean fNotifyUncaught = false;
}
class ThreadStepFilter {
/** ThreadReference of thread in which to step. */
protected ThreadReferenceImpl fThread = null;
/** Size of each step. */
protected int fThreadStepSize;
/** Relative call stack limit. */
protected int fThreadStepDepth;
}
}