blob: cd3c6e02aaae26dd191b009d1d68ceb08f1248d0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdi.internal.request;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
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;
public static final byte MODIF_KIND_SOURCE_NAME_FILTER = 12;
/** Mapping of command codes to strings. */
private static HashMap<Integer, String> fStepSizeMap = null;
private static HashMap<Integer, String> fStepDepthMap = null;
private static HashMap<Integer, String> fSuspendPolicyMap = null;
private static HashMap<Integer, String> fModifierKindMap = null;
/**
* Flag that indicates the request was generated from inside of this JDI
* implementation.
*/
private boolean fGeneratedInside = false;
/** User property map. */
private HashMap<Object, Object> 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<Integer> fCountFilters;
/** Thread filters. */
protected ArrayList<ThreadReference> fThreadFilters = null;
/** Class filters. */
protected ArrayList<String> fClassFilters = null;
/** Class filters. */
protected ArrayList<ReferenceType> fClassFilterRefs = null;
/** Class Exclusion filters. */
protected ArrayList<String> fClassExclusionFilters = null;
/** Location filters. */
protected ArrayList<LocationImpl> fLocationFilters = null;
/** Exception filters. */
protected ArrayList<ExceptionFilter> fExceptionFilters = null;
/** Field filters. */
protected ArrayList<FieldImpl> fFieldFilters = null;
/** Thread step filters. */
protected ArrayList<ThreadStepFilter> fThreadStepFilters = null;
/** Instance filters. */
protected ArrayList<ObjectReference> fInstanceFilters = null;
/**
* source name filters
*
* @since 3.3
*/
protected ArrayList<String> fSourceNameFilters = null;
/**
* Creates new EventRequest.
*/
protected EventRequestImpl(String description, VirtualMachineImpl vmImpl) {
super(description, vmImpl);
}
/**
* @return Returns string representation.
*/
@Override
public String toString() {
return super.toString()
+ (fRequestID == null ? RequestMessages.EventRequestImpl___not_enabled__1
: RequestMessages.EventRequestImpl____2 + fRequestID); //
}
/**
* @return Returns the value of the property with the specified key.
*/
@Override
public Object getProperty(Object key) {
if (fPropertyMap == null) {
return null;
}
return fPropertyMap.get(key);
}
/**
* Add an arbitrary key/value "property" to this request.
*/
@Override
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.
*/
@Override
public synchronized 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());
virtualMachineImpl().eventRequestManagerImpl()
.removeRequestIDMapping(this);
fRequestID = null;
} catch (IOException e) {
defaultIOExceptionHandler(e);
} finally {
handledJdwpRequest();
}
}
/**
* Enables event request.
*/
@Override
public synchronized 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.
*/
@Override
public synchronized final boolean isEnabled() {
return fRequestID != null;
}
/**
* Disables or enables event request.
*/
@Override
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.
*/
@Override
public void setSuspendPolicy(int suspendPolicy) {
fSuspendPolicy = (byte) suspendPolicy;
if (isEnabled()) {
disable();
enable();
}
}
/**
* @return Returns suspend policy.
*/
@Override
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.
*/
@Override
public void addCountFilter(int count) throws InvalidRequestStateException {
checkDisabled();
if (fCountFilters == null)
fCountFilters = new ArrayList<>();
fCountFilters.add(Integer.valueOf(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 {
checkDisabled();
// 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 {
checkDisabled();
// 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 {
checkDisabled();
// 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 {
checkDisabled();
// Used in createStepRequest.
checkVM(thread);
if (fThreadStepFilters == null)
fThreadStepFilters = new ArrayList<>();
ThreadStepFilter filter = new ThreadStepFilter();
filter.fThread = thread;
filter.fThreadStepSize = size;
filter.fThreadStepDepth = depth;
fThreadStepFilters.add(filter);
}
/**
* Helper method which allows instance filters to be added
*
* @param instance
* the object ref instance to add to the listing
*/
public void addInstanceFilter(ObjectReference instance) {
checkDisabled();
checkVM(instance);
if (fInstanceFilters == null) {
fInstanceFilters = new ArrayList<>();
}
fInstanceFilters.add(instance);
}
/**
* Adds a source name filter to the request. An exact match or pattern
* beginning OR ending in '*'.
*
* @param pattern
* source name pattern
* @since 3.3
*/
public void addSourceNameFilter(String pattern) {
checkDisabled();
if (fSourceNameFilters == null) {
fSourceNameFilters = new ArrayList<>();
}
fSourceNameFilters.add(pattern);
}
/**
* 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.EventRequestImpl_Invalid_suspend_policy_encountered___3
+ fSuspendPolicy);
}
}
/**
* @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.EventRequestImpl_Invalid_step_size_encountered___4
+ threadStepSize);
}
}
/**
* @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.EventRequestImpl_Invalid_step_depth_encountered___5
+ threadStepDepth);
}
}
/**
* @return Returns JDWP EventKind.
*/
protected abstract byte eventKind();
/**
* @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();
if (fSourceNameFilters != null) {
if (supportsSourceNameFilters()) {
count += fSourceNameFilters.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 (ThreadStepFilter filter : fThreadStepFilters) {
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 (FieldImpl field : fFieldFilters) {
writeByte(MODIF_KIND_FIELDONLY,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
field.writeWithReferenceType(this,
outData);
}
}
if (fExceptionFilters != null) {
for (ExceptionFilter filter : fExceptionFilters) {
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 (LocationImpl locationFilter : fLocationFilters) {
writeByte(MODIF_KIND_LOCATIONONLY,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
locationFilter.write(this, outData);
}
}
if (fClassExclusionFilters != null) {
for (String classExclusionFilter : fClassExclusionFilters) {
writeByte(MODIF_KIND_CLASSEXCLUDE,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
writeString(classExclusionFilter,
"class excl. filter", outData); //$NON-NLS-1$
}
}
if (fClassFilters != null) {
for (String classFilter : fClassFilters) {
writeByte(MODIF_KIND_CLASSMATCH,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
writeString(classFilter,
"class filter", outData); //$NON-NLS-1$
}
}
if (fClassFilterRefs != null) {
for (ReferenceType classFilterRef : fClassFilterRefs) {
writeByte(MODIF_KIND_CLASSONLY,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
((ReferenceTypeImpl) classFilterRef).write(this,
outData);
}
}
if (fThreadFilters != null) {
for (ThreadReference threadFilter : fThreadFilters) {
writeByte(MODIF_KIND_THREADONLY,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
((ThreadReferenceImpl) threadFilter).write(this,
outData);
}
}
if (fCountFilters != null) {
for (Integer countFilter : fCountFilters) {
writeByte(MODIF_KIND_COUNT,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
writeInt(countFilter.intValue(),
"count filter", outData); //$NON-NLS-1$
}
}
if (fInstanceFilters != null) {
for (ObjectReference instanceFilter : fInstanceFilters) {
writeByte(MODIF_KIND_INSTANCE,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
((ObjectReferenceImpl) instanceFilter).write(this,
outData);
}
}
if (fSourceNameFilters != null) {
if (supportsSourceNameFilters()) {
for (String sourceNameFilter : fSourceNameFilters) {
writeByte(MODIF_KIND_SOURCE_NAME_FILTER,
"modifier", modifierKindMap(), outData); //$NON-NLS-1$
writeString(sourceNameFilter,
"modifier", outData); //$NON-NLS-1$
}
}
}
}
/**
* Returns whether JDWP supports source name filters (a 1.6 feature).
*
* @return whether JDWP supports source name filters
*/
private boolean supportsSourceNameFilters() {
return ((VirtualMachineImpl) virtualMachine())
.isJdwpVersionGreaterOrEqual(1, 6);
}
/**
* Retrieves constant mappings.
*/
public static void getConstantMaps() {
if (fStepSizeMap != null)
return;
fStepSizeMap = new HashMap<>();
fStepDepthMap = new HashMap<>();
fSuspendPolicyMap = new HashMap<>();
fModifierKindMap = new HashMap<>();
for (Field field : EventRequestImpl.class.getDeclaredFields()) {
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 = Integer.valueOf(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<Integer, String> stepSizeMap() {
getConstantMaps();
return fStepSizeMap;
}
/**
* @return Returns a map with string representations of tags.
*/
public static Map<Integer, String> stepDepthMap() {
getConstantMaps();
return fStepDepthMap;
}
/**
* @return Returns a map with string representations of type tags.
*/
public static Map<Integer, String> suspendPolicyMap() {
getConstantMaps();
return fSuspendPolicyMap;
}
/**
* @return Returns a map with string representations of type tags.
*/
public static Map<Integer, String> 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;
}
}