/*
* Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.tools.jdi;
import com.sun.jdi.*;
import com.sun.jdi.request.*;
import com.sun.tools.jdi.JDWP;
import java.util.*;
/**
* This interface is used to create and remove Breakpoints, Watchpoints,
* etc.
* It include implementations of all the request interfaces..
*/
// Warnings from List filters and List[] requestLists is hard to fix.
// Remove SuppressWarning when we fix the warnings from List filters
// and List[] requestLists. The generic array is not supported.
@SuppressWarnings("unchecked")
class EventRequestManagerImpl extends MirrorImpl
implements EventRequestManager
{
List[] requestLists;
private static int methodExitEventCmd = 0;
static int JDWPtoJDISuspendPolicy(byte jdwpPolicy) {
switch(jdwpPolicy) {
case JDWP.SuspendPolicy.ALL:
return EventRequest.SUSPEND_ALL;
case JDWP.SuspendPolicy.EVENT_THREAD:
return EventRequest.SUSPEND_EVENT_THREAD;
case JDWP.SuspendPolicy.NONE:
return EventRequest.SUSPEND_NONE;
default:
throw new IllegalArgumentException("Illegal policy constant: " + jdwpPolicy);
}
}
static byte JDItoJDWPSuspendPolicy(int jdiPolicy) {
switch(jdiPolicy) {
case EventRequest.SUSPEND_ALL:
return JDWP.SuspendPolicy.ALL;
case EventRequest.SUSPEND_EVENT_THREAD:
return JDWP.SuspendPolicy.EVENT_THREAD;
case EventRequest.SUSPEND_NONE:
return JDWP.SuspendPolicy.NONE;
default:
throw new IllegalArgumentException("Illegal policy constant: " + jdiPolicy);
}
}
/*
* Override superclass back to default equality
*/
public boolean equals(Object obj) {
return this == obj;
}
public int hashCode() {
return System.identityHashCode(this);
}
abstract class EventRequestImpl extends MirrorImpl implements EventRequest {
int id;
/*
* This list is not protected by a synchronized wrapper. All
* access/modification should be protected by synchronizing on
* the enclosing instance of EventRequestImpl.
*/
List filters = new ArrayList();
boolean isEnabled = false;
boolean deleted = false;
byte suspendPolicy = JDWP.SuspendPolicy.ALL;
private Map<Object, Object> clientProperties = null;
EventRequestImpl() {
super(EventRequestManagerImpl.this.vm);
}
/*
* Override superclass back to default equality
*/
public boolean equals(Object obj) {
return this == obj;
}
public int hashCode() {
return System.identityHashCode(this);
}
abstract int eventCmd();
InvalidRequestStateException invalidState() {
return new InvalidRequestStateException(toString());
}
String state() {
return deleted? " (deleted)" :
(isEnabled()? " (enabled)" : " (disabled)");
}
/**
* @return all the event request of this kind
*/
List requestList() {
return EventRequestManagerImpl.this.requestList(eventCmd());
}
/**
* delete the event request
*/
void delete() {
if (!deleted) {
requestList().remove(this);
disable(); /* must do BEFORE delete */
deleted = true;
}
}
public boolean isEnabled() {
return isEnabled;
}
public void enable() {
setEnabled(true);
}
public void disable() {
setEnabled(false);
}
public synchronized void setEnabled(boolean val) {
if (deleted) {
throw invalidState();
} else {
if (val != isEnabled) {
if (isEnabled) {
clear();
} else {
set();
}
}
}
}
public synchronized void addCountFilter(int count) {
if (isEnabled() || deleted) {
throw invalidState();
}
if (count < 1) {
throw new IllegalArgumentException("count is less than one");
}
filters.add(JDWP.EventRequest.Set.Modifier.Count.create(count));
}
public void setSuspendPolicy(int policy) {
if (isEnabled() || deleted) {
throw invalidState();
}
suspendPolicy = JDItoJDWPSuspendPolicy(policy);
}
public int suspendPolicy() {
return JDWPtoJDISuspendPolicy(suspendPolicy);
}
/**
* set (enable) the event request
*/
synchronized void set() {
JDWP.EventRequest.Set.Modifier[] mods =
(JDWP.EventRequest.Set.Modifier[])
filters.toArray(
new JDWP.EventRequest.Set.Modifier[filters.size()]);
try {
id = JDWP.EventRequest.Set.process(vm, (byte)eventCmd(),
suspendPolicy, mods).requestID;
} catch (JDWPException exc) {
throw exc.toJDIException();
}
isEnabled = true;
}
synchronized void clear() {
try {
JDWP.EventRequest.Clear.process(vm, (byte)eventCmd(), id);
} catch (JDWPException exc) {
throw exc.toJDIException();
}
isEnabled = false;
}
/**
* @return a small Map
* @see #putProperty
* @see #getProperty
*/
private Map<Object, Object> getProperties() {
if (clientProperties == null) {
clientProperties = new HashMap<Object, Object>(2);
}
return clientProperties;
}
/**
* Returns the value of the property with the specified key. Only
* properties added with <code>putProperty</code> will return
* a non-null value.
*
* @return the value of this property or null
* @see #putProperty
*/
public final Object getProperty(Object key) {
if (clientProperties == null) {
return null;
} else {
return getProperties().get(key);
}
}
/**
* Add an arbitrary key/value "property" to this component.
*
* @see #getProperty
*/
public final void putProperty(Object key, Object value) {
if (value != null) {
getProperties().put(key, value);
} else {
getProperties().remove(key);
}
}
}
abstract class ThreadVisibleEventRequestImpl extends EventRequestImpl {
public synchronized void addThreadFilter(ThreadReference thread) {
validateMirror(thread);
if (isEnabled() || deleted) {
throw invalidState();
}
filters.add(JDWP.EventRequest.Set.Modifier.ThreadOnly
.create((ThreadReferenceImpl)thread));
}
}
abstract class ClassVisibleEventRequestImpl
extends ThreadVisibleEventRequestImpl {
public synchronized void addClassFilter(ReferenceType clazz) {
validateMirror(clazz);
if (isEnabled() || deleted) {
throw invalidState();
}
filters.add(JDWP.EventRequest.Set.Modifier.ClassOnly
.create((ReferenceTypeImpl)clazz));
}
public synchronized void addClassFilter(String classPattern) {
if (isEnabled() || deleted) {
throw invalidState();
}
if (classPattern == null) {
throw new NullPointerException();
}
filters.add(JDWP.EventRequest.Set.Modifier.ClassMatch
.create(classPattern));
}
public synchronized void addClassExclusionFilter(String classPattern) {
if (isEnabled() || deleted) {
throw invalidState();
}
if (classPattern == null) {
throw new NullPointerException();
}
filters.add(JDWP.EventRequest.Set.Modifier.ClassExclude
.create(classPattern));
}
public synchronized void addInstanceFilter(ObjectReference instance) {
validateMirror(instance);
if (isEnabled() || deleted) {
throw invalidState();
}
if (!vm.canUseInstanceFilters()) {
throw new UnsupportedOperationException(
"target does not support instance filters");
}
filters.add(JDWP.EventRequest.Set.Modifier.InstanceOnly
.create((ObjectReferenceImpl)instance));
}
}
class BreakpointRequestImpl extends ClassVisibleEventRequestImpl
implements BreakpointRequest {
private final Location location;
BreakpointRequestImpl(Location location) {
this.location = location;
filters.add(0,JDWP.EventRequest.Set.Modifier.LocationOnly
.create(location));
requestList().add(this);
}
public Location location() {
return location;
}
int eventCmd() {
return JDWP.EventKind.BREAKPOINT;
}
public String toString() {
return "breakpoint request " + location() + state();
}
}
class ClassPrepareRequestImpl extends ClassVisibleEventRequestImpl
implements ClassPrepareRequest {
ClassPrepareRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.CLASS_PREPARE;
}
public synchronized void addSourceNameFilter(String sourceNamePattern) {
if (isEnabled() || deleted) {
throw invalidState();
}
if (!vm.canUseSourceNameFilters()) {
throw new UnsupportedOperationException(
"target does not support source name filters");
}
if (sourceNamePattern == null) {
throw new NullPointerException();
}
filters.add(JDWP.EventRequest.Set.Modifier.SourceNameMatch
.create(sourceNamePattern));
}
public String toString() {
return "class prepare request " + state();
}
}
class ClassUnloadRequestImpl extends ClassVisibleEventRequestImpl
implements ClassUnloadRequest {
ClassUnloadRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.CLASS_UNLOAD;
}
public String toString() {
return "class unload request " + state();
}
}
class ExceptionRequestImpl extends ClassVisibleEventRequestImpl
implements ExceptionRequest {
ReferenceType exception = null;
boolean caught = true;
boolean uncaught = true;
ExceptionRequestImpl(ReferenceType refType,
boolean notifyCaught, boolean notifyUncaught) {
exception = refType;
caught = notifyCaught;
uncaught = notifyUncaught;
{
ReferenceTypeImpl exc;
if (exception == null) {
exc = new ClassTypeImpl(vm, 0);
} else {
exc = (ReferenceTypeImpl)exception;
}
filters.add(JDWP.EventRequest.Set.Modifier.ExceptionOnly.
create(exc, caught, uncaught));
}
requestList().add(this);
}
public ReferenceType exception() {
return exception;
}
public boolean notifyCaught() {
return caught;
}
public boolean notifyUncaught() {
return uncaught;
}
int eventCmd() {
return JDWP.EventKind.EXCEPTION;
}
public String toString() {
return "exception request " + exception() + state();
}
}
class MethodEntryRequestImpl extends ClassVisibleEventRequestImpl
implements MethodEntryRequest {
MethodEntryRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.METHOD_ENTRY;
}
public String toString() {
return "method entry request " + state();
}
}
class MethodExitRequestImpl extends ClassVisibleEventRequestImpl
implements MethodExitRequest {
MethodExitRequestImpl() {
if (methodExitEventCmd == 0) {
/*
* If we can get return values, then we always get them.
* Thus, for JDI MethodExitRequests, we always use the
* same JDWP EventKind. Here we decide which to use and
* save it so that it will be used for all future
* MethodExitRequests.
*
* This call to canGetMethodReturnValues can't
* be done in the EventRequestManager ctor because that is too early.
*/
if (vm.canGetMethodReturnValues()) {
methodExitEventCmd = JDWP.EventKind.METHOD_EXIT_WITH_RETURN_VALUE;
} else {
methodExitEventCmd = JDWP.EventKind.METHOD_EXIT;
}
}
requestList().add(this);
}
int eventCmd() {
return EventRequestManagerImpl.methodExitEventCmd;
}
public String toString() {
return "method exit request " + state();
}
}
class MonitorContendedEnterRequestImpl extends ClassVisibleEventRequestImpl
implements MonitorContendedEnterRequest {
MonitorContendedEnterRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.MONITOR_CONTENDED_ENTER;
}
public String toString() {
return "monitor contended enter request " + state();
}
}
class MonitorContendedEnteredRequestImpl extends ClassVisibleEventRequestImpl
implements MonitorContendedEnteredRequest {
MonitorContendedEnteredRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.MONITOR_CONTENDED_ENTERED;
}
public String toString() {
return "monitor contended entered request " + state();
}
}
class MonitorWaitRequestImpl extends ClassVisibleEventRequestImpl
implements MonitorWaitRequest {
MonitorWaitRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.MONITOR_WAIT;
}
public String toString() {
return "monitor wait request " + state();
}
}
class MonitorWaitedRequestImpl extends ClassVisibleEventRequestImpl
implements MonitorWaitedRequest {
MonitorWaitedRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.MONITOR_WAITED;
}
public String toString() {
return "monitor waited request " + state();
}
}
class StepRequestImpl extends ClassVisibleEventRequestImpl
implements StepRequest {
ThreadReferenceImpl thread;
int size;
int depth;
StepRequestImpl(ThreadReference thread, int size, int depth) {
this.thread = (ThreadReferenceImpl)thread;
this.size = size;
this.depth = depth;
/*
* Translate size and depth to corresponding JDWP values.
*/
int jdwpSize;
switch (size) {
case STEP_MIN:
jdwpSize = JDWP.StepSize.MIN;
break;
case STEP_LINE:
jdwpSize = JDWP.StepSize.LINE;
break;
default:
throw new IllegalArgumentException("Invalid step size");
}
int jdwpDepth;
switch (depth) {
case STEP_INTO:
jdwpDepth = JDWP.StepDepth.INTO;
break;
case STEP_OVER:
jdwpDepth = JDWP.StepDepth.OVER;
break;
case STEP_OUT:
jdwpDepth = JDWP.StepDepth.OUT;
break;
default:
throw new IllegalArgumentException("Invalid step depth");
}
/*
* Make sure this isn't a duplicate
*/
List requests = stepRequests();
Iterator iter = requests.iterator();
while (iter.hasNext()) {
StepRequest request = (StepRequest)iter.next();
if ((request != this) &&
request.isEnabled() &&
request.thread().equals(thread)) {
throw new DuplicateRequestException(
"Only one step request allowed per thread");
}
}
filters.add(JDWP.EventRequest.Set.Modifier.Step.
create(this.thread, jdwpSize, jdwpDepth));
requestList().add(this);
}
public int depth() {
return depth;
}
public int size() {
return size;
}
public ThreadReference thread() {
return thread;
}
int eventCmd() {
return JDWP.EventKind.SINGLE_STEP;
}
public String toString() {
return "step request " + thread() + state();
}
}
class ThreadDeathRequestImpl extends ThreadVisibleEventRequestImpl
implements ThreadDeathRequest {
ThreadDeathRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.THREAD_DEATH;
}
public String toString() {
return "thread death request " + state();
}
}
class ThreadStartRequestImpl extends ThreadVisibleEventRequestImpl
implements ThreadStartRequest {
ThreadStartRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.THREAD_START;
}
public String toString() {
return "thread start request " + state();
}
}
abstract class WatchpointRequestImpl extends ClassVisibleEventRequestImpl
implements WatchpointRequest {
final Field field;
WatchpointRequestImpl(Field field) {
this.field = field;
filters.add(0,
JDWP.EventRequest.Set.Modifier.FieldOnly.create(
(ReferenceTypeImpl)field.declaringType(),
((FieldImpl)field).ref()));
}
public Field field() {
return field;
}
}
class AccessWatchpointRequestImpl extends WatchpointRequestImpl
implements AccessWatchpointRequest {
AccessWatchpointRequestImpl(Field field) {
super(field);
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.FIELD_ACCESS;
}
public String toString() {
return "access watchpoint request " + field + state();
}
}
class ModificationWatchpointRequestImpl extends WatchpointRequestImpl
implements ModificationWatchpointRequest {
ModificationWatchpointRequestImpl(Field field) {
super(field);
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.FIELD_MODIFICATION;
}
public String toString() {
return "modification watchpoint request " + field + state();
}
}
class VMDeathRequestImpl extends EventRequestImpl
implements VMDeathRequest {
VMDeathRequestImpl() {
requestList().add(this);
}
int eventCmd() {
return JDWP.EventKind.VM_DEATH;
}
public String toString() {
return "VM death request " + state();
}
}
/**
* Constructor.
*/
EventRequestManagerImpl(VirtualMachine vm) {
super(vm);
java.lang.reflect.Field[] ekinds =
JDWP.EventKind.class.getDeclaredFields();
int highest = 0;
for (int i = 0; i < ekinds.length; ++i) {
int val;
try {
val = ekinds[i].getInt(null);
} catch (IllegalAccessException exc) {
throw new RuntimeException("Got: " + exc);
}
if (val > highest) {
highest = val;
}
}
requestLists = new List[highest+1];
for (int i=0; i <= highest; i++) {
requestLists[i] = new ArrayList();
}
}
public ClassPrepareRequest createClassPrepareRequest() {
return new ClassPrepareRequestImpl();
}
public ClassUnloadRequest createClassUnloadRequest() {
return new ClassUnloadRequestImpl();
}
public ExceptionRequest createExceptionRequest(ReferenceType refType,
boolean notifyCaught,
boolean notifyUncaught) {
validateMirrorOrNull(refType);
return new ExceptionRequestImpl(refType, notifyCaught, notifyUncaught);
}
public StepRequest createStepRequest(ThreadReference thread,
int size, int depth) {
validateMirror(thread);
return new StepRequestImpl(thread, size, depth);
}
public ThreadDeathRequest createThreadDeathRequest() {
return new ThreadDeathRequestImpl();
}
public ThreadStartRequest createThreadStartRequest() {
return new ThreadStartRequestImpl();
}
public MethodEntryRequest createMethodEntryRequest() {
return new MethodEntryRequestImpl();
}
public MethodExitRequest createMethodExitRequest() {
return new MethodExitRequestImpl();
}
public MonitorContendedEnterRequest createMonitorContendedEnterRequest() {
if (!vm.canRequestMonitorEvents()) {
throw new UnsupportedOperationException(
"target VM does not support requesting Monitor events");
}
return new MonitorContendedEnterRequestImpl();
}
public MonitorContendedEnteredRequest createMonitorContendedEnteredRequest() {
if (!vm.canRequestMonitorEvents()) {
throw new UnsupportedOperationException(
"target VM does not support requesting Monitor events");
}
return new MonitorContendedEnteredRequestImpl();
}
public MonitorWaitRequest createMonitorWaitRequest() {
if (!vm.canRequestMonitorEvents()) {
throw new UnsupportedOperationException(
"target VM does not support requesting Monitor events");
}
return new MonitorWaitRequestImpl();
}
public MonitorWaitedRequest createMonitorWaitedRequest() {
if (!vm.canRequestMonitorEvents()) {
throw new UnsupportedOperationException(
"target VM does not support requesting Monitor events");
}
return new MonitorWaitedRequestImpl();
}
public BreakpointRequest createBreakpointRequest(Location location) {
validateMirror(location);
if (location.codeIndex() == -1) {
throw new NativeMethodException("Cannot set breakpoints on native methods");
}
return new BreakpointRequestImpl(location);
}
public AccessWatchpointRequest
createAccessWatchpointRequest(Field field) {
validateMirror(field);
if (!vm.canWatchFieldAccess()) {
throw new UnsupportedOperationException(
"target VM does not support access watchpoints");
}
return new AccessWatchpointRequestImpl(field);
}
public ModificationWatchpointRequest
createModificationWatchpointRequest(Field field) {
validateMirror(field);
if (!vm.canWatchFieldModification()) {
throw new UnsupportedOperationException(
"target VM does not support modification watchpoints");
}
return new ModificationWatchpointRequestImpl(field);
}
public VMDeathRequest createVMDeathRequest() {
if (!vm.canRequestVMDeathEvent()) {
throw new UnsupportedOperationException(
"target VM does not support requesting VM death events");
}
return new VMDeathRequestImpl();
}
public void deleteEventRequest(EventRequest eventRequest) {
validateMirror(eventRequest);
((EventRequestImpl)eventRequest).delete();
}
public void deleteEventRequests(List<? extends EventRequest> eventRequests) {
validateMirrors(eventRequests);
// copy the eventRequests to avoid ConcurrentModificationException
Iterator iter = (new ArrayList(eventRequests)).iterator();
while (iter.hasNext()) {
((EventRequestImpl)iter.next()).delete();
}
}
public void deleteAllBreakpoints() {
requestList(JDWP.EventKind.BREAKPOINT).clear();
try {
JDWP.EventRequest.ClearAllBreakpoints.process(vm);
} catch (JDWPException exc) {
throw exc.toJDIException();
}
}
public List<StepRequest> stepRequests() {
return unmodifiableRequestList(JDWP.EventKind.SINGLE_STEP);
}
public List<ClassPrepareRequest> classPrepareRequests() {
return unmodifiableRequestList(JDWP.EventKind.CLASS_PREPARE);
}
public List<ClassUnloadRequest> classUnloadRequests() {
return unmodifiableRequestList(JDWP.EventKind.CLASS_UNLOAD);
}
public List<ThreadStartRequest> threadStartRequests() {
return unmodifiableRequestList(JDWP.EventKind.THREAD_START);
}
public List<ThreadDeathRequest> threadDeathRequests() {
return unmodifiableRequestList(JDWP.EventKind.THREAD_DEATH);
}
public List<ExceptionRequest> exceptionRequests() {
return unmodifiableRequestList(JDWP.EventKind.EXCEPTION);
}
public List<BreakpointRequest> breakpointRequests() {
return unmodifiableRequestList(JDWP.EventKind.BREAKPOINT);
}
public List<AccessWatchpointRequest> accessWatchpointRequests() {
return unmodifiableRequestList(JDWP.EventKind.FIELD_ACCESS);
}
public List<ModificationWatchpointRequest> modificationWatchpointRequests() {
return unmodifiableRequestList(JDWP.EventKind.FIELD_MODIFICATION);
}
public List<MethodEntryRequest> methodEntryRequests() {
return unmodifiableRequestList(JDWP.EventKind.METHOD_ENTRY);
}
public List<MethodExitRequest> methodExitRequests() {
return unmodifiableRequestList(
EventRequestManagerImpl.methodExitEventCmd);
}
public List<MonitorContendedEnterRequest> monitorContendedEnterRequests() {
return unmodifiableRequestList(JDWP.EventKind.MONITOR_CONTENDED_ENTER);
}
public List<MonitorContendedEnteredRequest> monitorContendedEnteredRequests() {
return unmodifiableRequestList(JDWP.EventKind.MONITOR_CONTENDED_ENTERED);
}
public List<MonitorWaitRequest> monitorWaitRequests() {
return unmodifiableRequestList(JDWP.EventKind.MONITOR_WAIT);
}
public List<MonitorWaitedRequest> monitorWaitedRequests() {
return unmodifiableRequestList(JDWP.EventKind.MONITOR_WAITED);
}
public List<VMDeathRequest> vmDeathRequests() {
return unmodifiableRequestList(JDWP.EventKind.VM_DEATH);
}
List unmodifiableRequestList(int eventCmd) {
return Collections.unmodifiableList(requestList(eventCmd));
}
EventRequest request(int eventCmd, int requestId) {
List rl = requestList(eventCmd);
for (int i = rl.size() - 1; i >= 0; i--) {
EventRequestImpl er = (EventRequestImpl)rl.get(i);
if (er.id == requestId) {
return er;
}
}
return null;
}
List<? extends EventRequest> requestList(int eventCmd) {
return requestLists[eventCmd];
}
}