Make our new NullPointerException detail messages more readable to Java programmers.

Two to-dos to-done.

Change-Id: I0276dd8b9c062e8e9523cb51defed2c3eda77e2b
diff --git a/src/utils.cc b/src/utils.cc
index c9a6e7e..389c280 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -184,6 +184,7 @@
     case 'J': c = "long;"; break;
     case 'S': c = "short;"; break;
     case 'Z': c = "boolean;"; break;
+    case 'V': c = "void;"; break; // Used when decoding return types.
     default: return descriptor;
     }
   }
@@ -227,6 +228,41 @@
   return result;
 }
 
+std::string PrettyArguments(const char* signature) {
+  std::string result;
+  result += '(';
+  CHECK_EQ(*signature, '(');
+  ++signature; // Skip the '('.
+  while (*signature != ')') {
+    size_t argument_length = 0;
+    while (signature[argument_length] == '[') {
+      ++argument_length;
+    }
+    if (signature[argument_length] == 'L') {
+      argument_length = (strchr(signature, ';') - signature + 1);
+    } else {
+      ++argument_length;
+    }
+    std::string argument_descriptor(signature, argument_length);
+    result += PrettyDescriptor(argument_descriptor);
+    if (signature[argument_length] != ')') {
+      result += ", ";
+    }
+    signature += argument_length;
+  }
+  CHECK_EQ(*signature, ')');
+  ++signature; // Skip the ')'.
+  result += ')';
+  return result;
+}
+
+std::string PrettyReturnType(const char* signature) {
+  const char* return_type = strchr(signature, ')');
+  CHECK(return_type != NULL);
+  ++return_type; // Skip ')'.
+  return PrettyDescriptor(return_type);
+}
+
 std::string PrettyMethod(const Method* m, bool with_signature) {
   if (m == NULL) {
     return "null";
@@ -236,9 +272,8 @@
   result += '.';
   result += mh.GetName();
   if (with_signature) {
-    // TODO: iterate over the signature's elements and pass them all to
-    // PrettyDescriptor? We'd need to pull out the return type specially, too.
-    result += mh.GetSignature();
+    std::string signature(mh.GetSignature());
+    result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str());
   }
   return result;
 }
@@ -249,9 +284,8 @@
   result += '.';
   result += dex_file.GetMethodName(method_id);
   if (with_signature) {
-    // TODO: iterate over the signature's elements and pass them all to
-    // PrettyDescriptor? We'd need to pull out the return type specially, too.
-    result += dex_file.GetMethodSignature(method_id);
+    std::string signature(dex_file.GetMethodSignature(method_id));
+    result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str());
   }
   return result;
 }
