Fix up some JDWP stuff

Notably:
- Claim to know about synthetic attributes (canGetSyntheticAttribute).
  Set an appropriate flag in the access flags of fields and methods.
  (Makes no difference that I can see in Eclipse.)
- Stop making copies of descriptor strings.  That ceased to be necessary
  after they were normalized in DEX (pre-1.0).
- Correct object type determination, especially w.r.t. the specially-
  tagged types (strings, threads, classloaders, etc).
- Handle a FIELD_ONLY mod (not tested).
- Added a bit of debugging stuff.

Change-Id: I5651b5b76d904a9edbd051e039423795faaa1650
diff --git a/vm/Debugger.c b/vm/Debugger.c
index 87ea008..9570b5c 100644
--- a/vm/Debugger.c
+++ b/vm/Debugger.c
@@ -634,17 +634,13 @@
 }
 
 /*
- * Generate the "JNI signature" for a class, e.g. "Ljava/lang/String;".
+ * Get the "JNI signature" for a class, e.g. "Ljava/lang/String;".
  *
- * Our class descriptors are in the correct format, so we just copy that.
- * TODO: figure out if we can avoid the copy now that we're using
- * descriptors instead of unadorned class names.
- *
- * Returns a newly-allocated string.
+ * Our class descriptors are in the correct format, so we just return that.
  */
-static char* generateJNISignature(ClassObject* clazz)
+static const char* jniSignature(ClassObject* clazz)
 {
-    return strdup(clazz->descriptor);
+    return clazz->descriptor;
 }
 
 /*
@@ -654,7 +650,7 @@
  * the class.
  */
 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
-    char** pSignature)
+    const char** pSignature)
 {
     ClassObject* clazz = refTypeIdToClassObject(classId);
 
@@ -673,7 +669,7 @@
             *pTypeTag = TT_CLASS;
     }
     if (pSignature != NULL)
-        *pSignature = generateJNISignature(clazz);
+        *pSignature = jniSignature(clazz);
 }
 
 /*
@@ -727,17 +723,15 @@
 
 /*
  * Get a class' signature.
- *
- * Returns a newly-allocated string.
  */
-char* dvmDbgGetSignature(RefTypeId refTypeId)
+const char* dvmDbgGetSignature(RefTypeId refTypeId)
 {
     ClassObject* clazz;
 
     clazz = refTypeIdToClassObject(refTypeId);
     assert(clazz != NULL);
 
-    return generateJNISignature(clazz);
+    return jniSignature(clazz);
 }
 
 /*
@@ -756,111 +750,106 @@
 }
 
 /*
- * Get an object's type name.  Converted to a "JNI signature".
- *
- * Returns a newly-allocated string.
+ * Get an object's type name.  (For log message display only.)
  */
-char* dvmDbgGetObjectTypeName(ObjectId objectId)
+const char* dvmDbgGetObjectTypeName(ObjectId objectId)
 {
+    if (objectId == 0)
+        return "(null)";
+
     Object* obj = objectIdToObject(objectId);
-
-    assert(obj != NULL);
-
-    return generateJNISignature(obj->clazz);
+    return jniSignature(obj->clazz);
 }
 
 /*
- * Given a type signature (e.g. "Ljava/lang/String;"), return the JDWP
- * "type tag".
- *
- * In many cases this is necessary but not sufficient.  For example, if
- * we have a NULL String object, we want to return JT_STRING.  If we have
- * a java/lang/Object that holds a String reference, we also want to
- * return JT_STRING.  See dvmDbgGetObjectTag().
+ * Determine whether or not a tag represents a primitive type.
  */
