|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.tools.jdi; |
|
|
|
import com.sun.jdi.*; |
|
|
|
import java.util.*; |
|
import java.util.ArrayList; |
|
|
|
public class ObjectReferenceImpl extends ValueImpl |
|
implements ObjectReference, VMListener { |
|
|
|
protected long ref; |
|
private ReferenceType type = null; |
|
private int gcDisableCount = 0; |
|
boolean addedListener = false; |
|
|
|
|
|
protected static class Cache { |
|
JDWP.ObjectReference.MonitorInfo monitorInfo = null; |
|
} |
|
|
|
private static final Cache noInitCache = new Cache(); |
|
private static final Cache markerCache = new Cache(); |
|
private Cache cache = noInitCache; |
|
|
|
private void disableCache() { |
|
synchronized (vm.state()) { |
|
cache = null; |
|
} |
|
} |
|
|
|
private void enableCache() { |
|
synchronized (vm.state()) { |
|
cache = markerCache; |
|
} |
|
} |
|
|
|
|
|
protected Cache newCache() { |
|
return new Cache(); |
|
} |
|
|
|
protected Cache getCache() { |
|
synchronized (vm.state()) { |
|
if (cache == noInitCache) { |
|
if (vm.state().isSuspended()) { |
|
// Set cache now, otherwise newly created objects are |
|
|
|
enableCache(); |
|
} else { |
|
disableCache(); |
|
} |
|
} |
|
if (cache == markerCache) { |
|
cache = newCache(); |
|
} |
|
return cache; |
|
} |
|
} |
|
|
|
// Return the ClassTypeImpl upon which to invoke a method. |
|
// By default it is our very own referenceType() but subclasses |
|
|
|
protected ClassTypeImpl invokableReferenceType(Method method) { |
|
return (ClassTypeImpl)referenceType(); |
|
} |
|
|
|
ObjectReferenceImpl(VirtualMachine aVm,long aRef) { |
|
super(aVm); |
|
|
|
ref = aRef; |
|
} |
|
|
|
protected String description() { |
|
return "ObjectReference " + uniqueID(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean vmSuspended(VMAction action) { |
|
enableCache(); |
|
return true; |
|
} |
|
|
|
public boolean vmNotSuspended(VMAction action) { |
|
|
|
synchronized (vm.state()) { |
|
if (cache != null && (vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) { |
|
vm.printTrace("Clearing temporary cache for " + description()); |
|
} |
|
disableCache(); |
|
if (addedListener) { |
|
|
|
|
|
|
|
|
|
*/ |
|
addedListener = false; |
|
return false; |
|
} else { |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
public boolean equals(Object obj) { |
|
if ((obj != null) && (obj instanceof ObjectReferenceImpl)) { |
|
ObjectReferenceImpl other = (ObjectReferenceImpl)obj; |
|
return (ref() == other.ref()) && |
|
super.equals(obj); |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
public int hashCode() { |
|
return(int)ref(); |
|
} |
|
|
|
public Type type() { |
|
return referenceType(); |
|
} |
|
|
|
public ReferenceType referenceType() { |
|
if (type == null) { |
|
try { |
|
JDWP.ObjectReference.ReferenceType rtinfo = |
|
JDWP.ObjectReference.ReferenceType.process(vm, this); |
|
type = vm.referenceType(rtinfo.typeID, |
|
rtinfo.refTypeTag); |
|
} catch (JDWPException exc) { |
|
throw exc.toJDIException(); |
|
} |
|
} |
|
return type; |
|
} |
|
|
|
public Value getValue(Field sig) { |
|
List<Field> list = new ArrayList<Field>(1); |
|
list.add(sig); |
|
Map<Field, Value> map = getValues(list); |
|
return map.get(sig); |
|
} |
|
|
|
public Map<Field,Value> getValues(List<? extends Field> theFields) { |
|
validateMirrors(theFields); |
|
|
|
List<Field> staticFields = new ArrayList<Field>(0); |
|
int size = theFields.size(); |
|
List<Field> instanceFields = new ArrayList<Field>(size); |
|
|
|
for (int i=0; i<size; i++) { |
|
Field field = (Field)theFields.get(i); |
|
|
|
|
|
((ReferenceTypeImpl)referenceType()).validateFieldAccess(field); |
|
|
|
// FIX ME! We need to do some sanity checking |
|
// here; make sure the field belongs to this |
|
|
|
if (field.isStatic()) |
|
staticFields.add(field); |
|
else { |
|
instanceFields.add(field); |
|
} |
|
} |
|
|
|
Map<Field, Value> map; |
|
if (staticFields.size() > 0) { |
|
map = referenceType().getValues(staticFields); |
|
} else { |
|
map = new HashMap<Field, Value>(size); |
|
} |
|
|
|
size = instanceFields.size(); |
|
|
|
JDWP.ObjectReference.GetValues.Field[] queryFields = |
|
new JDWP.ObjectReference.GetValues.Field[size]; |
|
for (int i=0; i<size; i++) { |
|
FieldImpl field = (FieldImpl)instanceFields.get(i); |
|
queryFields[i] = new JDWP.ObjectReference.GetValues.Field( |
|
field.ref()); |
|
} |
|
ValueImpl[] values; |
|
try { |
|
values = JDWP.ObjectReference.GetValues. |
|
process(vm, this, queryFields).values; |
|
} catch (JDWPException exc) { |
|
throw exc.toJDIException(); |
|
} |
|
|
|
if (size != values.length) { |
|
throw new InternalException( |
|
"Wrong number of values returned from target VM"); |
|
} |
|
for (int i=0; i<size; i++) { |
|
FieldImpl field = (FieldImpl)instanceFields.get(i); |
|
map.put(field, values[i]); |
|
} |
|
|
|
return map; |
|
} |
|
|
|
public void setValue(Field field, Value value) |
|
throws InvalidTypeException, ClassNotLoadedException { |
|
|
|
validateMirror(field); |
|
validateMirrorOrNull(value); |
|
|
|
|
|
((ReferenceTypeImpl)referenceType()).validateFieldSet(field); |
|
|
|
if (field.isStatic()) { |
|
ReferenceType type = referenceType(); |
|
if (type instanceof ClassType) { |
|
((ClassType)type).setValue(field, value); |
|
return; |
|
} else { |
|
throw new IllegalArgumentException( |
|
"Invalid type for static field set"); |
|
} |
|
} |
|
|
|
try { |
|
JDWP.ObjectReference.SetValues.FieldValue[] fvals = |
|
new JDWP.ObjectReference.SetValues.FieldValue[1]; |
|
fvals[0] = new JDWP.ObjectReference.SetValues.FieldValue( |
|
((FieldImpl)field).ref(), |
|
|
|
ValueImpl.prepareForAssignment(value, |
|
(FieldImpl)field)); |
|
try { |
|
JDWP.ObjectReference.SetValues.process(vm, this, fvals); |
|
} catch (JDWPException exc) { |
|
throw exc.toJDIException(); |
|
} |
|
} catch (ClassNotLoadedException e) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (value != null) { |
|
throw e; |
|
} |
|
} |
|
} |
|
|
|
void validateMethodInvocation(Method method, int options) |
|
throws InvalidTypeException, |
|
InvocationException { |
|
|
|
|
|
|
|
*/ |
|
ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType(); |
|
if (!declType.isAssignableFrom(this)) { |
|
throw new IllegalArgumentException("Invalid method"); |
|
} |
|
|
|
if (declType instanceof ClassTypeImpl) { |
|
validateClassMethodInvocation(method, options); |
|
} else if (declType instanceof InterfaceTypeImpl) { |
|
validateIfaceMethodInvocation(method, options); |
|
} else { |
|
throw new InvalidTypeException(); |
|
} |
|
} |
|
|
|
void validateClassMethodInvocation(Method method, int options) |
|
throws InvalidTypeException, |
|
InvocationException { |
|
|
|
ClassTypeImpl clazz = invokableReferenceType(method); |
|
|
|
|
|
|
|
*/ |
|
if (method.isConstructor()) { |
|
throw new IllegalArgumentException("Cannot invoke constructor"); |
|
} |
|
|
|
|
|
|
|
*/ |
|
if (isNonVirtual(options)) { |
|
if (method.isAbstract()) { |
|
throw new IllegalArgumentException("Abstract method"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
ClassTypeImpl invokedClass; |
|
if (isNonVirtual(options)) { |
|
|
|
invokedClass = clazz; |
|
} else { |
|
|
|
|
|
|
|
|
|
*/ |
|
Method invoker = clazz.concreteMethodByName(method.name(), |
|
method.signature()); |
|
|
|
invokedClass = (ClassTypeImpl)invoker.declaringType(); |
|
} |
|
/* The above code is left over from previous versions. |
|
* We haven't had time to divine the intent. jjh, 7/31/2003 |
|
*/ |
|
} |
|
|
|
void validateIfaceMethodInvocation(Method method, int options) |
|
throws InvalidTypeException, |
|
InvocationException { |
|
|
|
|
|
*/ |
|
if (isNonVirtual(options) && !method.isDefault()) { |
|
throw new IllegalArgumentException("Not a default method"); |
|
} |
|
} |
|
|
|
PacketStream sendInvokeCommand(final ThreadReferenceImpl thread, |
|
final ClassTypeImpl refType, |
|
final MethodImpl method, |
|
final ValueImpl[] args, |
|
final int options) { |
|
CommandSender sender = |
|
new CommandSender() { |
|
public PacketStream send() { |
|
return JDWP.ObjectReference.InvokeMethod.enqueueCommand( |
|
vm, ObjectReferenceImpl.this, |
|
thread, refType, |
|
method.ref(), args, options); |
|
} |
|
}; |
|
|
|
PacketStream stream; |
|
if ((options & INVOKE_SINGLE_THREADED) != 0) { |
|
stream = thread.sendResumingCommand(sender); |
|
} else { |
|
stream = vm.sendResumingCommand(sender); |
|
} |
|
return stream; |
|
} |
|
|
|
public Value invokeMethod(ThreadReference threadIntf, Method methodIntf, |
|
List<? extends Value> origArguments, int options) |
|
throws InvalidTypeException, |
|
IncompatibleThreadStateException, |
|
InvocationException, |
|
ClassNotLoadedException { |
|
validateMirror(threadIntf); |
|
validateMirror(methodIntf); |
|
validateMirrorsOrNulls(origArguments); |
|
|
|
MethodImpl method = (MethodImpl)methodIntf; |
|
ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf; |
|
|
|
if (method.isStatic()) { |
|
if (referenceType() instanceof InterfaceType) { |
|
InterfaceType type = (InterfaceType)referenceType(); |
|
return type.invokeMethod(thread, method, origArguments, options); |
|
} else if (referenceType() instanceof ClassType) { |
|
ClassType type = (ClassType)referenceType(); |
|
return type.invokeMethod(thread, method, origArguments, options); |
|
} else { |
|
throw new IllegalArgumentException("Invalid type for static method invocation"); |
|
} |
|
} |
|
|
|
validateMethodInvocation(method, options); |
|
|
|
List<Value> arguments = method.validateAndPrepareArgumentsForInvoke( |
|
origArguments); |
|
|
|
ValueImpl[] args = arguments.toArray(new ValueImpl[0]); |
|
JDWP.ObjectReference.InvokeMethod ret; |
|
try { |
|
PacketStream stream = |
|
sendInvokeCommand(thread, invokableReferenceType(method), |
|
method, args, options); |
|
ret = JDWP.ObjectReference.InvokeMethod.waitForReply(vm, stream); |
|
} catch (JDWPException exc) { |
|
if (exc.errorCode() == JDWP.Error.INVALID_THREAD) { |
|
throw new IncompatibleThreadStateException(); |
|
} else { |
|
throw exc.toJDIException(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
if ((options & INVOKE_SINGLE_THREADED) == 0) { |
|
vm.notifySuspend(); |
|
} |
|
|
|
if (ret.exception != null) { |
|
throw new InvocationException(ret.exception); |
|
} else { |
|
return ret.returnValue; |
|
} |
|
} |
|
|
|
|
|
public synchronized void disableCollection() { |
|
if (gcDisableCount == 0) { |
|
try { |
|
JDWP.ObjectReference.DisableCollection.process(vm, this); |
|
} catch (JDWPException exc) { |
|
throw exc.toJDIException(); |
|
} |
|
} |
|
gcDisableCount++; |
|
} |
|
|
|
|
|
public synchronized void enableCollection() { |
|
gcDisableCount--; |
|
|
|
if (gcDisableCount == 0) { |
|
try { |
|
JDWP.ObjectReference.EnableCollection.process(vm, this); |
|
} catch (JDWPException exc) { |
|
|
|
if (exc.errorCode() != JDWP.Error.INVALID_OBJECT) { |
|
throw exc.toJDIException(); |
|
} |
|
return; |
|
} |
|
} |
|
} |
|
|
|
public boolean isCollected() { |
|
try { |
|
return JDWP.ObjectReference.IsCollected.process(vm, this). |
|
isCollected; |
|
} catch (JDWPException exc) { |
|
throw exc.toJDIException(); |
|
} |
|
} |
|
|
|
public long uniqueID() { |
|
return ref(); |
|
} |
|
|
|
JDWP.ObjectReference.MonitorInfo jdwpMonitorInfo() |
|
throws IncompatibleThreadStateException { |
|
JDWP.ObjectReference.MonitorInfo info = null; |
|
try { |
|
Cache local; |
|
|
|
// getCache() and addlistener() must be synchronized |
|
|
|
synchronized (vm.state()) { |
|
local = getCache(); |
|
|
|
if (local != null) { |
|
info = local.monitorInfo; |
|
|
|
// Check if there will be something to cache |
|
|
|
if (info == null && !vm.state().hasListener(this)) { |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
vm.state().addListener(this); |
|
addedListener = true; |
|
} |
|
} |
|
} |
|
if (info == null) { |
|
info = JDWP.ObjectReference.MonitorInfo.process(vm, this); |
|
if (local != null) { |
|
local.monitorInfo = info; |
|
if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) { |
|
vm.printTrace("ObjectReference " + uniqueID() + |
|
" temporarily caching monitor info"); |
|
} |
|
} |
|
} |
|
} catch (JDWPException exc) { |
|
if (exc.errorCode() == JDWP.Error.THREAD_NOT_SUSPENDED) { |
|
throw new IncompatibleThreadStateException(); |
|
} else { |
|
throw exc.toJDIException(); |
|
} |
|
} |
|
return info; |
|
} |
|
|
|
public List<ThreadReference> waitingThreads() throws IncompatibleThreadStateException { |
|
return Arrays.asList((ThreadReference[])jdwpMonitorInfo().waiters); |
|
} |
|
|
|
public ThreadReference owningThread() throws IncompatibleThreadStateException { |
|
return jdwpMonitorInfo().owner; |
|
} |
|
|
|
public int entryCount() throws IncompatibleThreadStateException { |
|
return jdwpMonitorInfo().entryCount; |
|
} |
|
|
|
|
|
public List<ObjectReference> referringObjects(long maxReferrers) { |
|
if (!vm.canGetInstanceInfo()) { |
|
throw new UnsupportedOperationException( |
|
"target does not support getting referring objects"); |
|
} |
|
|
|
if (maxReferrers < 0) { |
|
throw new IllegalArgumentException("maxReferrers is less than zero: " |
|
+ maxReferrers); |
|
} |
|
|
|
int intMax = (maxReferrers > Integer.MAX_VALUE)? |
|
Integer.MAX_VALUE: (int)maxReferrers; |
|
// JDWP can't currently handle more than this (in mustang) |
|
|
|
try { |
|
return Arrays.asList((ObjectReference[])JDWP.ObjectReference.ReferringObjects. |
|
process(vm, this, intMax).referringObjects); |
|
} catch (JDWPException exc) { |
|
throw exc.toJDIException(); |
|
} |
|
} |
|
|
|
long ref() { |
|
return ref; |
|
} |
|
|
|
boolean isClassObject() { |
|
|
|
|
|
*/ |
|
return referenceType().name().equals("java.lang.Class"); |
|
} |
|
|
|
ValueImpl prepareForAssignmentTo(ValueContainer destination) |
|
throws InvalidTypeException, |
|
ClassNotLoadedException { |
|
|
|
validateAssignment(destination); |
|
return this; |
|
} |
|
|
|
void validateAssignment(ValueContainer destination) |
|
throws InvalidTypeException, ClassNotLoadedException { |
|
|
|
/* |
|
* Do these simpler checks before attempting a query of the destination's |
|
* type which might cause a confusing ClassNotLoadedException if |
|
* the destination is primitive or an array. |
|
*/ |
|
|
|
|
|
*/ |
|
if (destination.signature().length() == 1) { |
|
throw new InvalidTypeException("Can't assign object value to primitive"); |
|
} |
|
if ((destination.signature().charAt(0) == '[') && |
|
(type().signature().charAt(0) != '[')) { |
|
throw new InvalidTypeException("Can't assign non-array value to an array"); |
|
} |
|
if ("void".equals(destination.typeName())) { |
|
throw new InvalidTypeException("Can't assign object value to a void"); |
|
} |
|
|
|
|
|
ReferenceType destType = (ReferenceTypeImpl)destination.type(); |
|
ReferenceTypeImpl myType = (ReferenceTypeImpl)referenceType(); |
|
if (!myType.isAssignableTo(destType)) { |
|
JNITypeParser parser = new JNITypeParser(destType.signature()); |
|
String destTypeName = parser.typeName(); |
|
throw new InvalidTypeException("Can't assign " + |
|
type().name() + |
|
" to " + destTypeName); |
|
} |
|
} |
|
|
|
|
|
public String toString() { |
|
return "instance of " + referenceType().name() + "(id=" + uniqueID() + ")"; |
|
} |
|
|
|
byte typeValueKey() { |
|
return JDWP.Tag.OBJECT; |
|
} |
|
|
|
private static boolean isNonVirtual(int options) { |
|
return (options & INVOKE_NONVIRTUAL) != 0; |
|
} |
|
} |