/* * Copyright (c) 2000, 2001, 2002 Paul Kinnucan * * $Revision: 1.17 $ */ package jde.debugger; import com.sun.jdi.*; import java.util.*; /** * Rep.java *
* Responsible for providing static methods used in spewing out string * representations. *
* Now, the representation that is sent across for the threads (ie to the * jde) depends on the context. When it is sent with reference to thread * commands, eg. get_threads, get_thread, get_object_monitors; it has * a lot of thread specific information, eg. its state and all. *
* When it's sent treating the thread as an object, eg. get_object, it's * represented differently, and a different set of information is sent. *
* Similary, when an array command is used, a different set of information * is sent across, as against when it's treated as an object. *
* Created: Tue Aug 3 16:36:54 1999 * * @author Amit Kumar * @since 0.1 */ public class Rep implements Protocol { /** * Returns a representation of a Location *
* * Syntax: *
* (list "type-name" "sourcefile" lineNumber)
* (list "type-name" nil lineNumber)
*
*
* Comments:
* * * Syntax: *
* (list "name of method" return-type-name
* (list [argument-type-name]*)
* ["final"] ["static"] ["native"] ["constructor"] ["abstract"]
* ["synchronized"] ["static_initializer"])
*
*/
static LispForm getMethodRep(Method m) {
List l = m.argumentTypeNames();
String argList = "(list";
Iterator it = l.iterator();
while (it.hasNext()) {
argList += " \""+it.next().toString()+"\"";
}
argList += ")";
return new LispForm("(list \""+m.declaringType().name()+"\""
+" \""+m.name()+"\""
+" \""+m.returnTypeName()+"\""
+ BR +argList
+(m.isFinal()?" \"final\"":"")
+(m.isStatic()?" \"static\"":"")
+(m.isNative()?" \"native\"":"")
+(m.isConstructor()?" \"constructor\"":"")
+(m.isAbstract()?" \"abstract\"":"")
+(m.isSynchronized()?" \"synchronized\"":"")
+(m.isStaticInitializer()
?" \"static_initializer\"":"")
+")");
}
/**
* Returns a representation of a local variable on a stack frame
* * Syntax: *
* (list "name of variable" "type of variable")
*
*/
static public LispForm getLocalVariableRep(LocalVariable lv) {
return new LispForm("(list"
+ " \""+lv.name()+"\""
+ " \""+lv.typeName()+"\")");
}
/**
* Returns a representation of a (local variable, value) pair.
* * Syntax: *
* ({@link #getLocalVariableRep local-variable} . {@link #getValueRep value})
*
*/
static public LispForm getLocalVariableValueRep(LocalVariable lv, Value v,
ObjectStore s) {
return new LispForm("(cons "+getLocalVariableRep(lv)
+" "+getValueRep(v, s)+")");
}
/**
* Returns a list of (local variable, value) pairs.
* * Syntax: *
* (list [{@link #getLocalVariableValueRep (local variable, value) pair}]*)
*
*/
static public LispForm getLocalVariableValueMapRep(Map map, ObjectStore s) {
String localVariablesValuesString = "(list ";
Set keys = map.keySet();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
LocalVariable localVariable = (LocalVariable)iter.next();
Value val = (Value)map.get(localVariable);
localVariablesValuesString +=
BR +getLocalVariableValueRep(localVariable, val, s);
}
localVariablesValuesString += ")";
return new LispForm(localVariablesValuesString);
}
/**
* Returns a representation of a field.
* * Syntax: *
* (list "name of field" "type of field" ["transient"] ["volatile"]
* ["final"] ["static"])
*
*/
static LispForm getFieldRep(Field f) {
return new LispForm("(list"
+ " \""+f.name()+"\""
+ " \""+f.typeName()+"\""
+ (f.isTransient() ? " \"transient\"" : "")
+ (f.isVolatile() ? " \"volatile\"" : "")
+ (f.isFinal() ? " \"final\"" : "")
+ (f.isStatic() ? " \"static\"" : "")
+")");
}
/**
* Returns a representation of a (field, value) pair.
* * Syntax: *
* ({@link #getFieldRep field} . {@link #getValueRep value})
*
*/
static LispForm getFieldValueRep(Field f, Value v, ObjectStore s) {
return new LispForm("(cons "+getFieldRep(f)+" "+getValueRep(v, s)+")");
}
/**
* Returns a list of (field, value) pairs.
* * Syntax: *
* (list [{@link #getFieldValueRep (field, value) pair}]*)
*
*/
static LispForm getFieldValueMapRep(Map map, ObjectStore s) {
String fieldsValuesString = "(list ";
Set keys = map.keySet();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
Field field = (Field)iter.next();
Value val = (Value)map.get(field);
fieldsValuesString += BR +getFieldValueRep(field, val, s);
}
fieldsValuesString += ")";
return new LispForm(fieldsValuesString);
}
private static String filterFPValue(String fpValue) {
if (fpValue.equals("NaN"))
return "\"NaN\"";
else
if (fpValue.equals("-Infinity"))
return "\"-Infinity\"";
else
if (fpValue.equals("Infinity"))
return "\"Infinity\"";
else
return fpValue;
}
/**
* Returns a representation of a 'value', that can be primitive
* or an object reference, or void.
* * Syntax: *
* (list "null")
* (list "void")
*
* {@link #getObjectRep(ObjectReference,ObjectStore) object-rep}
*
* (list "boolean" "true") (list "boolean" "false")
* (list "byte" 'byte-value')
* (list "char" 'char-value')
* (list "double" double-value)
* (list "float" float-value)
* (list "int" int-value)
* (list "long" long-value)
* (list "short" short-value)
*
*/
static public LispForm getValueRep(Value value, ObjectStore store) {
if (value == null) {
return new LispForm("(list \"null\")");
} else if (value instanceof VoidValue) {
return new LispForm("(list \"void\")");
} else if (value instanceof ObjectReference) {
return getObjectRep((ObjectReference)value, store);
} else {
PrimitiveValue v = (PrimitiveValue)value;
if (v instanceof BooleanValue) {
return new LispForm("(list \"boolean\" \""+v.booleanValue()+"\")");
} else if (v instanceof ByteValue) {
return new LispForm("(list \"byte\" \""+v.byteValue()+"\")");
} else if (v instanceof CharValue) {
return new LispForm("(list \"char\" \""+
escapeString(String.valueOf(v.charValue()))+"\")");
} else if (v instanceof DoubleValue) {
String sv = "" + v.doubleValue();
return new LispForm("(list \"double\" "+filterFPValue(sv)+")");
} else if (v instanceof FloatValue) {
String sv = "" + v.floatValue();
return new LispForm("(list \"float\" "+filterFPValue(sv)+")");
} else if (v instanceof IntegerValue) {
return new LispForm("(list \"int\" \""+v.intValue()+"\")");
} else if (v instanceof LongValue) {
return new LispForm("(list \"long\" \""+v.longValue()+"\")");
} else if (v instanceof ShortValue) {
return new LispForm("(list \"short\" "+v.shortValue()+")");
}
}
return null;
}
/**
* Returns information about an array
* * * Syntax: *
* "Error message"
* (list "type name" uniqueID ['t|nil] length [element]*)
*
*
* Comments:
* * * @param index if -1, represents the begin of index from where * elements are to be sent * @param length Number of elements to be sent */ static public LispForm getArrayRep(ArrayReference a, ObjectStore store, int index, int length) { if (a == null) { return new LispForm("\"Error!\""); } else { store.put(a); String elementsString = ""; try { if (index != -1) { List elements = a.getValues(index, length); Iterator it = elements.iterator(); while (it.hasNext()) { elementsString += " "+getValueRep((Value)it.next(), store); } } } catch (IndexOutOfBoundsException ex) { elementsString = "\"Index out of bounds\""; } catch (ObjectCollectedException ex) { elementsString = "\"The object has already been collected\""; } return new LispForm("(list " + "\""+a.referenceType().name()+"\"" + " " +a.uniqueID() + (a.isCollected() ? " 't":" nil") + " " + a.length() + elementsString + ")"); } } /** * Prefix \ escapes to all \ and " characters in a string so that * the string can be read byte the Lisp interpreter. For efficiency, * if no such characters are found, the argument String itself * is returned. * * @param str String to be prefixed. * @return A String. * * @author David Hay * @author Mark Gibson * @author Steve Haflich * @author Charles Hart * @author David Dagon */ public static String escapeString (String str) { if ( str.indexOf('\\') == -1 && str.indexOf('"') == -1 ) { return str; } else { StringBuffer buf = new StringBuffer(str.length() + 16); for ( int i = 0; i < str.length(); i++ ) { char ch = str.charAt( i ); switch ( ch ) { case '"': buf.append("\\\"" ); break; case '\\': buf.append("\\\\" ); break; default: buf.append( ch ); break; } } return buf.toString(); } } /** * Returns the value of a string *
* * Syntax: *
* "Error message"
* (list "java.lang.String" uniqueID ['t|nil] "string-value")
*
* Comments:
* */ static public LispForm getStringRep(StringReference s, ObjectStore store) { if (s == null) { return new LispForm("\"Error!\""); } else { store.put(s); return new LispForm("(list " + "\""+s.referenceType().name()+"\"" + " "+s.uniqueID() + (s.isCollected() ? " 't":" nil") + " \"" + escapeString(s.value()) + "\")"); } } /** * Returns a non-detailed representation of an object. * * @see #getObjectRep(ObjectReference,ObjectStore,boolean) */ static public LispForm getObjectRep(ObjectReference o, ObjectStore store) { return getObjectRep(o, store, false); } /** * Returns a canonical representation of an object. *
* * Syntax: *
* "Error Message"
* (list "null")
* Non-detailed
* (list "type of object" uniqueID ['t|nil])
* Detailed
* (list "type of object" uniqueID ['t|nil] {@link #getFieldValueMapRep fields-values})
*
*
* Comments:
* * * Syntax: *
* (list uniqueID "type of object" ['t|nil] {@link #getThreadRep owning-thread} (list [{@link #getThreadRep waiting-thread}]*))
*
*
* Comments:
* * Syntax: *
* (list "ThreadGroup" uniqueID "name of threadgroup"
* (list [{@link #getThreadRep(ThreadReference, ObjectStore) child thread}]*)
* (list [{@link #getThreadGroupRep child threadgroup}]*))
*
*/
static LispForm getThreadGroupRep(ThreadGroupReference t,
ObjectStore store) {
store.put(t);
String rep = "(list \"ThreadGroup\" "+t.uniqueID()
+" \""+t.name()+"\" ";
List list = t.threads();
Iterator it = list.iterator();
rep += BR+"(list";
while (it.hasNext()) {
rep += BR +getThreadRep((ThreadReference)it.next(), store);
}
rep += ")";
list = t.threadGroups();
it = list.iterator();
rep += BR+"(list";
while (it.hasNext()) {
rep += BR +getThreadGroupRep((ThreadGroupReference)it.next(), store);
}
rep += ")";
rep += ")";
return new LispForm(rep);
}
/**
* Returns a detailed thread representation.
* @see #getThreadRep(ThreadReference, ObjectStore, boolean)
*/
static public LispForm getThreadRep(ThreadReference t, ObjectStore store) {
return getThreadRep(t, store, true);
}
/**
* Returns a canonical representation of a given ThreadReference.
* * * Syntax: *
* Non-detailed
* (list "Thread" uniqueID "name of thread" status currentState)
* Detailed
* (list "Thread" uniqueID "name of thread" status currentState
* (list [{@link #getStackFrameRep stack-frame}]*)
* owned-monitors-string
* current-contended-monitor-string)
*
*
* Comments:
*
* "Error Message"
* (list [{@link #getObjectRep(ObjectReference, ObjectStore) owned monitor}]*)
*
*
* "Error Message"
* nil
* {@link #getObjectRep(ObjectReference, ObjectStore) current contended monitor}
*
*
* (list "Thread" 53 "Thread 1, continuous"
* "suspended by debugger" "waiting on monitor"
* (list
* (list 0 "test.Test" "Test.java" 45))
* (list)
* (list "java.lang.String" 55))
*
* (list "Thread" 54 "Thread 2"
* "suspended by debugger" "waiting on monitor"
* (list
* (list 0 "java.lang.Thread" "Thread.java" -1)
* (list 1 "test.Test" "Test.java" 47))
* (list
* (list "java.lang.String" 55)
* (list "java.lang.Integer" 61))
* (list))
*
* * @param detailed True if a more detailed representation is desired: * includes the stackframe as well as information about the monitors. */ static public LispForm getThreadRep(ThreadReference t, ObjectStore store, boolean detailed) { int status = t.status(); String statusString = "unknown"; switch (status) { case ThreadReference.THREAD_STATUS_MONITOR: { statusString = "waiting on monitor"; break; } case ThreadReference.THREAD_STATUS_NOT_STARTED: { statusString = "not started"; break; } case ThreadReference.THREAD_STATUS_RUNNING: { statusString = "runnable"; break; } case ThreadReference.THREAD_STATUS_SLEEPING: { statusString = "sleeping"; break; } case ThreadReference.THREAD_STATUS_WAIT: { statusString = "waiting"; break; } case ThreadReference.THREAD_STATUS_ZOMBIE: { statusString = "zombie"; break; } case ThreadReference.THREAD_STATUS_UNKNOWN: { statusString = "unknown"; break; } default: { break; } } // note that the above status string refers to the state of the // thread *before* a suspension, if there was a suspension. /* Due to a bug in ThreadReference.isSuspended(), we need to use suspendCount() */ String stateString = "normal"; if (t.isAtBreakpoint()) { stateString = "suspended at breakpoint"; } else if (t.suspendCount() > 0) { stateString = "suspended by debugger"; } if (detailed) { // info on the stack String stackString; try { stackString = "(list"; // a list of the stackframes is also sent... List stackFrames = t.frames(); Iterator it = stackFrames.iterator(); int index = 0; while (it.hasNext()) { stackString += BR + getStackFrameRep((StackFrame)it.next(), index++); } stackString += ")"; } catch (IncompatibleThreadStateException ex) { stackString = "\"Information Not Available\""; } // info on the monitors // owned monitors String ownedMonitorsString; try { ownedMonitorsString = "(list"; List ownedMonitors = t.ownedMonitors(); Iterator it = ownedMonitors.iterator(); while (it.hasNext()) { ownedMonitorsString += BR +getObjectRep((ObjectReference)it.next(), store); } ownedMonitorsString += ")"; } catch (IncompatibleThreadStateException ex) { ownedMonitorsString = "\"Information Not Available\""; } catch (UnsupportedOperationException ex) { ownedMonitorsString = "\"VM has no information\""; } catch (ObjectCollectedException ex) { ownedMonitorsString = "\"The object has been collected\""; } // current contended monitor // note, however, from the jdi api: // The thread can be waiting for a monitor through entry into a // synchronized method, the synchronized statement, or // Object.wait(). The status() method can be used to // differentiate between the first two cases and the third. String currentContendedMonitorString; try { ObjectReference o = t.currentContendedMonitor(); if (o == null) { currentContendedMonitorString = "nil"; } else { currentContendedMonitorString = getObjectRep(o, store).toString(); } } catch (IncompatibleThreadStateException ex) { currentContendedMonitorString = "\"Information Not Available\""; } catch (UnsupportedOperationException ex) { currentContendedMonitorString = "\"VM has no information\""; } catch (ObjectCollectedException ex) { currentContendedMonitorString = "\"The object has been collected\""; } store.put(t); return new LispForm("(list \"Thread\"" +" "+t.uniqueID() +" \""+t.name()+"\"" +" \""+statusString+"\"" +" \""+stateString+"\"" + BR +stackString + BR +ownedMonitorsString + BR +currentContendedMonitorString +")"); } else { store.put(t); return new LispForm("(list \"Thread\"" +" "+t.uniqueID() +" \""+t.name()+"\"" +" \""+statusString+"\"" +" \""+stateString+"\")"); } } /** * Returns a canonical representation of a given StackFrame. *
* * Syntax: *
* (list "StackFrame" index "Information not available")
* (list "StackFrame" index "type name" "source name" lineNumber "method name")
*
*
* Comments:
*