-int dvmDbgGetSignatureTag(const char* type)
+static bool isTagPrimitive(u1 tag)
 {
-    /*
-     * We're not checking the class loader here (to guarantee that JT_STRING
-     * is truly the one and only String), but it probably doesn't matter
-     * for our purposes.
-     */
-    if (strcmp(type, "Ljava/lang/String;") == 0)
-        return JT_STRING;
-    else if (strcmp(type, "Ljava/lang/Class;") == 0)
-        return JT_CLASS_OBJECT;
-    else if (strcmp(type, "Ljava/lang/Thread;") == 0)
-        return JT_THREAD;
-    else if (strcmp(type, "Ljava/lang/ThreadGroup;") == 0)
-        return JT_THREAD_GROUP;
-    else if (strcmp(type, "Ljava/lang/ClassLoader;") == 0)
-        return JT_CLASS_LOADER;
-
-    switch (type[0]) {
-    case '[':       return JT_ARRAY;
-    case 'B':       return JT_BYTE;
-    case 'C':       return JT_CHAR;
-    case 'L':       return JT_OBJECT;
-    case 'F':       return JT_FLOAT;
-    case 'D':       return JT_DOUBLE;
-    case 'I':       return JT_INT;
-    case 'J':       return JT_LONG;
-    case 'S':       return JT_SHORT;
-    case 'V':       return JT_VOID;
-    case 'Z':       return JT_BOOLEAN;
+    switch (tag) {
+    case JT_BYTE:
+    case JT_CHAR:
+    case JT_FLOAT:
+    case JT_DOUBLE:
+    case JT_INT:
+    case JT_LONG:
+    case JT_SHORT:
+    case JT_VOID:
+    case JT_BOOLEAN:
+        return true;
+    case JT_ARRAY:
+    case JT_OBJECT:
+    case JT_STRING:
+    case JT_CLASS_OBJECT:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+        return false;
     default:
-        LOGE("ERROR: unhandled type '%s'\n", type);
+        LOGE("ERROR: unhandled tag '%c'\n", tag);
         assert(false);
-        return -1;
+        return false;
     }
 }
 
 /*
- * Methods declared to return Object might actually be returning one
- * of the "refined types".  We need to check the object explicitly.
+ * Determine the best tag type given an object's class.
  */
-static u1 resultTagFromObject(Object* obj)
+static u1 tagFromClass(ClassObject* clazz)
 {
-    ClassObject* clazz;
-
-    if (obj == NULL)
-        return JT_OBJECT;
-
-    clazz = obj->clazz;
-
-    /*
-     * Comparing against the known classes is faster than string
-     * comparisons.  It ensures that we only find the classes in the
-     * bootstrap class loader, which may or may not be what we want.
-     */
-    if (clazz == gDvm.classJavaLangString)
-        return JT_STRING;
-    else if (clazz == gDvm.classJavaLangClass)
-        return JT_CLASS_OBJECT;
-    else if (clazz == gDvm.classJavaLangThread)
-        return JT_THREAD;
-    else if (clazz == gDvm.classJavaLangThreadGroup)
-        return JT_THREAD_GROUP;
-    else if (strcmp(clazz->descriptor, "Ljava/lang/ClassLoader;") == 0)
-        return JT_CLASS_LOADER;
-    else if (clazz->descriptor[0] == '[')
+    if (dvmIsArrayClass(clazz))
         return JT_ARRAY;
-    else
+
+    if (clazz == gDvm.classJavaLangString) {
+        return JT_STRING;
+    } else if (clazz == gDvm.classJavaLangClass) {
+        return JT_CLASS_OBJECT;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
+        return JT_THREAD;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
+        return JT_THREAD_GROUP;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
+        return JT_CLASS_LOADER;
+    } else {
         return JT_OBJECT;
+    }
 }
 
 /*
- * Determine the tag for an object with a known type.
+ * Return a basic tag value based solely on a type descriptor.
+ *
+ * The ASCII value maps directly to the JDWP tag constants, so we don't
+ * need to do much here.  This does not return the fancier tags like
+ * JT_THREAD.
  */
-int dvmDbgGetObjectTag(ObjectId objectId, const char* type)
+static u1 basicTagFromDescriptor(const char* descriptor)
 {
-    u1 tag;
+    return descriptor[0];
+}
 
-    tag = dvmDbgGetSignatureTag(type);
-    if (tag == JT_OBJECT && objectId != 0)
-        tag = resultTagFromObject(objectIdToObject(objectId));
+/*
+ * Objects declared to hold Object might actually hold a more specific
+ * type.  The debugger may take a special interest in these (e.g. it
+ * wants to display the contents of Strings), so we want to return an
+ * appropriate tag.
+ *
+ * Null objects are tagged JT_OBJECT.
+ */
+static u1 tagFromObject(const Object* obj)
+{
+    if (obj == NULL)
+        return JT_OBJECT;
+    return tagFromClass(obj->clazz);
+}
 
-    return tag;
+/*
+ * Determine the tag for an object.
+ *
+ * "objectId" may be 0 (i.e. NULL reference).
+ */
+u1 dvmDbgGetObjectTag(ObjectId objectId)
+{
+    return tagFromObject(objectIdToObject(objectId));
 }
 
 /*
@@ -898,37 +887,6 @@
     }
 }
 
-/*
- * Determine whether or not a tag represents a primitive type.
- */
-static bool isTagPrimitive(u1 tag)
-{
-    switch (tag) {
-    case JT_BYTE:
-    case JT_CHAR:
-    case JT_FLOAT:
-    case JT_DOUBLE:
-    case JT_INT:
-    case JT_LONG:
-    case JT_SHORT:
-    case JT_VOID:
-    case JT_BOOLEAN:
-        return true;
-    case JT_ARRAY:
-    case JT_OBJECT:
-    case JT_STRING:
-    case JT_CLASS_OBJECT:
-    case JT_THREAD:
-    case JT_THREAD_GROUP:
-    case JT_CLASS_LOADER:
-        return false;
-    default:
-        LOGE("ERROR: unhandled tag '%c'\n", tag);
-        assert(false);
-        return false;
-    }
-}
-
 
 /*
  * Return the length of the specified array.
@@ -943,13 +901,18 @@
 /*
  * Return a tag indicating the general type of elements in the array.
  */
-int dvmDbgGetArrayElementTag(ObjectId arrayId)
+u1 dvmDbgGetArrayElementTag(ObjectId arrayId)
 {
     ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
 
-    assert(dvmIsArray(arrayObj));
+    ClassObject* arrayClass = arrayObj->obj.clazz;
+    u1 tag = basicTagFromDescriptor(arrayClass->descriptor + 1);
+    if (!isTagPrimitive(tag)) {
+        /* try to refine it */
+        tag = tagFromClass(arrayClass->elementClass);
+    }
 
-    return dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+    return tag;
 }
 
 /*
@@ -982,7 +945,7 @@
 }
 
 /*
- * Copy a series of values with the specified with, changing the
+ * Copy a series of values with the specified width, changing the
  * byte order from big-endian.
  */
 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
@@ -1030,7 +993,7 @@
         return false;
     }
 
