Add ClassAccessor

Aims to replace ClassDataItemIterator with a cleaner and simpler
abstraction.

Bug: 77709234
Bug: 79758018
Test: test-art-host

Change-Id: I871a3e1cf213e0d81bfe4bb77790fbab2d13e44c
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 19b1900..082e609 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -20,6 +20,7 @@
 #include "common_compiler_test.h"
 #include "compiled_method-inl.h"
 #include "compiler_callbacks.h"
+#include "dex/class_accessor-inl.h"
 #include "dex/dex_file.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
@@ -82,30 +83,21 @@
 
     // Unquicken the dex file.
     for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) {
-      const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i);
-      const uint8_t* class_data = updated_dex_file->GetClassData(class_def);
-      if (class_data == nullptr) {
-        continue;
-      }
-      ClassDataItemIterator it(*updated_dex_file, class_data);
-      it.SkipAllFields();
-
       // Unquicken each method.
-      while (it.HasNextMethod()) {
-        uint32_t method_idx = it.GetMemberIndex();
-        CompiledMethod* compiled_method =
-            compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
+      ClassAccessor accessor(*updated_dex_file, updated_dex_file->GetClassDef(i));
+      accessor.VisitMethods([&](const ClassAccessor::Method& method) {
+        CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(
+            MethodReference(updated_dex_file,
+                            method.GetIndex()));
         ArrayRef<const uint8_t> table;
         if (compiled_method != nullptr) {
           table = compiled_method->GetVmapTable();
         }
         optimizer::ArtDecompileDEX(*updated_dex_file,
-                                   *it.GetMethodCodeItem(),
+                                   *accessor.GetCodeItem(method),
                                    table,
                                    /* decompile_return_instruction */ true);
-        it.Next();
-      }
-      DCHECK(!it.HasNext());
+      });
     }
 
     // Make sure after unquickening we go back to the same contents as the original dex file.
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index e72d49e..f5a13f0 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -45,6 +45,7 @@
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
+#include "dex/class_accessor-inl.h"
 #include "dex/code_item_accessors-inl.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_exception_helpers.h"
@@ -611,19 +612,11 @@
           pClassDef.class_data_off_, pClassDef.class_data_off_);
 
   // Fields and methods.
