Add number of methods to OatClass.

And use it for checks when reading oat file data.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 148412019
Change-Id: I3a2433f94397107a2ce3c198d22e660871c505a8
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 9898e7e..dae9d89 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -271,7 +271,15 @@
   dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
 
   // Data to write.
-  uint32_t method_bitmap_size_;
+
+  // Number of methods recorded in OatClass. For `OatClassType::kNoneCompiled`
+  // this shall be zero and shall not be written to the file, otherwise it
+  // shall be the number of methods in the class definition. It is used to
+  // determine the size of `BitVector` data for `OatClassType::kSomeCompiled` and
+  // the size of the `OatMethodOffsets` table for `OatClassType::kAllCompiled`.
+  // (The size of the `OatMethodOffsets` table for `OatClassType::kSomeCompiled`
+  // is determined by the number of bits set in the `BitVector` data.)
+  uint32_t num_methods_;
 
   // Bit vector indexed by ClassDef method index. When OatClass::type_ is
   // OatClassType::kSomeCompiled, a set bit indicates the method has an
@@ -466,6 +474,7 @@
     size_oat_class_offsets_(0),
     size_oat_class_type_(0),
     size_oat_class_status_(0),
+    size_oat_class_num_methods_(0),
     size_oat_class_method_bitmaps_(0),
     size_oat_class_method_offsets_(0),
     size_method_bss_mappings_(0u),
@@ -2665,6 +2674,7 @@
     DO_STAT(size_oat_class_offsets_);
     DO_STAT(size_oat_class_type_);
     DO_STAT(size_oat_class_status_);
+    DO_STAT(size_oat_class_num_methods_);
     DO_STAT(size_oat_class_method_bitmaps_);
     DO_STAT(size_oat_class_method_offsets_);
     DO_STAT(size_method_bss_mappings_);
@@ -3986,15 +3996,18 @@
   method_headers_.resize(compiled_methods_with_code);
 
   uint32_t oat_method_offsets_offset_from_oat_class = OatClassHeader::SizeOf();
