Refactor ClassLinker & ImageWriter to use a common resolution routine

ImageWriter prepopulates DexCaches of an app image, but does resolution
directly on mirror::Class rather than going through the class linker
where hidden API checks happen. This patch removes the duplicate code.

Similarly, ClassLinker contains multiple methods for resolving methods
and fields. Consolidate these into three common routines:
- FindResolvedMethod
- FindResolvedField
- FindResolvedFieldJLS

The CL also passes the correct class loader to
ImageWriter::PruneAndPreloadDexCache because it would trip a DCHECK
in class linker.

Bug: 78548674
Test: art/test.py
Merged-In: I12e383290945d2f44b209c32e8a7617533d86063
Change-Id: I12e383290945d2f44b209c32e8a7617533d86063
(cherry picked from commit 1ab0fa89aed1100a3e6b631cb188db1d759b1efc)
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index a2ba816..5d99aef 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1057,18 +1057,12 @@
     }
     if (method == nullptr || i < stored_index) {
       if (last_class != nullptr) {
-        const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
-        Signature signature = dex_file.GetMethodSignature(method_id);
-        if (last_class->IsInterface()) {
-          method = last_class->FindInterfaceMethod(name, signature, target_ptr_size_);
-        } else {
-          method = last_class->FindClassMethod(name, signature, target_ptr_size_);
-        }
-        if (method != nullptr) {
-          // If the referenced class is in the image, the defining class must also be there.
-          DCHECK(KeepClass(method->GetDeclaringClass()));
-          dex_cache->SetResolvedMethod(i, method, target_ptr_size_);
-        }
+        // Try to resolve the method with the class linker, which will insert
+        // it into the dex cache if successful.
+        method = class_linker->FindResolvedMethod(last_class, dex_cache, class_loader, i);
+        // If the referenced class is in the image, the defining class must also be there.
+        DCHECK(method == nullptr || KeepClass(method->GetDeclaringClass()));
+        DCHECK(method == nullptr || dex_cache->GetResolvedMethod(i, target_ptr_size_) == method);
       }
     } else {
       DCHECK_EQ(i, stored_index);
@@ -1102,14 +1096,10 @@
     }
     if (field == nullptr || i < stored_index) {
       if (last_class != nullptr) {
-        const char* name = dex_file.StringDataByIdx(field_id.name_idx_);
-        const char* type = dex_file.StringByTypeIdx(field_id.type_idx_);
-        field = mirror::Class::FindField(Thread::Current(), last_class, name, type);
-        if (field != nullptr) {
-          // If the referenced class is in the image, the defining class must also be there.
-          DCHECK(KeepClass(field->GetDeclaringClass()));
-          dex_cache->SetResolvedField(i, field, target_ptr_size_);
-        }
+        field = class_linker->FindResolvedFieldJLS(last_class, dex_cache, class_loader, i);
+        // If the referenced class is in the image, the defining class must also be there.
+        DCHECK(field == nullptr || KeepClass(field->GetDeclaringClass()));
+        DCHECK(field == nullptr || dex_cache->GetResolvedField(i, target_ptr_size_) == field);
       }
     } else {
       DCHECK_EQ(i, stored_index);
@@ -1198,7 +1188,9 @@
     }
   }
   for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
-    PruneAndPreloadDexCache(dex_cache, class_loader);
+    // Pass the class loader associated with the DexCache. This can either be
+    // the app's `class_loader` or `nullptr` if boot class loader.
+    PruneAndPreloadDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
   }
 
   // Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d98e0b2..6daad88 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4839,6 +4839,9 @@
       const uint32_t field_idx = field->GetDexFieldIndex();
       ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
       if (resolved_field == nullptr) {
+        // Populating cache of a dex file which defines `klass` should always be allowed.
+        DCHECK_EQ(hiddenapi::GetMemberAction(
+            field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow);
         dex_cache->SetResolvedField(field_idx, field, image_pointer_size_);
       } else {
         DCHECK_EQ(field, resolved_field);
@@ -8042,26 +8045,8 @@
     return nullptr;
   }
   DCHECK(klass->IsResolved());
-  Thread* self = is_static ? Thread::Current() : nullptr;
 
-  // First try to find a field declared directly by `klass` by the field index.
-  ArtField* resolved_field = is_static
-      ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx)
-      : klass->FindInstanceField(dex_cache, field_idx);
-
-  if (resolved_field == nullptr) {
-    // If not found in `klass` by field index, search the class hierarchy using the name and type.
-    const char* name = dex_file.GetFieldName(field_id);
-    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
-    resolved_field = is_static
-        ? mirror::Class::FindStaticField(self, klass, name, type)
-        : klass->FindInstanceField(name, type);
-  }
-
-  if (resolved_field != nullptr) {
-    dex_cache->SetResolvedField(field_idx, resolved_field, image_pointer_size_);
-  }
-  return resolved_field;
+  return FindResolvedField(klass, dex_cache, class_loader, field_idx, is_static);
 }
 
 ArtField* ClassLinker::ResolveField(uint32_t field_idx,
@@ -8076,39 +8061,18 @@
   }
   const DexFile& dex_file = *dex_cache->GetDexFile();
   const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
