Notify the debugger of class preparation.

Change-Id: Ic9863d0cc1176c474df2239a286a01393845d589
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 0dab1ff..e32f8a5 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -9,6 +9,7 @@
 
 #include "casts.h"
 #include "class_loader.h"
+#include "debugger.h"
 #include "dex_cache.h"
 #include "dex_file.h"
 #include "dex_verifier.h"
@@ -1111,6 +1112,20 @@
     return NULL;
   }
   CHECK(klass->IsResolved());
+
+  /*
+   * We send CLASS_PREPARE events to the debugger from here.  The
+   * definition of "preparation" is creating the static fields for a
+   * class and initializing them to the standard default values, but not
+   * executing any code (that comes later, during "initialization").
+   *
+   * We did the static preparation in LinkClass.
+   *
+   * The class has been prepared and resolved but possibly not yet verified
+   * at this point.
+   */
+  Dbg::PostClassPrepare(klass.get());
+
   return klass.get();
 }
 
diff --git a/src/debugger.cc b/src/debugger.cc
index df55f4e..5840655 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -849,7 +849,7 @@
     MethodHelper mh(m);
     expandBufAddMethodId(pReply, ToMethodId(m));
     expandBufAddUtf8String(pReply, mh.GetName());
-    expandBufAddUtf8String(pReply, mh.GetSignature().c_str());
+    expandBufAddUtf8String(pReply, mh.GetSignature());
     if (with_generic) {
       static const char genericSignature[1] = "";
       expandBufAddUtf8String(pReply, genericSignature);
@@ -1436,6 +1436,7 @@
   if (!gDebuggerActive) {
     return;
   }
+
   JDWP::JdwpLocation throw_location;
   SetLocation(throw_location, throwMethod, throwNativePc);
   JDWP::JdwpLocation catch_location;
@@ -1461,7 +1462,16 @@
 }
 
 void Dbg::PostClassPrepare(Class* c) {
-  UNIMPLEMENTED(FATAL);
+  if (!gDebuggerActive) {
+    return;
+  }
+
+  // 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.
+  int state = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
+  JDWP::JdwpTypeTag tag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+  gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), ClassHelper(c).GetDescriptor(), state);
 }
 
 bool Dbg::WatchLocation(const JDWP::JdwpLocation* pLoc) {
@@ -1612,6 +1622,7 @@
     m = pReq->class_->FindVirtualMethodForVirtualOrInterface(pReq->method_);
   }
   CHECK(m != NULL);
+  LOG(VERBOSE) << "ExecuteMethod " << PrettyMethod(m);
 
   CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
 
diff --git a/src/jdwp/jdwp.h b/src/jdwp/jdwp.h
index 36e2657..03c70b0 100644
--- a/src/jdwp/jdwp.h
+++ b/src/jdwp/jdwp.h
@@ -205,7 +205,7 @@
   /*
    * Class has been prepared.
    */
-  bool PostClassPrepare(int tag, RefTypeId refTypeId, const char* signature, int status);
+  bool PostClassPrepare(JdwpTypeTag tag, RefTypeId refTypeId, const std::string& signature, int status);
 
   /*
    * The VM is about to stop.
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index 424d87f..22d3c24 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -1010,7 +1010,7 @@
  * Valid mods:
  *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
  */
