Add implementation of JDWP ClassType.NewInstance
Add a handler for the ClassType.NewInstance request, and some bits of
plumbing to go with it.
For bug 2157236.
diff --git a/vm/Debugger.c b/vm/Debugger.c
index 3ea130d..3119350 100644
--- a/vm/Debugger.c
+++ b/vm/Debugger.c
@@ -1117,6 +1117,19 @@
}
/*
+ * Allocate a new object of the specified type.
+ *
+ * Add it to the registry to prevent it from being GCed.
+ */
+ObjectId dvmDbgCreateObject(RefTypeId classId)
+{
+ ClassObject* clazz = refTypeIdToClassObject(classId);
+ Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+ dvmReleaseTrackedAlloc(newObj, NULL);
+ return objectToObjectId(newObj);
+}
+
+/*
* Determine if "instClassId" is an instance of "classId".
*/
bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
@@ -2796,9 +2809,11 @@
/*
* Translate the method through the vtable, unless we're calling a
- * static method or the debugger wants to suppress it.
+ * direct method or the debugger wants to suppress it.
*/
- if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL) {
+ if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
+ dvmIsDirectMethod(pReq->method))
+ {
meth = pReq->method;
} else {
meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
@@ -2809,8 +2824,8 @@
IF_LOGV() {
char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
- LOGV("JDWP invoking method %s.%s %s\n",
- meth->clazz->descriptor, meth->name, desc);
+ LOGV("JDWP invoking method %p/%p %s.%s:%s\n",
+ pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
free(desc);
}
@@ -2819,8 +2834,10 @@
pReq->exceptObj = objectToObjectId(dvmGetException(self));
pReq->resultTag = resultTagFromSignature(meth);
if (pReq->exceptObj != 0) {
- LOGD(" JDWP invocation returning with exceptObj=%p\n",
- dvmGetException(self));
+ Object* exc = dvmGetException(self);
+ LOGD(" JDWP invocation returning with exceptObj=%p (%s)\n",
+ exc, exc->clazz->descriptor);
+ //dvmLogExceptionStackTrace();
dvmClearException(self);
/*
* Nothing should try to use this, but it looks like something is.
diff --git a/vm/Debugger.h b/vm/Debugger.h
index fcf07c7..b41318c 100644
--- a/vm/Debugger.h
+++ b/vm/Debugger.h
@@ -188,6 +188,7 @@
const u1* buf);
ObjectId dvmDbgCreateString(const char* str);
+ObjectId dvmDbgCreateObject(RefTypeId classId);
bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId);
diff --git a/vm/jdwp/JdwpHandler.c b/vm/jdwp/JdwpHandler.c
index ff6ecf4..16d9a5d 100644
--- a/vm/jdwp/JdwpHandler.c
+++ b/vm/jdwp/JdwpHandler.c
@@ -110,10 +110,14 @@
/*
* Common code for *_InvokeMethod requests.
+ *
+ * If "isConstructor" is set, this returns "objectId" rather than the
+ * expected-to-be-void return value of the called function.
*/
static JdwpError finishInvoke(JdwpState* state,
const u1* buf, int dataLen, ExpandBuf* pReply,
- ObjectId threadId, ObjectId objectId, RefTypeId classId, MethodId methodId)
+ ObjectId threadId, ObjectId objectId, RefTypeId classId, MethodId methodId,
+ bool isConstructor)
{
JdwpError err = ERR_NONE;
u8* argArray = NULL;
@@ -121,6 +125,8 @@
u4 options; /* enum InvokeOptions bit flags */
int i;
+ assert(!isConstructor || objectId != 0);
+
numArgs = read4BE(&buf);
LOGV(" --> threadId=%llx objectId=%llx\n", threadId, objectId);
@@ -163,11 +169,16 @@
goto bail;
if (err == ERR_NONE) {
- int width = dvmDbgGetTagWidth(resultTag);
+ if (isConstructor) {
+ expandBufAdd1(pReply, JT_OBJECT);
+ expandBufAddObjectId(pReply, objectId);
+ } else {
+ int width = dvmDbgGetTagWidth(resultTag);
- expandBufAdd1(pReply, resultTag);
- if (width != 0)
- jdwpWriteValue(pReply, width, resultValue);
+ expandBufAdd1(pReply, resultTag);
+ if (width != 0)
+ jdwpWriteValue(pReply, width, resultValue);
+ }
expandBufAdd1(pReply, JT_OBJECT);
expandBufAddObjectId(pReply, exceptObjId);
@@ -400,8 +411,10 @@
LOGV(" Req to create string '%s'\n", str);
stringId = dvmDbgCreateString(str);
- expandBufAddObjectId(pReply, stringId);
+ if (stringId == 0)
+ return ERR_OUT_OF_MEMORY;
+ expandBufAddObjectId(pReply, stringId);
return ERR_NONE;
}
@@ -834,7 +847,36 @@
methodId = dvmReadMethodId(&buf);
return finishInvoke(state, buf, dataLen, pReply,
- threadId, 0, classId, methodId);
+ threadId, 0, classId, methodId, false);
+}
+
+/*
+ * Create a new object of the requested type, and invoke the specified
+ * constructor.
+ *
+ * Example: in IntelliJ, create a watch on "new String(myByteArray)" to
+ * see the contents of a byte[] as a string.
+ */
+static JdwpError handleCT_NewInstance(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId classId;
+ ObjectId threadId;
+ MethodId methodId;
+ ObjectId objectId;
+ u4 numArgs;
+
+ classId = dvmReadRefTypeId(&buf);
+ threadId = dvmReadObjectId(&buf);
+ methodId = dvmReadMethodId(&buf);
+
+ LOGV("Creating instance of %s\n", dvmDbgGetClassDescriptor(classId));
+ objectId = dvmDbgCreateObject(classId);
+ if (objectId == 0)
+ return ERR_OUT_OF_MEMORY;
+
+ return finishInvoke(state, buf, dataLen, pReply,
+ threadId, objectId, classId, methodId, true);
}
/*
@@ -1011,7 +1053,7 @@
methodId = dvmReadMethodId(&buf);
return finishInvoke(state, buf, dataLen, pReply,
- threadId, objectId, classId, methodId);
+ threadId, objectId, classId, methodId, false);
}
/*
@@ -1955,7 +1997,7 @@
{ 3, 1, handleCT_Superclass, "ClassType.Superclass" },
{ 3, 2, handleCT_SetValues, "ClassType.SetValues" },
{ 3, 3, handleCT_InvokeMethod, "ClassType.InvokeMethod" },
- //3, 4, NewInstance
+ { 3, 4, handleCT_NewInstance, "ClassType.NewInstance" },
/* ArrayType command set (4) */
//4, 1, NewInstance