blob: 2ebe80a84d2ccec294bb72b6e2a5cc82ea11603c [file] [log] [blame]
package org.eclipse.jdi.internal;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
import org.eclipse.jdi.internal.connect.*;
import org.eclipse.jdi.internal.request.*;
import org.eclipse.jdi.internal.event.*;
import org.eclipse.jdi.internal.jdwp.*;
import org.eclipse.jdi.internal.spy.*;
import java.util.*;
import java.io.*;
/**
* this class implements the corresponding interfaces
* declared by the JDI specification. See the com.sun.jdi package
* for more information.
*
*/
public class ObjectReferenceImpl extends ValueImpl implements ObjectReference {
/** JDWP Tag. */
public static final byte tag = JdwpID.OBJECT_TAG;
/** ObjectID of object that corresponds to this reference. */
private JdwpObjectID fObjectID;
/**
* Creates new ObjectReferenceImpl.
*/
public ObjectReferenceImpl(VirtualMachineImpl vmImpl, JdwpObjectID objectID) {
this("ObjectReference", vmImpl, objectID);
}
/**
* Creates new ObjectReferenceImpl.
*/
public ObjectReferenceImpl(String description, VirtualMachineImpl vmImpl, JdwpObjectID objectID) {
super(description, vmImpl);
fObjectID = objectID;
}
/**
* @returns tag.
*/
public byte getTag() {
return tag;
}
/**
* @return Returns Jdwp Object ID.
*/
public JdwpObjectID getObjectID() {
return fObjectID;
}
/**
* Prevents garbage collection for this object.
*/
public void disableCollection() {
initJdwpRequest();
try {
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_DISABLE_COLLECTION, this);
defaultReplyErrorHandler(replyPacket.errorCode());
} finally {
handledJdwpRequest();
}
}
/**
* Permits garbage collection for this object.
*/
public void enableCollection() {
initJdwpRequest();
try {
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_ENABLE_COLLECTION, this);
defaultReplyErrorHandler(replyPacket.errorCode());
} finally {
handledJdwpRequest();
}
}
/**
* Inner class used to return monitor info.
*/
private class MonitorInfo {
ThreadReferenceImpl owner;
int entryCount;
ArrayList waiters;
}
/**
* @return Returns monitor info.
*/
private MonitorInfo monitorInfo() throws IncompatibleThreadStateException {
// Note that this information should not be cached.
initJdwpRequest();
try {
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_MONITOR_INFO, this);
switch (replyPacket.errorCode()) {
case JdwpReplyPacket.INVALID_THREAD:
throw new IncompatibleThreadStateException();
case JdwpReplyPacket.THREAD_NOT_SUSPENDED:
throw new IncompatibleThreadStateException();
}
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
MonitorInfo result = new MonitorInfo();
result.owner = ThreadReferenceImpl.read(this, replyData);
result.entryCount = readInt("entry count", replyData);
int nrOfWaiters = readInt("nr of waiters", replyData);
result.waiters = new ArrayList(nrOfWaiters);
for (int i = 0; i < nrOfWaiters; i++)
result.waiters.add(ThreadReferenceImpl.read(this, replyData));
return result;
} catch (IOException e) {
defaultIOExceptionHandler(e);
return null;
} finally {
handledJdwpRequest();
}
}
/**
* @return Returns an ThreadReference for the thread, if any, which currently owns this object's monitor.
*/
public ThreadReference owningThread() throws IncompatibleThreadStateException {
return monitorInfo().owner;
}
/**
* @return Returns the number times this object's monitor has been entered by the current owning thread.
*/
public int entryCount() throws IncompatibleThreadStateException {
return monitorInfo().entryCount;
}
/**
* @return Returns a List containing a ThreadReference for each thread currently waiting for this object's monitor.
*/
public List waitingThreads() throws IncompatibleThreadStateException {
return monitorInfo().waiters;
}
/**
* @return Returns the value of a given instance or static field in this object.
*/
public Value getValue(Field field) {
ArrayList list = new ArrayList(1);
list.add(field);
return (ValueImpl)getValues(list).get(field);
}
/**
* @return Returns the value of multiple instance and/or static fields in this object.
*/
public Map getValues(List allFields) {
// Note that this information should not be cached.
initJdwpRequest();
try {
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
DataOutputStream outData = new DataOutputStream(outBytes);
/*
* Distinguish static fields from non-static fields:
* For static fields ReferencTypeImpl.getValues() must be used.
*/
Vector staticFields = new Vector();
Vector nonStaticFields = new Vector();
// Separate static and non-static fields.
int allFieldsSize = allFields.size();
for (int i = 0; i < allFieldsSize; i++) {
FieldImpl field = (FieldImpl)allFields.get(i);
checkVM(field);
if (field.isStatic())
staticFields.add(field);
else
nonStaticFields.add(field);
}
// First get values for the static fields.
Map resultMap = referenceType().getValues(staticFields);
// Then get the values for the non-static fields.
int nonStaticFieldsSize = nonStaticFields.size();
write(this, outData);
writeInt(nonStaticFieldsSize, "size", outData);
for (int i = 0; i < nonStaticFieldsSize; i++) {
FieldImpl field = (FieldImpl)nonStaticFields.get(i);
field.write(this, outData);
}
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_GET_VALUES, outBytes);
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
int nrOfElements = readInt("elements", replyData);
if (nrOfElements != nonStaticFieldsSize)
throw new InternalError("Retrieved a different number of values from the VM than requested.");
for (int i = 0; i < nrOfElements; i++) {
resultMap.put(nonStaticFields.get(i), ValueImpl.readWithTag(this, replyData));
}
return resultMap;
} catch (IOException e) {
defaultIOExceptionHandler(e);
return null;
} finally {
handledJdwpRequest();
}
}
/**
* @return Returns the hash code value.
*/
public int hashCode() {
return fObjectID.hashCode();
}
/**
* @return Returns true if two mirrors refer to the same entity in the target VM.
* @see java.lang.Object#equals(Object)
*/
public boolean equals(Object object) {
return object != null
&& object.getClass().equals(this.getClass())
&& fObjectID.equals(((ObjectReferenceImpl)object).fObjectID)
&& virtualMachine().equals(((MirrorImpl)object).virtualMachine());
}
/**
* @return Returns Jdwp version of given options.
*/
private int optionsToJdwpOptions(int options) {
int jdwpOptions = 0;
if ((options & INVOKE_SINGLE_THREADED) != 0)
jdwpOptions |= MethodImpl.INVOKE_SINGLE_THREADED_JDWP;
return jdwpOptions;
}
/**
* Invokes the specified static Method in the target VM.
* @return Returns a Value mirror of the invoked method's return value.
*/
public Value invokeMethod(ThreadReference thread, Method method, List arguments, int options) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException {
checkVM(thread);
checkVM(method);
ThreadReferenceImpl threadImpl = (ThreadReferenceImpl)thread;
MethodImpl methodImpl = (MethodImpl)method;
// Perform some checks for IllegalArgumentException.
if (!referenceType().visibleMethods().contains(method))
throw new IllegalArgumentException("Class does not contain given method.");
if (method.argumentTypeNames().size() != arguments.size())
throw new IllegalArgumentException("Number of arguments doesn't match.");
if (method.isConstructor() || method.isStaticInitializer())
throw new IllegalArgumentException("Method is constructor or intitializer.");
if ((options & INVOKE_NONVIRTUAL) != 0 && method.isAbstract())
throw new IllegalArgumentException("Method is abstract and can therefore not be invoked nonvirtual.");
initJdwpRequest();
try {
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
DataOutputStream outData = new DataOutputStream(outBytes);
write(this, outData);
threadImpl.write(this, outData);
((ReferenceTypeImpl)referenceType()).write(this, outData);
methodImpl.write(this, outData);
writeInt(arguments.size(), "size", outData);
Iterator iter = arguments.iterator();
while(iter.hasNext()) {
ValueImpl elt = (ValueImpl)iter.next();
if (elt != null) {
checkVM(elt);
elt.writeWithTag(this, outData);
} else {
ValueImpl.writeNullWithTag(this, outData);
}
}
writeInt(optionsToJdwpOptions(options),"options", MethodImpl.invokeOptionsVector(), outData);
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_INVOKE_METHOD, outBytes);
switch (replyPacket.errorCode()) {
case JdwpReplyPacket.TYPE_MISMATCH:
throw new InvalidTypeException();
case JdwpReplyPacket.INVALID_CLASS:
throw new ClassNotLoadedException("One of the arguments of ObjectReference.invokeMethod()");
case JdwpReplyPacket.INVALID_THREAD:
throw new IncompatibleThreadStateException();
case JdwpReplyPacket.THREAD_NOT_SUSPENDED:
throw new IncompatibleThreadStateException();
}
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
ValueImpl value = ValueImpl.readWithTag(this, replyData);
ObjectReferenceImpl exception = ObjectReferenceImpl.readObjectRefWithTag(this, replyData);
if (exception != null)
throw new InvocationException(exception);
return value;
} catch (IOException e) {
defaultIOExceptionHandler(e);
return null;
} finally {
handledJdwpRequest();
}
}
/**
* @return Returns if this object has been garbage collected in the target VM.
*/
public boolean isCollected() {
// Note that this information should not be cached.
initJdwpRequest();
try {
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_IS_COLLECTED, this);
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
boolean result = readBoolean("is collected", replyData);
return result;
} catch (IOException e) {
defaultIOExceptionHandler(e);
return false;
} finally {
handledJdwpRequest();
}
}
/**
* @return Returns the ReferenceType that mirrors the type of this object.
*/
public ReferenceType referenceType() {
initJdwpRequest();
try {
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_REFERENCE_TYPE, this);
defaultReplyErrorHandler(replyPacket.errorCode());
DataInputStream replyData = replyPacket.dataInStream();
return ReferenceTypeImpl.readWithTypeTag(this, replyData);
} catch (IOException e) {
defaultIOExceptionHandler(e);
return null;
} finally {
handledJdwpRequest();
}
}
/**
* @return Returns the Type that mirrors the type of this object.
*/
public Type type() {
return referenceType();
}
/**
* Sets the value of a given instance or static field in this object.
*/
public void setValue(Field field, Value value) throws InvalidTypeException, ClassNotLoadedException {
// Note that this information should not be cached.
initJdwpRequest();
try {
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
DataOutputStream outData = new DataOutputStream(outBytes);
write(this, outData);
writeInt(1, "size", outData); // We only set one field
checkVM(field);
((FieldImpl)field).write(this, outData);
if (value != null) {
checkVM(value);
((ValueImpl)value).write(this, outData);
} else {
ValueImpl.writeNull(this, outData);
}
JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.OR_SET_VALUES, outBytes);
switch (replyPacket.errorCode()) {
case JdwpReplyPacket.TYPE_MISMATCH:
throw new InvalidTypeException();
case JdwpReplyPacket.INVALID_CLASS:
throw new ClassNotLoadedException(referenceType().name());
}
defaultReplyErrorHandler(replyPacket.errorCode());
} catch (IOException e) {
defaultIOExceptionHandler(e);
} finally {
handledJdwpRequest();
}
}
/**
* @return Returns a unique identifier for this ObjectReference.
*/
public long uniqueID() {
if (isCollected())
throw new ObjectCollectedException();
return fObjectID.value();
}
/**
* @return Returns string with value of ID.
*/
public String idString() {
return "(id=" + fObjectID + ")";
}
/**
* @return Returns description of Mirror object.
*/
public String toString() {
try {
return type().toString() + " " + idString();
} catch (ObjectCollectedException e) {
return "(Garbage Collected) ObjectReference " + idString();
} catch (Exception e) {
return fDescription;
}
}
/**
* @return Reads JDWP representation and returns new instance.
*/
public static ObjectReferenceImpl readObjectRefWithoutTag(MirrorImpl target, DataInputStream in) throws IOException {
VirtualMachineImpl vmImpl = target.virtualMachineImpl();
JdwpObjectID ID = new JdwpObjectID(vmImpl);
ID.read(in);
if (target.fVerboseWriter != null)
target.fVerboseWriter.println("objectReference", ID.value());
if (ID.isNull())
return null;
ObjectReferenceImpl mirror = new ObjectReferenceImpl(vmImpl, ID);
return mirror;
}
/**
* @return Reads JDWP representation and returns new instance.
*/
public static ObjectReferenceImpl readObjectRefWithTag(MirrorImpl target, DataInputStream in) throws IOException {
byte tag = target.readByte("object tag", JdwpID.tagMap(), in);
switch (tag) {
case 0:
return null;
case ObjectReferenceImpl.tag:
return ObjectReferenceImpl.readObjectRefWithoutTag(target, in);
case ArrayReferenceImpl.tag:
return ArrayReferenceImpl.read(target, in);
case ClassLoaderReferenceImpl.tag:
return ClassLoaderReferenceImpl.read(target, in);
case ClassObjectReferenceImpl.tag:
return ClassObjectReferenceImpl.read(target, in);
case StringReferenceImpl.tag:
return StringReferenceImpl.read(target, in);
case ThreadGroupReferenceImpl.tag:
return ThreadGroupReferenceImpl.read(target, in);
case ThreadReferenceImpl.tag:
return ThreadReferenceImpl.read(target, in);
}
throw new InternalException("Invalid ObjectID tag encountered: " + tag);
}
/**
* Writes JDWP representation without tag.
*/
public void write(MirrorImpl target, DataOutputStream out) throws IOException {
fObjectID.write(out);
if (target.fVerboseWriter != null)
target.fVerboseWriter.println("objectReference", fObjectID.value());
}
}