Return values for locals, so "locals" can show them.

Also implement a couple of other JDWP commands so jdb can show the length
of arrays and the value of strings.

Change-Id: Ib2a4dc2ee784ee10bdb924e91b0150aa8a96845d
diff --git a/src/context.h b/src/context.h
index b4a8b65..75eb8ca 100644
--- a/src/context.h
+++ b/src/context.h
@@ -3,6 +3,7 @@
 #ifndef ART_SRC_CONTEXT_H_
 #define ART_SRC_CONTEXT_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
 namespace art {
@@ -34,6 +35,42 @@
   virtual void DoLongJump() = 0;
 };
 
+class VmapTable {
+ public:
+  VmapTable(const uint16_t* table) : table_(table) {
+  }
+
+  uint16_t operator[](size_t i) const {
+    return table_[i + 1];
+  }
+
+  size_t size() const {
+    return table_[0];
+  }
+
+  // Is register 'reg' in the context or on the stack?
+  bool IsInContext(size_t reg, uint32_t& vmap_offset) const {
+    vmap_offset = 0xEBAD0FF5;
+    // TODO: take advantage of the registers being ordered
+    for (size_t i = 0; i < size(); ++i) {
+      // Stop if we find what we are are looking for...
+      if (table_[i + 1] == reg) {
+        vmap_offset = i;
+        return true;
+      }
+      // ...or the INVALID_VREG that marks lr.
+      // TODO: x86?
+      if (table_[i + 1] == 0xffff) {
+        break;
+      }
+    }
+    return false;
+  }
+
+ private:
+  const uint16_t* table_;
+};
+
 }  // namespace art
 
 #endif  // ART_SRC_CONTEXT_H_
diff --git a/src/debugger.cc b/src/debugger.cc
index 1ad8cc2..976fb89 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -21,6 +21,7 @@
 #include <set>
 
 #include "class_linker.h"
