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 * * * Copyright (c) 2000, 2001, 2003 Paul Kinnucan * * * * @author Amit Kumar * @author Paul Kinnucan * @since 0.1 * @version $Revision: 1.18 $ */ 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 String getMethodRep(Method m) { List l = m.argumentTypeNames(); StringBuffer argList = new StringBuffer("(list"); Iterator it = l.iterator(); while (it.hasNext()) { argList.append(" \""); argList.append(it.next().toString()); argList.append("\""); } argList.append(")"); // The below code results in an extra StringBuffer being created, but I think // that's OK from a performance perspective. return "(list \""+m.declaringType().name()+"\"" +" \""+m.name()+"\"" +" \""+m.returnTypeName()+"\"" + BR + argList.toString() +(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 String getLocalVariableRep(LocalVariable lv) { return "(list" + " \""+lv.name()+"\" \""+lv.typeName()+"\")"; } /** * Returns a representation of a (local variable, value) pair. *
* Syntax: *
* ({@link #getLocalVariableRep local-variable} . {@link #getValueRep value}) **/ static public String getLocalVariableValueRep(LocalVariable lv, Value v) { return "(cons "+getLocalVariableRep(lv) +" "+getValueRep(v)+")"; } /** * Returns a list of (local variable, value) pairs. *
* Syntax: *
* (list [{@link #getLocalVariableValueRep (local variable, value) pair}]*) **/ static public String getLocalVariableValueMapRep(Map map) { StringBuffer localVariablesValuesString = new StringBuffer("(list "); Set keys = map.keySet(); Iterator iter = keys.iterator(); while (iter.hasNext()) { LocalVariable localVariable = (LocalVariable)iter.next(); Value val = (Value)map.get(localVariable); localVariablesValuesString.append(BR); localVariablesValuesString.append(getLocalVariableValueRep(localVariable, val)); } localVariablesValuesString.append(")"); return localVariablesValuesString.toString(); } /** * Returns a representation of a field. *
* Syntax: *
* (list "name of field" "type of field" ["transient"] ["volatile"] * ["final"] ["static"]) **/ static String getFieldRep(Field f) { return "(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 String getFieldValueRep(Field f, Value v) { return "(cons "+getFieldRep(f)+" "+getValueRep(v)+")"; } /** * Returns a list of (field, value) pairs. *
* Syntax: *
* (list [{@link #getFieldValueRep (field, value) pair}]*) **/ static String getFieldValueMapRep(Map map) { StringBuffer fieldsValuesString = new StringBuffer("(list "); Set keys = map.keySet(); Iterator iter = keys.iterator(); while (iter.hasNext()) { Field field = (Field)iter.next(); Value val = (Value)map.get(field); fieldsValuesString.append(BR); fieldsValuesString.append(getFieldValueRep(field, val)); } fieldsValuesString.append(")"); return fieldsValuesString.toString(); } 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) 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 String getValueRep(Value value) { if (value == null) { return "(list \"null\")"; } else if (value instanceof VoidValue) { return "(list \"void\")"; } else if (value instanceof ObjectReference) { return getObjectRep((ObjectReference)value); } else { PrimitiveValue v = (PrimitiveValue)value; if (v instanceof BooleanValue) { return "(list \"boolean\" \""+v.booleanValue()+"\")"; } else if (v instanceof ByteValue) { return "(list \"byte\" \""+v.byteValue()+"\")"; } else if (v instanceof CharValue) { return "(list \"char\" \""+ escapeString(String.valueOf(v.charValue()))+"\")"; } else if (v instanceof DoubleValue) { return "(list \"double\" " + filterFPValue(String.valueOf(v.doubleValue()))+")"; } else if (v instanceof FloatValue) { return "(list \"float\" "+filterFPValue(String.valueOf(v.floatValue()))+")"; } else if (v instanceof IntegerValue) { return "(list \"int\" \""+v.intValue()+"\")"; } else if (v instanceof LongValue) { return "(list \"long\" \""+v.longValue()+"\")"; } else if (v instanceof ShortValue) { return "(list \"short\" "+v.shortValue()+")"; } } return null; } /** * Returns information about an array *
* * Syntax: *
* "Error message" * (list "type name" uniqueID ['t|nil] length [element]*) ** * Comments: *
* * @param String a description of the array elements */ static public String getArrayRep(ArrayReference a, String elements) { if (a == null) { return "\"Error! null array reference in Rep.getArrayRep!\""; } else { return "(list " + "\""+a.referenceType().name()+"\"" + " " +a.uniqueID() + (a.isCollected() ? " 't":" nil") + " " + a.length() + elements + ")"; } } /** * 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 String getStringRep(StringReference s) { if (s == null) { return "\"Error!\""; } else { return "(list " + "\""+s.referenceType().name()+"\"" + " "+s.uniqueID() + (s.isCollected() ? " 't":" nil") + " \"" + escapeString(s.value()) + "\")"; } } /** * Returns a non-detailed representation of an object. * * @see #getObjectRep(ObjectReference,boolean) */ static public String getObjectRep(ObjectReference o) { return getObjectRep(o, 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) child thread}]*) * (list [{@link #getThreadGroupRep child threadgroup}]*)) **/ public static String getThreadGroupRep(ThreadGroupReference t) { StringBuffer rep = new StringBuffer("(list \"ThreadGroup\" "); rep.append(t.uniqueID()); rep.append(" \""); rep.append(t.name()); rep.append("\" "); List list = t.threads(); Iterator it = list.iterator(); rep.append(BR); rep.append("(list"); while (it.hasNext()) { rep.append(BR); rep.append(getThreadRep((ThreadReference)it.next())); } rep.append(")"); list = t.threadGroups(); it = list.iterator(); rep.append(BR); rep.append("(list"); while (it.hasNext()) { rep.append(BR); rep.append(getThreadGroupRep((ThreadGroupReference)it.next())); } rep.append("))"); return rep.toString(); } /** * Returns a detailed thread representation. * @see #getThreadRep(ThreadReference, boolean) */ static public String getThreadRep(ThreadReference t) { return getThreadRep(t, 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) owned monitor}]*) **
* "Error Message" * nil * {@link #getObjectRep(ObjectReference) 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 String getThreadRep(ThreadReference t, 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 StringBuffer stackStringBuffer = new StringBuffer("(list"); String stackString; try { // a list of the stackframes is also sent... List stackFrames = t.frames(); Iterator it = stackFrames.iterator(); int index = 0; while (it.hasNext()) { stackStringBuffer.append(BR); stackStringBuffer.append(getStackFrameRep((StackFrame)it.next(), index++)); } stackStringBuffer.append(")"); stackString = stackStringBuffer.toString(); } catch (IncompatibleThreadStateException ex) { stackString = "\"Information Not Available\""; } // info on the monitors // owned monitors StringBuffer ownedMonitorsStringBuffer = new StringBuffer("(list"); String ownedMonitorsString; try { List ownedMonitors = t.ownedMonitors(); Iterator it = ownedMonitors.iterator(); while (it.hasNext()) { ownedMonitorsStringBuffer.append(BR); ownedMonitorsStringBuffer.append(getObjectRep((ObjectReference)it.next())); } ownedMonitorsStringBuffer.append(")"); ownedMonitorsString = ownedMonitorsStringBuffer.toString(); } 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); } } catch (IncompatibleThreadStateException ex) { currentContendedMonitorString = "\"Information Not Available\""; } catch (UnsupportedOperationException ex) { currentContendedMonitorString = "\"VM has no information\""; } catch (ObjectCollectedException ex) { currentContendedMonitorString = "\"The object has been collected\""; } return "(list \"Thread\"" +" "+t.uniqueID() +" \""+t.name()+"\"" +" \""+statusString+"\"" +" \""+stateString+"\"" + BR +stackString + BR +ownedMonitorsString + BR +currentContendedMonitorString +")"; } else { return "(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: *