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/libartbase/base/stl_util.h b/libartbase/base/stl_util.h
index 1e071ce..fbafd53 100644
--- a/libartbase/base/stl_util.h
+++ b/libartbase/base/stl_util.h
@@ -18,10 +18,13 @@
 #define ART_LIBARTBASE_BASE_STL_UTIL_H_
 
 #include <algorithm>
+#include <iterator>
 #include <sstream>
 
 #include <android-base/logging.h>
 
+#include "base/iteration_range.h"
+
 namespace art {
 
 // STLDeleteContainerPointers()
@@ -146,6 +149,80 @@
   return result;
 }
 
+template <typename IterLeft, typename IterRight>
+class ZipLeftIter : public std::iterator<
+                        std::forward_iterator_tag,
+                        std::pair<typename IterLeft::value_type, typename IterRight::value_type>> {
+ public:
+  ZipLeftIter(IterLeft left, IterRight right) : left_iter_(left), right_iter_(right) {}
+  ZipLeftIter<IterLeft, IterRight>& operator++() {
+    ++left_iter_;
+    ++right_iter_;
+    return *this;
+  }
+  ZipLeftIter<IterLeft, IterRight> operator++(int) {
+    ZipLeftIter<IterLeft, IterRight> ret(left_iter_, right_iter_);
+    ++(*this);
+    return ret;
+  }
+  bool operator==(const ZipLeftIter<IterLeft, IterRight>& other) const {
+    return left_iter_ == other.left_iter_;
+  }
+  bool operator!=(const ZipLeftIter<IterLeft, IterRight>& other) const {
+    return !(*this == other);
+  }
+  std::pair<typename IterLeft::value_type, typename IterRight::value_type> operator*() const {
+    return std::make_pair(*left_iter_, *right_iter_);
+  }
+
+ private:
+  IterLeft left_iter_;
+  IterRight right_iter_;
+};
+
+class CountIter : public std::iterator<std::forward_iterator_tag, size_t, size_t, size_t, size_t> {
+ public:
+  CountIter() : count_(0) {}
+  explicit CountIter(size_t count) : count_(count) {}
+  CountIter& operator++() {
+    ++count_;
+    return *this;
+  }
+  CountIter operator++(int) {
+    size_t ret = count_;
+    ++count_;
+    return CountIter(ret);
+  }
+  bool operator==(const CountIter& other) const {
+    return count_ == other.count_;
+  }
+  bool operator!=(const CountIter& other) const {
+    return !(*this == other);
+  }
+  size_t operator*() const {
+    return count_;
+  }
+
+ private:
+  size_t count_;
+};
+
+// Make an iteration range that returns a pair of the element and the index of the element.
+template <typename Iter>
+static inline IterationRange<ZipLeftIter<Iter, CountIter>> ZipCount(IterationRange<Iter> iter) {
+  return IterationRange(ZipLeftIter(iter.begin(), CountIter(0)),
+                        ZipLeftIter(iter.end(), CountIter(-1)));
+}
+
+// Make an iteration range that returns a pair of the outputs of two iterators. Stops when the first
+// (left) one is exhausted. The left iterator must be at least as long as the right one.
+template <typename IterLeft, typename IterRight>
+static inline IterationRange<ZipLeftIter<IterLeft, IterRight>> ZipLeft(
+    IterationRange<IterLeft> iter_left, IterationRange<IterRight> iter_right) {
+  return IterationRange(ZipLeftIter(iter_left.begin(), iter_right.begin()),
+                        ZipLeftIter(iter_left.end(), iter_right.end()));
+}
+
 }  // namespace art
 
 #endif  // ART_LIBARTBASE_BASE_STL_UTIL_H_
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 82ce916..4d6b41a 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -726,8 +726,8 @@
           art::annotations::GetSignatureAnnotationForClass(h_klass);
       if (str_array != nullptr) {
         std::ostringstream oss;
-        for (int32_t i = 0; i != str_array->GetLength(); ++i) {
-          oss << str_array->Get(i)->ToModifiedUtf8();
+        for (auto str : str_array->Iterate()) {
+          oss << str->ToModifiedUtf8();
         }
         std::string output_string = oss.str();
         jvmtiError ret;
diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h
index 9b04841..29ea684 100644
--- a/openjdkjvmti/ti_class_loader-inl.h
+++ b/openjdkjvmti/ti_class_loader-inl.h
@@ -57,10 +57,8 @@
     return;
   }
 
-  size_t num_elements = dex_elements_list->GetLength();
   // Iterate over the DexPathList$Element to find the right one
-  for (size_t i = 0; i < num_elements; i++) {
-    art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i);
+  for (auto current_element : dex_elements_list.Iterate<art::mirror::Object>()) {
     CHECK(!current_element.IsNull());
     art::ObjPtr<art::mirror::Object> dex_file(element_dex_file_field->GetObject(current_element));
     if (!dex_file.IsNull()) {
diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc
index e5b9637..d4c0ec8 100644
--- a/openjdkjvmti/ti_field.cc
+++ b/openjdkjvmti/ti_field.cc
@@ -176,8 +176,8 @@
           art::annotations::GetSignatureAnnotationForField(art_field);
       if (str_array != nullptr) {
         std::ostringstream oss;
-        for (int32_t i = 0; i != str_array->GetLength(); ++i) {
-          oss << str_array->Get(i)->ToModifiedUtf8();
+        for (auto str : str_array->Iterate()) {
+          oss << str->ToModifiedUtf8();
         }
         std::string output_string = oss.str();
         jvmtiError ret;
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index 702a1c4..1d18390 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -1166,16 +1166,14 @@
     if (array->IsObjectArray()) {
       art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> obj_array =
           array->AsObjectArray<art::mirror::Object>();
-      int32_t length = obj_array->GetLength();
-      for (int32_t i = 0; i != length; ++i) {
-        art::ObjPtr<art::mirror::Object> elem = obj_array->GetWithoutChecks(i);
-        if (elem != nullptr) {
+      for (auto elem_pair : art::ZipCount(obj_array->Iterate())) {
+        if (elem_pair.first != nullptr) {
           jvmtiHeapReferenceInfo reference_info;
-          reference_info.array.index = i;
+          reference_info.array.index = elem_pair.second;
           stop_reports_ = !ReportReferenceMaybeEnqueue(JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT,
                                                        &reference_info,
                                                        array,
-                                                       elem.Ptr());
+                                                       elem_pair.first.Ptr());
           if (stop_reports_) {
             break;
           }
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index d36e2c9..e7f071f 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -354,8 +354,8 @@
           art::annotations::GetSignatureAnnotationForMethod(art_method);
       if (str_array != nullptr) {
         std::ostringstream oss;
-        for (int32_t i = 0; i != str_array->GetLength(); ++i) {
-          oss << str_array->Get(i)->ToModifiedUtf8();
+        for (auto str : str_array->Iterate()) {
+          oss << str->ToModifiedUtf8();
         }
         std::string output_string = oss.str();
         jvmtiError ret;
diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc
index f2e5c90..bc912cf 100644
--- a/openjdkjvmti/ti_threadgroup.cc
+++ b/openjdkjvmti/ti_threadgroup.cc
@@ -207,8 +207,7 @@
       groups_array->AsObjectArray<art::mirror::Object>();
 
   // Copy all non-null elements.
-  for (int32_t i = 0; i < groups_array_as_array->GetLength(); ++i) {
-    art::ObjPtr<art::mirror::Object> entry = groups_array_as_array->Get(i);
+  for (auto entry : groups_array_as_array->Iterate()) {
     if (entry != nullptr) {
       thread_groups->push_back(entry);
     }
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