-    tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+    tag = basicTagFromDescriptor(arrayObj->obj.clazz->descriptor + 1);
 
     if (isTagPrimitive(tag)) {
         int width = dvmDbgGetTagWidth(tag);
@@ -1052,7 +1015,7 @@
         for (i = 0; i < count; i++, pObjects++) {
             u1 thisTag;
             if (*pObjects != NULL)
-                thisTag = resultTagFromObject(*pObjects);
+                thisTag = tagFromObject(*pObjects);
             else
                 thisTag = tag;
             expandBufAdd1(pReply, thisTag);
@@ -1081,7 +1044,7 @@
         return false;
     }
 
-    tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+    tag = basicTagFromDescriptor(arrayObj->obj.clazz->descriptor + 1);
 
     if (isTagPrimitive(tag)) {
         int width = dvmDbgGetTagWidth(tag);
@@ -1179,8 +1142,21 @@
 }
 
 /*
+ * Augment the access flags for synthetic methods and fields by setting
+ * the (as described by the spec) "0xf0000000 bit".
+ */
+static u4 augmentedAccessFlags(u4 accessFlags)
+{
+    if ((accessFlags & ACC_SYNTHETIC) != 0) {
+        return accessFlags | 0xf0000000;
+    } else {
+        return accessFlags;
+    }
+}
+
+/*
  * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
- * output all fields declared by the class.  Inerhited fields are
+ * output all fields declared by the class.  Inherited fields are
  * not included.
  */
 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
@@ -1206,7 +1182,7 @@
         expandBufAddUtf8String(pReply, (const u1*) field->signature);
         if (withGeneric)
             expandBufAddUtf8String(pReply, genericSignature);
-        expandBufAdd4BE(pReply, field->accessFlags);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
     }
     for (i = 0; i < clazz->ifieldCount; i++) {
         field = (Field*) &clazz->ifields[i];
@@ -1216,7 +1192,7 @@
         expandBufAddUtf8String(pReply, (const u1*) field->signature);
         if (withGeneric)
             expandBufAddUtf8String(pReply, genericSignature);
-        expandBufAdd4BE(pReply, field->accessFlags);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
     }
 }
 
@@ -1255,7 +1231,7 @@
 
         if (withGeneric)
             expandBufAddUtf8String(pReply, genericSignature);
-        expandBufAdd4BE(pReply, meth->accessFlags);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
     }
     for (i = 0; i < clazz->virtualMethodCount; i++) {
         meth = &clazz->virtualMethods[i];
@@ -1269,7 +1245,7 @@
 
         if (withGeneric)
             expandBufAddUtf8String(pReply, genericSignature);
-        expandBufAdd4BE(pReply, meth->accessFlags);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
     }
 
     dexStringCacheRelease(&stringCache);
@@ -1457,78 +1433,82 @@
 }
 
 /*
- * Get the type tag for the field's type.
+ * Get the basic tag for an instance field.
  */
-int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId)
+u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
 {
     Object* obj = objectIdToObject(objId);
     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
-    Field* field = fieldIdToField(classId, fieldId);
-
-    return dvmDbgGetSignatureTag(field->signature);
+    const Field* field = fieldIdToField(classId, fieldId);
+    return basicTagFromDescriptor(field->signature);
 }
 
 /*
- * Get the type tag for the static field's type.
+ * Get the basic tag for a static field.
  */
-int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId)
+u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
 {
-    Field* field = fieldIdToField(refTypeId, fieldId);
-    return dvmDbgGetSignatureTag(field->signature);
+    const Field* field = fieldIdToField(refTypeId, fieldId);
+    return basicTagFromDescriptor(field->signature);
 }
 
+
 /*
- * Copy the value of a field into the specified buffer.
+ * Copy the value of a static field into the output buffer, preceded
+ * by an appropriate tag.  The tag is based on the value held by the
+ * field, not the field's type.
  */
