| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_RUNTIME_CLASS_LOADER_UTILS_H_ |
| #define ART_RUNTIME_CLASS_LOADER_UTILS_H_ |
| |
| #include "art_field-inl.h" |
| #include "base/locks.h" |
| #include "handle_scope.h" |
| #include "jni/jni_internal.h" |
| #include "mirror/class_loader.h" |
| #include "mirror/object-inl.h" |
| #include "mirror/object.h" |
| #include "native/dalvik_system_DexFile.h" |
| #include "scoped_thread_state_change-inl.h" |
| #include "well_known_classes.h" |
| |
| namespace art { |
| |
| // Returns true if the given class loader derives from BaseDexClassLoader. |
| inline bool IsInstanceOfBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, |
| Handle<mirror::ClassLoader> class_loader) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| return class_loader->InstanceOf( |
| soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_BaseDexClassLoader)); |
| } |
| |
| // Returns true if the given class loader is either a PathClassLoader or a DexClassLoader. |
| // (they both have the same behaviour with respect to class lookup order) |
| inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, |
| Handle<mirror::ClassLoader> class_loader) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass(); |
| return |
| (class_loader_class == |
| soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) || |
| (class_loader_class == |
| soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader)); |
| } |
| |
| // Returns true if the given class loader is an InMemoryDexClassLoader. |
| inline bool IsInMemoryDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, |
| Handle<mirror::ClassLoader> class_loader) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass(); |
| return (class_loader_class == |
| soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_InMemoryDexClassLoader)); |
| } |
| |
| inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, |
| Handle<mirror::ClassLoader> class_loader) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass(); |
| return class_loader_class == |
| 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 (auto element : dex_elements.Iterate<mirror::Object>()) { |
| if (element == nullptr) { |
| // Should never happen, fail. |
| break; |
| } |
| RetType ret_value; |
| if (!fn(element, &ret_value)) { |
| return ret_value; |
| } |
| } |
| } |
| } |
| 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** ret) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| #ifdef __clang_analyzer__ |
| *ret = nullptr; |
| #else |
| UNUSED(ret); |
| #endif |
| |
| return fn(dex_file); |
| }; |
| VisitClassLoaderDexFiles<decltype(helper), void*>(soa, |
| class_loader, |
| helper, |
| /* defaultReturn= */ nullptr); |
| } |
| |
| } // namespace art |
| |
| #endif // ART_RUNTIME_CLASS_LOADER_UTILS_H_ |