Materialize method descriptors when instantiating method objects.

Previously, when comparing method descriptors, one had to piecewise
compare the leaves of a method structure for equality.

With this change a flat method descriptor is computed and associated
to an object.  This will simplify comparisons for descriptor equality
used during verification and reflective method retrieval.

Change-Id: I91e5ac76fb3816a36716b34fe43d05cd7364897b
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 09454d8..78e2c31 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -427,8 +427,14 @@
   const DexFile::MethodId& method_id = dex_file.GetMethodId(src.method_idx_);
   dst->klass_ = klass;
   dst->java_name_ = ResolveString(klass, method_id.name_idx_);
+  {
+    int32_t utf16_length;
+    scoped_ptr<char> utf8(dex_file.CreateMethodDescriptor(method_id.proto_idx_,
+                                                          &utf16_length));
+    dst->descriptor_ = String::AllocFromModifiedUtf8(utf16_length, utf8.get());
+  }
   dst->proto_idx_ = method_id.proto_idx_;
-  dst->shorty_.set(dex_file.GetShorty(method_id.proto_idx_));
+  dst->shorty_ = dex_file.GetShorty(method_id.proto_idx_);
   dst->access_flags_ = src.access_flags_;
 
   // TODO: check for finalize method
@@ -821,7 +827,7 @@
 
   InitializeStaticFields(klass);
 
-  Method* clinit = klass->FindDirectMethodLocally("<clinit>", "()V");
+  Method* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
   if (clinit != NULL) {
   } else {
     // JValue unused;
diff --git a/src/common_test.h b/src/common_test.h
index f5d7059..685a06c 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -178,6 +178,25 @@
   "AAAACQAAAAwBAAAGAAAAAgAAAFQBAAABIAAAAgAAAJQBAAABEAAABAAAAMABAAACIAAAEwAAAOIB"
   "AAADIAAAAgAAAHUCAAAAIAAAAgAAAH8CAAAAEAAAAQAAALACAAA=";
 
+// class CreateMethodDescriptor {
+//     Float m1(int a, double b, long c, Object d) { return null; }
+//     CreateMethodDescriptor m2(boolean x, short y, char z) { return null; }
+// }
+static const char kCreateMethodDescriptorDex[] =
+  "ZGV4CjAzNQBSU7aKdNXwH+uOpti/mvZ4/Dk8wM8VtNbgAgAAcAAAAHhWNBIAAAAAAAAAAEwCAAAQ"
+  "AAAAcAAAAAoAAACwAAAAAwAAANgAAAAAAAAAAAAAAAQAAAD8AAAAAQAAABwBAACkAQAAPAEAAJQB"
+  "AACcAQAAnwEAALwBAAC/AQAAwgEAAMUBAADfAQAA5gEAAOwBAAD/AQAAEwIAABYCAAAZAgAAHAIA"
+  "ACACAAABAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAKAAAACwAAAAwAAAANAAAACAAAAAQAAAB8AQAA"
+  "BwAAAAUAAACIAQAADAAAAAgAAAAAAAAABAACAAAAAAAEAAEADgAAAAQAAAAPAAAABgACAAAAAAAE"
+  "AAAAAAAAAAYAAAAAAAAAAgAAAAAAAAA6AgAAAAAAAAEAAQABAAAAJAIAAAQAAABwEAMAAAAOAAgA"
+  "BwAAAAAAKQIAAAIAAAASABEABQAEAAAAAAAyAgAAAgAAABIAEQADAAAACQAHAAAAAAAEAAAAAgAB"
+  "AAMABgAGPGluaXQ+AAFDABtDcmVhdGVNZXRob2REZXNjcmlwdG9yLmphdmEAAUQAAUkAAUoAGExD"
+  "cmVhdGVNZXRob2REZXNjcmlwdG9yOwAFTElESkwABExaU0MAEUxqYXZhL2xhbmcvRmxvYXQ7ABJM"
+  "amF2YS9sYW5nL09iamVjdDsAAVMAAVYAAVoAAm0xAAJtMgABAAcOAAIEAAAAAAcOAAMDAAAABw4A"
+  "AAABAgCAgAS8AgEA1AIBAOgCDAAAAAAAAAABAAAAAAAAAAEAAAAQAAAAcAAAAAIAAAAKAAAAsAAA"
+  "AAMAAAADAAAA2AAAAAUAAAAEAAAA/AAAAAYAAAABAAAAHAEAAAEgAAADAAAAPAEAAAEQAAACAAAA"
+  "fAEAAAIgAAAQAAAAlAEAAAMgAAADAAAAJAIAAAAgAAABAAAAOgIAAAAQAAABAAAATAIAAA==";
+
 static inline DexFile* OpenDexFileBase64(const char* base64) {
   CHECK(base64 != NULL);
   size_t length;
diff --git a/src/dex_file.cc b/src/dex_file.cc
index b61babf..0cf98b1 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -342,6 +342,40 @@
   }
 }
 