-void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, u1* buf,
-    int expectedLen)
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
 {
     Object* obj = objectIdToObject(objectId);
     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
-    InstField* field = (InstField*) fieldIdToField(classId, fieldId);
-    Object* objVal;
-    u4 intVal;
-    u8 longVal;
+    InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
+    u1 tag = basicTagFromDescriptor(ifield->field.signature);
 
-    switch (field->field.signature[0]) {
-    case JT_BOOLEAN:
-        assert(expectedLen == 1);
-        intVal = dvmGetFieldBoolean(obj, field->byteOffset);
-        set1(buf, intVal != 0);
-        break;
-    case JT_BYTE:
-        assert(expectedLen == 1);
-        intVal = dvmGetFieldInt(obj, field->byteOffset);
-        set1(buf, intVal);
-        break;
-    case JT_SHORT:
-    case JT_CHAR:
-        assert(expectedLen == 2);
-        intVal = dvmGetFieldInt(obj, field->byteOffset);
-        set2BE(buf, intVal);
-        break;
-    case JT_INT:
-    case JT_FLOAT:
-        assert(expectedLen == 4);
-        intVal = dvmGetFieldInt(obj, field->byteOffset);
-        set4BE(buf, intVal);
-        break;
-    case JT_ARRAY:
-    case JT_OBJECT:
-        assert(expectedLen == sizeof(ObjectId));
-        objVal = dvmGetFieldObject(obj, field->byteOffset);
-        dvmSetObjectId(buf, objectToObjectId(objVal));
-        break;
-    case JT_DOUBLE:
-    case JT_LONG:
-        assert(expectedLen == 8);
-        longVal = dvmGetFieldLong(obj, field->byteOffset);
-        set8BE(buf, longVal);
-        break;
-    default:
-        LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
-        assert(false);
-        break;
+    if (tag == JT_ARRAY || tag == JT_OBJECT) {
+        Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
+        tag = tagFromObject(objVal);
+        expandBufAdd1(pReply, tag);
+        expandBufAddObjectId(pReply, objectToObjectId(objVal));
+        LOGV("    --> ifieldId %x --> tag '%c' %p\n", fieldId, tag, objVal);
+    } else {
+        JValue value;
+
+        LOGV("    --> ifieldId %x --> tag '%c'\n", fieldId, tag);
+        expandBufAdd1(pReply, tag);
+
+        switch (tag) {
+        case JT_BOOLEAN:
+            expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
+            break;
+        case JT_BYTE:
+            expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
+            break;
+        case JT_SHORT:
+            expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
+            break;
+        case JT_CHAR:
+            expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
+            break;
+        case JT_INT:
+            expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
+            break;
+        case JT_FLOAT:
+            value.f = dvmGetFieldInt(obj, ifield->byteOffset);
+            expandBufAdd4BE(pReply, value.i);
+            break;
+        case JT_LONG:
+            expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
+            break;
+        case JT_DOUBLE:
+            value.d = dvmGetFieldInt(obj, ifield->byteOffset);
+            expandBufAdd8BE(pReply, value.j);
+            break;
+        default:
+            LOGE("ERROR: unhandled field type '%s'\n", ifield->field.signature);
+            assert(false);
+            break;
+        }
     }
 }
 
@@ -1579,60 +1559,60 @@
 }
 
 /*
- * Copy the value of a static field into the specified buffer.
+ * Copy the value of a static field into the output buffer, preceded
+ * by an appropriate tag.  The tag is based on the value held by the
+ * field, not the field's type.
  */
-void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* buf,
-    int expectedLen)
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    ExpandBuf* pReply)
 {
     StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
-    Object* objVal;
-    JValue value;
+    u1 tag = basicTagFromDescriptor(sfield->field.signature);
 
-    switch (sfield->field.signature[0]) {
-    case JT_BOOLEAN:
-        assert(expectedLen == 1);
-        set1(buf, dvmGetStaticFieldBoolean(sfield));
-        break;
-    case JT_BYTE:
-        assert(expectedLen == 1);
-        set1(buf, dvmGetStaticFieldByte(sfield));
-        break;
-    case JT_SHORT:
-        assert(expectedLen == 2);
-        set2BE(buf, dvmGetStaticFieldShort(sfield));
-        break;
-    case JT_CHAR:
-        assert(expectedLen == 2);
-        set2BE(buf, dvmGetStaticFieldChar(sfield));
-        break;
-    case JT_INT:
-        assert(expectedLen == 4);
-        set4BE(buf, dvmGetStaticFieldInt(sfield));
-        break;
-    case JT_FLOAT:
-        assert(expectedLen == 4);
-        value.f = dvmGetStaticFieldFloat(sfield);
-        set4BE(buf, value.i);
-        break;
-    case JT_ARRAY:
-    case JT_OBJECT:
-        assert(expectedLen == sizeof(ObjectId));
-        objVal = dvmGetStaticFieldObject(sfield);
-        dvmSetObjectId(buf, objectToObjectId(objVal));
-        break;
-    case JT_LONG:
-        assert(expectedLen == 8);
-        set8BE(buf, dvmGetStaticFieldLong(sfield));
-        break;
-    case JT_DOUBLE:
-        assert(expectedLen == 8);
-        value.d = dvmGetStaticFieldDouble(sfield);
-        set8BE(buf, value.j);
-        break;
-    default:
-        LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
-        assert(false);
-        break;
+    if (tag == JT_ARRAY || tag == JT_OBJECT) {
+        Object* objVal = dvmGetStaticFieldObject(sfield);
+        tag = tagFromObject(objVal);
+        expandBufAdd1(pReply, tag);
+        expandBufAddObjectId(pReply, objectToObjectId(objVal));
+        LOGV("    --> sfieldId %x --> tag '%c' %p\n", fieldId, tag, objVal);
+    } else {
+        JValue value;
+
+        LOGV("    --> sfieldId %x --> tag '%c'\n", fieldId, tag);
+        expandBufAdd1(pReply, tag);
+
+        switch (tag) {
+        case JT_BOOLEAN:
+            expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
+            break;
+        case JT_BYTE:
+            expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
+            break;
+        case JT_SHORT:
+            expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
+            break;
+        case JT_CHAR:
+            expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
+            break;
+        case JT_INT:
+            expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
+            break;
+        case JT_FLOAT:
+            value.f = dvmGetStaticFieldFloat(sfield);
+            expandBufAdd4BE(pReply, value.i);
+            break;
+        case JT_LONG:
+            expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
+            break;
+        case JT_DOUBLE:
+            value.d = dvmGetStaticFieldDouble(sfield);
+            expandBufAdd8BE(pReply, value.j);
+            break;
+        default:
+            LOGE("ERROR: unhandled field type '%s'\n", sfield->field.signature);
+            assert(false);
+            break;
+        }
     }
 }
 
@@ -2385,21 +2365,14 @@
         {
             /* convert to "ObjectId" */
             objVal = (Object*)framePtr[slot];
-            //char* name;
 
-            if (objVal != NULL) {
-                if (!dvmIsValidObject(objVal)) {
-                    LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
-                        slot, objVal);
-                    dvmAbort();         // DEBUG: make it obvious
-                    objVal = NULL;
-                }
-                //name = generateJNISignature(objVal->clazz);
-                tag = resultTagFromObject(objVal);
-                //free(name);
-            } else {
-                tag = JT_OBJECT;
+            if (objVal != NULL && !dvmIsValidObject(objVal)) {
+                LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
+                    slot, objVal);
+                dvmAbort();         // DEBUG: make it obvious
+                objVal = NULL;
             }
+            tag = tagFromObject(objVal);
             dvmSetObjectId(buf+1, objectToObjectId(objVal));
         }
         break;
