Merge "ART: Faster PrettyMethod()."
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index ece853f..d4297df 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -777,26 +777,16 @@
}
std::string ArtMethod::PrettyMethod(bool with_signature) {
- ArtMethod* m = this;
- if (!m->IsRuntimeMethod()) {
- m = m->GetInterfaceMethodIfProxy(Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ if (UNLIKELY(IsRuntimeMethod())) {
+ std::string result = GetDeclaringClassDescriptor();
+ result += '.';
+ result += GetName();
+ // Do not add "<no signature>" even if `with_signature` is true.
+ return result;
}
- std::string result(PrettyDescriptor(m->GetDeclaringClassDescriptor()));
- result += '.';
- result += m->GetName();
- if (UNLIKELY(m->IsFastNative())) {
- result += "!";
- }
- if (with_signature) {
- const Signature signature = m->GetSignature();
- std::string sig_as_string(signature.ToString());
- if (signature == Signature::NoSignature()) {
- return result + sig_as_string;
- }
- result = PrettyReturnType(sig_as_string.c_str()) + " " + result +
- PrettyArguments(sig_as_string.c_str());
- }
- return result;
+ ArtMethod* m =
+ GetInterfaceMethodIfProxy(Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ return m->GetDexFile()->PrettyMethod(m->GetDexMethodIndex(), with_signature);
}
std::string ArtMethod::JniShortName() {
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index f70846b..be157a3 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1302,17 +1302,27 @@
return StringPrintf("<<invalid-method-idx-%d>>", method_idx);
}
const DexFile::MethodId& method_id = GetMethodId(method_idx);
- std::string result(PrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id)));
+ std::string result;
+ const DexFile::ProtoId* proto_id = with_signature ? &GetProtoId(method_id.proto_idx_) : nullptr;
+ if (with_signature) {
+ AppendPrettyDescriptor(StringByTypeIdx(proto_id->return_type_idx_), &result);
+ result += ' ';
+ }
+ AppendPrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id), &result);
result += '.';
result += GetMethodName(method_id);
if (with_signature) {
- const Signature signature = GetMethodSignature(method_id);
- std::string sig_as_string(signature.ToString());
- if (signature == Signature::NoSignature()) {
- return result + sig_as_string;
+ result += '(';
+ const DexFile::TypeList* params = GetProtoParameters(*proto_id);
+ if (params != nullptr) {
+ const char* separator = "";
+ for (uint32_t i = 0u, size = params->Size(); i != size; ++i) {
+ result += separator;
+ separator = ", ";
+ AppendPrettyDescriptor(StringByTypeIdx(params->GetTypeItem(i).type_idx_), &result);
+ }
}
- result = PrettyReturnType(sig_as_string.c_str()) + " " + result +
- PrettyArguments(sig_as_string.c_str());
+ result += ')';
}
return result;
}
@@ -1327,7 +1337,7 @@
result += GetFieldTypeDescriptor(field_id);
result += ' ';
}
- result += PrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id));
+ AppendPrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id), &result);
result += '.';
result += GetFieldName(field_id);
return result;
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 1a73062..a7bf59e 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -423,28 +423,83 @@
ASSERT_EQ("()V", signature);
}
- // Check both virtual methods.
- ASSERT_EQ(2U, it.NumVirtualMethods());
- {
+ // Check all virtual methods.
+ struct Result {
+ const char* name;
+ const char* signature;
+ const char* pretty_method;
+ };
+ static const Result results[] = {
+ {
+ "m1",
+ "(IDJLjava/lang/Object;)Ljava/lang/Float;",
+ "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m2",
+ "(ZSC)LGetMethodSignature;",
+ "GetMethodSignature GetMethodSignature.m2(boolean, short, char)"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m3",
+ "()V",
+ "void GetMethodSignature.m3()"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m4",
+ "(I)V",
+ "void GetMethodSignature.m4(int)"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m5",
+ "(II)V",
+ "void GetMethodSignature.m5(int, int)"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m6",
+ "(II[[I)V",
+ "void GetMethodSignature.m6(int, int, int[][])"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m7",
+ "(II[[ILjava/lang/Object;)V",
+ "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m8",
+ "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V",
+ "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "m9",
+ "()I",
+ "int GetMethodSignature.m9()"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "mA",
+ "()[[I",
+ "int[][] GetMethodSignature.mA()"
+ },
+ { // NOLINT [whitespace/braces] [4]
+ "mB",
+ "()[[Ljava/lang/Object;",
+ "java.lang.Object[][] GetMethodSignature.mB()"
+ },
+ };
+ ASSERT_EQ(arraysize(results), it.NumVirtualMethods());
+ for (const Result& r : results) {
it.Next();
const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
const char* name = raw->StringDataByIdx(method_id.name_idx_);
- ASSERT_STREQ("m1", name);
+ ASSERT_STREQ(r.name, name);
std::string signature(raw->GetMethodSignature(method_id).ToString());
- ASSERT_EQ("(IDJLjava/lang/Object;)Ljava/lang/Float;", signature);
- }
+ ASSERT_EQ(r.signature, signature);
- {
- it.Next();
- const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
-
- const char* name = raw->StringDataByIdx(method_id.name_idx_);
- ASSERT_STREQ("m2", name);
-
- std::string signature(raw->GetMethodSignature(method_id).ToString());
- ASSERT_EQ("(ZSC)LGetMethodSignature;", signature);
+ std::string plain_method = std::string("GetMethodSignature.") + r.name;
+ ASSERT_EQ(plain_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ false));
+ ASSERT_EQ(r.pretty_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ true));
}
}
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 3fe18c7..fc1c91a 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -148,7 +148,7 @@
}
}
-std::string PrettyDescriptor(const char* descriptor) {
+void AppendPrettyDescriptor(const char* descriptor, std::string* result) {
// Count the number of '['s to get the dimensionality.
const char* c = descriptor;
size_t dim = 0;
@@ -166,74 +166,41 @@
// To make life easier, we make primitives look like unqualified
// reference types.
switch (*c) {
- case 'B': c = "byte;"; break;
- case 'C': c = "char;"; break;
- case 'D': c = "double;"; break;
- case 'F': c = "float;"; break;
- case 'I': c = "int;"; break;
- 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;
+ case 'B': c = "byte;"; break;
+ case 'C': c = "char;"; break;
+ case 'D': c = "double;"; break;
+ case 'F': c = "float;"; break;
+ case 'I': c = "int;"; break;
+ 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: result->append(descriptor); return;
}
}
// At this point, 'c' is a string of the form "fully/qualified/Type;"
// or "primitive;". Rewrite the type with '.' instead of '/':
- std::string result;
const char* p = c;
while (*p != ';') {
char ch = *p++;
if (ch == '/') {
ch = '.';
}
- result.push_back(ch);
+ result->push_back(ch);
}
// ...and replace the semicolon with 'dim' "[]" pairs:
for (size_t i = 0; i < dim; ++i) {
- result += "[]";
+ result->append("[]");
}
- return result;
}
-std::string PrettyArguments(const char* signature) {
+std::string PrettyDescriptor(const char* descriptor) {
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.c_str());
- }
- if (signature[argument_length] != ')') {
- result += ", ";
- }
- signature += argument_length;
- }
- CHECK_EQ(*signature, ')');
- ++signature; // Skip the ')'.
- result += ')';
+ AppendPrettyDescriptor(descriptor, &result);
return result;
}
-std::string PrettyReturnType(const char* signature) {
- const char* return_type = strchr(signature, ')');
- CHECK(return_type != nullptr);
- ++return_type; // Skip ')'.
- return PrettyDescriptor(return_type);
-}
-
std::string PrettyJavaAccessFlags(uint32_t access_flags) {
std::string result;
if ((access_flags & kAccPublic) != 0) {
diff --git a/runtime/utils.h b/runtime/utils.h
index 739681d..9945298 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -81,13 +81,10 @@
// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
// "[[I" would be "int[][]", "[Ljava/lang/String;" would be
// "java.lang.String[]", and so forth.
+void AppendPrettyDescriptor(const char* descriptor, std::string* result);
std::string PrettyDescriptor(const char* descriptor);
std::string PrettyDescriptor(Primitive::Type type);
-// Utilities for printing the types for method signatures.
-std::string PrettyArguments(const char* signature);
-std::string PrettyReturnType(const char* signature);
-
// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
// (note the trailing whitespace).
std::string PrettyJavaAccessFlags(uint32_t access_flags);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index ee8eb36..decb243 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -91,23 +91,6 @@
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) {
ScopedObjectAccess soa(Thread::Current());
EXPECT_EQ("null", mirror::Object::PrettyTypeOf(nullptr));
diff --git a/test/GetMethodSignature/GetMethodSignature.java b/test/GetMethodSignature/GetMethodSignature.java
index c2ba948..d2f96bb 100644
--- a/test/GetMethodSignature/GetMethodSignature.java
+++ b/test/GetMethodSignature/GetMethodSignature.java
@@ -17,4 +17,13 @@
class GetMethodSignature {
Float m1(int a, double b, long c, Object d) { return null; }
GetMethodSignature m2(boolean x, short y, char z) { return null; }
+ void m3() { }
+ void m4(int i) { }
+ void m5(int i, int j) { }
+ void m6(int i, int j, int[][] array1) { }
+ void m7(int i, int j, int[][] array1, Object o) { }
+ void m8(int i, int j, int[][] array1, Object o, Object[][] array2) { }
+ int m9() { return 0; }
+ int[][] mA() { return null; }
+ Object[][] mB() { return null; }
}