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