More JDWP array support.

This lets you examine array elements in jdb.

Change-Id: I74698496311015b4ba8fc5bdad15a21904106316
diff --git a/src/debugger.cc b/src/debugger.cc
index 976fb89..7128fdc 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -144,6 +144,63 @@
 static size_t gAllocRecordHead = 0;
 static size_t gAllocRecordCount = 0;
 
+static JDWP::JdwpTag BasicTagFromDescriptor(const char* descriptor) {
+  // JDWP deliberately uses the descriptor characters' ASCII values for its enum.
+  // Note that by "basic" we mean that we don't get more specific than JT_OBJECT.
+  return static_cast<JDWP::JdwpTag>(descriptor[0]);
+}
+
+static JDWP::JdwpTag TagFromClass(Class* c) {
+  if (c->IsArrayClass()) {
+    return JDWP::JT_ARRAY;
+  }
+
+  if (c->IsStringClass()) {
+    return JDWP::JT_STRING;
+  } else if (c->IsClassClass()) {
+    return JDWP::JT_CLASS_OBJECT;
+#if 0 // TODO
+  } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
+    return JDWP::JT_THREAD;
+  } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
+    return JDWP::JT_THREAD_GROUP;
+  } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
+    return JDWP::JT_CLASS_LOADER;
+#endif
+  } else {
+    return JDWP::JT_OBJECT;
+  }
+}
+
+/*
+ * 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 JDWP::JdwpTag TagFromObject(const Object* o) {
+  return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
+}
+
+static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
+  switch (tag) {
+  case JDWP::JT_BOOLEAN:
+  case JDWP::JT_BYTE:
+  case JDWP::JT_CHAR:
+  case JDWP::JT_FLOAT:
+  case JDWP::JT_DOUBLE:
+  case JDWP::JT_INT:
+  case JDWP::JT_LONG:
+  case JDWP::JT_SHORT:
+  case JDWP::JT_VOID:
+    return true;
+  default:
+    return false;
+  }
+}
+
 /*
  * Handle one of the JDWP name/value pairs.
  *
@@ -517,8 +574,8 @@
 }
 
 uint8_t Dbg::GetObjectTag(JDWP::ObjectId objectId) {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  Object* o = gRegistry->Get<Object*>(objectId);
+  return TagFromObject(o);
 }
 
 size_t Dbg::GetTagWidth(int tag) {
@@ -558,13 +615,55 @@
 }
 
 uint8_t Dbg::GetArrayElementTag(JDWP::ObjectId arrayId) {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  Object* o = gRegistry->Get<Object*>(arrayId);
+  Array* a = o->AsArray();
+  std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
+  JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
+  if (!IsPrimitiveTag(tag)) {
+    tag = TagFromClass(a->GetClass()->GetComponentType());
+  }
+  return tag;
 }
 
-bool Dbg::OutputArray(JDWP::ObjectId arrayId, int firstIndex, int count, JDWP::ExpandBuf* pReply) {
-  UNIMPLEMENTED(FATAL);
-  return false;
+bool Dbg::OutputArray(JDWP::ObjectId arrayId, int offset, int count, JDWP::ExpandBuf* pReply) {
+  Object* o = gRegistry->Get<Object*>(arrayId);
+  Array* a = o->AsArray();
+
+  if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
+    LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
+    return false;
+  }
+
+  std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
+  JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
+
+  if (IsPrimitiveTag(tag)) {
+    size_t width = GetTagWidth(tag);
+    const uint8_t* src = reinterpret_cast<uint8_t*>(a->GetRawData());
+    uint8_t* dst = expandBufAddSpace(pReply, count * width);
+    if (width == 8) {
+      const uint64_t* src8 = reinterpret_cast<const uint64_t*>(src);
+      for (int i = 0; i < count; ++i) JDWP::Write8BE(&dst, src8[offset + i]);
+    } else if (width == 4) {
+      const uint32_t* src4 = reinterpret_cast<const uint32_t*>(src);
+      for (int i = 0; i < count; ++i) JDWP::Write4BE(&dst, src4[offset + i]);
+    } else if (width == 2) {
+      const uint16_t* src2 = reinterpret_cast<const uint16_t*>(src);
+      for (int i = 0; i < count; ++i) JDWP::Write2BE(&dst, src2[offset + i]);
+    } else {
+      memcpy(dst, &src[offset * width], count * width);
+    }
+  } else {
+    ObjectArray<Object>* oa = a->AsObjectArray<Object>();
+    for (int i = 0; i < count; ++i) {
+      Object* element = oa->Get(i);
+      JDWP::JdwpTag specific_tag = (element != NULL) ? TagFromObject(element) : tag;
+      expandBufAdd1(pReply, specific_tag);
+      expandBufAddObjectId(pReply, gRegistry->Add(element));
+    }
+  }
+
+  return true;
 }
 
 bool Dbg::SetArrayElements(JDWP::ObjectId arrayId, int firstIndex, int count, const uint8_t* buf) {
@@ -633,40 +732,6 @@
   return accessFlags;
 }
 
-static JDWP::JdwpTag TagFromClass(Class* c) {
-  if (c->IsArrayClass()) {
-    return JDWP::JT_ARRAY;
-  }
-
-  if (c->IsStringClass()) {
-    return JDWP::JT_STRING;
-  } else if (c->IsClassClass()) {
-    return JDWP::JT_CLASS_OBJECT;
-#if 0 // TODO
-  } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
-    return JDWP::JT_THREAD;
-  } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
-    return JDWP::JT_THREAD_GROUP;
-  } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
-    return JDWP::JT_CLASS_LOADER;
-#endif
-  } else {
-    return JDWP::JT_OBJECT;
-  }
-}
-
-/*
- * 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 JDWP::JdwpTag TagFromObject(const Object* o) {
-  return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
-}
-
 static const uint16_t kEclipseWorkaroundSlot = 1000;
 
 /*
diff --git a/src/jdwp/jdwp_bits.h b/src/jdwp/jdwp_bits.h
index faa3960..344d2de9 100644
--- a/src/jdwp/jdwp_bits.h
+++ b/src/jdwp/jdwp_bits.h
@@ -147,18 +147,26 @@
   *buf = (uint8_t)(val);
 }
 
-// @deprecated
 static inline void Write1BE(uint8_t** dst, uint8_t value) {
   Set1(*dst, value);
   *dst += sizeof(value);
 }
 
-// @deprecated
+static inline void Write2BE(uint8_t** dst, uint16_t value) {
+  Set2BE(*dst, value);
+  *dst += sizeof(value);
+}
+
 static inline void Write4BE(uint8_t** dst, uint32_t value) {
   Set4BE(*dst, value);
   *dst += sizeof(value);
 }
 
+static inline void Write8BE(uint8_t** dst, uint64_t value) {
+  Set8BE(*dst, value);
+  *dst += sizeof(value);
+}
+
 }  // namespace JDWP
 
 }  // namespace art