@@ -2596,8 +2569,8 @@
  */
 void dvmDbgPostClassPrepare(ClassObject* clazz)
 {
+    const char* signature;
     int tag;
-    char* signature;
 
     if (dvmIsInterfaceClass(clazz))
         tag = TT_INTERFACE;
@@ -2607,10 +2580,9 @@
     // TODO - we currently always send both "verified" and "prepared" since
     // debuggers seem to like that.  There might be some advantage to honesty,
     // since the class may not yet be verified.
-    signature = generateJNISignature(clazz);
+    signature = jniSignature(clazz);
     dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
         signature, CS_VERIFIED | CS_PREPARED);
-    free(signature);
 }
 
 /*
@@ -2833,12 +2805,12 @@
 }
 
 /*
- * Determine the tag type for the return value for this method.
+ * Return a basic tag value for the return type.
  */
-static u1 resultTagFromSignature(const Method* method)
+static u1 getReturnTypeBasicTag(const Method* method)
 {
     const char* descriptor = dexProtoGetReturnType(&method->prototype);
-    return dvmDbgGetSignatureTag(descriptor);
+    return basicTagFromDescriptor(descriptor);
 }
 
 /*
@@ -2891,7 +2863,7 @@
     dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
         (jvalue*)pReq->argArray);
     pReq->exceptObj = objectToObjectId(dvmGetException(self));
-    pReq->resultTag = resultTagFromSignature(meth);
+    pReq->resultTag = getReturnTypeBasicTag(meth);
     if (pReq->exceptObj != 0) {
         Object* exc = dvmGetException(self);
         LOGD("  JDWP invocation returning with exceptObj=%p (%s)\n",
@@ -2905,7 +2877,7 @@
         pReq->resultValue.j = 0; /*0xadadadad;*/
     } else if (pReq->resultTag == JT_OBJECT) {
         /* if no exception thrown, examine object result more closely */
-        u1 newTag = resultTagFromObject((Object*)pReq->resultValue.l);
+        u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
         if (newTag != pReq->resultTag) {
             LOGVV("  JDWP promoted result from %d to %d\n",
                 pReq->resultTag, newTag);
diff --git a/vm/Debugger.h b/vm/Debugger.h
index a842442..262e110 100644
--- a/vm/Debugger.h
+++ b/vm/Debugger.h
@@ -171,21 +171,20 @@
 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
         RefTypeId** pClassRefBuf);
 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
-    char** pSignature);
+    const char** pSignature);
 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
         RefTypeId* pRefTypeId);
 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
     RefTypeId* pRefTypeId);
 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId);
-char* dvmDbgGetSignature(RefTypeId refTypeId);
+const char* dvmDbgGetSignature(RefTypeId refTypeId);
 const char* dvmDbgGetSourceFile(RefTypeId refTypeId);
-char* dvmDbgGetObjectTypeName(ObjectId objectId);
-int dvmDbgGetSignatureTag(const char* signature);
-int dvmDbgGetObjectTag(ObjectId objectId, const char* type);
+const char* dvmDbgGetObjectTypeName(ObjectId objectId);
+u1 dvmDbgGetObjectTag(ObjectId objectId);
 int dvmDbgGetTagWidth(int tag);
 
 int dvmDbgGetArrayLength(ObjectId arrayId);
-int dvmDbgGetArrayElementTag(ObjectId arrayId);
+u1 dvmDbgGetArrayElementTag(ObjectId arrayId);
 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
     ExpandBuf* pReply);
 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
@@ -211,13 +210,13 @@
 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId id,
     bool withGeneric, ExpandBuf* pReply);
 
-int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId);
-int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId);
-void dvmDbgGetFieldValue(ObjectId objId, FieldId fieldId, u1* ptr, int width);
+u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId);
+u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId);
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply);
 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
     int width);
-void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* ptr,
-    int width);
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    ExpandBuf* pReply);
 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
     u8 rawValue, int width);
 
diff --git a/vm/Globals.h b/vm/Globals.h
index db329e4..f2f9778 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -262,6 +262,7 @@
      * Quick lookups for popular classes used internally.
      */
     ClassObject* classJavaLangClassArray;
+    ClassObject* classJavaLangClassLoader;
     ClassObject* classJavaLangObject;
     ClassObject* classJavaLangObjectArray;
     ClassObject* classJavaLangString;
