Keep objects for Integer.valueOf() intrinsic alive.

Keep boot image objects referenced by the intrinsic alive
through boot image roots, so that they can never be dead as
the intrinsic would still be able to resurrect them later.

Note that currently the GC considers all boot image objects
live forever. That risks leaking memory and should be fixed.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 71526895
Change-Id: Iec25cc27e9c5c4bd3c5711991e3111bfb19ef182
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 056f533..02f736d 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -272,34 +272,33 @@
   ClassLinker* class_linker = runtime->GetClassLinker();
   gc::Heap* heap = runtime->GetHeap();
   IntegerValueOfInfo info;
-  info.integer_cache =
-      class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;").Ptr();
-  if (info.integer_cache == nullptr) {
-    self->ClearException();
+  info.integer_cache = class_linker->LookupClass(self,
+                                                 "Ljava/lang/Integer$IntegerCache;",
+                                                 /* class_loader */ nullptr).Ptr();
+  if (info.integer_cache == nullptr || !info.integer_cache->IsInitialized()) {
+    // Optimization only works if the class is initialized.
     return info;
   }
-  if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
-    // Optimization only works if the class is initialized and in the boot image.
+  if (!heap->ObjectIsInBootImageSpace(info.integer_cache)) {
+    // Optimization only works if the class is in the boot image.
+    // TODO: Implement the intrinsic for boot image compilation.
     return info;
   }
-  info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;").Ptr();
-  if (info.integer == nullptr) {
-    self->ClearException();
-    return info;
-  }
-  if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
-    // Optimization only works if the class is initialized and in the boot image.
+  info.integer =
+      class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr).Ptr();
+  DCHECK(info.integer != nullptr);
+  DCHECK(info.integer->IsInitialized());  // Must be initialized since IntegerCache is initialized.
+  if (!heap->ObjectIsInBootImageSpace(info.integer)) {
+    // Optimization only works if the class is in the boot image.
     return info;
   }
 
   ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
       field->GetObject(info.integer_cache).Ptr());
   if (info.cache == nullptr) {
-    return info;
+    return info;  // Did someone mess up the IntegerCache using reflection?
   }
 
   if (!heap->ObjectIsInBootImageSpace(info.cache)) {
@@ -308,21 +307,15 @@
   }
 
   field = info.integer->FindDeclaredInstanceField("value", "I");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.value_offset = field->GetOffset().Int32Value();
 
   field = info.integer_cache->FindDeclaredStaticField("low", "I");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.low = field->GetInt(info.integer_cache);
 
   field = info.integer_cache->FindDeclaredStaticField("high", "I");
-  if (field == nullptr) {
-    return info;
-  }
+  CHECK(field != nullptr);
   info.high = field->GetInt(info.integer_cache);
 
   DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index dc07090..f1cb068 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1261,15 +1261,8 @@
   return nullptr;
 }
 
-
-ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
-  Runtime* runtime = Runtime::Current();
-  ClassLinker* class_linker = runtime->GetClassLinker();
-  Thread* self = Thread::Current();
-  StackHandleScope<3> hs(self);
-  Handle<Class> object_array_class(hs.NewHandle(
-      class_linker->FindSystemClass(self, "[Ljava/lang/Object;")));
-
+ObjPtr<mirror::ObjectArray<mirror::Object>> ImageWriter::CollectDexCaches(Thread* self,
+                                                                          size_t oat_index) const {
   std::unordered_set<const DexFile*> image_dex_files;
   for (auto& pair : dex_file_oat_index_map_) {
     const DexFile* image_dex_file = pair.first;
@@ -1284,6 +1277,7 @@
   // ObjectArray, we lock the dex lock twice, first to get the number
   // of dex caches first and then lock it again to copy the dex
   // caches. We check that the number of dex caches does not change.
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   size_t dex_cache_count = 0;
   {
     ReaderMutexLock mu(self, *Locks::dex_lock_);
@@ -1300,8 +1294,8 @@
       }
     }
   }
-  Handle<ObjectArray<Object>> dex_caches(
-      hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count)));
+  ObjPtr<ObjectArray<Object>> dex_caches = ObjectArray<Object>::Alloc(
+      self, GetClassRoot<ObjectArray<Object>>(class_linker), dex_cache_count);
   CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array.";
   {
     ReaderMutexLock mu(self, *Locks::dex_lock_);
@@ -1335,11 +1329,62 @@
       }
     }
   }
+  return dex_caches;
+}
+
+static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(Thread* self,
+                                                                      ClassLinker* class_linker)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::Class> integer_cache_class = class_linker->LookupClass(
+      self, "Ljava/lang/Integer$IntegerCache;", /* class_linker */ nullptr);
+  if (integer_cache_class == nullptr || !integer_cache_class->IsInitialized()) {
+    return nullptr;
+  }
+  ArtField* cache_field =
+      integer_cache_class->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
+  CHECK(cache_field != nullptr);
+  ObjPtr<ObjectArray<mirror::Object>> integer_cache =
+      ObjPtr<ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(integer_cache_class));
+  CHECK(integer_cache != nullptr);
+  return integer_cache;
+}
+
+static ObjPtr<mirror::ObjectArray<mirror::Object>> CollectBootImageLiveObjects(
+    Thread* self,
+    ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_) {
+  // The objects used for the Integer.valueOf() intrinsic must remain live even if references
+  // to them are removed using reflection. Image roots are not accessible through reflection,
+  // so the array we construct here shall keep them alive.
+  StackHandleScope<1> hs(self);
+  Handle<ObjectArray<mirror::Object>> integer_cache =
+      hs.NewHandle(LookupIntegerCache(self, class_linker));
+  size_t live_objects_size =
+      (integer_cache != nullptr) ? (/* cache */ 1u + integer_cache->GetLength()) : 0u;
+  ObjPtr<mirror::ObjectArray<mirror::Object>> live_objects = ObjectArray<Object>::Alloc(
+      self, GetClassRoot<ObjectArray<Object>>(class_linker), live_objects_size);
+  int32_t index = 0;
+  if (integer_cache != nullptr) {
+    live_objects->Set(index++, integer_cache.Get());
+    for (int32_t i = 0, length = integer_cache->GetLength(); i != length; ++i) {
+      live_objects->Set(index++, integer_cache->Get(i));
+    }
+  }
+  CHECK_EQ(index, live_objects->GetLength());
+  return live_objects;
+}
+
+ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  Thread* self = Thread::Current();
+  StackHandleScope<2> hs(self);
+
+  Handle<ObjectArray<Object>> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index)));
 
   // build an Object[] of the roots needed to restore the runtime
   int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
