ART: Refactor known-classloader visits

Refactor visiting dex Elements and DexFiles in known classloaders,
unifying the walking code.

Test: m test-art-host
Change-Id: I4203ac4fbb0ee68660aadc0dfbf8affacbc03b8b
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index d160a51..1439f11 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -17,8 +17,12 @@
 #ifndef ART_RUNTIME_CLASS_LOADER_UTILS_H_
 #define ART_RUNTIME_CLASS_LOADER_UTILS_H_
 
+#include "art_field-inl.h"
+#include "base/mutex.h"
 #include "handle_scope.h"
+#include "jni_internal.h"
 #include "mirror/class_loader.h"
+#include "native/dalvik_system_DexFile.h"
 #include "scoped_thread_state_change-inl.h"
 #include "well_known_classes.h"
 
@@ -26,7 +30,7 @@
 
 // Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
 // (they both have the same behaviour with respect to class lockup order)
-static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                    Handle<mirror::ClassLoader> class_loader)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   mirror::Class* class_loader_class = class_loader->GetClass();
@@ -37,7 +41,7 @@
           soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
 }
 
-static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                       Handle<mirror::ClassLoader> class_loader)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   mirror::Class* class_loader_class = class_loader->GetClass();
@@ -45,6 +49,114 @@
       soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
 }
 
+// Visit the DexPathList$Element instances in the given classloader with the given visitor.
+// Constraints on the visitor:
+//   * The visitor should return true to continue visiting more Elements.
+//   * The last argument of the visitor is an out argument of RetType. It will be returned
+//     when the visitor ends the visit (by returning false).
+// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
+template <typename Visitor, typename RetType>
+inline RetType VisitClassLoaderDexElements(ScopedObjectAccessAlreadyRunnable& soa,
+                                           Handle<mirror::ClassLoader> class_loader,
+                                           Visitor fn,
+                                           RetType defaultReturn)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Thread* self = soa.Self();
+  ObjPtr<mirror::Object> dex_path_list =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
+          GetObject(class_loader.Get());
+  if (dex_path_list != nullptr) {
+    // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+    ObjPtr<mirror::Object> dex_elements_obj =
+        jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+            GetObject(dex_path_list);
+    // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+    // at the mCookie which is a DexFile vector.
+    if (dex_elements_obj != nullptr) {
+      StackHandleScope<1> hs(self);
+      Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+          hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+      for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+        ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
+        if (element == nullptr) {
+          // Should never happen, fail.
+          break;
+        }
+        RetType ret_value;
+        if (!fn(element, &ret_value)) {
+          return ret_value;
+        }
+      }
+    }
+    self->AssertNoPendingException();
+  }
+  return defaultReturn;
+}
+
+// Visit the DexFiles in the given classloader with the given visitor.
+// Constraints on the visitor:
+//   * The visitor should return true to continue visiting more DexFiles.
+//   * The last argument of the visitor is an out argument of RetType. It will be returned
+//     when the visitor ends the visit (by returning false).
+// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
+template <typename Visitor, typename RetType>
+inline RetType VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+                                        Handle<mirror::ClassLoader> class_loader,
+                                        Visitor fn,
+                                        RetType defaultReturn)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ArtField* const cookie_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+  ArtField* const dex_file_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+  if (dex_file_field == nullptr || cookie_field == nullptr) {
+    return defaultReturn;
+  }
+  auto visit_dex_files = [&](ObjPtr<mirror::Object> element, RetType* ret)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
+    if (dex_file != nullptr) {
+      ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
+      if (long_array == nullptr) {
+        // This should never happen so log a warning.
+        LOG(WARNING) << "Null DexFile::mCookie";
+        *ret = defaultReturn;
+        return true;
+      }
+      int32_t long_array_size = long_array->GetLength();
+      // First element is the oat file.
+      for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
+        const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
+            long_array->GetWithoutChecks(j)));
+        RetType ret_value;
+        if (!fn(cp_dex_file, /* out */ &ret_value)) {
+          *ret = ret_value;
+          return false;
+        }
+      }
+    }
+    return true;
+  };
+
+  return VisitClassLoaderDexElements(soa, class_loader, visit_dex_files, defaultReturn);
+}
+
+// Simplified version of the above, w/o out argument.
+template <typename Visitor>
+inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+                                     Handle<mirror::ClassLoader> class_loader,
+                                     Visitor fn)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  auto helper = [&fn](const art::DexFile* dex_file, void** ATTRIBUTE_UNUSED)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return fn(dex_file);
+  };
+  VisitClassLoaderDexFiles<decltype(helper), void*>(soa,
+                                                    class_loader,
+                                                    helper,
+                                                    /* default */ nullptr);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_CLASS_LOADER_UTILS_H_