-  Thread* const self = Thread::Current();
   ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader);
   if (klass == nullptr) {
     DCHECK(Thread::Current()->IsExceptionPending());
     return nullptr;
   }
 
-  if (is_static) {
-    resolved = mirror::Class::FindStaticField(self, klass, dex_cache.Get(), field_idx);
-  } else {
-    resolved = klass->FindInstanceField(dex_cache.Get(), field_idx);
-  }
-
+  resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static);
   if (resolved == nullptr) {
     const char* name = dex_file.GetFieldName(field_id);
     const char* type = dex_file.GetFieldTypeDescriptor(field_id);
-    if (is_static) {
-      resolved = mirror::Class::FindStaticField(self, klass, name, type);
-    } else {
-      resolved = klass->FindInstanceField(name, type);
-    }
-  }
-
-  if (resolved == nullptr ||
-      hiddenapi::GetMemberAction(
-          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
-    const char* name = dex_file.GetFieldName(field_id);
-    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
     ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name);
-    return nullptr;
   }
-
-  dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
   return resolved;
 }
 
@@ -8123,29 +8087,80 @@
   }
   const DexFile& dex_file = *dex_cache->GetDexFile();
   const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
-  Thread* self = Thread::Current();
   ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader);
   if (klass == nullptr) {
     DCHECK(Thread::Current()->IsExceptionPending());
     return nullptr;
   }
 
-  StringPiece name(dex_file.GetFieldName(field_id));
-  StringPiece type(dex_file.GetFieldTypeDescriptor(field_id));
-  resolved = mirror::Class::FindField(self, klass, name, type);
-  if (resolved != nullptr &&
-      hiddenapi::GetMemberAction(
-          resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
-    resolved = nullptr;
-  }
-  if (resolved != nullptr) {
-    dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
-  } else {
+  resolved = FindResolvedFieldJLS(klass, dex_cache.Get(), class_loader.Get(), field_idx);
+  if (resolved == nullptr) {
+    const char* name = dex_file.GetFieldName(field_id);
+    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
     ThrowNoSuchFieldError("", klass, type, name);
   }
   return resolved;
 }
 
+ArtField* ClassLinker::FindResolvedField(ObjPtr<mirror::Class> klass,
+                                         ObjPtr<mirror::DexCache> dex_cache,
+                                         ObjPtr<mirror::ClassLoader> class_loader,
+                                         uint32_t field_idx,
+                                         bool is_static) {
+  ArtField* resolved = nullptr;
+  Thread* self = is_static ? Thread::Current() : nullptr;
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+
+  resolved = is_static ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx)
+                       : klass->FindInstanceField(dex_cache, field_idx);
+
+  if (resolved == nullptr) {
+    const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
+    const char* name = dex_file.GetFieldName(field_id);
+    const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+    resolved = is_static ? mirror::Class::FindStaticField(self, klass, name, type)
+                         : klass->FindInstanceField(name, type);
+  }
+
+  if (resolved != nullptr &&
+      hiddenapi::GetMemberAction(
+          resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+    resolved = nullptr;
+  }
+
+  if (resolved != nullptr) {
+    dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
+  }
+
+  return resolved;
+}
+
+ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr<mirror::Class> klass,
+                                            ObjPtr<mirror::DexCache> dex_cache,
+                                            ObjPtr<mirror::ClassLoader> class_loader,
+                                            uint32_t field_idx) {
+  ArtField* resolved = nullptr;
+  Thread* self = Thread::Current();
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+  const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
+
+  const char* name = dex_file.GetFieldName(field_id);
+  const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+  resolved = mirror::Class::FindField(self, klass, name, type);
+
+  if (resolved != nullptr &&
+      hiddenapi::GetMemberAction(
+          resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+    resolved = nullptr;
+  }
+
+  if (resolved != nullptr) {
+    dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
+  }
+
+  return resolved;
+}
+
 ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(
     Thread* self,
     uint32_t proto_idx,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index c46e827..94c3b39 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -384,6 +384,27 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
 
+  // Find a field with a given ID from the DexFile associated with the given DexCache
+  // and ClassLoader, storing the result in DexCache. The declaring class is assumed
+  // to have been already resolved into `klass`. The `is_static` argument is used to
+  // determine if we are resolving a static or non-static field.
+  ArtField* FindResolvedField(ObjPtr<mirror::Class> klass,
+                              ObjPtr<mirror::DexCache> dex_cache,
+                              ObjPtr<mirror::ClassLoader> class_loader,
+                              uint32_t field_idx,
+                              bool is_static)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Find a field with a given ID from the DexFile associated with the given DexCache
+  // and ClassLoader, storing the result in DexCache. The declaring class is assumed
+  // to have been already resolved into `klass`. No is_static argument is provided
+  // so that Java field resolution semantics are followed.
+  ArtField* FindResolvedFieldJLS(ObjPtr<mirror::Class> klass,
+                                 ObjPtr<mirror::DexCache> dex_cache,
+                                 ObjPtr<mirror::ClassLoader> class_loader,
+                                 uint32_t field_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Resolve a method type with a given ID from the DexFile associated with a given DexCache
   // and ClassLoader, storing the result in the DexCache.
   ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self,