Support array classes in runtime app image.
Test: 845-data-image
Bug: 260557058
Change-Id: Iebe377f655b7aa364bafcb10d4c8dcaa8f32521a
diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc
index 6739aad..f999569 100644
--- a/runtime/runtime_image.cc
+++ b/runtime/runtime_image.cc
@@ -477,12 +477,23 @@
if (cls == nullptr) {
return true;
}
- const dex::ClassDef* class_def = cls->GetClassDef();
- if (class_def == nullptr) {
- // Covers array classes and proxy classes.
- // TODO: Handle these differently.
+ if (cls->IsProxyClass()) {
return false;
}
+ if (cls->IsArrayClass()) {
+ if (cls->IsBootStrapClassLoaded()) {
+ // For boot classpath arrays, we can only emit them if they are
+ // in the boot image already.
+ return helper_->IsInBootImage(cls.Get());
+ }
+ ObjPtr<mirror::Class> temp = cls.Get();
+ while ((temp = temp->GetComponentType())->IsArrayClass()) {}
+ StackHandleScope<1> hs(self_);
+ Handle<mirror::Class> other_class = hs.NewHandle(temp);
+ return CanEmit(other_class);
+ }
+ const dex::ClassDef* class_def = cls->GetClassDef();
+ DCHECK_NE(class_def, nullptr);
auto existing = visited_.find(class_def);
if (existing != visited_.end()) {
// Already processed;
@@ -712,6 +723,11 @@
cls->FixupNativePointers(cls, kRuntimePointerSize, visitor);
RelocateMethodPointerArrays(cls, visitor);
}
+ for (auto it : array_classes_) {
+ mirror::Class* cls = reinterpret_cast<mirror::Class*>(&objects_[it.second]);
+ cls->FixupNativePointers(cls, kRuntimePointerSize, visitor);
+ RelocateMethodPointerArrays(cls, visitor);
+ }
for (auto it : native_relocations_) {
if (it.second.first == NativeRelocationKind::kImTable) {
ImTable* im_table = reinterpret_cast<ImTable*>(im_tables_.data() + it.second.second);
@@ -1229,13 +1245,26 @@
}
uint32_t CopyClass(ObjPtr<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) {
- const dex::ClassDef* class_def = cls->GetClassDef();
- auto it = classes_.find(class_def);
- if (it != classes_.end()) {
- return it->second;
+ DCHECK(!cls->IsBootStrapClassLoaded());
+ uint32_t offset = 0u;
+ if (cls->IsArrayClass()) {
+ std::string class_name;
+ cls->GetDescriptor(&class_name);
+ auto it = array_classes_.find(class_name);
+ if (it != array_classes_.end()) {
+ return it->second;
+ }
+ offset = CopyObject(cls);
+ array_classes_[class_name] = offset;
+ } else {
+ const dex::ClassDef* class_def = cls->GetClassDef();
+ auto it = classes_.find(class_def);
+ if (it != classes_.end()) {
+ return it->second;
+ }
+ offset = CopyObject(cls);
+ classes_[class_def] = offset;
}
- uint32_t offset = CopyObject(cls);
- classes_[class_def] = offset;
uint32_t hash = cls->DescriptorHash();
// Save the hash, the `HashSet` implementation requires to find it.
@@ -1249,7 +1278,11 @@
// Clear internal state.
mirror::Class* copy = reinterpret_cast<mirror::Class*>(objects_.data() + offset);
copy->SetClinitThreadId(static_cast<pid_t>(0u));
- copy->SetStatusInternal(cls->IsVerified() ? ClassStatus::kVerified : ClassStatus::kResolved);
+ if (cls->IsArrayClass()) {
+ DCHECK(copy->IsVisiblyInitialized());
+ } else {
+ copy->SetStatusInternal(cls->IsVerified() ? ClassStatus::kVerified : ClassStatus::kResolved);
+ }
copy->SetObjectSizeAllocFastPath(std::numeric_limits<uint32_t>::max());
copy->SetAccessFlags(copy->GetAccessFlags() & ~kAccRecursivelyInitialized);
@@ -1417,6 +1450,7 @@
std::vector<uint32_t> object_offsets_;
std::map<const dex::ClassDef*, uint32_t> classes_;
+ std::map<std::string, uint32_t> array_classes_;
std::map<const DexFile*, uint32_t> dex_caches_;
std::map<uint32_t, uint32_t> class_hashes_;
diff --git a/test/845-data-image/src-art/Main.java b/test/845-data-image/src-art/Main.java
index 90a3d86..9d802ea 100644
--- a/test/845-data-image/src-art/Main.java
+++ b/test/845-data-image/src-art/Main.java
@@ -18,6 +18,7 @@
import dalvik.system.VMRuntime;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.CyclicBarrier;
@@ -214,6 +215,18 @@
assertEquals(3, foo.someMethod());
assertEquals(42, foo.someDefaultMethod());
+ // Test with array classes.
+ assertEquals("[LMain;", Main[].class.getName());
+ assertEquals("[[LMain;", Main[][].class.getName());
+
+ assertEquals("[LMain;", new Main[4].getClass().getName());
+ assertEquals("[[LMain;", new Main[1][2].getClass().getName());
+
+ Main array[] = new Main[] { new Main() };
+ assertEquals("[LMain;", array.getClass().getName());
+
+ assertEquals(Object[][][][].class, Array.newInstance(Object.class, 0, 0, 0, 0).getClass());
+
// Call all interface methods to trigger the creation of a imt conflict method.
itf2.defaultMethod1();
itf2.defaultMethod2();
@@ -274,7 +287,7 @@
}
}
- private static void assertEquals(String expected, String actual) {
+ private static void assertEquals(Object expected, Object actual) {
if (!expected.equals(actual)) {
throw new Error("Expected \"" + expected + "\", got \"" + actual + "\"");
}