+// Materializes the method descriptor for a method prototype.  Method
+// descriptors are not stored directly in the dex file.  Instead, one
+// must assemble the descriptor from references in the prototype.
+char* DexFile::CreateMethodDescriptor(uint32_t proto_idx,
+                                      int32_t* unicode_length) const {
+  CHECK(unicode_length != NULL);
+  const ProtoId& proto_id = GetProtoId(proto_idx);
+  std::string descriptor;
+  descriptor.push_back('(');
+  const TypeList* type_list = GetProtoParameters(proto_id);
+  size_t parameter_length = 0;
+  if (type_list != NULL) {
+    // A non-zero number of arguments.  Append the type names.
+    for (size_t i = 0; i < type_list->Size(); ++i) {
+      const TypeItem& type_item = type_list->GetTypeItem(i);
+      uint32_t type_idx = type_item.type_idx_;
+      int32_t type_length;
+      const char* name = dexStringByTypeIdx(type_idx, &type_length);
+      parameter_length += type_length;
+      descriptor.append(name);
+    }
+  }
+  descriptor.push_back(')');
+  uint32_t return_type_idx = proto_id.return_type_idx_;
+  int32_t return_type_length;
+  const char* name = dexStringByTypeIdx(return_type_idx, &return_type_length);
+  descriptor.append(name);
+  // TODO: should this just return a std::string?
+  scoped_ptr<char> c_string(new char[descriptor.size() + 1]);
+  strcpy(c_string.get(), descriptor.c_str());
+  *unicode_length = parameter_length + return_type_length + 2;  // 2 for ( and )
+  return c_string.release();
+}
+
 // Read a signed integer.  "zwidth" is the zero-based byte count.
 static int32_t ReadSignedInt(const byte* ptr, int zwidth)
 {
diff --git a/src/dex_file.h b/src/dex_file.h
index e8f415d..6cc7b10 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -357,6 +357,9 @@
     }
   }
 