-  // We only create this instance if there are at least some compiled.
-  if (oat_class_type == enum_cast<uint16_t>(OatClassType::kSomeCompiled)) {
-    method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
-    method_bitmap_size_ = method_bitmap_->GetSizeOf();
-    oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
-    oat_method_offsets_offset_from_oat_class += method_bitmap_size_;
-  } else {
-    method_bitmap_ = nullptr;
-    method_bitmap_size_ = 0;
+  // We only write method-related data if there are at least some compiled methods.
+  num_methods_ = 0u;
+  DCHECK(method_bitmap_ == nullptr);
+  if (oat_class_type != enum_cast<uint16_t>(OatClassType::kNoneCompiled)) {
+    num_methods_ = num_methods;
+    oat_method_offsets_offset_from_oat_class += sizeof(num_methods_);
+    if (oat_class_type == enum_cast<uint16_t>(OatClassType::kSomeCompiled)) {
+      method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
+      uint32_t bitmap_size = BitVector::BitsToWords(num_methods) * BitVector::kWordBytes;
+      DCHECK_EQ(bitmap_size, method_bitmap_->GetSizeOf());
+      oat_method_offsets_offset_from_oat_class += bitmap_size;
+    }
   }
 
   for (size_t i = 0; i < num_methods; i++) {
@@ -4012,9 +4025,9 @@
 }
 
 size_t OatWriter::OatClass::SizeOf() const {
-  return ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
-          + method_bitmap_size_
-          + (sizeof(method_offsets_[0]) * method_offsets_.size());
+  return ((num_methods_ == 0) ? 0 : sizeof(num_methods_)) +
+         ((method_bitmap_ != nullptr) ? method_bitmap_->GetSizeOf() : 0u) +
+         (sizeof(method_offsets_[0]) * method_offsets_.size());
 }
 
 bool OatWriter::OatClassHeader::Write(OatWriter* oat_writer,
@@ -4036,18 +4049,20 @@
 }
 
 bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const {
-  if (method_bitmap_size_ != 0) {
-    if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
-      PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
+  if (num_methods_ != 0u) {
+    if (!out->WriteFully(&num_methods_, sizeof(num_methods_))) {
+      PLOG(ERROR) << "Failed to write number of methods to " << out->GetLocation();
       return false;
     }
-    oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_);
+    oat_writer->size_oat_class_num_methods_ += sizeof(num_methods_);
+  }
 
-    if (!out->WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
+  if (method_bitmap_ != nullptr) {
+    if (!out->WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_->GetSizeOf())) {
       PLOG(ERROR) << "Failed to write method bitmap to " << out->GetLocation();
       return false;
     }
-    oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_;
+    oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_->GetSizeOf();
   }
 
   if (!out->WriteFully(method_offsets_.data(), GetMethodOffsetsRawSize())) {
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index 5fbc01b..87174eb 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -538,6 +538,7 @@
   uint32_t size_oat_class_offsets_;
   uint32_t size_oat_class_type_;
   uint32_t size_oat_class_status_;
+  uint32_t size_oat_class_num_methods_;
   uint32_t size_oat_class_method_bitmaps_;
   uint32_t size_oat_class_method_offsets_;
   uint32_t size_method_bss_mappings_;
diff --git a/runtime/oat.h b/runtime/oat.h
index 9d872f9..8205935 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
-  // Last oat version changed reason: Move TypeLookupTables in .vdex file.
-  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '9', '3', '\0' } };
+  // Last oat version changed reason: Record number of methods in OatClass.
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '9', '4', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b646eb1..d4afe0c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -2089,30 +2089,35 @@
   ClassStatus status = enum_cast<ClassStatus>(status_value);
   OatClassType type = enum_cast<OatClassType>(type_value);
 
-  uint32_t bitmap_size = 0;
+  uint32_t num_methods = 0;
   const uint32_t* bitmap_pointer = nullptr;
   const OatMethodOffsets* methods_pointer = nullptr;
   if (type != OatClassType::kNoneCompiled) {
+    CHECK_LE(sizeof(uint32_t), static_cast<size_t>(oat_file_->End() - current_pointer))
+        << oat_file_->GetLocation();
+    num_methods = *reinterpret_cast<const uint32_t*>(current_pointer);
+    current_pointer += sizeof(uint32_t);
+    CHECK_NE(num_methods, 0u) << oat_file_->GetLocation();
+    uint32_t num_method_offsets;
     if (type == OatClassType::kSomeCompiled) {
-      CHECK_LE(sizeof(uint32_t), static_cast<size_t>(oat_file_->End() - current_pointer))
-          << oat_file_->GetLocation();
-      bitmap_size = *reinterpret_cast<const uint32_t*>(current_pointer);
-      current_pointer += sizeof(uint32_t);
+      uint32_t bitmap_size = BitVector::BitsToWords(num_methods) * BitVector::kWordBytes;
       CHECK_LE(bitmap_size, static_cast<size_t>(oat_file_->End() - current_pointer))
           << oat_file_->GetLocation();
       bitmap_pointer = reinterpret_cast<const uint32_t*>(current_pointer);
       current_pointer += bitmap_size;
-      CHECK_LE(BitVector::NumSetBits(bitmap_pointer, bitmap_size * kBitsPerByte),
-               static_cast<size_t>(oat_file_->End() - current_pointer) / sizeof(OatMethodOffsets))
-          << oat_file_->GetLocation();
+      // Note: The bits in range [num_methods, bitmap_size * kBitsPerByte)
+      // should be zero but we're not verifying that.
+      num_method_offsets = BitVector::NumSetBits(bitmap_pointer, num_methods);
     } else {
-      // TODO: We do not have enough information here to check if the array extends beyond
-      // the end of the oat file. We should record the number of methods in the oat file.
+      num_method_offsets = num_methods;
     }
+    CHECK_LE(num_method_offsets,
+             static_cast<size_t>(oat_file_->End() - current_pointer) / sizeof(OatMethodOffsets))
+        << oat_file_->GetLocation();
     methods_pointer = reinterpret_cast<const OatMethodOffsets*>(current_pointer);
   }
 
-  return OatFile::OatClass(oat_file_, status, type, bitmap_size, bitmap_pointer, methods_pointer);
+  return OatFile::OatClass(oat_file_, status, type, num_methods, bitmap_pointer, methods_pointer);
 }
 
 const dex::ClassDef* OatDexFile::FindClassDef(const DexFile& dex_file,
@@ -2182,35 +2187,18 @@
 OatFile::OatClass::OatClass(const OatFile* oat_file,
                             ClassStatus status,
                             OatClassType type,
-                            uint32_t bitmap_size,
+                            uint32_t num_methods,
                             const uint32_t* bitmap_pointer,
                             const OatMethodOffsets* methods_pointer)
-    : oat_file_(oat_file), status_(status), type_(type),
-      bitmap_(bitmap_pointer), methods_pointer_(methods_pointer) {
-    switch (type_) {
-      case OatClassType::kAllCompiled: {
-        CHECK_EQ(0U, bitmap_size);
-        CHECK(bitmap_pointer == nullptr);
-        CHECK(methods_pointer != nullptr);
-        break;
-      }
-      case OatClassType::kSomeCompiled: {
-        CHECK_NE(0U, bitmap_size);
-        CHECK(bitmap_pointer != nullptr);
-        CHECK(methods_pointer != nullptr);
-        break;
-      }
-      case OatClassType::kNoneCompiled: {
-        CHECK_EQ(0U, bitmap_size);
-        CHECK(bitmap_pointer == nullptr);
-        CHECK(methods_pointer_ == nullptr);
-        break;
-      }
-      case OatClassType::kOatClassMax: {
-        LOG(FATAL) << "Invalid OatClassType " << type_;
-        UNREACHABLE();
-      }
-    }
+    : oat_file_(oat_file),
+      status_(status),
+      type_(type),
+      num_methods_(num_methods),
+      bitmap_(bitmap_pointer),
+      methods_pointer_(methods_pointer) {
+  DCHECK_EQ(num_methods != 0u, type != OatClassType::kNoneCompiled);
+  DCHECK_EQ(bitmap_pointer != nullptr, type == OatClassType::kSomeCompiled);
+  DCHECK_EQ(methods_pointer != nullptr, type != OatClassType::kNoneCompiled);
 }
 
 uint32_t OatFile::OatClass::GetOatMethodOffsetsOffset(uint32_t method_index) const {
@@ -2222,11 +2210,13 @@
 }
 
 const OatMethodOffsets* OatFile::OatClass::GetOatMethodOffsets(uint32_t method_index) const {
-  // NOTE: We don't keep the number of methods and cannot do a bounds check for method_index.
+  // NOTE: We don't keep the number of methods for `kNoneCompiled` and cannot do
+  // a bounds check for `method_index` in that case.
   if (methods_pointer_ == nullptr) {
     CHECK_EQ(OatClassType::kNoneCompiled, type_);
     return nullptr;
   }
+  CHECK_LT(method_index, num_methods_) << oat_file_->GetLocation();
   size_t methods_pointer_index;
   if (bitmap_ == nullptr) {
     CHECK_EQ(OatClassType::kAllCompiled, type_);
@@ -2239,6 +2229,12 @@
     size_t num_set_bits = BitVector::NumSetBits(bitmap_, method_index);
     methods_pointer_index = num_set_bits;
   }
+  if (kIsDebugBuild) {
+    size_t size_until_end = dchecked_integral_cast<size_t>(
+        oat_file_->End() - reinterpret_cast<const uint8_t*>(methods_pointer_));
+    CHECK_LE(methods_pointer_index, size_until_end / sizeof(OatMethodOffsets))
+        << oat_file_->GetLocation();
+  }
   const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index];
   return &oat_method_offsets;
 }
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 3940bfe..0fd7032 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -283,18 +283,15 @@
     OatClass(const OatFile* oat_file,
              ClassStatus status,
              OatClassType type,
-             uint32_t bitmap_size,
+             uint32_t num_methods,
              const uint32_t* bitmap_pointer,
              const OatMethodOffsets* methods_pointer);
 
     const OatFile* const oat_file_;
-
     const ClassStatus status_;
-
     const OatClassType type_;
-
+    const uint32_t num_methods_;
     const uint32_t* const bitmap_;
-
     const OatMethodOffsets* const methods_pointer_;
 
     friend class art::OatDexFile;