ART: Add UnstartedRuntime support for Class.getSimpleName
This requires two native methods, isAnonymousClass and
getDeclaringClass.
Add tests.
Bug: 34890992
Test: m
Test: m test-art-host
Test: Device boots
Change-Id: Ib3fca2a6bb6e367ef202ff864719cab979d6c793
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index bc08384..e525808 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -113,6 +113,7 @@
ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
+ART_GTEST_unstarted_runtime_test_DEX_DEPS := Nested
ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex
ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 0fec856..9dca4c0 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -338,13 +338,16 @@
ScopedObjectAccess soa(Thread::Current());
std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
ASSERT_TRUE(raw.get() != nullptr);
- EXPECT_EQ(2U, raw->NumClassDefs());
+ EXPECT_EQ(3U, raw->NumClassDefs());
const DexFile::ClassDef& c0 = raw->GetClassDef(0);
- EXPECT_STREQ("LNested$Inner;", raw->GetClassDescriptor(c0));
+ EXPECT_STREQ("LNested$1;", raw->GetClassDescriptor(c0));
const DexFile::ClassDef& c1 = raw->GetClassDef(1);
- EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c1));
+ EXPECT_STREQ("LNested$Inner;", raw->GetClassDescriptor(c1));
+
+ const DexFile::ClassDef& c2 = raw->GetClassDef(2);
+ EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c2));
}
TEST_F(DexFileTest, GetMethodSignature) {
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index feb6e08..371e2f1 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -401,6 +401,25 @@
result->SetL(constructor);
}
+void UnstartedRuntime::UnstartedClassGetDeclaringClass(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> klass(hs.NewHandle(
+ reinterpret_cast<mirror::Class*>(shadow_frame->GetVRegReference(arg_offset))));
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ result->SetL(nullptr);
+ return;
+ }
+ // Return null for anonymous classes.
+ JValue is_anon_result;
+ UnstartedClassIsAnonymousClass(self, shadow_frame, &is_anon_result, arg_offset);
+ if (is_anon_result.GetZ() != 0) {
+ result->SetL(nullptr);
+ return;
+ }
+ result->SetL(annotations::GetDeclaringClass(klass));
+}
+
void UnstartedRuntime::UnstartedClassGetEnclosingClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
StackHandleScope<1> hs(self);
@@ -420,6 +439,23 @@
result->SetI(mirror::Class::GetInnerClassFlags(klass, default_value));
}
+void UnstartedRuntime::UnstartedClassIsAnonymousClass(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> klass(hs.NewHandle(
+ reinterpret_cast<mirror::Class*>(shadow_frame->GetVRegReference(arg_offset))));
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ result->SetZ(false);
+ return;
+ }
+ mirror::String* class_name = nullptr;
+ if (!annotations::GetInnerClass(klass, &class_name)) {
+ result->SetZ(false);
+ return;
+ }
+ result->SetZ(class_name == nullptr);
+}
+
static std::unique_ptr<MemMap> FindAndExtractEntry(const std::string& jar_file,
const char* entry_name,
size_t* size,
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index b8553b5..96b35e4 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -28,8 +28,10 @@
V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \
V(ClassGetDeclaredMethod, "java.lang.reflect.Method java.lang.Class.getDeclaredMethodInternal(java.lang.String, java.lang.Class[])") \
V(ClassGetDeclaredConstructor, "java.lang.reflect.Constructor java.lang.Class.getDeclaredConstructorInternal(java.lang.Class[])") \
+ V(ClassGetDeclaringClass, "java.lang.Class java.lang.Class.getDeclaringClass()") \
V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \
V(ClassGetInnerClassFlags, "int java.lang.Class.getInnerClassFlags(int)") \
+ V(ClassIsAnonymousClass, "boolean java.lang.Class.isAnonymousClass()") \
V(ClassLoaderGetResourceAsStream, "java.io.InputStream java.lang.ClassLoader.getResourceAsStream(java.lang.String)") \
V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index b190c81..ae55f4c 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -885,5 +885,64 @@
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
+TEST_F(UnstartedRuntimeTest, IsAnonymousClass) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ mirror::Class* class_klass = mirror::Class::GetJavaLangClass();
+ shadow_frame->SetVRegReference(0, class_klass);
+ UnstartedClassIsAnonymousClass(self, shadow_frame, &result, 0);
+ EXPECT_EQ(result.GetZ(), 0);
+
+ jobject class_loader = LoadDex("Nested");
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ mirror::Class* c = class_linker_->FindClass(soa.Self(), "LNested$1;", loader);
+ ASSERT_TRUE(c != nullptr);
+ shadow_frame->SetVRegReference(0, c);
+ UnstartedClassIsAnonymousClass(self, shadow_frame, &result, 0);
+ EXPECT_EQ(result.GetZ(), 1);
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
+TEST_F(UnstartedRuntimeTest, GetDeclaringClass) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ jobject class_loader = LoadDex("Nested");
+ StackHandleScope<4> hs(self);
+ Handle<mirror::ClassLoader> loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+
+ Handle<mirror::Class> nested_klass(hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), "LNested;", loader)));
+ Handle<mirror::Class> inner_klass(hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), "LNested$Inner;", loader)));
+ Handle<mirror::Class> anon_klass(hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), "LNested$1;", loader)));
+
+ shadow_frame->SetVRegReference(0, nested_klass.Get());
+ UnstartedClassGetDeclaringClass(self, shadow_frame, &result, 0);
+ EXPECT_EQ(result.GetL(), nullptr);
+
+ shadow_frame->SetVRegReference(0, inner_klass.Get());
+ UnstartedClassGetDeclaringClass(self, shadow_frame, &result, 0);
+ EXPECT_EQ(result.GetL(), nested_klass.Get());
+
+ shadow_frame->SetVRegReference(0, anon_klass.Get());
+ UnstartedClassGetDeclaringClass(self, shadow_frame, &result, 0);
+ EXPECT_EQ(result.GetL(), nullptr);
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
} // namespace interpreter
} // namespace art
diff --git a/test/Nested/Nested.java b/test/Nested/Nested.java
index 78b273b..f493989 100644
--- a/test/Nested/Nested.java
+++ b/test/Nested/Nested.java
@@ -17,4 +17,6 @@
class Nested {
class Inner {
}
+ Object x = new Object() {
+ };
}