diff --git a/src/utils_test.cc b/src/utils_test.cc
index 31fccc5..3228bb6 100644
--- a/src/utils_test.cc
+++ b/src/utils_test.cc
@@ -20,54 +20,67 @@
 
 namespace art {
 
+std::string PrettyArguments(const char* signature);
+std::string PrettyReturnType(const char* signature);
+
 class UtilsTest : public CommonTest {
 };
 
-#define EXPECT_DESCRIPTOR(pretty_descriptor, descriptor) \
-  do { \
-    SirtRef<String> s(String::AllocFromModifiedUtf8(descriptor)); \
-    std::string result(PrettyDescriptor(s.get())); \
-    EXPECT_EQ(pretty_descriptor, result); \
-  } while (false)
-
 TEST_F(UtilsTest, PrettyDescriptor_ArrayReferences) {
-  EXPECT_DESCRIPTOR("java.lang.Class[]", "[Ljava/lang/Class;");
-  EXPECT_DESCRIPTOR("java.lang.Class[][]", "[[Ljava/lang/Class;");
+  EXPECT_EQ("java.lang.Class[]", PrettyDescriptor("[Ljava/lang/Class;"));
+  EXPECT_EQ("java.lang.Class[][]", PrettyDescriptor("[[Ljava/lang/Class;"));
 }
 
 TEST_F(UtilsTest, PrettyDescriptor_ScalarReferences) {
-  EXPECT_DESCRIPTOR("java.lang.String", "Ljava.lang.String;");
-  EXPECT_DESCRIPTOR("java.lang.String", "Ljava/lang/String;");
+  EXPECT_EQ("java.lang.String", PrettyDescriptor("Ljava.lang.String;"));
+  EXPECT_EQ("java.lang.String", PrettyDescriptor("Ljava/lang/String;"));
 }
 
 TEST_F(UtilsTest, PrettyDescriptor_PrimitiveArrays) {
-  EXPECT_DESCRIPTOR("boolean[]", "[Z");
-  EXPECT_DESCRIPTOR("boolean[][]", "[[Z");
-  EXPECT_DESCRIPTOR("byte[]", "[B");
-  EXPECT_DESCRIPTOR("byte[][]", "[[B");
-  EXPECT_DESCRIPTOR("char[]", "[C");
-  EXPECT_DESCRIPTOR("char[][]", "[[C");
-  EXPECT_DESCRIPTOR("double[]", "[D");
-  EXPECT_DESCRIPTOR("double[][]", "[[D");
-  EXPECT_DESCRIPTOR("float[]", "[F");
-  EXPECT_DESCRIPTOR("float[][]", "[[F");
-  EXPECT_DESCRIPTOR("int[]", "[I");
-  EXPECT_DESCRIPTOR("int[][]", "[[I");
-  EXPECT_DESCRIPTOR("long[]", "[J");
-  EXPECT_DESCRIPTOR("long[][]", "[[J");
-  EXPECT_DESCRIPTOR("short[]", "[S");
-  EXPECT_DESCRIPTOR("short[][]", "[[S");
+  EXPECT_EQ("boolean[]", PrettyDescriptor("[Z"));
+  EXPECT_EQ("boolean[][]", PrettyDescriptor("[[Z"));
+  EXPECT_EQ("byte[]", PrettyDescriptor("[B"));
+  EXPECT_EQ("byte[][]", PrettyDescriptor("[[B"));
+  EXPECT_EQ("char[]", PrettyDescriptor("[C"));
+  EXPECT_EQ("char[][]", PrettyDescriptor("[[C"));
+  EXPECT_EQ("double[]", PrettyDescriptor("[D"));
+  EXPECT_EQ("double[][]", PrettyDescriptor("[[D"));
+  EXPECT_EQ("float[]", PrettyDescriptor("[F"));
+  EXPECT_EQ("float[][]", PrettyDescriptor("[[F"));
+  EXPECT_EQ("int[]", PrettyDescriptor("[I"));
+  EXPECT_EQ("int[][]", PrettyDescriptor("[[I"));
+  EXPECT_EQ("long[]", PrettyDescriptor("[J"));
+  EXPECT_EQ("long[][]", PrettyDescriptor("[[J"));
+  EXPECT_EQ("short[]", PrettyDescriptor("[S"));
+  EXPECT_EQ("short[][]", PrettyDescriptor("[[S"));
 }
 
 TEST_F(UtilsTest, PrettyDescriptor_PrimitiveScalars) {
-  EXPECT_DESCRIPTOR("boolean", "Z");
-  EXPECT_DESCRIPTOR("byte", "B");
-  EXPECT_DESCRIPTOR("char", "C");
-  EXPECT_DESCRIPTOR("double", "D");
-  EXPECT_DESCRIPTOR("float", "F");
-  EXPECT_DESCRIPTOR("int", "I");
-  EXPECT_DESCRIPTOR("long", "J");
-  EXPECT_DESCRIPTOR("short", "S");
+  EXPECT_EQ("boolean", PrettyDescriptor("Z"));
+  EXPECT_EQ("byte", PrettyDescriptor("B"));
+  EXPECT_EQ("char", PrettyDescriptor("C"));
+  EXPECT_EQ("double", PrettyDescriptor("D"));
+  EXPECT_EQ("float", PrettyDescriptor("F"));
+  EXPECT_EQ("int", PrettyDescriptor("I"));
+  EXPECT_EQ("long", PrettyDescriptor("J"));
+  EXPECT_EQ("short", PrettyDescriptor("S"));
+}
+
+TEST_F(UtilsTest, PrettyArguments) {
+  EXPECT_EQ("()", PrettyArguments("()V"));
+  EXPECT_EQ("(int)", PrettyArguments("(I)V"));
+  EXPECT_EQ("(int, int)", PrettyArguments("(II)V"));
+  EXPECT_EQ("(int, int, int[][])", PrettyArguments("(II[[I)V"));
+  EXPECT_EQ("(int, int, int[][], java.lang.Poop)", PrettyArguments("(II[[ILjava/lang/Poop;)V"));
+  EXPECT_EQ("(int, int, int[][], java.lang.Poop, java.lang.Poop[][])", PrettyArguments("(II[[ILjava/lang/Poop;[[Ljava/lang/Poop;)V"));
+}
+
+TEST_F(UtilsTest, PrettyReturnType) {
+  EXPECT_EQ("void", PrettyReturnType("()V"));
+  EXPECT_EQ("int", PrettyReturnType("()I"));
+  EXPECT_EQ("int[][]", PrettyReturnType("()[[I"));
+  EXPECT_EQ("java.lang.Poop", PrettyReturnType("()Ljava/lang/Poop;"));
+  EXPECT_EQ("java.lang.Poop[][]", PrettyReturnType("()[[Ljava/lang/Poop;"));
 }
 
 TEST_F(UtilsTest, PrettyTypeOf) {
diff --git a/test/034-call-null/expected.txt b/test/034-call-null/expected.txt
index 7ed2215..343226f 100644
--- a/test/034-call-null/expected.txt
+++ b/test/034-call-null/expected.txt
@@ -1,2 +1,2 @@
-java.lang.NullPointerException: Attempt to invoke direct method 'Main.doStuff()V' on a null object reference
+java.lang.NullPointerException: Attempt to invoke direct method 'void Main.doStuff(int, int[][], java.lang.String, java.lang.String[][])' on a null object reference
 	at Main.main(Main.java:26)
diff --git a/test/034-call-null/src/Main.java b/test/034-call-null/src/Main.java
index 5b60c71..cc89bb4 100644
--- a/test/034-call-null/src/Main.java
+++ b/test/034-call-null/src/Main.java
@@ -17,12 +17,12 @@
 public class Main {
     int mFoo = 27;
 
-    private void doStuff() {
+    private void doStuff(int i, int[][] is, String s, String[][] ss) {
         System.out.println("mFoo is " + mFoo);
     }
 
     public static void main(String[] args) {
         Main instance = null;
-        instance.doStuff();
+        instance.doStuff(0, null, null, null);
     }
 }
diff --git a/test/038-inner-null/expected.txt b/test/038-inner-null/expected.txt
index 37f984c..ba411f0 100644
--- a/test/038-inner-null/expected.txt
+++ b/test/038-inner-null/expected.txt
@@ -1,4 +1,4 @@
 new Special()
-java.lang.NullPointerException: Attempt to invoke virtual method 'Main$Blort.repaint()V' on a null object reference
+java.lang.NullPointerException: Attempt to invoke virtual method 'void Main$Blort.repaint()' on a null object reference
 	at Main$Special.callInner(Main.java:31)
 	at Main.main(Main.java:20)