+  char* CreateMethodDescriptor(uint32_t proto_idx,
+                               int32_t* unicode_length) const;
+
   const byte* GetEncodedArray(const ClassDef& class_def) const {
     if (class_def.static_values_off_ == 0) {
       return 0;
@@ -376,20 +379,35 @@
 
   // Returns a pointer to the UTF-8 string data referred to by the
   // given string_id.
-  const char* GetStringData(const StringId& string_id) const {
+  const char* GetStringData(const StringId& string_id, int32_t* length) const {
+    CHECK(length != NULL);
     const byte* ptr = base_ + string_id.string_data_off_;
-    // Skip the uleb128 length.
-    while (*(ptr++) > 0x7f) /* empty */ ;
+    *length = DecodeUnsignedLeb128(&ptr);
     return reinterpret_cast<const char*>(ptr);
   }
 
+  const char* GetStringData(const StringId& string_id) const {
+    int32_t length;
+    return GetStringData(string_id, &length);
+  }
+
   // return the UTF-8 encoded string with the specified string_id index
-  const char* dexStringById(uint32_t idx) const {
+  const char* dexStringById(uint32_t idx, int32_t* unicode_length) const {
     const StringId& string_id = GetStringId(idx);
-    return GetStringData(string_id);
+    return GetStringData(string_id, unicode_length);
+  }
+
+  const char* dexStringById(uint32_t idx) const {
+    int32_t unicode_length;
+    return dexStringById(idx, &unicode_length);
   }
 
   // Get the descriptor string associated with a given type index.
+  const char* dexStringByTypeIdx(uint32_t idx, int32_t* unicode_length) const {
+    const TypeId& type_id = GetTypeId(idx);
+    return dexStringById(type_id.descriptor_idx_, unicode_length);
+  }
+
   const char* dexStringByTypeIdx(uint32_t idx) const {
     const TypeId& type_id = GetTypeId(idx);
     return dexStringById(type_id.descriptor_idx_);
diff --git a/src/dex_file_test.cc b/src/dex_file_test.cc
index a6b5bd5..33c43c9 100644
--- a/src/dex_file_test.cc
+++ b/src/dex_file_test.cc
@@ -54,4 +54,70 @@
   EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c1));
 }
 
+TEST(DexFileTest, CreateMethodDescriptor) {
+  scoped_ptr<DexFile> raw(OpenDexFileBase64(kCreateMethodDescriptorDex));
+  ASSERT_TRUE(raw != NULL);
+  EXPECT_EQ(1U, raw->NumClassDefs());
+
+  const DexFile::ClassDef& class_def = raw->GetClassDef(0);
+  ASSERT_STREQ("LCreateMethodDescriptor;", raw->GetClassDescriptor(class_def));
+
+  const byte* class_data = raw->GetClassData(class_def);
+  ASSERT_TRUE(class_data != NULL);
+  DexFile::ClassDataHeader header = raw->ReadClassDataHeader(&class_data);
+
+  EXPECT_EQ(1u, header.direct_methods_size_);
+
+  // Check the descriptor for the static initializer.
+  {
+    uint32_t last_idx = 0;
+    ASSERT_EQ(1U, header.direct_methods_size_);
+    DexFile::Method method;
+    raw->dexReadClassDataMethod(&class_data, &method, &last_idx);
+    const DexFile::MethodId& method_id = raw->GetMethodId(method.method_idx_);
+    uint32_t proto_idx = method_id.proto_idx_;
+    const char* name = raw->dexStringById(method_id.name_idx_);
+    ASSERT_STREQ("<init>", name);
+    int32_t length;
+    scoped_ptr<const char> descriptor(raw->CreateMethodDescriptor(proto_idx,
+                                                                  &length));
+    ASSERT_STREQ("()V", descriptor.get());
+  }
+
+  // Check both virtual methods.
+  ASSERT_EQ(2U, header.virtual_methods_size_);
+  uint32_t last_idx = 0;
+
+  {
+    DexFile::Method method;
+    raw->dexReadClassDataMethod(&class_data, &method, &last_idx);
+    const DexFile::MethodId& method_id = raw->GetMethodId(method.method_idx_);
+
+    const char* name = raw->dexStringById(method_id.name_idx_);
+    ASSERT_STREQ("m1", name);
+
+    uint32_t proto_idx = method_id.proto_idx_;
+    int32_t length;
+    scoped_ptr<const char> descriptor(raw->CreateMethodDescriptor(proto_idx,
+                                                                  &length));
+    ASSERT_STREQ("(IDJLjava/lang/Object;)Ljava/lang/Float;", descriptor.get());
+  }
+
+  {
+    DexFile::Method method;
+    raw->dexReadClassDataMethod(&class_data, &method, &last_idx);
+    const DexFile::MethodId& method_id = raw->GetMethodId(method.method_idx_);
+
+    const char* name = raw->dexStringById(method_id.name_idx_);
+    ASSERT_STREQ("m2", name);
+
+    uint32_t proto_idx = method_id.proto_idx_;
+    int32_t length;
+    scoped_ptr<const char> descriptor(raw->CreateMethodDescriptor(proto_idx,
+                                                                  &length));
+    ASSERT_STREQ("(ZSC)LCreateMethodDescriptor;", descriptor.get());
+  }
+
+}
+
 }  // namespace art
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 7dcfdc2..3041927 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -205,8 +205,12 @@
 TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindVirtualMethod(String::AllocFromAscii("foo"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindVirtualMethod("foo", "()V");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -230,8 +234,12 @@
 TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindVirtualMethod(String::AllocFromAscii("fooI"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindVirtualMethod("fooI", "(I)I");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -257,8 +265,12 @@
 TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindVirtualMethod(String::AllocFromAscii("fooII"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindVirtualMethod("fooII", "(II)I");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -287,8 +299,12 @@
 TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindVirtualMethod(String::AllocFromAscii("fooDD"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindVirtualMethod("fooDD", "(DD)D");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -316,8 +332,14 @@
 TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindVirtualMethod(String::AllocFromAscii("fooIOO"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindVirtualMethod(
+      "fooIOO",
+      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -371,8 +393,14 @@
 TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindDirectMethod(String::AllocFromAscii("fooSIOO"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod(
+      "fooSIOO",
+      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -423,8 +451,14 @@
 TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindDirectMethod(String::AllocFromAscii("fooSSIOO"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod(
+      "fooSSIOO",
+      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -481,8 +515,12 @@
 TEST_F(JniCompilerTest, SuspendCountAcknolewdgement) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindVirtualMethod(String::AllocFromAscii("fooI"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindVirtualMethod("fooI", "(I)I");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
@@ -522,8 +560,12 @@
 TEST_F(JniCompilerTest, ExceptionHandling) {
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
   class_linker_->RegisterDexFile(dex.get());
+
   Class* klass = class_linker_->FindClass("LMyClass;", NULL, dex.get());
-  Method* method = klass->FindVirtualMethod(String::AllocFromAscii("foo"));
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindVirtualMethod("foo", "()V");
+  ASSERT_TRUE(method != NULL);
 
   Assembler jni_asm;
   JniCompiler jni_compiler;
diff --git a/src/object.cc b/src/object.cc
index 96c5116..98b6e43 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -157,33 +157,50 @@
   return ShortyCharToSize(shorty_[0]);
 }
 
-Method* Class::FindDirectMethod(const String* name) const {
-  Method* result = NULL;
-  for (size_t i = 0; i < NumDirectMethods(); i++) {
+Method* Class::FindDeclaredDirectMethod(const StringPiece& name,
+                                        const StringPiece& descriptor) {
+  for (size_t i = 0; i < NumDirectMethods(); ++i) {
     Method* method = GetDirectMethod(i);
-    if (String::Equals(method->GetName(), name)) {
-      result = method;
-      break;
+    if (String::EqualsUtf8(method->GetName(), name.data()) &&
+        String::EqualsUtf8(method->GetDescriptor(), descriptor.data())) {
+      return method;
     }
   }
-  return result;
+  return NULL;
 }
 
-Method* Class::FindVirtualMethod(const String* name) const {
-  Method* result = NULL;
-  for (size_t i = 0; i < NumVirtualMethods(); i++) {
+Method* Class::FindDirectMethod(const StringPiece& name,
+                                const StringPiece& descriptor) {
+  for (Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+    Method* method = klass->FindDeclaredDirectMethod(name, descriptor);
+    if (method != NULL) {
+      return method;
+    }
+  }
+  return NULL;
+}
+
+Method* Class::FindDeclaredVirtualMethod(const StringPiece& name,
+                                         const StringPiece& descriptor) {
+  for (size_t i = 0; i < NumVirtualMethods(); ++i) {
     Method* method = GetVirtualMethod(i);
-    if (String::Equals(method->GetName(), name)) {
-      result = method;
-      break;
+    if (String::EqualsUtf8(method->GetName(), name.data()) &&
+        String::EqualsUtf8(method->GetDescriptor(), descriptor.data())) {
+      return method;
     }
   }
-  return result;
+  return NULL;
 }
 
-Method* Class::FindDirectMethodLocally(const StringPiece& name,
-                                       const StringPiece& descriptor) const {
-  return NULL;  // TODO
+Method* Class::FindVirtualMethod(const StringPiece& name,
+                                 const StringPiece& descriptor) {
+  for (Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+    Method* method = klass->FindDeclaredVirtualMethod(name, descriptor);
+    if (method != NULL) {
+      return method;
+    }
+  }
+  return NULL;
 }
 
 // TODO: get global references for these
diff --git a/src/object.h b/src/object.h
index 7eede5d..3a9888e 100644
--- a/src/object.h
+++ b/src/object.h
@@ -382,6 +382,10 @@
     return java_name_;
   }
 
+  const String* GetDescriptor() const {
+    return descriptor_;
+  }
+
   Class* GetDeclaringClass() const {
     return declaring_class_;
   }
@@ -545,6 +549,18 @@
   // method name, e.g. "<init>" or "eatLunch"
   StringPiece name_;
 
+  // The method descriptor.  This represents the parameters a method
+  // takes and value it returns.  This string is a list of the type
+  // descriptors for the parameters enclosed in parenthesis followed
+  // by the return type descriptor.  For example, for the method
+  //
+  //   Object mymethod(int i, double d, Thread t)
+  //
+  // the method descriptor would be
+  //
+  //   (IDLjava/lang/Thread;)Ljava/lang/Object;
+  String* descriptor_;
+
   // Method prototype descriptor string (return and argument types).
   uint32_t proto_idx_;
 
@@ -781,6 +797,12 @@
     direct_methods_->Set(i, f);
   }
 
+  Method* FindDeclaredDirectMethod(const StringPiece& name,
+                                   const StringPiece& descriptor);
+
+  Method* FindDirectMethod(const StringPiece& name,
+                           const StringPiece& descriptor);
+
   // Returns the number of non-inherited virtual methods.
   size_t NumVirtualMethods() const {
     return (virtual_methods_ != NULL) ? virtual_methods_->GetLength() : 0;
@@ -796,6 +818,12 @@
     virtual_methods_->Set(i, f);
   }
 
+  Method* FindDeclaredVirtualMethod(const StringPiece& name,
+                                    const StringPiece& descriptor);
+
+  Method* FindVirtualMethod(const StringPiece& name,
+                            const StringPiece& descriptor);
+
   size_t NumInstanceFields() const {
     return (ifields_ != NULL) ? ifields_->GetLength() : 0;
   }
@@ -837,10 +865,6 @@
     reference_offsets_ = new_reference_offsets;
   }
 
-  Method* FindDirectMethod(const String* name) const;
-
-  Method* FindVirtualMethod(const String* name) const;
-
   size_t NumInterfaces() const {
     return (interfaces_ != NULL) ? interfaces_->GetLength() : 0;
   }
@@ -855,9 +879,6 @@
     interfaces_->Set(i, f);
   }
 
-  Method* FindDirectMethodLocally(const StringPiece& name,
-                                  const StringPiece& descriptor) const;
-
  public:  // TODO: private
   // leave space for instance data; we could access fields directly if
   // we freeze the definition of java/lang/Class