diff --git a/vm/InitRefs.c b/vm/InitRefs.c
index 953db8e..3296e15 100644
--- a/vm/InitRefs.c
+++ b/vm/InitRefs.c
@@ -58,6 +58,7 @@
 
         /* Slightly less core, but still down there, classes */
         { &gDvm.classJavaLangClassArray,             "[Ljava/lang/Class;" },
+        { &gDvm.classJavaLangClassLoader,            "Ljava/lang/ClassLoader;" },
         { &gDvm.classJavaLangObjectArray,            "[Ljava/lang/Object;"},
         { &gDvm.classJavaLangStackTraceElement,      "Ljava/lang/StackTraceElement;" },
         { &gDvm.classJavaLangStackTraceElementArray, "[Ljava/lang/StackTraceElement;" },
diff --git a/vm/jdwp/JdwpConstants.c b/vm/jdwp/JdwpConstants.c
index 898fe2c..4b1418b 100644
--- a/vm/jdwp/JdwpConstants.c
+++ b/vm/jdwp/JdwpConstants.c
@@ -174,6 +174,27 @@
 }
 
 /*
+ * Return a string for the ModKind.
+ */
+const char* dvmJdwpModKindStr(enum JdwpModKind kind)
+{
+    switch (kind) {
+    case MK_COUNT:              return "COUNT";
+    case MK_CONDITIONAL:        return "CONDITIONAL";
+    case MK_THREAD_ONLY:        return "THREAD_ONLY";
+    case MK_CLASS_ONLY:         return "CLASS_ONLY";
+    case MK_CLASS_MATCH:        return "CLASS_MATCH";
+    case MK_CLASS_EXCLUDE:      return "CLASS_EXCLUDE";
+    case MK_LOCATION_ONLY:      return "LOCATION_ONLY";
+    case MK_EXCEPTION_ONLY:     return "EXCEPTION_ONLY";
+    case MK_FIELD_ONLY:         return "FIELD_ONLY";
+    case MK_STEP:               return "STEP";
+    case MK_INSTANCE_ONLY:      return "INSTANCE_ONLY";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
  * Return a string for the StepDepth.
  */
 const char* dvmJdwpStepDepthStr(enum JdwpStepDepth depth)
diff --git a/vm/jdwp/JdwpConstants.h b/vm/jdwp/JdwpConstants.h
index 922dbcd..746bc59 100644
--- a/vm/jdwp/JdwpConstants.h
+++ b/vm/jdwp/JdwpConstants.h
@@ -137,6 +137,7 @@
     MK_STEP                 = 10,
     MK_INSTANCE_ONLY        = 11,
 };
+const char* dvmJdwpModKindStr(enum JdwpModKind kind);
 
 /*
  * InvokeOptions constants (bit flags).
diff --git a/vm/jdwp/JdwpEvent.c b/vm/jdwp/JdwpEvent.c
index 371fdda..fbcae52 100644
--- a/vm/jdwp/JdwpEvent.c
+++ b/vm/jdwp/JdwpEvent.c
@@ -161,6 +161,26 @@
 }
 
 /*
+ * Dump an event to the log file.
+ */
+static void dumpEvent(const JdwpEvent* pEvent)
+{
+    LOGI("Event id=0x%4x %p (prev=%p next=%p):\n",
+        pEvent->requestId, pEvent, pEvent->prev, pEvent->next);
+    LOGI("  kind=%s susp=%s modCount=%d\n",
+        dvmJdwpEventKindStr(pEvent->eventKind),
+        dvmJdwpSuspendPolicyStr(pEvent->suspendPolicy),
+        pEvent->modCount);
+
+    int i;
+    for (i = 0; i < pEvent->modCount; i++) {
+        const JdwpEventMod* pMod = &pEvent->mods[i];
+        LOGI("  %s\n", dvmJdwpModKindStr(pMod->modKind));
+        /* TODO - show details */
+    }
+}
+
+/*
  * Add an event to the list.  Ordering is not important.
  *
  * If something prevents the event from being registered, e.g. it's a
@@ -180,19 +200,21 @@
     assert(pEvent->next == NULL);
 
     /*
-     * If one or more LocationOnly mods are used, register them with
+     * If one or more "break"-type mods are used, register them with
      * the interpreter.
      */
     for (i = 0; i < pEvent->modCount; i++) {
-        JdwpEventMod* pMod = &pEvent->mods[i];
+        const JdwpEventMod* pMod = &pEvent->mods[i];
         if (pMod->modKind == MK_LOCATION_ONLY) {
             /* should only be for Breakpoint, Step, and Exception */
             dvmDbgWatchLocation(&pMod->locationOnly.loc);
-        }
-        if (pMod->modKind == MK_STEP) {
+        } else if (pMod->modKind == MK_STEP) {
             /* should only be for EK_SINGLE_STEP; should only be one */
             dvmDbgConfigureStep(pMod->step.threadId, pMod->step.size,
                 pMod->step.depth);
+        } else if (pMod->modKind == MK_FIELD_ONLY) {
+            /* should be for EK_FIELD_ACCESS or EK_FIELD_MODIFICATION */
+            dumpEvent(pEvent);  /* TODO - need for field watches */
         }
     }
 
@@ -472,8 +494,7 @@
                 return false;
             break;
         case MK_CLASS_ONLY:
-            if (!dvmDbgMatchType(basket->classId,
-                    pMod->classOnly.referenceTypeId))
+            if (!dvmDbgMatchType(basket->classId, pMod->classOnly.refTypeId))
                 return false;
             break;
         case MK_CLASS_MATCH:
@@ -500,7 +521,9 @@
                 return false;
             break;
         case MK_FIELD_ONLY:
-            // TODO
+            if (!dvmDbgMatchType(basket->classId, pMod->fieldOnly.refTypeId) ||
+                    pMod->fieldOnly.fieldId != basket->field)
+                return false;
             break;
         case MK_STEP:
             if (pMod->step.threadId != basket->threadId)
@@ -1242,7 +1265,7 @@
  *    InstanceOnly
  */
 bool dvmJdwpPostFieldAccess(JdwpState* state, int STUFF, ObjectId thisPtr,
-    bool modified)
+    bool modified, JValue newValue)
 {
     assert(false);      // TODO
     return false;
diff --git a/vm/jdwp/JdwpEvent.h b/vm/jdwp/JdwpEvent.h
index 1a6a2c7..288ab8c 100644
--- a/vm/jdwp/JdwpEvent.h
+++ b/vm/jdwp/JdwpEvent.h
@@ -41,7 +41,7 @@
     } threadOnly;
     struct {
         u1          modKind;
-        RefTypeId   referenceTypeId;
+        RefTypeId   refTypeId;
     } classOnly;
     struct {
         u1          modKind;
diff --git a/vm/jdwp/JdwpHandler.c b/vm/jdwp/JdwpHandler.c
index d2a657d..f04fb95 100644
--- a/vm/jdwp/JdwpHandler.c
+++ b/vm/jdwp/JdwpHandler.c
@@ -432,7 +432,7 @@
     expandBufAdd1(pReply, false);   /* canWatchFieldModification */
     expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
     expandBufAdd1(pReply, false);   /* canGetBytecodes */
-    expandBufAdd1(pReply, false);   /* canGetSyntheticAttribute */
+    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
     expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
     expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
     expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
@@ -493,7 +493,7 @@
     expandBufAdd1(pReply, false);   /* canWatchFieldModification */
     expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
     expandBufAdd1(pReply, false);   /* canGetBytecodes */
-    expandBufAdd1(pReply, false);   /* canGetSyntheticAttribute */
+    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
     expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
     expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
     expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
@@ -535,7 +535,7 @@
     for (i = 0; i < (int) numClasses; i++) {
         static const u1 genericSignature[1] = "";
         u1 refTypeTag;
-        char* signature;
+        const char* signature;
         u4 status;
 
         dvmDbgGetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
@@ -545,8 +545,6 @@
         expandBufAddUtf8String(pReply, (const u1*) signature);
         expandBufAddUtf8String(pReply, genericSignature);
         expandBufAdd4BE(pReply, status);
-
-        free(signature);
     }
 
     free(classRefBuf);
@@ -561,7 +559,7 @@
 static JdwpError handleRT_Signature(JdwpState* state,
     const u1* buf, int dataLen, ExpandBuf* pReply)
 {
-    char* signature;
+    const char* signature;
     RefTypeId refTypeId;
 
     refTypeId = dvmReadRefTypeId(&buf);
@@ -569,7 +567,6 @@
     LOGV("  Req for signature of refTypeId=0x%llx\n", refTypeId);
     signature = dvmDbgGetSignature(refTypeId);
     expandBufAddUtf8String(pReply, (const u1*) signature);
-    free(signature);
 
     return ERR_NONE;
 }
@@ -604,20 +601,12 @@
     refTypeId = dvmReadRefTypeId(&buf);
     numFields = read4BE(&buf);
 
+    LOGV("  RT_GetValues %u:\n", numFields);
+
     expandBufAdd4BE(pReply, numFields);
     for (i = 0; i < (int) numFields; i++) {
-        FieldId fieldId;
-        u1 fieldTag;
-        int width;
-        u1* ptr;
-
-        fieldId = dvmReadFieldId(&buf);
-        fieldTag = dvmDbgGetFieldTag(refTypeId, fieldId);
-        width = dvmDbgGetTagWidth(fieldTag);
-
-        expandBufAdd1(pReply, fieldTag);
-        ptr = expandBufAddSpace(pReply, width);
-        dvmDbgGetStaticFieldValue(refTypeId, fieldId, ptr, width);
+        FieldId fieldId = dvmReadFieldId(&buf);
+        dvmDbgGetStaticFieldValue(refTypeId, fieldId, pReply);
     }
 
     return ERR_NONE;
@@ -717,19 +706,20 @@
     const u1* buf, int dataLen, ExpandBuf* pReply)
 {
     static const u1 genericSignature[1] = "";
-    char* signature;
+    const char* signature;
     RefTypeId refTypeId;
 
     refTypeId = dvmReadRefTypeId(&buf);
 
     LOGV("  Req for signature of refTypeId=0x%llx\n", refTypeId);
     signature = dvmDbgGetSignature(refTypeId);
-    if (signature != NULL)
+    if (signature != NULL) {
         expandBufAddUtf8String(pReply, (const u1*) signature);
-    else
-        expandBufAddUtf8String(pReply, (const u1*) "Lunknown;");  /* native? */
+    } else {
+        LOGW("No signature for refTypeId=0x%llx\n", refTypeId);
+        expandBufAddUtf8String(pReply, (const u1*) "Lunknown;");
+    }
     expandBufAddUtf8String(pReply, genericSignature);
-    free(signature);
 
     return ERR_NONE;
 }