-bool JdwpState::PostClassPrepare(int tag, RefTypeId refTypeId, const char* signature, int status) {
+bool JdwpState::PostClassPrepare(JdwpTypeTag tag, RefTypeId refTypeId, const std::string& signature, int status) {
   ModBasket basket;
 
   memset(&basket, 0, sizeof(basket));
@@ -1033,7 +1033,7 @@
     FindMatchingEvents(EK_CLASS_PREPARE, &basket, matchList, &matchCount);
     if (matchCount != 0) {
       LOG(VERBOSE) << "EVENT: " << matchList[0]->eventKind << "(" << matchCount << " total) "
-                   << "thread=" << (void*) basket.threadId << ")";
+                   << "thread=" << (void*) basket.threadId << ") " << signature;
 
       suspendPolicy = scanSuspendPolicy(matchList, matchCount);
       LOG(VERBOSE) << "  suspendPolicy=" << suspendPolicy;
diff --git a/src/jdwp/jdwp_expand_buf.cc b/src/jdwp/jdwp_expand_buf.cc
index 30ebf00..96c2cfe 100644
--- a/src/jdwp/jdwp_expand_buf.cc
+++ b/src/jdwp/jdwp_expand_buf.cc
@@ -166,14 +166,19 @@
  * they can be null-terminated (either they don't have null bytes or they
  * have stored null bytes in a multi-byte encoding).
  */
-void expandBufAddUtf8String(ExpandBuf* pBuf, const char* str) {
-  int strLen = strlen(str);
-
+void expandBufAddUtf8String(ExpandBuf* pBuf, const char* s) {
+  int strLen = strlen(s);
   ensureSpace(pBuf, sizeof(uint32_t) + strLen);
-  SetUtf8String(pBuf->storage + pBuf->curLen, str, strLen);
+  SetUtf8String(pBuf->storage + pBuf->curLen, s, strLen);
   pBuf->curLen += sizeof(uint32_t) + strLen;
 }
 
+void expandBufAddUtf8String(ExpandBuf* pBuf, const std::string& s) {
+  ensureSpace(pBuf, sizeof(uint32_t) + s.size());
+  SetUtf8String(pBuf->storage + pBuf->curLen, s.data(), s.size());
+  pBuf->curLen += sizeof(uint32_t) + s.size();
+}
+
 }  // namespace JDWP
 
 }  // namespace art
diff --git a/src/jdwp/jdwp_expand_buf.h b/src/jdwp/jdwp_expand_buf.h
index 287f05e..55d568a 100644
--- a/src/jdwp/jdwp_expand_buf.h
+++ b/src/jdwp/jdwp_expand_buf.h
@@ -56,7 +56,8 @@
 void expandBufAdd2BE(ExpandBuf* pBuf, uint16_t val);
 void expandBufAdd4BE(ExpandBuf* pBuf, uint32_t val);
 void expandBufAdd8BE(ExpandBuf* pBuf, uint64_t val);
-void expandBufAddUtf8String(ExpandBuf* pBuf, const char* str);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const char* s);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const std::string& s);
 
 }  // namespace JDWP
 
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 238775e..c7db5e9 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -176,7 +176,7 @@
 static JdwpError handleVM_Version(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
   /* text information on runtime version */
   std::string version(StringPrintf("Android Runtime %s", Runtime::Current()->GetVersion()));
-  expandBufAddUtf8String(pReply, version.c_str());
+  expandBufAddUtf8String(pReply, version);
   /* JDWP version numbers */
   expandBufAdd4BE(pReply, 1);        // major
   expandBufAdd4BE(pReply, 5);        // minor
@@ -442,7 +442,7 @@
 
     expandBufAdd1(pReply, refTypeTag);
     expandBufAddRefTypeId(pReply, classRefBuf[i]);
-    expandBufAddUtf8String(pReply, descriptor.c_str());
+    expandBufAddUtf8String(pReply, descriptor);
     expandBufAddUtf8String(pReply, genericSignature);
     expandBufAdd4BE(pReply, status);
   }
@@ -461,7 +461,7 @@
 
   LOG(VERBOSE) << StringPrintf("  Req for signature of refTypeId=0x%llx", refTypeId);
   std::string signature(Dbg::GetSignature(refTypeId));
-  expandBufAddUtf8String(pReply, signature.c_str());
+  expandBufAddUtf8String(pReply, signature);
 
   return ERR_NONE;
 }
@@ -503,7 +503,7 @@
   if (!Dbg::GetSourceFile(refTypeId, source_file)) {
     return ERR_ABSENT_INFORMATION;
   }
-  expandBufAddUtf8String(pReply, source_file.c_str());
+  expandBufAddUtf8String(pReply, source_file);
   return ERR_NONE;
 }
 
@@ -569,7 +569,7 @@
   LOG(VERBOSE) << StringPrintf("  Req for signature of refTypeId=0x%llx", refTypeId);
   std::string signature(Dbg::GetSignature(refTypeId));
   if (signature != NULL) {
-    expandBufAddUtf8String(pReply, signature.c_str());
+    expandBufAddUtf8String(pReply, signature);
   } else {
     LOG(WARNING) << StringPrintf("No signature for refTypeId=0x%llx", refTypeId);
     expandBufAddUtf8String(pReply, "Lunknown;");
@@ -862,7 +862,7 @@
 
   LOG(VERBOSE) << StringPrintf("  Req for str %llx --> '%s'", stringObject, str.c_str());
 
-  expandBufAddUtf8String(pReply, str.c_str());
+  expandBufAddUtf8String(pReply, str);
 
   return ERR_NONE;
 }
@@ -879,7 +879,7 @@
     return ERR_INVALID_THREAD;
   }
   LOG(VERBOSE) << StringPrintf("  Name of thread 0x%llx is \"%s\"", threadId, name.c_str());
-  expandBufAddUtf8String(pReply, name.c_str());
+  expandBufAddUtf8String(pReply, name);
 
   return ERR_NONE;
 }
@@ -1062,7 +1062,7 @@
   ObjectId threadGroupId = ReadObjectId(&buf);
   LOG(VERBOSE) << StringPrintf("  Req for name of threadGroupId=0x%llx", threadGroupId);
 
-  expandBufAddUtf8String(pReply, Dbg::GetThreadGroupName(threadGroupId).c_str());
+  expandBufAddUtf8String(pReply, Dbg::GetThreadGroupName(threadGroupId));
 
   return ERR_NONE;
 }