-  auto image_roots(hs.NewHandle(
-      ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
+  Handle<ObjectArray<Object>> image_roots(hs.NewHandle(ObjectArray<Object>::Alloc(
+      self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size)));
   image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
   image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
   image_roots->Set<false>(ImageHeader::kOomeWhenThrowingException,
@@ -1350,10 +1395,16 @@
                           runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
   image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
                           runtime->GetPreAllocatedNoClassDefFoundError());
-  // image_roots[ImageHeader::kClassLoader] will be set later for app image.
-  static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
-                "Class loader should be the last image root.");
-  for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) {
+  if (!compile_app_image_) {
+    ObjPtr<ObjectArray<Object>> boot_image_live_objects =
+        CollectBootImageLiveObjects(self, class_linker);
+    image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects);
+  }
+  for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) {
+    if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) {
+      // image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image.
+      continue;
+    }
     CHECK(image_roots->Get(i) != nullptr);
   }
   return image_roots.Get();
@@ -1781,7 +1832,7 @@
     CHECK_EQ(class_loaders_.size(), 1u);
     CHECK_EQ(image_roots.size(), 1u);
     CHECK(*class_loaders_.begin() != nullptr);
-    image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin());
+    image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, *class_loaders_.begin());
   }
 
   // Verify that all objects have assigned image bin slots.
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 960d698..c282a2a 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -450,6 +450,8 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
   void CreateHeader(size_t oat_index)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  ObjPtr<mirror::ObjectArray<mirror::Object>> CollectDexCaches(Thread* self, size_t oat_index) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
   mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const
       REQUIRES_SHARED(Locks::mutator_lock_);
   void CalculateObjectBinSlots(mirror::Object* obj)
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index be636d8..45332c8 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -990,8 +990,7 @@
   class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
       ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(MakeObjPtr(
           spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))));
-  DCHECK_EQ(GetClassRoot(ClassRoot::kJavaLangClass, this)->GetClassFlags(),
-            mirror::kClassFlagClass);
+  DCHECK_EQ(GetClassRoot<mirror::Class>(this)->GetClassFlags(), mirror::kClassFlagClass);
 
   ObjPtr<mirror::Class> java_lang_Object = GetClassRoot<mirror::Object>(this);
   java_lang_Object->SetObjectSize(sizeof(mirror::Object));
@@ -1610,10 +1609,9 @@
       hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
   Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
       header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
-  static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
-                "Class loader should be the last image root.");
   MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
-      app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
+      app_image ? header.GetImageRoot(ImageHeader::kAppImageClassLoader)->AsClassLoader()
+                : nullptr));
   DCHECK(class_roots != nullptr);
   if (class_roots->GetLength() != static_cast<int32_t>(ClassRoot::kMax)) {
     *error_msg = StringPrintf("Expected %d class roots but got %d",
diff --git a/runtime/image.cc b/runtime/image.cc
index 17fc664..7819c0b 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '1', '\0' };  // Pre-allocated Throwables.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '2', '\0' };  // Boot image live objects.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index c6fc052..c1cde0a 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -211,8 +211,12 @@
     kOomeWhenThrowingOome,            // Pre-allocated OOME when throwing OOME.
     kOomeWhenHandlingStackOverflow,   // Pre-allocated OOME when handling StackOverflowError.
     kNoClassDefFoundError,            // Pre-allocated NoClassDefFoundError.
-    kClassLoader,                     // App image only.
+    kSpecialRoots,                    // Different for boot image and app image, see aliases below.
     kImageRootsMax,
+
+    // Aliases.
+    kAppImageClassLoader = kSpecialRoots,   // The class loader used to build the app image.
+    kBootImageLiveObjects = kSpecialRoots,  // Array of boot image objects that must be kept live.
   };
 
   enum ImageSections {
@@ -229,8 +233,10 @@
     kSectionCount,  // Number of elements in enum.
   };
 
-  static size_t NumberOfImageRoots(bool app_image) {
-    return app_image ? kImageRootsMax : kImageRootsMax - 1u;
+  static size_t NumberOfImageRoots(bool app_image ATTRIBUTE_UNUSED) {
+    // At the moment, boot image and app image have the same number of roots,
+    // though the meaning of the kSpecialRoots is different.
+    return kImageRootsMax;
   }
 
   ArtMethod* GetImageMethod(ImageMethod index) const;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1e327fc..b4dfc11 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2045,6 +2045,7 @@
   pre_allocated_OutOfMemoryError_when_handling_stack_overflow_
       .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
   pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+  VisitImageRoots(visitor);
   verifier::MethodVerifier::VisitStaticRoots(visitor);
   VisitTransactionRoots(visitor);
 }