@@ -761,11 +751,7 @@
 
     refTypeId = dvmReadRefTypeId(&buf);
     LOGV("  Req for fields in refTypeId=0x%llx\n", refTypeId);
-    {
-        char* tmp = dvmDbgGetSignature(refTypeId);
-        LOGV("  --> '%s'\n", tmp);
-        free(tmp);
-    }
+    LOGV("  --> '%s'\n", dvmDbgGetSignature(refTypeId));
 
     dvmDbgOutputAllFields(refTypeId, true, pReply);
 
@@ -784,11 +770,7 @@
     refTypeId = dvmReadRefTypeId(&buf);
 
     LOGV("  Req for methods in refTypeId=0x%llx\n", refTypeId);
-    {
-        char* tmp = dvmDbgGetSignature(refTypeId);
-        LOGV("  --> '%s'\n", tmp);
-        free(tmp);
-    }
+    LOGV("  --> '%s'\n", dvmDbgGetSignature(refTypeId));
 
     dvmDbgOutputAllMethods(refTypeId, true, pReply);
 
@@ -835,7 +817,7 @@
         int width;
 
         fieldId = dvmReadFieldId(&buf);
-        fieldTag = dvmDbgGetStaticFieldTag(classId, fieldId);
+        fieldTag = dvmDbgGetStaticFieldBasicTag(classId, fieldId);
         width = dvmDbgGetTagWidth(fieldTag);
         value = jdwpReadValue(&buf, width);
 