-  const u1* pEncodedData = pDexFile->GetClassData(pClassDef);
-  if (pEncodedData != nullptr) {
-    ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
-    fprintf(gOutFile, "static_fields_size  : %d\n", pClassData.NumStaticFields());
-    fprintf(gOutFile, "instance_fields_size: %d\n", pClassData.NumInstanceFields());
-    fprintf(gOutFile, "direct_methods_size : %d\n", pClassData.NumDirectMethods());
-    fprintf(gOutFile, "virtual_methods_size: %d\n", pClassData.NumVirtualMethods());
-  } else {
-    fprintf(gOutFile, "static_fields_size  : 0\n");
-    fprintf(gOutFile, "instance_fields_size: 0\n");
-    fprintf(gOutFile, "direct_methods_size : 0\n");
-    fprintf(gOutFile, "virtual_methods_size: 0\n");
-  }
+  ClassAccessor accessor(*pDexFile, pClassDef);
+  fprintf(gOutFile, "static_fields_size  : %d\n", accessor.NumStaticFields());
+  fprintf(gOutFile, "instance_fields_size: %d\n", accessor.NumInstanceFields());
+  fprintf(gOutFile, "direct_methods_size : %d\n", accessor.NumDirectMethods());
+  fprintf(gOutFile, "virtual_methods_size: %d\n", accessor.NumVirtualMethods());
   fprintf(gOutFile, "\n");
 }
 
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
new file mode 100644
index 0000000..bcd0a7b
--- /dev/null
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
+#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
+
+#include "class_accessor.h"
+
+#include "base/leb128.h"
+
+namespace art {
+
+inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def)
+    : ClassAccessor(dex_file, dex_file.GetClassData(class_def)) {}
+
+inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const uint8_t* class_data)
+    : dex_file_(dex_file),
+      ptr_pos_(class_data),
+      num_static_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
+      num_instance_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
+      num_direct_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
+      num_virtual_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {}
+
+inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) {
+  method_idx_ += DecodeUnsignedLeb128(&ptr);
+  access_flags_ = DecodeUnsignedLeb128(&ptr);
+  code_off_ = DecodeUnsignedLeb128(&ptr);
+  return ptr;
+}
+
+inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) {
+  field_idx_ += DecodeUnsignedLeb128(&ptr);
+  access_flags_ = DecodeUnsignedLeb128(&ptr);
+  return ptr;
+}
+
+template <typename StaticFieldVisitor,
+          typename InstanceFieldVisitor,
+          typename DirectMethodVisitor,
+          typename VirtualMethodVisitor>
+inline void ClassAccessor::VisitMethodsAndFields(
+    const StaticFieldVisitor& static_field_visitor,
+    const InstanceFieldVisitor& instance_field_visitor,
+    const DirectMethodVisitor& direct_method_visitor,
+    const VirtualMethodVisitor& virtual_method_visitor) const {
+  const uint8_t* ptr = ptr_pos_;
+  for (size_t i = 0; i < num_static_fields_; ++i) {
+    Field data;
+    ptr = data.Read(ptr);
+    static_field_visitor(data);
+  }
+  for (size_t i = 0; i < num_instance_fields_; ++i) {
+    Field data;
+    ptr = data.Read(ptr);
+    instance_field_visitor(data);
+  }
+  for (size_t i = 0; i < num_direct_methods_; ++i) {
+    Method data;
+    ptr = data.Read(ptr);
+    direct_method_visitor(data);
+  }
+  for (size_t i = 0; i < num_virtual_methods_; ++i) {
+    Method data;
+    ptr = data.Read(ptr);
+    virtual_method_visitor(data);
+  }
+}
+
+template <typename DirectMethodVisitor,
+          typename VirtualMethodVisitor>
+inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor,
+                                        const VirtualMethodVisitor& virtual_method_visitor) const {
+  VisitMethodsAndFields(VoidFunctor(),
+                        VoidFunctor(),
+                        direct_method_visitor,
+                        virtual_method_visitor);
+}
+
+// Visit direct and virtual methods.
+template <typename MethodVisitor>
+inline void ClassAccessor::VisitMethods(const MethodVisitor& method_visitor) const {
+  VisitMethods(method_visitor, method_visitor);
+}
+
+inline const DexFile::CodeItem* ClassAccessor::GetCodeItem(const Method& method) const {
+  return dex_file_.GetCodeItem(method.GetCodeItemOffset());
+}
+
+}  // namespace art
+
+#endif  // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_
diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h
new file mode 100644
index 0000000..59a6b5d
--- /dev/null
+++ b/libdexfile/dex/class_accessor.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
+#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
+
+#include "base/utils.h"
+#include "dex_file.h"
+
+namespace art {
+
+// Classes to access Dex data.
+class ClassAccessor {
+ public:
+  // Class method data.
+  class Method {
+   public:
+    uint32_t GetIndex() const {
+      return method_idx_;
+    }
+
+    uint32_t GetAccessFlags() const {
+      return access_flags_;
+    }
+
+    uint32_t GetCodeItemOffset() const {
+      return code_off_;
+    }
+
+   private:
+    const uint8_t* Read(const uint8_t* ptr);
+
+    // A decoded version of the method of a class_data_item.
+    uint32_t method_idx_ = 0u;
+    uint32_t access_flags_ = 0u;
+    uint32_t code_off_ = 0u;
+
+    friend class ClassAccessor;
+  };
+
+  // Class field data.
+  class Field {
+   public:
+    uint32_t GetIndex() const {
+      return field_idx_;
+    }
+
+    uint32_t GetAccessFlags() const {
+      return access_flags_;
+    }
+
+   private:
+    const uint8_t* Read(const uint8_t* ptr);
+
+    // A decoded version of the field of a class_data_item.
+    uint32_t field_idx_ = 0u;
+    uint32_t access_flags_ = 0u;
+
+    friend class ClassAccessor;
+  };
+
+  ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def);
+
+  // Return the code item for a method.
+  const DexFile::CodeItem* GetCodeItem(const Method& method) const;
+
+  // Iterator data is not very iterator friendly, use visitors to get around this.
+  template <typename StaticFieldVisitor,
+            typename InstanceFieldVisitor,
+            typename DirectMethodVisitor,
+            typename VirtualMethodVisitor>
+  void VisitMethodsAndFields(const StaticFieldVisitor& static_field_visitor,
+                             const InstanceFieldVisitor& instance_field_visitor,
+                             const DirectMethodVisitor& direct_method_visitor,
+                             const VirtualMethodVisitor& virtual_method_visitor) const;
+
+  template <typename DirectMethodVisitor,
+            typename VirtualMethodVisitor>
+  void VisitMethods(const DirectMethodVisitor& direct_method_visitor,
+                    const VirtualMethodVisitor& virtual_method_visitor) const;
+
+  // Visit direct and virtual methods.
+  template <typename MethodVisitor>
+  void VisitMethods(const MethodVisitor& method_visitor) const;
+
+  uint32_t NumStaticFields() const {
+    return num_static_fields_;
+  }
+
+  uint32_t NumInstanceFields() const {
+    return num_instance_fields_;
+  }
+
+  uint32_t NumDirectMethods() const {
+    return num_direct_methods_;
+  }
+
+  uint32_t NumVirtualMethods() const {
+    return num_virtual_methods_;
+  }
+
+ protected:
+  ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const uint8_t* class_data);
+
+  const DexFile& dex_file_;
+  const uint8_t* ptr_pos_ = nullptr;  // Pointer into stream of class_data_item.
+  const uint32_t num_static_fields_ = 0u;
+  const uint32_t num_instance_fields_ = 0u;
+  const uint32_t num_direct_methods_ = 0u;
+  const uint32_t num_virtual_methods_ = 0u;
+  // Only cache descriptor.
+  const void* class_def_ = nullptr;
+  const void* class_data_ = nullptr;
+};
+
+}  // namespace art
+
+#endif  // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index adc5154..f9bf45d 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include "android-base/stringprintf.h"
+#include "dex/class_accessor-inl.h"
 #include "dex/code_item_accessors-inl.h"
 #include "dex/dex_instruction-inl.h"
 #include "dex/standard_dex_file.h"
