Allow unresolved exception classes in boot image.

And incorporate dex2oat_image_test changes from
    https://android-review.googlesource.com/1302586
to test that this works.

Test: m test-art-host-gtest
Bug: 153648261
Change-Id: I810de9c84973156c06a2c871bf2e0921c80a3fa4
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index d01b64f..71e9b1b 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -184,13 +184,11 @@
   }
   // Compile only a subset of the libcore dex files to make this test shorter.
   std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
-  // The primary image must contain at least core-oj and core-libart to initialize the runtime
-  // and we also need the core-icu4j if we want to compile these with full profile.
+  // The primary image must contain at least core-oj and core-libart to initialize the runtime.
   ASSERT_NE(std::string::npos, libcore_dex_files[0].find("core-oj"));
   ASSERT_NE(std::string::npos, libcore_dex_files[1].find("core-libart"));
-  ASSERT_NE(std::string::npos, libcore_dex_files[2].find("core-icu4j"));
   ArrayRef<const std::string> dex_files =
-      ArrayRef<const std::string>(libcore_dex_files).SubArray(/*pos=*/ 0u, /*length=*/ 3u);
+      ArrayRef<const std::string>(libcore_dex_files).SubArray(/*pos=*/ 0u, /*length=*/ 2u);
 
   ImageSizes base_sizes = CompileImageAndGetSizes(dex_files, {});
   ImageSizes everything_sizes;
@@ -274,19 +272,17 @@
 
   ArrayRef<const std::string> full_bcp(libcore_dex_files);
   size_t total_dex_files = full_bcp.size();
-  ASSERT_GE(total_dex_files, 5u);  // 3 for "head", 1 for "tail", at least one for "mid", see below.
+  ASSERT_GE(total_dex_files, 4u);  // 2 for "head", 1 for "tail", at least one for "mid", see below.
 
-  // The primary image must contain at least core-oj and core-libart to initialize the runtime
-  // and we also need the core-icu4j if we want to compile these with full profile.
+  // The primary image must contain at least core-oj and core-libart to initialize the runtime.
   ASSERT_NE(std::string::npos, full_bcp[0].find("core-oj"));
   ASSERT_NE(std::string::npos, full_bcp[1].find("core-libart"));
-  ASSERT_NE(std::string::npos, full_bcp[2].find("core-icu4j"));
-  ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 3u);
+  ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 2u);
   // Middle part is everything else except for conscrypt.
   ASSERT_NE(std::string::npos, full_bcp[full_bcp.size() - 1u].find("conscrypt"));
   ArrayRef<const std::string> mid_bcp =
       full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ total_dex_files - 1u);
-  ArrayRef<const std::string> mid_dex_files = mid_bcp.SubArray(/*pos=*/ 3u);
+  ArrayRef<const std::string> mid_dex_files = mid_bcp.SubArray(/*pos=*/ 2u);
   // Tail is just the conscrypt.
   ArrayRef<const std::string> tail_dex_files =
       full_bcp.SubArray(/*pos=*/ total_dex_files - 1u, /*length=*/ 1u);
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index e21f492..b97c1f0 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -976,8 +976,7 @@
     return true;
   }
 
-  void FindExceptionTypesToResolve(
-      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
+  void FindExceptionTypesToResolve(std::set<TypeReference>* exceptions_to_resolve)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (ObjPtr<mirror::Class> klass : classes_) {
@@ -990,7 +989,7 @@
  private:
   void FindExceptionTypesToResolveForMethod(
       ArtMethod* method,
-      std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
+      std::set<TypeReference>* exceptions_to_resolve)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     if (method->GetCodeItem() == nullptr) {
       return;  // native or abstract method
@@ -1013,8 +1012,8 @@
             dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
         // Add to set of types to resolve if not already in the dex cache resolved types
         if (!method->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
-          exceptions_to_resolve->emplace(encoded_catch_handler_handlers_type_idx,
-                                         method->GetDexFile());
+          exceptions_to_resolve->emplace(method->GetDexFile(),
+                                         encoded_catch_handler_handlers_type_idx);
         }
         // ignore address associated with catch handler
         DecodeUnsignedLeb128(&encoded_catch_handler_list);
@@ -1107,10 +1106,12 @@
   // Resolve exception classes referenced by the loaded classes. The catch logic assumes
   // exceptions are resolved by the verifier when there is a catch block in an interested method.
   // Do this here so that exception classes appear to have been specified image classes.
-  std::set<std::pair<dex::TypeIndex, const DexFile*>> unresolved_exception_types;
-  StackHandleScope<1> hs(self);
+  std::set<TypeReference> unresolved_exception_types;
+  StackHandleScope<2u> hs(self);
   Handle<mirror::Class> java_lang_Throwable(
       hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
+  MutableHandle<mirror::DexCache> dex_cache = hs.NewHandle(java_lang_Throwable->GetDexCache());
+  DCHECK(dex_cache != nullptr);
   do {
     unresolved_exception_types.clear();
     {
@@ -1121,24 +1122,25 @@
       class_linker->VisitClasses(&visitor);
       visitor.FindExceptionTypesToResolve(&unresolved_exception_types);
     }
-    for (const auto& exception_type : unresolved_exception_types) {
-      dex::TypeIndex exception_type_idx = exception_type.first;
-      const DexFile* dex_file = exception_type.second;
-      StackHandleScope<1> hs2(self);
-      Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file,
-                                                                                     nullptr)));
-      ObjPtr<mirror::Class> klass =
-          (dex_cache != nullptr)
-              ? class_linker->ResolveType(exception_type_idx,
-                                          dex_cache,
-                                          ScopedNullHandle<mirror::ClassLoader>())
-              : nullptr;
+    for (auto it = unresolved_exception_types.begin(); it != unresolved_exception_types.end(); ) {
+      dex::TypeIndex exception_type_idx = it->TypeIndex();
+      const DexFile* dex_file = it->dex_file;
+      if (dex_cache->GetDexFile() != dex_file) {
+        dex_cache.Assign(class_linker->RegisterDexFile(*dex_file, /*class_loader=*/ nullptr));
+        DCHECK(dex_cache != nullptr);
+      }
+      ObjPtr<mirror::Class> klass = class_linker->ResolveType(
+          exception_type_idx, dex_cache, ScopedNullHandle<mirror::ClassLoader>());
       if (klass == nullptr) {
         const dex::TypeId& type_id = dex_file->GetTypeId(exception_type_idx);
         const char* descriptor = dex_file->GetTypeDescriptor(type_id);
-        LOG(FATAL) << "Failed to resolve class " << descriptor;
+        VLOG(compiler) << "Failed to resolve exception class " << descriptor;
+        self->ClearException();
+        it = unresolved_exception_types.erase(it);
+      } else {
+        DCHECK(java_lang_Throwable->IsAssignableFrom(klass));
+        ++it;
       }
-      DCHECK(java_lang_Throwable->IsAssignableFrom(klass));
     }
     // Resolving exceptions may load classes that reference more exceptions, iterate until no
     // more are found