@@ -1010,22 +992,8 @@
     expandBufAdd4BE(pReply, numFields);
 
     for (i = 0; i < (int) numFields; i++) {
-        FieldId fieldId;
-        u1 fieldTag;
-        int width;
-        u1* ptr;
-
-        fieldId = dvmReadFieldId(&buf);
-
-        fieldTag = dvmDbgGetFieldTag(objectId, fieldId);
-        width = dvmDbgGetTagWidth(fieldTag);
-
-        LOGV("    --> fieldId %x --> tag '%c'(%d)\n",
-            fieldId, fieldTag, width);
-
-        expandBufAdd1(pReply, fieldTag);
-        ptr = expandBufAddSpace(pReply, width);
-        dvmDbgGetFieldValue(objectId, fieldId, ptr, width);
+        FieldId fieldId = dvmReadFieldId(&buf);
+        dvmDbgGetFieldValue(objectId, fieldId, pReply);
     }
 
     return ERR_NONE;
@@ -1054,7 +1022,7 @@
 
         fieldId = dvmReadFieldId(&buf);
 
-        fieldTag = dvmDbgGetFieldTag(objectId, fieldId);
+        fieldTag = dvmDbgGetFieldBasicTag(objectId, fieldId);
         width = dvmDbgGetTagWidth(fieldTag);
         value = jdwpReadValue(&buf, width);
 
@@ -1642,7 +1610,7 @@
                 RefTypeId clazzId = dvmReadRefTypeId(&buf);
                 LOGVV("    ClassOnly: %llx (%s)\n",
                     clazzId, dvmDbgGetClassDescriptor(clazzId));
-                pEvent->mods[idx].classOnly.referenceTypeId = clazzId;
+                pEvent->mods[idx].classOnly.refTypeId = clazzId;
             }
             break;
         case MK_CLASS_MATCH:    /* restrict events to matching classes */
@@ -1704,7 +1672,7 @@
                 FieldId fieldId = dvmReadFieldId(&buf);
                 LOGVV("    FieldOnly: %llx %x\n", declaring, fieldId);
                 pEvent->mods[idx].fieldOnly.refTypeId = declaring;
-                pEvent->mods[idx].fieldOnly.fieldId = fieldId;;
+                pEvent->mods[idx].fieldOnly.fieldId = fieldId;
             }
             break;
         case MK_STEP:           /* for use with EK_SINGLE_STEP */
@@ -1870,7 +1838,6 @@
     FrameId frameId;
     u1 objectTag;
     ObjectId objectId;
-    char* typeName;
 
     threadId = dvmReadObjectId(&buf);
     frameId = dvmReadFrameId(&buf);
@@ -1878,16 +1845,10 @@
     if (!dvmDbgGetThisObject(threadId, frameId, &objectId))
         return ERR_INVALID_FRAMEID;
 
-    if (objectId == 0) {
-        typeName = strdup("null");
-        objectTag = 0;
-    } else {
-        typeName = dvmDbgGetObjectTypeName(objectId);
-        objectTag = dvmDbgGetObjectTag(objectId, typeName);
-    }
+    objectTag = dvmDbgGetObjectTag(objectId);
     LOGV("  Req for 'this' in thread=%llx frame=%llx --> %llx %s '%c'\n",
-        threadId, frameId, objectId, typeName, (char)objectTag);
-    free(typeName);
+        threadId, frameId, objectId, dvmDbgGetObjectTypeName(objectId),
+        (char)objectTag);
 
     expandBufAdd1(pReply, objectTag);
     expandBufAddObjectId(pReply, objectId);