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_