Implement STL iterators on ObjectArray and add helpers
Iterating over an ObjectArray is rather cumbersome, requiring manual
for-loops. To improve ergonomics and STL standard-ness this implements
std::iterator's for ObjectArray and converts code to use this (in
simple situations). This should allow us to use standard STL functions
in the future when dealing with ObjectArrays.
Also adds some helpers such as ZipCount and ZipLeft.
Test: ./test.py --host
Change-Id: Ifd515b8321775424b3256a6faf47b2ba970177d3
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0445584..b813117 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -113,6 +113,7 @@
#include "mirror/method_type.h"
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
+#include "mirror/object.h"
#include "mirror/object_array-alloc-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/object_reference.h"
@@ -1468,11 +1469,11 @@
return false;
}
- for (int32_t i = 0; i < array1->GetLength(); ++i) {
+ for (auto clp : ZipLeft(array1->Iterate(), array2->Iterate())) {
// Do a full comparison of the class loaders, including comparing their dex files.
if (!CompareClassLoaders(soa,
- array1->Get(i),
- array2->Get(i),
+ clp.first,
+ clp.second,
/*check_dex_file_names=*/ true,
error_msg)) {
return false;
@@ -1774,9 +1775,7 @@
{
// Register dex caches with the class loader.
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- const size_t num_dex_caches = dex_caches->GetLength();
- for (size_t i = 0; i < num_dex_caches; i++) {
- ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ for (auto dex_cache : dex_caches.Iterate<mirror::DexCache>()) {
const DexFile* const dex_file = dex_cache->GetDexFile();
{
WriterMutexLock mu2(self, *Locks::dex_lock_);
@@ -1968,8 +1967,7 @@
ObjPtr<mirror::ObjectArray<mirror::DexCache>> dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
const OatFile* oat_file = space->GetOatFile();
- for (int32_t i = 0, length = dex_caches->GetLength(); i != length; ++i) {
- ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ for (auto dex_cache : dex_caches->Iterate()) {
std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
dex_file_location.c_str(),
@@ -2158,8 +2156,7 @@
}
}
// Check that all non-primitive classes in dex caches are also in the class table.
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ for (auto dex_cache : dex_caches.ConstIterate<mirror::DexCache>()) {
mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
@@ -2233,8 +2230,7 @@
return false;
}
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ for (auto dex_cache : dex_caches.Iterate<mirror::DexCache>()) {
std::string dex_file_location = dex_cache->GetLocation()->ToModifiedUtf8();
if (class_loader == nullptr) {
// For app images, we'll see the relative location. b/130666977.
@@ -2292,8 +2288,7 @@
// comparisons for their shared libraries and parent.
auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
std::list<ObjPtr<mirror::String>> loader_dex_file_names;
- for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
- ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
+ for (auto element : elements->Iterate()) {
if (element != nullptr) {
// If we are somewhere in the middle of the array, there may be nulls at the end.
ObjPtr<mirror::String> name;
@@ -2329,8 +2324,7 @@
}
if (kSanityCheckObjects) {
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ for (auto dex_cache : dex_caches.Iterate<mirror::DexCache>()) {
for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
if (field != nullptr) {
@@ -2983,8 +2977,8 @@
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
MutableHandle<mirror::ClassLoader> temp_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
- for (int32_t i = 0; i < shared_libraries->GetLength(); ++i) {
- temp_loader.Assign(shared_libraries->Get(i));
+ for (auto loader : shared_libraries.Iterate<mirror::ClassLoader>()) {
+ temp_loader.Assign(loader);
if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result)) {
return false; // One of the shared libraries is not supported.
}
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 7b571aa..b5b156e 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -33,6 +33,7 @@
#include "handle_scope-inl.h"
#include "jni/jni_internal.h"
#include "mirror/class_loader-inl.h"
+#include "mirror/object.h"
#include "mirror/object_array-alloc-inl.h"
#include "nativehelper/scoped_local_ref.h"
#include "oat_file_assistant.h"
@@ -940,8 +941,7 @@
StackHandleScope<1> hs(soa.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);
+ for (auto element : dex_elements.Iterate<mirror::Object>()) {
if (element == nullptr) {
// Should never happen, log an error and break.
// TODO(calin): It's unclear if we should just assert here.
@@ -975,8 +975,7 @@
const ObjPtr<mirror::Class> dexfile_class = soa.Decode<mirror::Class>(
WellKnownClasses::dalvik_system_DexFile);
- for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
- ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
+ for (auto element : dex_elements.Iterate<mirror::Object>()) {
// We can hit a null element here because this is invoked with a partially filled dex_elements
// array from DexPathList. DexPathList will open each dex sequentially, each time passing the
// list of dex files which were opened before.
@@ -1086,8 +1085,8 @@
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries =
hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>());
MutableHandle<mirror::ClassLoader> temp_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
- for (int32_t i = 0; i < shared_libraries->GetLength(); ++i) {
- temp_loader.Assign(shared_libraries->Get(i));
+ for (auto library : shared_libraries.Iterate<mirror::ClassLoader>()) {
+ temp_loader.Assign(library);
if (!CreateInfoFromClassLoader(
soa, temp_loader, null_dex_elements, info, /*is_shared_library=*/ true)) {
return false;
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 2e85043..1c35360 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -23,6 +23,7 @@
#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"
@@ -86,8 +87,7 @@
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);
+ for (auto element : dex_elements.Iterate<mirror::Object>()) {
if (element == nullptr) {
// Should never happen, fail.
break;
diff --git a/runtime/handle.h b/runtime/handle.h
index f6ed173..6de4e88 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -32,6 +32,14 @@
class Thread;
template<class T> class Handle;
+template<typename T> class IterationRange;
+
+namespace mirror {
+template<typename T> class ObjectArray;
+template<typename T, typename C> class ArrayIter;
+template<typename T> using HandleArrayIter = ArrayIter<T, Handle<ObjectArray<T>>>;
+template<typename T> using ConstHandleArrayIter = ArrayIter<T, const Handle<ObjectArray<T>>>;
+} // namespace mirror
// Handles are memory locations that contain GC roots. As the mirror::Object*s within a handle are
// GC visible then the GC may move the references within them, something that couldn't be done with
@@ -67,6 +75,19 @@
return down_cast<T*>(reference_->AsMirrorPtr());
}
+ template <typename Type,
+ typename = typename std::enable_if_t<std::is_same_v<mirror::ObjectArray<Type>, T>>>
+ ALWAYS_INLINE IterationRange<mirror::ConstHandleArrayIter<Type>> ConstIterate() const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return T::ConstIterate(*this);
+ }
+ template <typename Type,
+ typename = typename std::enable_if_t<std::is_same_v<mirror::ObjectArray<Type>, T>>>
+ ALWAYS_INLINE IterationRange<mirror::HandleArrayIter<Type>> Iterate()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return T::Iterate(*this);
+ }
+
ALWAYS_INLINE bool IsNull() const {
// It's safe to null-check it without a read barrier.
return reference_->IsNull();
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 154302e..57bd191 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
#define ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
+#include "base/globals.h"
#include "object_array.h"
#include <string>
@@ -330,6 +331,49 @@
}
}
+template <class T>
+inline ConstObjPtrArrayIter<T> ObjectArray<T>::cbegin() const {
+ return ConstObjPtrArrayIter<T>(this, 0);
+}
+template <class T>
+inline ConstObjPtrArrayIter<T> ObjectArray<T>::cend() const {
+ return ConstObjPtrArrayIter<T>(this, GetLength());
+}
+template <class T>
+inline ConstHandleArrayIter<T> ObjectArray<T>::cbegin(const Handle<ObjectArray<T>>& h_this) {
+ return ConstHandleArrayIter<T>(h_this, 0);
+}
+template <class T>
+inline ConstHandleArrayIter<T> ObjectArray<T>::cend(const Handle<ObjectArray<T>>& h_this) {
+ return ConstHandleArrayIter<T>(h_this, h_this->GetLength());
+}
+
+template <class T>
+inline ObjPtrArrayIter<T> ObjectArray<T>::begin() {
+ return ObjPtrArrayIter<T>(this, 0);
+}
+template <class T>
+inline ObjPtrArrayIter<T> ObjectArray<T>::end() {
+ return ObjPtrArrayIter<T>(this, GetLength());
+}
+template <class T>
+inline HandleArrayIter<T> ObjectArray<T>::begin(Handle<ObjectArray<T>>& h_this) {
+ return HandleArrayIter<T>(h_this, 0);
+}
+template <class T>
+inline HandleArrayIter<T> ObjectArray<T>::end(Handle<ObjectArray<T>>& h_this) {
+ return HandleArrayIter<T>(h_this, h_this->GetLength());
+}
+
+template<typename T, typename C>
+inline void ArrayIter<T, C>::CheckIdx() const {
+ if (kIsDebugBuild) {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ }
+ DCHECK_LE(0, idx_);
+ DCHECK_LE(idx_, array_->GetLength());
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 7f43cd2..20362b2 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -17,12 +17,20 @@
#ifndef ART_RUNTIME_MIRROR_OBJECT_ARRAY_H_
#define ART_RUNTIME_MIRROR_OBJECT_ARRAY_H_
+#include <iterator>
#include "array.h"
+#include "base/iteration_range.h"
#include "obj_ptr.h"
namespace art {
namespace mirror {
+template<typename T, typename Container> class ArrayIter;
+template <typename T> using ConstObjPtrArrayIter = ArrayIter<T, const ObjPtr<ObjectArray<T>>>;
+template <typename T> using ConstHandleArrayIter = ArrayIter<T, const Handle<ObjectArray<T>>>;
+template <typename T> using ObjPtrArrayIter = ArrayIter<T, ObjPtr<ObjectArray<T>>>;
+template <typename T> using HandleArrayIter = ArrayIter<T, Handle<ObjectArray<T>>>;
+
template<class T>
class MANAGED ObjectArray: public Array {
public:
@@ -107,6 +115,34 @@
static MemberOffset OffsetOfElement(int32_t i);
+ inline ConstObjPtrArrayIter<T> cbegin() const REQUIRES_SHARED(Locks::mutator_lock_);
+ inline ConstObjPtrArrayIter<T> cend() const REQUIRES_SHARED(Locks::mutator_lock_);
+ inline IterationRange<ConstObjPtrArrayIter<T>> ConstIterate() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return IterationRange(cbegin(), cend());
+ }
+ inline ObjPtrArrayIter<T> begin() REQUIRES_SHARED(Locks::mutator_lock_);
+ inline ObjPtrArrayIter<T> end() REQUIRES_SHARED(Locks::mutator_lock_);
+ inline IterationRange<ObjPtrArrayIter<T>> Iterate() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return IterationRange(begin(), end());
+ }
+
+ static inline ConstHandleArrayIter<T> cbegin(const Handle<ObjectArray<T>>& h_this)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ static inline ConstHandleArrayIter<T> cend(const Handle<ObjectArray<T>>& h_this)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ static inline IterationRange<ConstHandleArrayIter<T>> ConstIterate(
+ const Handle<ObjectArray<T>>& h_this) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return IterationRange(cbegin(h_this), cend(h_this));
+ }
+ static inline HandleArrayIter<T> begin(Handle<ObjectArray<T>>& h_this)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ static inline HandleArrayIter<T> end(Handle<ObjectArray<T>>& h_this)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ static inline IterationRange<HandleArrayIter<T>> Iterate(Handle<ObjectArray<T>>& h_this)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return IterationRange(begin(h_this), end(h_this));
+ }
+
private:
// TODO fix thread safety analysis broken by the use of template. This should be
// REQUIRES_SHARED(Locks::mutator_lock_).
@@ -117,6 +153,65 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectArray);
};
+// Everything is NO_THREAD_SAFETY_ANALYSIS to work-around STL incompat with thread-annotations.
+// Everything should have REQUIRES_SHARED(Locks::mutator_lock_).
+template <typename T, typename Container>
+class ArrayIter : public std::iterator<std::forward_iterator_tag, ObjPtr<T>> {
+ private:
+ using Iter = ArrayIter<T, Container>;
+
+ public:
+ ArrayIter(Container array, int32_t idx) NO_THREAD_SAFETY_ANALYSIS : array_(array), idx_(idx) {
+ CheckIdx();
+ }
+
+ ArrayIter(const Iter& other) = default; // NOLINT(runtime/explicit)
+ Iter& operator=(const Iter& other) = default;
+
+ bool operator!=(const Iter& other) const NO_THREAD_SAFETY_ANALYSIS {
+ CheckIdx();
+ return !(*this == other);
+ }
+ bool operator==(const Iter& other) const NO_THREAD_SAFETY_ANALYSIS {
+ return Ptr(other.array_) == Ptr(array_) && other.idx_ == idx_;
+ }
+ Iter& operator++() NO_THREAD_SAFETY_ANALYSIS {
+ idx_++;
+ CheckIdx();
+ return *this;
+ }
+ Iter operator++(int) const NO_THREAD_SAFETY_ANALYSIS {
+ Iter res(this);
+ idx_++;
+ CheckIdx();
+ return res;
+ }
+ ObjPtr<T> operator->() const NO_THREAD_SAFETY_ANALYSIS {
+ CheckIdx();
+ return array_->GetWithoutChecks(idx_);
+ }
+ ObjPtr<T> operator*() const NO_THREAD_SAFETY_ANALYSIS {
+ CheckIdx();
+ return array_->GetWithoutChecks(idx_);
+ }
+
+ private:
+ // Checks current index and that locks are properly held.
+ void CheckIdx() const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static ObjectArray<T>* Ptr(const Handle<ObjectArray<T>>& p)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return p.Get();
+ }
+ static ObjectArray<T>* Ptr(const ObjPtr<ObjectArray<T>>& p)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return p.Ptr();
+ }
+
+ Container array_;
+ int32_t idx_;
+};
+
} // namespace mirror
} // namespace art