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);