+#include "context.h"
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "stack_indirect_reference_table.h"
@@ -551,8 +552,9 @@
 }
 
 int Dbg::GetArrayLength(JDWP::ObjectId arrayId) {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  Object* o = gRegistry->Get<Object*>(arrayId);
+  Array* a = o->AsArray();
+  return a->GetLength();
 }
 
 uint8_t Dbg::GetArrayElementTag(JDWP::ObjectId arrayId) {
@@ -689,17 +691,14 @@
 /*
  * Reverse Eclipse hack.
  */
-static uint16_t DemangleSlot(uint16_t slot, Method** sp) {
-  int newSlot = slot;
+static uint16_t DemangleSlot(uint16_t slot, Frame& f) {
   if (slot == kEclipseWorkaroundSlot) {
-    newSlot = 0;
+    return 0;
   } else if (slot == 0) {
-    Frame f;
-    f.SetSP(sp);
     Method* m = f.GetMethod();
-    newSlot = m->NumRegisters() - m->NumIns();
+    return m->NumRegisters() - m->NumIns();
   }
-  return newSlot;
+  return slot;
 }
 
 void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply) {
@@ -811,10 +810,10 @@
     static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char *name, const char *descriptor, const char *signature) {
       DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
 
-      slot = MangleSlot(slot, name);
-
       LOG(VERBOSE) << StringPrintf("    %2d: %d(%d) '%s' '%s' '%s' slot=%d", pContext->numItems, startAddress, endAddress - startAddress, name, descriptor, signature, slot);
 
+      slot = MangleSlot(slot, name);
+
       expandBufAdd8BE(pContext->pReply, startAddress);
       expandBufAddUtf8String(pContext->pReply, name);
       expandBufAddUtf8String(pContext->pReply, descriptor);
@@ -875,9 +874,9 @@
   UNIMPLEMENTED(FATAL);
 }
 
-char* Dbg::StringToUtf8(JDWP::ObjectId strId) {
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+std::string Dbg::StringToUtf8(JDWP::ObjectId strId) {
+  String* s = gRegistry->Get<String*>(strId);
+  return s->ToModifiedUtf8();
 }
 
 Thread* DecodeThread(JDWP::ObjectId threadId) {
@@ -1119,61 +1118,70 @@
 
 void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen) {
   Method** sp = reinterpret_cast<Method**>(frameId);
-  slot = DemangleSlot(slot, sp);
+  Frame f;
+  f.SetSP(sp);
+  uint16_t reg = DemangleSlot(slot, f);
+  Method* m = f.GetMethod();
+
+  const VmapTable vmap_table(m->GetVmapTableRaw());
+  uint32_t vmap_offset;
+  if (vmap_table.IsInContext(reg, vmap_offset)) {
+    UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
+  }
 
   switch (tag) {
   case JDWP::JT_BOOLEAN:
     {
-      UNIMPLEMENTED(WARNING) << "get boolean local " << slot;
       CHECK_EQ(expectedLen, 1U);
-      uint32_t intVal = 0; // framePtr[slot];
+      uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
+      LOG(WARNING) << "get boolean local " << reg << " = " << intVal;
       JDWP::Set1(buf+1, intVal != 0);
     }
     break;
   case JDWP::JT_BYTE:
     {
-      UNIMPLEMENTED(WARNING) << "get byte local " << slot;
       CHECK_EQ(expectedLen, 1U);
-      uint32_t intVal = 0; // framePtr[slot];
+      uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
+      LOG(WARNING) << "get byte local " << reg << " = " << intVal;
       JDWP::Set1(buf+1, intVal);
     }
     break;
   case JDWP::JT_SHORT:
   case JDWP::JT_CHAR:
     {
-      UNIMPLEMENTED(WARNING) << "get 16-bit local " << slot;
       CHECK_EQ(expectedLen, 2U);
-      uint32_t intVal = 0; // framePtr[slot];
+      uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
+      LOG(WARNING) << "get short/char local " << reg << " = " << intVal;
       JDWP::Set2BE(buf+1, intVal);
     }
     break;
   case JDWP::JT_INT:
   case JDWP::JT_FLOAT:
     {
-      UNIMPLEMENTED(WARNING) << "get 32-bit local " << slot;
       CHECK_EQ(expectedLen, 4U);
-      uint32_t intVal = 0; // framePtr[slot];
+      uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
+      LOG(WARNING) << "get int/float local " << reg << " = " << intVal;
       JDWP::Set4BE(buf+1, intVal);
     }
     break;
   case JDWP::JT_ARRAY:
     {
-      UNIMPLEMENTED(WARNING) << "get array local " << slot;
       CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
-      Object* o = NULL; // (Object*)framePtr[slot];
+      Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
+      LOG(WARNING) << "get array local " << reg << " = " << o;
       if (o != NULL && !Heap::IsHeapAddress(o)) {
-        LOG(FATAL) << "slot " << slot << " expected to hold array: " << o;
+        LOG(FATAL) << "reg " << reg << " expected to hold array: " << o;
       }
       JDWP::SetObjectId(buf+1, gRegistry->Add(o));
     }
     break;
   case JDWP::JT_OBJECT:
     {
-      UNIMPLEMENTED(WARNING) << "get object local " << slot;
       CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
-      Object* o = NULL; // (Object*)framePtr[slot];
+      Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
+      LOG(WARNING) << "get object local " << reg << " = " << o;
       if (o != NULL && !Heap::IsHeapAddress(o)) {
-        LOG(FATAL) << "slot " << slot << " expected to hold object: " << o;
+        LOG(FATAL) << "reg " << reg << " expected to hold object: " << o;
       }
       tag = TagFromObject(o);
       JDWP::SetObjectId(buf+1, gRegistry->Add(o));
@@ -1182,9 +1190,9 @@
   case JDWP::JT_DOUBLE:
   case JDWP::JT_LONG:
     {
-      UNIMPLEMENTED(WARNING) << "get 64-bit local " << slot;
+      UNIMPLEMENTED(WARNING) << "get 64-bit local " << reg;
       CHECK_EQ(expectedLen, 8U);
-      uint64_t longVal = 0; // memcpy(&longVal, &framePtr[slot], 8);
+      uint64_t longVal = 0; // memcpy(&longVal, &framePtr[reg], 8);
       JDWP::Set8BE(buf+1, longVal);
     }
     break;
diff --git a/src/debugger.h b/src/debugger.h
index 538ebba..ec20458 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -171,7 +171,7 @@
   static void GetStaticFieldValue(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply);
   static void SetStaticFieldValue(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId, uint64_t rawValue, int width);
 
-  static char* StringToUtf8(JDWP::ObjectId strId);
+  static std::string StringToUtf8(JDWP::ObjectId strId);
 
   /*
    * Thread, ThreadGroup, Frame
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 3d00930..c0a44e0 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -157,9 +157,7 @@
     /* show detailed debug output */
     if (resultTag == JT_STRING && exceptObjId == 0) {
       if (resultValue != 0) {
-        char* str = Dbg::StringToUtf8(resultValue);
-        LOG(VERBOSE) << StringPrintf("      string '%s'", str);
-        free(str);
+        LOG(VERBOSE) << "      string '" << Dbg::StringToUtf8(resultValue) << "'";
       } else {
         LOG(VERBOSE) << "      string (null)";
       }
@@ -875,12 +873,11 @@
  */
 static JdwpError handleSR_Value(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
   ObjectId stringObject = ReadObjectId(&buf);
-  char* str = Dbg::StringToUtf8(stringObject);
+  std::string str(Dbg::StringToUtf8(stringObject));
 
-  LOG(VERBOSE) << StringPrintf("  Req for str %llx --> '%s'", stringObject, str);
+  LOG(VERBOSE) << StringPrintf("  Req for str %llx --> '%s'", stringObject, str.c_str());
 
-  expandBufAddUtf8String(pReply, str);
-  free(str);
+  expandBufAddUtf8String(pReply, str.c_str());
 
   return ERR_NONE;
 }
diff --git a/src/object.h b/src/object.h
index 91fca63..85542d2 100644
--- a/src/object.h
+++ b/src/object.h
@@ -808,22 +808,7 @@
     SetMappingTable(reinterpret_cast<const uint32_t*>(mapping_table_offset));
   }
 
-  const uint16_t* GetVmapTable() const {
-    const uint16_t* vmap = GetVmapTableRaw();
-    if (vmap == NULL) {
-      return vmap;
-    }
-    return vmap + 1;
-  }
-
-  uint16_t GetVmapTableLength() const {
-    const uint16_t* vmap = GetVmapTableRaw();
-    if (vmap == NULL) {
-      return 0;
-    }
-    return *vmap;
-  }
-
+  // Callers should wrap the uint16_t* in a VmapTable instance for convenient access.
   const uint16_t* GetVmapTableRaw() const {
     return GetFieldPtr<const uint16_t*>(OFFSET_OF_OBJECT_MEMBER(Method, vmap_table_), false);
   }
diff --git a/src/thread.cc b/src/thread.cc
index 8dc81c2..b6962f9 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1431,29 +1431,15 @@
       verifier::PcToReferenceMap map(m);
       const uint8_t* reg_bitmap = map.FindBitMap(m->ToDexPC(pc));
       CHECK(reg_bitmap != NULL);
-      const uint16_t* vmap = m->GetVmapTable();
+      const VmapTable vmap_table(m->GetVmapTableRaw());
       // For all dex registers in the bitmap
       size_t num_regs = std::min(map.RegWidth() * 8, static_cast<size_t>(m->NumRegisters()));
       for (size_t reg = 0; reg < num_regs; ++reg) {
         // Does this register hold a reference?
         if (TestBitmap(reg, reg_bitmap)) {
-          // Is the reference in the context or on the stack?
-          bool in_context = false;
-          uint32_t vmap_offset = 0xEBAD0FF5;
-          // TODO: take advantage of the registers being ordered
-          for (int i = 0; i < m->GetVmapTableLength(); i++) {
-            // stop if we find what we are are looking for or the INVALID_VREG that marks lr
-            if (vmap[i] == 0xffff) {
-              break;
-            }
-            if (vmap[i] == reg) {
-              in_context = true;
-              vmap_offset = i;
-              break;
-            }
-          }
+          uint32_t vmap_offset;
           Object* ref;
-          if (in_context) {
+          if (vmap_table.IsInContext(reg, vmap_offset)) {
             // Compute the register we need to load from the context
             uint32_t spill_mask = m->GetCoreSpillMask();
             CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));