@@ -126,79 +127,73 @@
   num_class_defs_ += dex_file.NumClassDefs();
   for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); ++class_def_index) {
     const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-    const uint8_t* class_data = dex_file.GetClassData(class_def);
-    if (class_data == nullptr) {
-      continue;
-    }
-    ClassDataItemIterator it(dex_file, class_data);
-    it.SkipAllFields();
+    ClassAccessor accessor(dex_file, class_def);
     std::set<size_t> unique_method_ids;
     std::set<size_t> unique_string_ids;
-    while (it.HasNextMethod()) {
-      const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
-      if (code_item != nullptr) {
-        CodeItemInstructionAccessor instructions(dex_file, code_item);
-        const uint16_t* code_ptr = instructions.Insns();
-        dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]);
-        for (const DexInstructionPcPair& inst : instructions) {
-          switch (inst->Opcode()) {
-            case Instruction::CONST_STRING: {
-              const dex::StringIndex string_index(inst->VRegB_21c());
-              unique_string_ids.insert(string_index.index_);
-              ++num_string_ids_from_code_;
-              break;
-            }
-            case Instruction::CONST_STRING_JUMBO: {
-              const dex::StringIndex string_index(inst->VRegB_31c());
-              unique_string_ids.insert(string_index.index_);
-              ++num_string_ids_from_code_;
-              break;
-            }
-            // Invoke cases.
-            case Instruction::INVOKE_VIRTUAL:
-            case Instruction::INVOKE_VIRTUAL_RANGE: {
-              bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
-              uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
-              if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
-                ++same_class_virtual_;
-              } else {
-                ++other_class_virtual_;
-                unique_method_ids.insert(method_idx);
-              }
-              break;
-            }
-            case Instruction::INVOKE_DIRECT:
-            case Instruction::INVOKE_DIRECT_RANGE: {
-              bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
-              uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
-              if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
-                ++same_class_direct_;
-              } else {
-                ++other_class_direct_;
-                unique_method_ids.insert(method_idx);
-              }
-              break;
-            }
-            case Instruction::INVOKE_STATIC:
-            case Instruction::INVOKE_STATIC_RANGE: {
-              bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE);
-              uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
-              if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
-                ++same_class_static_;
-              } else {
-                ++other_class_static_;
-                unique_method_ids.insert(method_idx);
-              }
-              break;
-            }
-            default:
-              break;
+    accessor.VisitMethods([&](const ClassAccessor::Method& method) {
+      const DexFile::CodeItem* code_item = accessor.GetCodeItem(method);
+      if (code_item == nullptr) {
+        return;
+      }
+      CodeItemInstructionAccessor instructions(dex_file, code_item);
+      const uint16_t* code_ptr = instructions.Insns();
+      dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]);
+      for (const DexInstructionPcPair& inst : instructions) {
+        switch (inst->Opcode()) {
+          case Instruction::CONST_STRING: {
+            const dex::StringIndex string_index(inst->VRegB_21c());
+            unique_string_ids.insert(string_index.index_);
+            ++num_string_ids_from_code_;
+            break;
           }
+          case Instruction::CONST_STRING_JUMBO: {
+            const dex::StringIndex string_index(inst->VRegB_31c());
+            unique_string_ids.insert(string_index.index_);
+            ++num_string_ids_from_code_;
+            break;
+          }
+          // Invoke cases.
+          case Instruction::INVOKE_VIRTUAL:
+          case Instruction::INVOKE_VIRTUAL_RANGE: {
+            bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
+            uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+            if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
+              ++same_class_virtual_;
+            } else {
+              ++other_class_virtual_;
+              unique_method_ids.insert(method_idx);
+            }
+            break;
+          }
+          case Instruction::INVOKE_DIRECT:
+          case Instruction::INVOKE_DIRECT_RANGE: {
+            bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
+            uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+            if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
+              ++same_class_direct_;
+            } else {
+              ++other_class_direct_;
+              unique_method_ids.insert(method_idx);
+            }
+            break;
+          }
+          case Instruction::INVOKE_STATIC:
+          case Instruction::INVOKE_STATIC_RANGE: {
+            bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE);
+            uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+            if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) {
+              ++same_class_static_;
+            } else {
+              ++other_class_static_;
+              unique_method_ids.insert(method_idx);
+            }
+            break;
+          }
+          default:
+            break;
         }
       }
-      it.Next();
-    }
-    DCHECK(!it.HasNext());
+    });
     total_unique_method_idx_ += unique_method_ids.size();
     total_unique_string_ids_ += unique_string_ids.size();
   }