Fix various debugger method invocation bugs.
(Also fix DexFile to admit that string lengths are always non-negative.)
Change-Id: I2d01ef2411b5c7e594527790daf3e0aaa3a1b67f
diff --git a/src/debugger.cc b/src/debugger.cc
index a456f23..494ee73 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -1991,7 +1991,35 @@
gSingleStepControl.dex_pcs.clear();
}
-JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptionId) {
+static char JdwpTagToShortyChar(JDWP::JdwpTag tag) {
+ switch (tag) {
+ default:
+ LOG(FATAL) << "unknown JDWP tag: " << PrintableChar(tag);
+
+ // Primitives.
+ case JDWP::JT_BYTE: return 'B';
+ case JDWP::JT_CHAR: return 'C';
+ case JDWP::JT_FLOAT: return 'F';
+ case JDWP::JT_DOUBLE: return 'D';
+ case JDWP::JT_INT: return 'I';
+ case JDWP::JT_LONG: return 'J';
+ case JDWP::JT_SHORT: return 'S';
+ case JDWP::JT_VOID: return 'V';
+ case JDWP::JT_BOOLEAN: return 'Z';
+
+ // Reference types.
+ case JDWP::JT_ARRAY:
+ case JDWP::JT_OBJECT:
+ case JDWP::JT_STRING:
+ case JDWP::JT_THREAD:
+ case JDWP::JT_THREAD_GROUP:
+ case JDWP::JT_CLASS_LOADER:
+ case JDWP::JT_CLASS_OBJECT:
+ return 'L';
+ }
+}
+
+JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t arg_count, uint64_t* arg_values, JDWP::JdwpTag* arg_types, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptionId) {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Thread* targetThread = NULL;
@@ -2030,22 +2058,54 @@
}
JDWP::JdwpError status;
- req->receiver_ = gRegistry->Get<Object*>(objectId);
- if (req->receiver_ == kInvalidObject) {
+ Object* receiver = gRegistry->Get<Object*>(objectId);
+ if (receiver == kInvalidObject) {
return JDWP::ERR_INVALID_OBJECT;
}
- req->thread_ = gRegistry->Get<Object*>(threadId); // TODO: check that this is actually a thread!
- if (req->thread_ == kInvalidObject) {
+
+ Object* thread = gRegistry->Get<Object*>(threadId);
+ if (thread == kInvalidObject) {
return JDWP::ERR_INVALID_OBJECT;
}
- req->class_ = DecodeClass(classId, status);
- if (req->class_ == NULL) {
+ // TODO: check that 'thread' is actually a java.lang.Thread!
+
+ Class* c = DecodeClass(classId, status);
+ if (c == NULL) {
return status;
}
- req->method_ = FromMethodId(methodId);
- req->num_args_ = numArgs;
- // TODO: check that the argument list is valid.
- req->arg_array_ = argArray;
+
+ Method* m = FromMethodId(methodId);
+ if (m->IsStatic() != (receiver == NULL)) {
+ return JDWP::ERR_INVALID_METHODID;
+ }
+ if (m->IsStatic()) {
+ if (m->GetDeclaringClass() != c) {
+ return JDWP::ERR_INVALID_METHODID;
+ }
+ } else {
+ if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
+ return JDWP::ERR_INVALID_METHODID;
+ }
+ }
+
+ // Check the argument list matches the method.
+ MethodHelper mh(m);
+ if (mh.GetShortyLength() - 1 != arg_count) {
+ return JDWP::ERR_ILLEGAL_ARGUMENT;
+ }
+ const char* shorty = mh.GetShorty();
+ for (size_t i = 0; i < arg_count; ++i) {
+ if (shorty[i + 1] != JdwpTagToShortyChar(arg_types[i])) {
+ return JDWP::ERR_ILLEGAL_ARGUMENT;
+ }
+ }
+
+ req->receiver_ = receiver;
+ req->thread_ = thread;
+ req->class_ = c;
+ req->method_ = m;
+ req->arg_count_ = arg_count;
+ req->arg_values_ = arg_values;
req->options_ = options;
req->invoke_needed_ = true;
}
@@ -2124,16 +2184,20 @@
// Translate the method through the vtable, unless the debugger wants to suppress it.
Method* m = pReq->method_;
- VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m);
if ((pReq->options_ & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver_ != NULL) {
- m = pReq->class_->FindVirtualMethodForVirtualOrInterface(pReq->method_);
- VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m);
+ Method* actual_method = pReq->class_->FindVirtualMethodForVirtualOrInterface(pReq->method_);
+ if (actual_method != m) {
+ VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m) << " to " << PrettyMethod(actual_method);
+ m = actual_method;
+ }
}
+ VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m);
CHECK(m != NULL);
CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
- pReq->result_value = InvokeWithJValues(self, pReq->receiver_, m, reinterpret_cast<JValue*>(pReq->arg_array_));
+ LOG(INFO) << "self=" << self << " pReq->receiver_=" << pReq->receiver_ << " m=" << m << " #" << pReq->arg_count_ << " " << pReq->arg_values_;
+ pReq->result_value = InvokeWithJValues(self, pReq->receiver_, m, reinterpret_cast<JValue*>(pReq->arg_values_));
pReq->exception = gRegistry->Add(self->GetException());
pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty());