Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  class.cc

  Sprache: C
 

/*
 * Copyright (C) 2011 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.
 */


#include "class.h"

#include <unordered_set>
#include <string_view>

#include "android-base/macros.h"
#include "android-base/stringprintf.h"

#include "array-inl.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/inlined_vector.h"
#include "base/logging.h"  // For VLOG.
#include "base/pointer_size.h"
#include "base/sdk_version.h"
#include "base/utils.h"
#include "class-inl.h"
#include "class_ext-inl.h"
#include "class_linker-inl.h"
#include "class_loader.h"
#include "class_root-inl.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_annotations.h"
#include "dex/signature-inl.h"
#include "dex_cache-inl.h"
#include "field.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/heap-inl.h"
#include "handle_scope-inl.h"
#include "hidden_api.h"
#include "jni_id_type.h"
#include "mirror/class_flags.h"
#include "subtype_check.h"
#include "method.h"
#include "object-inl.h"
#include "object-refvisitor-inl.h"
#include "object_array-alloc-inl.h"
#include "object_array-inl.h"
#include "object_lock.h"
#include "string-inl.h"
#include "runtime.h"
#include "thread.h"
#include "throwable.h"
#include "well_known_classes.h"

namespace art HIDDEN {

namespace mirror {

using android::base::StringPrintf;

bool Class::IsMirrored() {
  if (LIKELY(!IsBootStrapClassLoaded())) {
    return false;
  }
  if (IsPrimitive() || IsArrayClass() || IsProxyClass()) {
    return true;
  }
  std::string name_storage;
  const std::string_view name(this->GetDescriptor(&name_storage));
  return IsMirroredDescriptor(name);
}

ObjPtr<mirror::ClassClass::GetPrimitiveClass(ObjPtr<mirror::String> name) {
  const char* expected_name = nullptr;
  ClassRoot class_root = ClassRoot::kJavaLangObject;  // Invalid.
  if (name != nullptr && name->GetLength() >= 2) {
    // Perfect hash for the expected values: from the second letters of the primitive types,
    // only 'y' has the bit 0x10 set, so use it to change 'b' to 'B'.
    char hash = name->CharAt(0) ^ ((name->CharAt(1) & 0x10) << 1);
    switch (hash) {
      case 'b': expected_name = "boolean"; class_root = ClassRoot::kPrimitiveBoolean; break;
      case 'B': expected_name = "byte";    class_root = ClassRoot::kPrimitiveByte;    break;
      case 'c': expected_name = "char";    class_root = ClassRoot::kPrimitiveChar;    break;
      case 'd': expected_name = "double";  class_root = ClassRoot::kPrimitiveDouble;  break;
      case 'f': expected_name = "float";   class_root = ClassRoot::kPrimitiveFloat;   break;
      case 'i': expected_name = "int";     class_root = ClassRoot::kPrimitiveInt;     break;
      case 'l': expected_name = "long";    class_root = ClassRoot::kPrimitiveLong;    break;
      case 's': expected_name = "short";   class_root = ClassRoot::kPrimitiveShort;   break;
      case 'v': expected_name = "void";    class_root = ClassRoot::kPrimitiveVoid;    break;
      defaultbreak;
    }
  }
  if (expected_name != nullptr && name->Equals(expected_name)) {
    ObjPtr<mirror::Class> klass = GetClassRoot(class_root);
    DCHECK(klass != nullptr);
    return klass;
  } else {
    Thread* self = Thread::Current();
    if (name == nullptr) {
      // Note: ThrowNullPointerException() requires a message which we deliberately want to omit.
      self->ThrowNewException("Ljava/lang/NullPointerException;"/* msg= */ nullptr);
    } else {
      self->ThrowNewException("Ljava/lang/ClassNotFoundException;", name->ToModifiedUtf8().c_str());
    }
    return nullptr;
  }
}

ObjPtr<ClassExt> Class::EnsureExtDataPresent(Handle<Class> h_this, Thread* self) {
  ObjPtr<ClassExt> existing(h_this->GetExtData());
  if (!existing.IsNull()) {
    return existing;
  }
  StackHandleScope<2> hs(self);
  // Clear exception so we can allocate.
  Handle<Throwable> throwable(hs.NewHandle(self->GetException()));
  self->ClearException();
  // Allocate the ClassExt
  Handle<ClassExt> new_ext(hs.NewHandle(ClassExt::Alloc(self)));
  if (new_ext == nullptr) {
    // OOM allocating the classExt.
    // TODO Should we restore the suppressed exception?
    self->AssertPendingOOMException();
    return nullptr;
  } else {
    MemberOffset ext_offset(OFFSET_OF_OBJECT_MEMBER(Class, ext_data_));
    bool set;
    // Set the ext_data_ field using CAS semantics.
    if (Runtime::Current()->IsActiveTransaction()) {
      set = h_this->CasFieldObject<true>(ext_offset,
                                         nullptr,
                                         new_ext.Get(),
                                         CASMode::kStrong,
                                         std::memory_order_seq_cst);
    } else {
      set = h_this->CasFieldObject<false>(ext_offset,
                                          nullptr,
                                          new_ext.Get(),
                                          CASMode::kStrong,
                                          std::memory_order_seq_cst);
    }
    ObjPtr<ClassExt> ret(set ? new_ext.Get() : h_this->GetExtData());
    DCHECK_IMPLIES(set, h_this->GetExtData() == new_ext.Get());
    CHECK(!ret.IsNull());
    // Restore the exception if there was one.
    if (throwable != nullptr) {
      self->SetException(throwable.Get());
    }
    return ret;
  }
}

template <typename T>
static void CheckSetStatus(Thread* self, T thiz, ClassStatus new_status, ClassStatus old_status)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  if (UNLIKELY(new_status <= old_status && new_status != ClassStatus::kErrorUnresolved &&
               new_status != ClassStatus::kErrorResolved && new_status != ClassStatus::kRetired)) {
    LOG(FATAL) << "Unexpected change back of class status for " << thiz->PrettyClass() << " "
               << old_status << " -> " << new_status;
  }
  if (old_status == ClassStatus::kInitialized) {
    // We do not hold the lock for making the class visibly initialized
    // as this is unnecessary and could lead to deadlocks.
    CHECK_EQ(new_status, ClassStatus::kVisiblyInitialized);
  } else if ((new_status >= ClassStatus::kResolved || old_status >= ClassStatus::kResolved) &&
             !Locks::mutator_lock_->IsExclusiveHeld(self)) {
    // When classes are being resolved the resolution code should hold the
    // lock or have everything else suspended
    CHECK(thiz->IsLockOwnedByMe(self))
        << "Attempt to change status of class while not holding its lock: " << thiz->PrettyClass()
        << " " << old_status << " -> " << new_status;
  }
  if (UNLIKELY(Locks::mutator_lock_->IsExclusiveHeld(self))) {
    CHECK(!Class::IsErroneous(new_status))
        << "status " << new_status
        << " cannot be set while suspend-all is active. Would require allocations.";
    CHECK(thiz->IsResolved())
        << thiz->PrettyClass()
        << " not resolved during suspend-all status change. Waiters might be missed!";
  }
}

void Class::SetStatusInternal(ClassStatus new_status) {
  if (kBitstringSubtypeCheckEnabled) {
    // FIXME: This looks broken with respect to aborted transactions.
    SubtypeCheck<ObjPtr<mirror::Class>>::WriteStatus(this, new_status);
  } else {
    // The ClassStatus is always in the 4 most-significant bits of status_.
    static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32");
    uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize);
    if (Runtime::Current()->IsActiveTransaction()) {
      SetField32Volatile<true>(StatusOffset(), new_status_value);
    } else {
      SetField32Volatile<false>(StatusOffset(), new_status_value);
    }
  }
}

void Class::SetStatusLocked(ClassStatus new_status) {
  ClassStatus old_status = GetStatus();
  CheckSetStatus(Thread::Current(), this, new_status, old_status);
  SetStatusInternal(new_status);
}

void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self) {
  ClassStatus old_status = h_this->GetStatus();
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized();
  if (LIKELY(class_linker_initialized)) {
    CheckSetStatus(self, h_this, new_status, old_status);
  }
  if (UNLIKELY(IsErroneous(new_status))) {
    CHECK(!h_this->IsErroneous())
        << "Attempt to set as erroneous an already erroneous class "
        << h_this->PrettyClass()
        << " old_status: " << old_status << " new_status: " << new_status;
    CHECK_EQ(new_status == ClassStatus::kErrorResolved, old_status >= ClassStatus::kResolved);
    if (VLOG_IS_ON(class_linker)) {
      LOG(ERROR) << "Setting " << h_this->PrettyDescriptor() << " to erroneous.";
      if (self->IsExceptionPending()) {
        LOG(ERROR) << "Exception: " << self->GetException()->Dump();
      }
    }

    ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self));
    if (!ext.IsNull()) {
      self->AssertPendingException();
      ext->SetErroneousStateError(self->GetException());
    } else {
      self->AssertPendingOOMException();
    }
    self->AssertPendingException();
  }

  h_this->SetStatusInternal(new_status);

  // Setting the object size alloc fast path needs to be after the status write so that if the
  // alloc path sees a valid object size, we would know that it's initialized as long as it has a
  // load-acquire/fake dependency.
  if (new_status == ClassStatus::kVisiblyInitialized && !h_this->IsVariableSize()) {
    DCHECK_EQ(h_this->GetObjectSizeAllocFastPath(), std::numeric_limits<uint32_t>::max());
    // Finalizable objects must always go slow path.
    if (!h_this->IsFinalizable()) {
      h_this->SetObjectSizeAllocFastPath(RoundUp(h_this->GetObjectSize(), kObjectAlignment));
    }
  }

  if (!class_linker_initialized) {
    // When the class linker is being initialized its single threaded and by definition there can be
    // no waiters. During initialization classes may appear temporary but won't be retired as their
    // size was statically computed.
  } else {
    // Classes that are being resolved or initialized need to notify waiters that the class status
    // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass.
    if (h_this->IsTemp()) {
      // Class is a temporary one, ensure that waiters for resolution get notified of retirement
      // so that they can grab the new version of the class from the class linker's table.
      CHECK_LT(new_status, ClassStatus::kResolved) << h_this->PrettyDescriptor();
      if (new_status == ClassStatus::kRetired || new_status == ClassStatus::kErrorUnresolved) {
        h_this->NotifyAll(self);
      }
    } else if (old_status == ClassStatus::kInitialized) {
      // Do not notify for transition from kInitialized to ClassStatus::kVisiblyInitialized.
      // This is a hidden transition, not observable by bytecode.
      DCHECK_EQ(new_status, ClassStatus::kVisiblyInitialized);  // Already CHECK()ed above.
    } else {
      CHECK_NE(new_status, ClassStatus::kRetired);
      if (old_status >= ClassStatus::kResolved || new_status >= ClassStatus::kResolved) {
        h_this->NotifyAll(self);
      }
    }
  }
}

void Class::SetStatusForPrimitiveOrArray(ClassStatus new_status) {
  DCHECK(IsPrimitive<kVerifyNone>() || IsArrayClass<kVerifyNone>());
  DCHECK(!IsErroneous(new_status));
  DCHECK(!IsErroneous(GetStatus<kVerifyNone>()));
  DCHECK_GT(new_status, GetStatus<kVerifyNone>());

  if (kBitstringSubtypeCheckEnabled) {
    LOG(FATAL) << "Unimplemented";
  }
  // The ClassStatus is always in the 4 most-significant bits of status_.
  static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32");
  uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize);
  // Use normal store. For primitives and core arrays classes (Object[],
  // Class[], String[] and primitive arrays), the status is set while the
  // process is still single threaded. For other arrays classes, it is set
  // in a pre-fence visitor which initializes all fields and the subsequent
  // fence together with address dependency shall ensure memory visibility.
  SetField32</*kTransactionActive=*/ false,
             /*kCheckTransaction=*/ false,
             kVerifyNone>(StatusOffset(), new_status_value);

  // Do not update `object_alloc_fast_path_`. Arrays are variable size and
  // instances of primitive classes cannot be created at all.

  // There can be no waiters to notify as these classes are initialized
  // before another thread can see them.
}

void Class::SetDexCache(ObjPtr<DexCache> new_dex_cache) {
  SetFieldObjectTransaction(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache);
}

void Class::SetClassSize(uint32_t new_class_size) {
  if (kIsDebugBuild && new_class_size < GetClassSize()) {
    DumpClass(LOG_STREAM(FATAL_WITHOUT_ABORT), kDumpClassFullDetail);
    LOG(FATAL_WITHOUT_ABORT) << new_class_size << " vs " << GetClassSize();
    LOG(FATAL) << "class=" << PrettyTypeOf();
  }
  SetField32</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
      OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size);
}

ObjPtr<ClassClass::GetObsoleteClass() {
  ObjPtr<ClassExt> ext(GetExtData());
  if (ext.IsNull()) {
    return nullptr;
  } else {
    return ext->GetObsoleteClass();
  }
}

// Return the class' name. The exact format is bizarre, but it's the specified behavior for
// Class.getName: keywords for primitive types, regular "[I" form for primitive arrays (so "int"
// but "[I"), and arrays of reference types written between "L" and ";" but with dots rather than
// slashes (so "java.lang.String" but "[Ljava.lang.String;"). Madness.
ObjPtr<String> Class::ComputeName(Handle<Class> h_this) {
  ObjPtr<String> name = h_this->GetName();
  if (name != nullptr) {
    return name;
  }
  std::string temp;
  const char* descriptor = h_this->GetDescriptor(&temp);
  Thread* self = Thread::Current();
  if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
    // The descriptor indicates that this is the class for
    // a primitive type; special-case the return value.
    const char* c_name = nullptr;
    switch (descriptor[0]) {
    case 'Z': c_name = "boolean"break;
    case 'B': c_name = "byte";    break;
    case 'C': c_name = "char";    break;
    case 'S': c_name = "short";   break;
    case 'I': c_name = "int";     break;
    case 'J': c_name = "long";    break;
    case 'F': c_name = "float";   break;
    case 'D': c_name = "double";  break;
    case 'V': c_name = "void";    break;
    default:
      LOG(FATAL) << "Unknown primitive type: " << PrintableChar(descriptor[0]);
    }
    name = String::AllocFromModifiedUtf8(self, c_name);
  } else {
    // Convert the UTF-8 name to a java.lang.String. The name must use '.' to separate package
    // components.
    name = String::AllocFromModifiedUtf8(self, DescriptorToDot(descriptor).c_str());
  }
  h_this->SetName(name);
  return name;
}

void Class::DumpClass(std::ostream& os, int flags) {
  ScopedAssertNoThreadSuspension ants(__FUNCTION__);
  if ((flags & kDumpClassFullDetail) == 0) {
    os << PrettyClass();
    if ((flags & kDumpClassClassLoader) != 0) {
      os << ' ' << GetClassLoader();
    }
    if ((flags & kDumpClassInitialized) != 0) {
      os << ' ' << GetStatus();
    }
    os << "\n";
    return;
  }

  ObjPtr<Class> super = GetSuperClass();
  auto image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();

  std::string temp;
  os << "----- " << (IsInterface() ? "interface" : "class") << " "
     << "'" << GetDescriptor(&temp) << "' cl=" << GetClassLoader() << " -----\n"
     << "  objectSize=" << SizeOf() << " "
     << "(" << (super != nullptr ? super->SizeOf() : -1) << " from super)\n"
     << StringPrintf("  access=0x%04x.%04x\n",
                     GetAccessFlags() >> 16,
                     GetAccessFlags() & kAccJavaFlagsMask);
  if (super != nullptr) {
    os << "  super='" << super->PrettyClass() << "' (cl=" << super->GetClassLoader() << ")\n";
  }
  if (IsArrayClass()) {
    os << "  componentType=" << PrettyClass(GetComponentType()) << "\n";
  }
  const size_t num_direct_interfaces = NumDirectInterfaces();
  if (num_direct_interfaces > 0) {
    os << "  interfaces (" << num_direct_interfaces << "):\n";
    for (size_t i = 0; i < num_direct_interfaces; ++i) {
      ObjPtr<Class> interface = GetDirectInterface(i);
      if (interface == nullptr) {
        os << StringPrintf("    %2zd: nullptr!\n", i);
      } else {
        ObjPtr<ClassLoader> cl = interface->GetClassLoader();
        os << StringPrintf("    %2zd: %s (cl=%p)\n", i, PrettyClass(interface).c_str(), cl.Ptr());
      }
    }
  }
  if (!IsLoaded()) {
    os << "  class not yet loaded";
  } else {
    os << "  vtable (" << NumVirtualMethods() << " entries, "
        << (super != nullptr ? super->NumVirtualMethods() : 0) << " in super):\n";
    size_t index = 0;
    for (ArtMethod& method : GetDeclaredMethods(image_pointer_size)) {
      os << StringPrintf("    %2zd: %s\n", index++, method.PrettyMethod().c_str());
    }
    if (NumFields() > 0) {
      os << "  fields (" << NumFields() << " entries):\n";
      if (IsResolved()) {
        for (size_t i = 0; i < NumFields(); ++i) {
          ArtField* field = GetField(i);
          os << StringPrintf("    %2zd: %s %s\n",
                             i,
                             field->IsStatic() ? "static" : "instance",
                             ArtField::PrettyField(field).c_str());
        }
      } else {
        os << "    <not yet available>";
      }
    }
  }
}

void Class::SetReferenceInstanceOffsets(uint32_t new_reference_offsets) {
  if (kIsDebugBuild) {
    // Check that the number of bits set in the reference offset bitmap
    // agrees with the number of references.
    uint32_t count = 0;
    for (ObjPtr<Class> c = this; c != nullptr; c = c->GetSuperClass()) {
      count += c->NumReferenceInstanceFieldsDuringLinking();
    }
    uint32_t pop_cnt;
    if ((new_reference_offsets & kVisitReferencesSlowpathMask) == 0) {
      pop_cnt = static_cast<uint32_t>(POPCOUNT(new_reference_offsets));
    } else {
      uint32_t bitmap_num_words = new_reference_offsets & ~kVisitReferencesSlowpathMask;
      uint32_t* overflow_bitmap =
          reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(this) +
                                      (GetClassSize() - bitmap_num_words * sizeof(uint32_t)));
      pop_cnt = 0;
      for (uint32_t i = 0; i < bitmap_num_words; i++) {
        pop_cnt += static_cast<uint32_t>(POPCOUNT(overflow_bitmap[i]));
      }
    }
    // +1 for the Class in Object.
    CHECK_EQ(pop_cnt + 1, count);
  }
  // Not called within a transaction.
  SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
                    new_reference_offsets);
}

bool Class::IsInSamePackage(std::string_view descriptor1, std::string_view descriptor2) {
  static_assert(std::string_view::npos + 1u == 0u);
  size_t d1_after_package = descriptor1.rfind('/') + 1u;
  return descriptor2.starts_with(descriptor1.substr(0u, d1_after_package)) &&
         descriptor2.find('/', d1_after_package) == std::string_view::npos;
}

bool Class::IsInSamePackage(ObjPtr<Class> that) {
  ObjPtr<Class> klass1 = this;
  ObjPtr<Class> klass2 = that;
  if (klass1 == klass2) {
    return true;
  }
  // Class loaders must match.
  if (klass1->GetClassLoader() != klass2->GetClassLoader()) {
    return false;
  }
  // Arrays are in the same package when their element classes are.
  while (klass1->IsArrayClass()) {
    klass1 = klass1->GetComponentType();
  }
  while (klass2->IsArrayClass()) {
    klass2 = klass2->GetComponentType();
  }
  // trivial check again for array types
  if (klass1 == klass2) {
    return true;
  }
  // Compare the package part of the descriptor string.
  if (UNLIKELY(klass1->IsProxyClass()) || UNLIKELY(klass2->IsProxyClass())) {
    std::string temp1, temp2;
    return IsInSamePackage(klass1->GetDescriptor(&temp1), klass2->GetDescriptor(&temp2));
  }
  if (UNLIKELY(klass1->IsPrimitive()) || UNLIKELY(klass2->IsPrimitive())) {
    if (klass1->IsPrimitive() && klass2->IsPrimitive()) {
      return true;
    }
    ObjPtr<Class> other_class = klass1->IsPrimitive() ? klass2 : klass1;
    return other_class->GetDescriptorView().find('/') == std::string_view::npos;
  }
  return IsInSamePackage(klass1->GetDescriptorView(), klass2->GetDescriptorView());
}

bool Class::IsThrowableClass() {
  return GetClassRoot<mirror::Throwable>()->IsAssignableFrom(this);
}

template <typename SignatureType>
inline ArtMethod* Class::FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
                                                          std::string_view name,
                                                          const SignatureType& signature,
                                                          PointerSize pointer_size)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  // If the current class is not an interface, skip the search of its declared methods;
  // such lookup is used only to distinguish between IncompatibleClassChangeError and
  // NoSuchMethodError and the caller has already tried to search methods in the class.
  if (LIKELY(klass->IsInterface())) {
    // Search declared methods, both direct and virtual.
    // (This lookup is used also for invoke-static on interface classes.)
    for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) {
      if (method.GetNameView() == name && method.GetSignature() == signature) {
        return &method;
      }
    }
  }

  // TODO: If there is a unique maximally-specific non-abstract superinterface method,
  // we should return it, otherwise an arbitrary one can be returned.
  ObjPtr<IfTable> iftable = klass->GetIfTable();
  for (int32_t i = 0, iftable_count = iftable->Count(); i < iftable_count; ++i) {
    ObjPtr<Class> iface = iftable->GetInterface(i);
    for (ArtMethod& method : iface->GetMethodsSlice(pointer_size)) {
      if (method.IsVirtual() &&
          method.GetNameView() == name &&
          method.GetSignature() == signature) {
        return &method;
      }
    }
  }

  // Then search for public non-static methods in the java.lang.Object.
  if (LIKELY(klass->IsInterface())) {
    ObjPtr<Class> object_class = klass->GetSuperClass();
    DCHECK(object_class->IsObjectClass());
    for (ArtMethod& method : object_class->GetDeclaredMethodsSlice(pointer_size)) {
      if (method.IsPublic() && !method.IsStatic() &&
          method.GetNameView() == name && method.GetSignature() == signature) {
        return &method;
      }
    }
  }
  return nullptr;
}

ArtMethod* Class::FindInterfaceMethod(std::string_view name,
                                      std::string_view signature,
                                      PointerSize pointer_size) {
  return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
}

ArtMethod* Class::FindInterfaceMethod(std::string_view name,
                                      const Signature& signature,
                                      PointerSize pointer_size) {
  return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
}

ArtMethod* Class::FindInterfaceMethod(ObjPtr<DexCache> dex_cache,
                                      uint32_t dex_method_idx,
                                      PointerSize pointer_size) {
  // First try to find a declared method by dex_method_idx if we have a dex_cache match.
  if (GetDexCache() == dex_cache) {
    ArtMethod* method = nullptr;
    if (pointer_size == kRuntimePointerSize) {
      method = FindDeclaredClassMethod</* kOnlyLookAtIndex= */ false, kRuntimePointerSize>(
          dex_method_idx);
    } else {
      constexpr PointerSize kOtherPointerSize =
          (kRuntimePointerSize == PointerSize::k64) ? PointerSize::k32 : PointerSize::k64;
      method = FindDeclaredClassMethod</* kOnlyLookAtIndex */ false, kOtherPointerSize>(
          dex_method_idx);
    }
    if (method != nullptr) {
      // This method is only called for interface classes, except from
      // `ClassLinker::FindIncompatibleMethod` where we have not found one.
      DCHECK(IsInterface());
      return method;
    }
  }

  // Otherwise search by name and signature, ignoring the type index in the MethodId.
  const DexFile& dex_file = *dex_cache->GetDexFile();
  const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
  std::string_view name = dex_file.GetStringView(method_id.name_idx_);
  const Signature signature = dex_file.GetMethodSignature(method_id);
  return FindInterfaceMethod(name, signature, pointer_size);
}

static inline bool IsValidInheritanceCheck(ObjPtr<mirror::Class> klass,
                                           ObjPtr<mirror::Class> declaring_class)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  if (klass->IsArrayClass()) {
    return declaring_class->IsObjectClass();
  } else if (klass->IsInterface()) {
    return declaring_class->IsObjectClass() || declaring_class == klass;
  } else {
    return klass->IsSubClass(declaring_class);
  }
}

static inline bool IsInheritedMethod(ObjPtr<mirror::Class> klass,
                                     ObjPtr<mirror::Class> declaring_class,
                                     ArtMethod& method)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  DCHECK_EQ(declaring_class, method.GetDeclaringClass());
  DCHECK_NE(klass, declaring_class);
  DCHECK(IsValidInheritanceCheck(klass, declaring_class));
  uint32_t access_flags = method.GetAccessFlags();
  if ((access_flags & (kAccPublic | kAccProtected)) != 0) {
    return true;
  }
  if ((access_flags & kAccPrivate) != 0) {
    return false;
  }
  for (; klass != declaring_class; klass = klass->GetSuperClass()) {
    if (!klass->IsInSamePackage(declaring_class)) {
      return false;
    }
  }
  return true;
}

template <typename SignatureType>
static inline ArtMethod* FindClassMethodWithSignature(ObjPtr<Class> this_klass,
                                                      std::string_view name,
                                                      const SignatureType& signature,
                                                      PointerSize pointer_size)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  // Search declared methods first.
  for (ArtMethod& method : this_klass->GetDeclaredMethodsSlice(pointer_size)) {
    ArtMethod* np_method = method.GetInterfaceMethodIfProxy(pointer_size);
    if (np_method->GetNameView() == name && np_method->GetSignature() == signature) {
      return &method;
    }
  }

  // Then search the superclass chain. If we find an inherited method, return it.
  // If we find a method that's not inherited because of access restrictions,
  // try to find a method inherited from an interface in copied methods.
  ObjPtr<Class> klass = this_klass->GetSuperClass();
  ArtMethod* uninherited_method = nullptr;
  for (; klass != nullptr; klass = klass->GetSuperClass()) {
    DCHECK(!klass->IsProxyClass());
    for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) {
      if (method.GetNameView() == name && method.GetSignature() == signature) {
        if (IsInheritedMethod(this_klass, klass, method)) {
          return &method;
        }
        uninherited_method = &method;
        break;
      }
    }
    if (uninherited_method != nullptr) {
      break;
    }
  }

  // Then search copied methods.
  // If we found a method that's not inherited, stop the search in its declaring class.
  ObjPtr<Class> end_klass = klass;
  DCHECK_EQ(uninherited_method != nullptr, end_klass != nullptr);
  klass = this_klass;
  if (UNLIKELY(klass->IsProxyClass())) {
    DCHECK(klass->GetCopiedMethodsSlice(pointer_size).empty());
    klass = klass->GetSuperClass();
  }
  for (; klass != end_klass; klass = klass->GetSuperClass()) {
    DCHECK(!klass->IsProxyClass());
    for (ArtMethod& method : klass->GetCopiedMethodsSlice(pointer_size)) {
      if (method.GetNameView() == name && method.GetSignature() == signature) {
        return &method;  // No further check needed, copied methods are inherited by definition.
      }
    }
  }
  return uninherited_method;  // Return the `uninherited_method` if any.
}


ArtMethod* Class::FindClassMethod(std::string_view name,
                                  std::string_view signature,
                                  PointerSize pointer_size) {
  return FindClassMethodWithSignature(this, name, signature, pointer_size);
}

ArtMethod* Class::FindClassMethod(std::string_view name,
                                  const Signature& signature,
                                  PointerSize pointer_size) {
  return FindClassMethodWithSignature(this, name, signature, pointer_size);
}

// Binary search a range with a three-way compare function.
//
// Return a tuple consisting of a `success` value, the index of the match (`mid`) and
// the remaining range when we found the match (`begin` and `end`). This is useful for
// subsequent binary search with a secondary comparator, see `ClassMemberBinarySearch()`.
template <typename Compare>
ALWAYS_INLINE
std::tuple<bool, uint32_t, uint32_t, uint32_t> BinarySearch(uint32_t begin,
                                                            uint32_t end,
                                                            Compare&& cmp)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  while (begin != end) {
    uint32_t mid = (begin + end) >> 1;
    auto cmp_result = cmp(mid);
    if (cmp_result == 0) {
      return {true, mid, begin, end};
    }
    if (cmp_result > 0) {
      begin = mid + 1u;
    } else {
      end = mid;
    }
  }
  return {false0u, 0u, 0u};
}

// Binary search for class members. The range passed to this search must be sorted, so
// declared methods or fields cannot be searched directly but declared direct methods,
// declared virtual methods, declared static fields or declared instance fields can.
template <typename NameCompare, typename SecondCompare, typename NameIndexGetter>
ALWAYS_INLINE
std::tuple<bool, uint32_t> ClassMemberBinarySearch(uint32_t begin,
                                                   uint32_t end,
                                                   NameCompare&& name_cmp,
                                                   SecondCompare&& second_cmp,
                                                   NameIndexGetter&& get_name_idx)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  // First search for the item with the given name.
  bool success;
  uint32_t mid;
  std::tie(success, mid, begin, end) = BinarySearch(begin, end, name_cmp);
  if (!success) {
    return {false0u};
  }
  // If found, do the secondary comparison.
  auto second_cmp_result = second_cmp(mid);
  if (second_cmp_result == 0) {
    return {true, mid};
  }
  // We have matched the name but not the secondary comparison. We no longer need to
  // search for the name as string as we know the matching name string index.
  // Repeat the above binary searches and secondary comparisons with a simpler name
  // index compare until the search range contains only matching name.
  auto name_idx = get_name_idx(mid);
  if (second_cmp_result > 0) {
    do {
      begin = mid + 1u;
      auto name_index_cmp = [&](uint32_t mid2) REQUIRES_SHARED(Locks::mutator_lock_) {
        DCHECK_LE(name_idx, get_name_idx(mid2));
        return (name_idx != get_name_idx(mid2)) ? -1 : 0;
      };
      std::tie(success, mid, begin, end) = BinarySearch(begin, end, name_index_cmp);
      if (!success) {
        return {false0u};
      }
      second_cmp_result = second_cmp(mid);
    } while (second_cmp_result > 0);
    end = mid;
  } else {
    do {
      end = mid;
      auto name_index_cmp = [&](uint32_t mid2) REQUIRES_SHARED(Locks::mutator_lock_) {
        DCHECK_GE(name_idx, get_name_idx(mid2));
        return (name_idx != get_name_idx(mid2)) ? 1 : 0;
      };
      std::tie(success, mid, begin, end) = BinarySearch(begin, end, name_index_cmp);
      if (!success) {
        return {false0u};
      }
      second_cmp_result = second_cmp(mid);
    } while (second_cmp_result < 0);
    begin = mid + 1u;
  }
  if (second_cmp_result == 0) {
    return {true, mid};
  }
  // All items in the remaining range have a matching name, so search with secondary comparison.
  std::tie(success, mid, std::ignore, std::ignore) = BinarySearch(begin, end, second_cmp);
  return {success, mid};
}

static std::tuple<bool, ArtMethod*> FindDeclaredClassMethodInternal(ObjPtr<mirror::Class> klass,
                                                                    const DexFile& dex_file,
                                                                    std::string_view name,
                                                                    Signature signature,
                                                                    PointerSize pointer_size)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  DCHECK(&klass->GetDexFile() == &dex_file);
  DCHECK(!name.empty());

  ArraySlice<ArtMethod> declared_methods = klass->GetDeclaredMethodsSlice(pointer_size);
  DCHECK(!declared_methods.empty());
  auto get_method_id = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE
      -> const dex::MethodId& {
    ArtMethod& method = declared_methods[mid];
    DCHECK(method.GetDexFile() == &dex_file);
    DCHECK_NE(method.GetDexMethodIndex(), dex::kDexNoIndex);
    return dex_file.GetMethodId(method.GetDexMethodIndex());
  };
  auto name_cmp = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
    // Do not use ArtMethod::GetNameView() to avoid reloading dex file through the same
    // declaring class from different methods and also avoid the runtime method check.
    const dex::MethodId& method_id = get_method_id(mid);
    return DexFile::CompareMemberNames(name, dex_file.GetMethodNameView(method_id));
  };
  auto signature_cmp = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
    // Do not use ArtMethod::GetSignature() to avoid reloading dex file through the same
    // declaring class from different methods and also avoid the runtime method check.
    const dex::MethodId& method_id = get_method_id(mid);
    return signature.Compare(dex_file.GetMethodSignature(method_id));
  };
  auto get_name_idx = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
    const dex::MethodId& method_id = get_method_id(mid);
    return method_id.name_idx_;
  };

  // Use binary search in the sorted methods.
  uint32_t num_declared_methods = dchecked_integral_cast<uint32_t>(declared_methods.size());
  auto [success, mid] =
      ClassMemberBinarySearch(0, num_declared_methods, name_cmp, signature_cmp, get_name_idx);
  if (success) {
    return {true, &declared_methods[mid]};
  }

  // Did not find a declared method.
  return {false, nullptr};
}

FLATTEN
ArtMethod* Class::FindClassMethod(ObjPtr<DexCache> dex_cache,
                                  uint32_t dex_method_idx,
                                  PointerSize pointer_size) {
  // FIXME: Hijacking a proxy class by a custom class loader can break this assumption.
  DCHECK(!IsProxyClass());

  // First try to find a declared method by dex_method_idx if we have a dex_cache match.
  ObjPtr<DexCache> this_dex_cache = GetDexCache();
  if (this_dex_cache == dex_cache) {
    ArtMethod* method = nullptr;
    if (pointer_size == kRuntimePointerSize) {
      method = FindDeclaredClassMethod</* kOnlyLookAtIndex= */ false, kRuntimePointerSize>(
          dex_method_idx);
    } else {
      constexpr PointerSize kOtherPointerSize =
          (kRuntimePointerSize == PointerSize::k64) ? PointerSize::k32 : PointerSize::k64;
      method =
          FindDeclaredClassMethod</* kOnlyLookAtIndex= */ false, kOtherPointerSize>(dex_method_idx);
    }
    if (method != nullptr) {
      return method;
    }
  }

  // If not found, we need to search by name and signature.
  const DexFile& dex_file = *dex_cache->GetDexFile();
  const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
  const Signature signature = dex_file.GetMethodSignature(method_id);
  std::string_view name;  // Do not touch the dex file string data until actually needed.

  // If we do not have a dex_cache match, try to find the declared method in this class now.
  if (this_dex_cache != dex_cache && !GetDeclaredMethodsSlice(pointer_size).empty()) {
    DCHECK(name.empty());
    name = dex_file.GetMethodNameView(method_id);
    auto [success, method] = FindDeclaredClassMethodInternal(
        this, *this_dex_cache->GetDexFile(), name, signature, pointer_size);
    DCHECK_EQ(success, method != nullptr);
    if (success) {
      return method;
    }
  }

  // Then search the superclass chain. If we find an inherited method, return it.
  // If we find a method that's not inherited because of access restrictions,
  // try to find a method inherited from an interface in copied methods.
  ArtMethod* uninherited_method = nullptr;
  ObjPtr<Class> klass = GetSuperClass();
  for (; klass != nullptr; klass = klass->GetSuperClass()) {
    ArtMethod* candidate_method = nullptr;
    ArraySlice<ArtMethod> declared_methods = klass->GetDeclaredMethodsSlice(pointer_size);
    ObjPtr<DexCache> klass_dex_cache = klass->GetDexCache();
    if (klass_dex_cache == dex_cache) {
      // Matching dex_cache. We cannot compare the `dex_method_idx` anymore because
      // the type index differs, so compare the name index and proto index.
      for (ArtMethod& method : declared_methods) {
        const dex::MethodId& cmp_method_id = dex_file.GetMethodId(method.GetDexMethodIndex());
        if (cmp_method_id.name_idx_ == method_id.name_idx_ &&
            cmp_method_id.proto_idx_ == method_id.proto_idx_) {
          candidate_method = &method;
          break;
        }
      }
    } else if (!declared_methods.empty()) {
      if (name.empty()) {
        name = dex_file.GetMethodNameView(method_id);
      }
      auto [success, method] = FindDeclaredClassMethodInternal(
          klass, *klass_dex_cache->GetDexFile(), name, signature, pointer_size);
      DCHECK_EQ(success, method != nullptr);
      if (success) {
        candidate_method = method;
      }
    }
    if (candidate_method != nullptr) {
      if (IsInheritedMethod(this, klass, *candidate_method)) {
        return candidate_method;
      } else {
        uninherited_method = candidate_method;
        break;
      }
    }
  }

  // Then search copied methods.
  // If we found a method that's not inherited, stop the search in its declaring class.
  ObjPtr<Class> end_klass = klass;
  DCHECK_EQ(uninherited_method != nullptr, end_klass != nullptr);
  // After we have searched the declared methods of the super-class chain,
  // search copied methods which can contain methods from interfaces.
  for (klass = this; klass != end_klass; klass = klass->GetSuperClass()) {
    ArraySlice<ArtMethod> copied_methods = klass->GetCopiedMethodsSlice(pointer_size);
    if (!copied_methods.empty() && name.empty()) {
      name = dex_file.GetMethodNameView(method_id);
    }
    for (ArtMethod& method : copied_methods) {
      if (method.GetNameView() == name && method.GetSignature() == signature) {
        return &method;  // No further check needed, copied methods are inherited by definition.
      }
    }
  }
  return uninherited_method;  // Return the `uninherited_method` if any.
}

ArtMethod* Class::FindConstructor(std::string_view signature, PointerSize pointer_size) {
  // Internal helper, never called on proxy classes. We can skip GetInterfaceMethodIfProxy().
  DCHECK(!IsProxyClass());
  for (ArtMethod& method : GetDeclaredMethodsSliceUnchecked(pointer_size)) {
    DCHECK_IMPLIES(method.IsConstructor(), !method.IsVirtual());
    if (method.IsInstanceConstructor() && method.GetSignature() == signature) {
      DCHECK(method.GetName() == std::string_view("<init>"));
      return &method;
    }
  }
  return nullptr;
}

ArtMethod* Class::FindDeclaredDirectMethodByName(std::string_view name, PointerSize pointer_size) {
  for (auto& method : GetDeclaredMethods(pointer_size)) {
    if (!method.IsVirtual()) {
      ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
      if (name == np_method->GetName()) {
        return &method;
      }
    }
  }
  return nullptr;
}

ArtMethod* Class::FindDeclaredVirtualMethodByName(std::string_view name, PointerSize pointer_size) {
  for (auto& method : GetDeclaredMethods(pointer_size)) {
    if (method.IsVirtual()) {
      ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
      if (name == np_method->GetName()) {
        return &method;
      }
    }
  }
  return nullptr;
}

ArtMethod* Class::FindVirtualMethodForInterfaceSuper(ArtMethod* method, PointerSize pointer_size) {
  DCHECK(method->GetDeclaringClass()->IsInterface());
  DCHECK(IsInterface()) << "Should only be called on a interface class";
  // Check if we have one defined on this interface first. This includes searching copied ones to
  // get any conflict methods. Conflict methods are copied into each subtype from the supertype. We
  // don't do any indirect method checks here.
  for (ArtMethod& iface_method : GetMethods(pointer_size)) {
    if (iface_method.IsVirtual() && method->HasSameNameAndSignature(&iface_method)) {
      return &iface_method;
    }
  }

  std::vector<ArtMethod*> abstract_methods;
  // Search through the IFTable for a working version. We don't need to check for conflicts
  // because if there was one it would appear in this classes virtual_methods_ above.

  Thread* self = Thread::Current();
  StackHandleScope<2> hs(self);
  MutableHandle<IfTable> iftable(hs.NewHandle(GetIfTable()));
  MutableHandle<Class> iface(hs.NewHandle<Class>(nullptr));
  size_t iftable_count = GetIfTableCount();
  // Find the method. We don't need to check for conflicts because they would have been in the
  // copied virtuals of this interface.  Order matters, traverse in reverse topological order; most
  // subtypiest interfaces get visited first.
  for (size_t k = iftable_count; k != 0;) {
    k--;
    DCHECK_LT(k, iftable->Count());
    iface.Assign(iftable->GetInterface(k));
    // Iterate through every declared method on this interface. Each direct method's name/signature
    // is unique so the order of the inner loop doesn't matter.
    for (auto& method_iter : iface->GetDeclaredMethods(pointer_size)) {
      if (!method_iter.IsVirtual()) {
        continue;
      }
      ArtMethod* current_method = &method_iter;
      if (current_method->HasSameNameAndSignature(method)) {
        if (current_method->IsDefault()) {
          // Handle JLS soft errors, a default method from another superinterface tree can
          // "override" an abstract method(s) from another superinterface tree(s).  To do this,
          // ignore any [default] method which are dominated by the abstract methods we've seen so
          // far. Check if overridden by any in abstract_methods. We do not need to check for
          // default_conflicts because we would hit those before we get to this loop.
          bool overridden = false;
          for (ArtMethod* possible_override : abstract_methods) {
            DCHECK(possible_override->HasSameNameAndSignature(current_method));
            if (iface->IsAssignableFrom(possible_override->GetDeclaringClass())) {
              overridden = true;
              break;
            }
          }
          if (!overridden) {
            return current_method;
          }
        } else {
          // Is not default.
          // This might override another default method. Just stash it for now.
          abstract_methods.push_back(current_method);
        }
      }
    }
  }
  // If we reach here we either never found any declaration of the method (in which case
  // 'abstract_methods' is empty or we found no non-overriden default methods in which case
  // 'abstract_methods' contains a number of abstract implementations of the methods. We choose one
  // of these arbitrarily.
  return abstract_methods.empty() ? nullptr : abstract_methods[0];
}

ArtMethod* Class::FindClassInitializer(PointerSize pointer_size) {
  for (ArtMethod& method : GetDeclaredMethods(pointer_size)) {
    if (method.IsClassInitializer()) {
      DCHECK_STREQ(method.GetName(), "<clinit>");
      DCHECK_STREQ(method.GetSignature().ToString().c_str(), "()V");
      return &method;
    }
  }
  return nullptr;
}

static std::tuple<bool, ArtField*> FindFieldByNameAndType(const DexFile& dex_file,
                                                          LengthPrefixedArray<ArtField>* fields,
                                                          std::string_view name,
                                                          std::string_view type)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  DCHECK(fields != nullptr);
  DCHECK(!name.empty());
  DCHECK(!type.empty());

  // Fields are sorted by class, then name, then type descriptor. This is verified in dex file
  // verifier. There can be multiple fields with the same name in the same class due to proguard.
  // Note: `std::string_view::compare()` uses lexicographical comparison and treats the `char`
  // as unsigned; for Modified-UTF-8 without embedded nulls this is consistent with the
  // `CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues()` ordering.
  auto get_field_id = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE
      -> const dex::FieldId& {
    ArtField& field = fields->At(mid);
    DCHECK(field.GetDexFile() == &dex_file);
    return dex_file.GetFieldId(field.GetDexFieldIndex());
  };
  auto name_cmp = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
    const dex::FieldId& field_id = get_field_id(mid);
    return DexFile::CompareMemberNames(name, dex_file.GetFieldNameView(field_id));
  };
  auto type_cmp = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
    const dex::FieldId& field_id = get_field_id(mid);
    return DexFile::CompareDescriptors(
        type, dex_file.GetTypeDescriptorView(dex_file.GetTypeId(field_id.type_idx_)));
  };
  auto get_name_idx = [&](uint32_t mid) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
    const dex::FieldId& field_id = get_field_id(mid);
    return field_id.name_idx_;
  };

  // Use binary search in the sorted fields.
  auto [success, mid] =
      ClassMemberBinarySearch(/*begin=*/ 0u, fields->size(), name_cmp, type_cmp, get_name_idx);

  if (kIsDebugBuild) {
    ArtField* found = nullptr;
    for (ArtField& field : MakeIterationRangeFromLengthPrefixedArray(fields)) {
      if (name == field.GetName() && type == field.GetTypeDescriptor()) {
        found = &field;
        break;
      }
    }

    ArtField* ret = success ? &fields->At(mid) : nullptr;
    CHECK_EQ(found, ret)
        << "Found " << ArtField::PrettyField(found) << " vs " << ArtField::PrettyField(ret);
  }

  if (success) {
    return {true, &fields->At(mid)};
  }

  return {false, nullptr};
}

ArtField* Class::FindDeclaredField(std::string_view name, std::string_view type) {
  // Binary search by name. Interfaces are not relevant because they can't contain instance fields.
  LengthPrefixedArray<ArtField>* fields = GetFieldsPtr();
  if (fields == nullptr) {
    return nullptr;
  }
  DCHECK(!IsProxyClass());
  auto [success, field] = FindFieldByNameAndType(GetDexFile(), fields, name, type);
  DCHECK_EQ(success, field != nullptr);
  return field;
}

ArtField* Class::FindDeclaredInstanceField(std::string_view name, std::string_view type) {
  ArtField* f = FindDeclaredField(name, type);
  if (f != nullptr && !f->IsStatic()) {
    return f;
  }
  return nullptr;
}

ArtField* Class::FindDeclaredInstanceField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) {
  ArtField* f = FindDeclaredField(dex_cache, dex_field_idx);
  if (f != nullptr && !f->IsStatic()) {
    return f;
  }
  return nullptr;
}

ArtField* Class::FindInstanceField(std::string_view name, std::string_view type) {
  // Is the field in this class, or any of its superclasses?
  // Interfaces are not relevant because they can't contain instance fields.
  for (ObjPtr<Class> c = this; c != nullptr; c = c->GetSuperClass()) {
    ArtField* f = c->FindDeclaredInstanceField(name, type);
    if (f != nullptr) {
      return f;
    }
  }
  return nullptr;
}

ArtField* Class::FindDeclaredStaticField(std::string_view name, std::string_view type) {
  DCHECK(!type.empty());
  LengthPrefixedArray<ArtField>* fields = GetFieldsPtr();
  if (fields == nullptr) {
    return nullptr;
  }
  if (UNLIKELY(IsProxyClass())) {
    // Proxy fields do not have appropriate dex field indexes required by
    // `FindFieldByNameAndType()`. However, each proxy class has exactly
    // the same artificial fields created by the `ClassLinker`.
    DCHECK_EQ(fields->size(), 2u);
    DCHECK_EQ(strcmp(fields->At(0).GetName(), "interfaces"), 0);
    DCHECK_EQ(strcmp(fields->At(0).GetTypeDescriptor(), "[Ljava/lang/Class;"), 0);
    DCHECK(fields->At(0).IsStatic());
    DCHECK_EQ(strcmp(fields->At(1).GetName(), "throws"), 0);
    DCHECK_EQ(strcmp(fields->At(1).GetTypeDescriptor(), "[[Ljava/lang/Class;"), 0);
    DCHECK(fields->At(1).IsStatic());
    if (name == "interfaces") {
      return (type == "[Ljava/lang/Class;") ? &fields->At(0) : nullptr;
    } else if (name == "throws") {
      return (type == "[[Ljava/lang/Class;") ? &fields->At(1) : nullptr;
    } else {
      return nullptr;
    }
  }
  ArtField* f = FindDeclaredField(name, type);
  if (f != nullptr && f->IsStatic()) {
    return f;
  }
  return nullptr;
}

ArtField* Class::FindDeclaredField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) {
  return (dex_cache == GetDexCache()) ? FindDeclaredField(dex_field_idx) : nullptr;
}

ArtField* Class::FindDeclaredStaticField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) {
  ArtField* f = FindDeclaredField(dex_cache, dex_field_idx);
  if (f != nullptr && f->IsStatic()) {
    return f;
  }
  return nullptr;
}

ObjPtr<mirror::ObjectArray<mirror::Field>> Class::GetDeclaredFields(
    Thread* self,
    bool public_only,
    bool force_resolve) REQUIRES_SHARED(Locks::mutator_lock_) {
  if (UNLIKELY(IsObsoleteObject())) {
    ThrowRuntimeException("Obsolete Object!");
    return nullptr;
  }
  // Collect all discoverable fields.
  auto hiddenapi_context = hiddenapi::GetReflectionCallerAccessContext(self);
  static constexpr size_t kMaxStackEntries = 8u;
  InlinedVector<ArtField*, kMaxStackEntries> fields;
  for (ArtField& field : GetFields()) {
    if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
      fields.push_back(&field);
    }
  }
  StackHandleScope<1> hs(self);
  auto object_array = hs.NewHandle(mirror::ObjectArray<mirror::Field>::Alloc(
      self, GetClassRoot<mirror::ObjectArray<mirror::Field>>(), fields.size()));
  if (object_array == nullptr) {
    return nullptr;
  }
  size_t array_idx = 0;
  for (ArtField* field : fields.GetArray()) {
    ObjPtr<mirror::Field> reflect_field =
        mirror::Field::CreateFromArtField(self, field, force_resolve);
    if (reflect_field == nullptr) {
      if (kIsDebugBuild) {
        self->AssertPendingException();
      }
      // Maybe null due to OOME or type resolving exception.
      return nullptr;
    }
    // We're initializing a newly allocated object, so we do not need to record that under
    // a transaction. If the transaction is aborted, the whole object shall be unreachable.
    object_array->SetWithoutChecks<
        /*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(array_idx++, reflect_field);
  }
  DCHECK_EQ(array_idx, fields.size());
  return object_array.Get();
}

ArtField* Class::FindStaticField(std::string_view name, std::string_view type) {
  ScopedAssertNoThreadSuspension ants(__FUNCTION__);
  // Is the field in this class (or its interfaces), or any of its
  // superclasses (or their interfaces)?
  for (ObjPtr<Class> k = this; k != nullptr; k = k->GetSuperClass()) {
    // Is the field in this class?
    ArtField* f = k->FindDeclaredStaticField(name, type);
    if (f != nullptr) {
      return f;
    }
    // Is this field in any of this class' interfaces?
    for (uint32_t i = 0, num_interfaces = k->NumDirectInterfaces(); i != num_interfaces; ++i) {
      ObjPtr<Class> interface = k->GetDirectInterface(i);
      DCHECK(interface != nullptr);
      f = interface->FindStaticField(name, type);
      if (f != nullptr) {
        return f;
      }
    }
  }
  return nullptr;
}

// Find a field using the JLS field resolution order.
// Template arguments can be used to extend the search to static fields of interfaces.
// The search should be limited only if we know that a full search would yield a field of
// the right type or no field at all. This can be known for field references in a method
// if we have previously verified that method and did not find a field type mismatch.
template <bool kSearchStaticFieldsInInterfaces>
ALWAYS_INLINE
ArtField* FindFieldImpl(ObjPtr<mirror::Class> klass,
                        ObjPtr<mirror::DexCache> dex_cache,
                        uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_) {
  // FIXME: Hijacking a proxy class by a custom class loader can break this assumption.
  DCHECK(!klass->IsProxyClass());

  ScopedAssertNoThreadSuspension ants(__FUNCTION__);

  // First try to find a declared field by `field_idx` if we have a `dex_cache` match.
  ObjPtr<DexCache> klass_dex_cache = klass->GetDexCache();
  if (klass_dex_cache == dex_cache) {
    // Lookup is always performed in the class referenced by the FieldId.
    DCHECK_EQ(klass->GetDexTypeIndex(),
              klass_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_);
    ArtField* f = klass->FindDeclaredField(klass_dex_cache, field_idx);
    if (f != nullptr) {
      return f;
    }
  }

  const DexFile& dex_file = *dex_cache->GetDexFile();
  const dex::FieldId& field_id = dex_file.GetFieldId(field_idx);

  std::string_view name;  // Do not touch the dex file string data until actually needed.
  std::string_view type;
  auto ensure_name_and_type_initialized = [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
    if (name.empty()) {
      name = dex_file.GetFieldNameView(field_id);
      type = dex_file.GetFieldTypeDescriptorView(field_id);
    }
  };

  auto search_direct_interfaces = [&](ObjPtr<mirror::Class> k)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    // TODO: The `FindStaticField()` performs a recursive search and it's possible to
    // construct interface hierarchies that make the time complexity exponential in depth.
    // Rewrite this with a `HashSet<mirror::Class*>` to mark classes we have already
    // searched for the field, so that we call `FindDeclaredStaticField()` only once
    // on each interface. And use a work queue to avoid unlimited recursion depth.
    // TODO: Once we call `FindDeclaredStaticField()` directly, use search by indexes
    // instead of strings if the interface's dex cache matches `dex_cache`. This shall
    // allow delaying the `ensure_name_and_type_initialized()` call further.
    uint32_t num_interfaces = k->NumDirectInterfaces();
    if (num_interfaces != 0u) {
      ensure_name_and_type_initialized();
      for (uint32_t i = 0; i != num_interfaces; ++i) {
        ObjPtr<Class> interface = k->GetDirectInterface(i);
        DCHECK(interface != nullptr);
        ArtField* f = interface->FindStaticField(name, type);
        if (f != nullptr) {
          return f;
        }
      }
    }
    return static_cast<ArtField*>(nullptr);
  };

  auto find_field_by_name_and_type = [&](ObjPtr<mirror::Class> k, ObjPtr<DexCache> k_dex_cache)
      REQUIRES_SHARED(Locks::mutator_lock_) -> std::tuple<bool, ArtField*> {
    if (k->GetFieldsPtr() == nullptr) {
      return {false, nullptr};
    }
    ensure_name_and_type_initialized();
    const DexFile& k_dex_file = *k_dex_cache->GetDexFile();
    auto [success, field] = FindFieldByNameAndType(k_dex_file, k->GetFieldsPtr(), name, type);
    DCHECK_EQ(success, field != nullptr);
    return {success, field};
  };

  // If we had a dex cache mismatch, search declared fields by name and type.
  if (klass_dex_cache != dex_cache) {
    auto [success, field] = find_field_by_name_and_type(klass, klass_dex_cache);
    DCHECK_EQ(success, field != nullptr);
    if (success) {
      return field;
    }
  }

  // Search direct interfaces for static fields.
  if (kSearchStaticFieldsInInterfaces) {
    ArtField* f = search_direct_interfaces(klass);
    if (f != nullptr) {
      return f;
    }
  }

  // Continue searching in superclasses.
  for (ObjPtr<Class> k = klass->GetSuperClass(); k != nullptr; k = k->GetSuperClass()) {
    // Is the field in this class?
    ObjPtr<DexCache> k_dex_cache = k->GetDexCache();
    if (k_dex_cache == dex_cache) {
      // Matching dex_cache. We cannot compare the `field_idx` anymore because
      // the type index differs, so compare the name index and type index.
      for (ArtField& field : k->GetFields()) {
        const dex::FieldId& other_field_id = dex_file.GetFieldId(field.GetDexFieldIndex());
        if (other_field_id.name_idx_ == field_id.name_idx_ &&
            other_field_id.type_idx_ == field_id.type_idx_) {
          return &field;
        }
      }
    } else {
      auto [success, field] = find_field_by_name_and_type(k, k_dex_cache);
      DCHECK_EQ(success, field != nullptr);
      if (success) {
        return field;
      }
    }
    if (kSearchStaticFieldsInInterfaces) {
      // Is this field in any of this class' interfaces?
      ArtField* f = search_direct_interfaces(k);
      if (f != nullptr) {
        return f;
      }
    }
  }
  return nullptr;
}

FLATTEN
ArtField* Class::FindField(ObjPtr<mirror::DexCache> dex_cache, uint32_t field_idx) {
  return FindFieldImpl</*kSearchStaticFieldsInInterfaces*/ true>(this, dex_cache, field_idx);
}

FLATTEN
ArtField* Class::FindInstanceField(ObjPtr<mirror::DexCache> dex_cache, uint32_t field_idx) {
  return FindFieldImpl</*kSearchStaticFieldsInInterfaces*/ false>(this, dex_cache, field_idx);
}

FLATTEN
ArtField* Class::FindStaticField(ObjPtr<mirror::DexCache> dex_cache, uint32_t field_idx) {
  return FindFieldImpl</*kSearchStaticFieldsInInterfaces*/ true>(this, dex_cache, field_idx);
}

void Class::ClearSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
  DCHECK(IsVerified());
  for (auto& m : GetMethods(pointer_size)) {
    if (m.IsManagedAndInvokable()) {
      m.ClearSkipAccessChecks();
    }
  }
}

void Class::ClearMustCountLocksFlagOnAllMethods(PointerSize pointer_size) {
  DCHECK(IsVerified());
  for (auto& m : GetMethods(pointer_size)) {
    if (m.IsManagedAndInvokable()) {
      m.ClearMustCountLocks();
    }
  }
}

void Class::ClearDontCompileFlagOnAllMethods(PointerSize pointer_size) {
  DCHECK(IsVerified());
  for (auto& m : GetMethods(pointer_size)) {
    if (m.IsManagedAndInvokable()) {
      m.ClearDontCompile();
    }
  }
}

void Class::SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
  DCHECK(IsVerified());
  for (auto& m : GetMethods(pointer_size)) {
    // Copied methods that have code come from default interface methods. The
    // flag should be set on these copied methods at the point of copy, which is
    // after the interface has been verified.
    if (m.IsManagedAndInvokable() && !m.IsCopied()) {
      m.SetSkipAccessChecks();
    }
  }
}

const charClass::GetDescriptor(std::string* storage) {
  size_t dim = 0u;
  ObjPtr<mirror::Class> klass = this;
  while (klass->IsArrayClass()) {
    ++dim;
    // No read barrier needed, we're reading a chain of constant references for comparison
    // with null. Then we follow up below with reading constant references to read constant
    // primitive data in both proxy and non-proxy paths. See ReadBarrierOption.
    klass = klass->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>();
  }
  if (klass->IsProxyClass()) {
    // No read barrier needed, the `name` field is constant for proxy classes and
    // the contents of the String are also constant. See ReadBarrierOption.
    ObjPtr<mirror::String> name = klass->GetName<kVerifyNone, kWithoutReadBarrier>();
    DCHECK(name != nullptr);
    *storage = DotToDescriptor(name->ToModifiedUtf8());
  } else {
    const char* descriptor;
    if (klass->IsPrimitive()) {
      descriptor = Primitive::Descriptor(klass->GetPrimitiveType());
    } else {
      const DexFile& dex_file = klass->GetDexFile();
      const dex::TypeId& type_id = dex_file.GetTypeId(klass->GetDexTypeIndex());
      descriptor = dex_file.GetTypeDescriptor(type_id);
    }
    if (dim == 0) {
      return descriptor;
    }
    *storage = descriptor;
  }
  storage->insert(0u, dim, '[');
  return storage->c_str();
}

uint32_t Class::ComputeDescriptorHash() {
  // No read barriers needed, we're reading a chain of constant references for comparison with null
  // and retrieval of constant primitive data. See `ReadBarrierOption` and `Class::GetDescriptor()`.
  ObjPtr<mirror::Class> klass = this;
  uint32_t hash = StartModifiedUtf8Hash();
  while (klass->IsArrayClass()) {
    klass = klass->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>();
    hash = UpdateModifiedUtf8Hash(hash, '[');
  }
  if (UNLIKELY(klass->IsProxyClass())) {
    hash = UpdateHashForProxyClass(hash, klass);
  } else if (klass->IsPrimitive()) {
    hash = UpdateModifiedUtf8Hash(hash, Primitive::Descriptor(klass->GetPrimitiveType())[0]);
  } else {
    const DexFile& dex_file = klass->GetDexFile();
    const dex::TypeId& type_id = dex_file.GetTypeId(klass->GetDexTypeIndex());
    std::string_view descriptor = dex_file.GetTypeDescriptorView(type_id);
    hash = UpdateModifiedUtf8Hash(hash, descriptor);
  }

  if (kIsDebugBuild) {
    std::string temp;
    CHECK_EQ(hash, ComputeModifiedUtf8Hash(GetDescriptor(&temp)));
  }

  return hash;
}

const dex::ClassDef* Class::GetClassDef() {
  uint16_t class_def_idx = GetDexClassDefIndex();
  if (class_def_idx == DexFile::kDexNoIndex16) {
    return nullptr;
  }
  return &GetDexFile().GetClassDef(class_def_idx);
}

dex::TypeIndex Class::GetDirectInterfaceTypeIdx(uint32_t idx) {
  DCHECK(!IsPrimitive());
  DCHECK(!IsArrayClass());
  return GetInterfaceTypeList()->GetTypeItem(idx).type_idx_;
}

ObjPtr<ClassClass::GetDirectInterface(uint32_t idx) {
  DCHECK(!IsPrimitive());
  if (IsArrayClass()) {
    ObjPtr<IfTable> iftable = GetIfTable();
    DCHECK(iftable != nullptr);
    DCHECK_EQ(iftable->Count(), 2u);
    DCHECK_LT(idx, 2u);
    ObjPtr<Class> interface = iftable->GetInterface(idx);
    DCHECK(interface != nullptr);
    return interface;
  } else if (IsProxyClass()) {
    ObjPtr<ObjectArray<Class>> interfaces = GetProxyInterfaces();
    DCHECK(interfaces != nullptr);
    return interfaces->Get(idx);
  } else {
    dex::TypeIndex type_idx = GetDirectInterfaceTypeIdx(idx);
    ObjPtr<Class> interface = Runtime::Current()->GetClassLinker()->LookupResolvedType(
        type_idx, GetDexCache(), GetClassLoader());
    return interface;
  }
}

ObjPtr<ClassClass::ResolveDirectInterface(Thread* self, Handle<Class> klass, uint32_t idx) {
  ObjPtr<Class> interface = klass->GetDirectInterface(idx);
  if (interface == nullptr) {
    DCHECK(!klass->IsArrayClass());
    DCHECK(!klass->IsProxyClass());
    dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx);
    interface = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, klass.Get());
    CHECK_IMPLIES(interface == nullptr, self->IsExceptionPending());
  }
  return interface;
}

ObjPtr<ClassClass::GetCommonSuperClass(Handle<Class> klass) {
  DCHECK(klass != nullptr);
  DCHECK(!klass->IsInterface());
  DCHECK(!IsInterface());
  ObjPtr<Class> common_super_class = this;
  while (!common_super_class->IsAssignableFrom(klass.Get())) {
    ObjPtr<Class> old_common = common_super_class;
    common_super_class = old_common->GetSuperClass();
    DCHECK(common_super_class != nullptr) << old_common->PrettyClass();
  }
  return common_super_class;
}

const charClass::GetSourceFile() {
  const DexFile& dex_file = GetDexFile();
  const dex::ClassDef* dex_class_def = GetClassDef();
  if (dex_class_def == nullptr) {
    // Generated classes have no class def.
    return nullptr;
  }
  return dex_file.GetSourceFile(*dex_class_def);
}

std::string Class::GetLocation() {
  ObjPtr<DexCache> dex_cache = GetDexCache();
  if (dex_cache != nullptr && !IsProxyClass()) {
    return dex_cache->GetLocation()->ToModifiedUtf8();
  }
  // Arrays and proxies are generated and have no corresponding dex file location.
  return "generated class";
}

const dex::TypeList* Class::GetInterfaceTypeList() {
  const dex::ClassDef* class_def = GetClassDef();
  if (class_def == nullptr) {
    return nullptr;
  }
  return GetDexFile().GetInterfacesList(*class_def);
}

void Class::PopulateEmbeddedVTable(PointerSize pointer_size) {
  ObjPtr<PointerArray> table = GetVTableDuringLinking();
  CHECK(table != nullptr) << PrettyClass();
  const size_t table_length = table->GetLength();
  SetEmbeddedVTableLength(table_length);
  AddRemoveClassFlags(kClassFlagHasEmbeddedVTable);
  for (size_t i = 0; i < table_length; i++) {
    SetEmbeddedVTableEntry(i, table->GetElementPtrSize<ArtMethod*>(i, pointer_size), pointer_size);
  }
  // Keep java.lang.Object class's vtable around for since it's easier
  // to be reused by array classes during their linking.
  if (!IsObjectClass()) {
    SetVTable(nullptr);
  }
}

// Set the bitmap of reference instance field offsets.
void Class::PopulateReferenceOffsetBitmap() {
  size_t num_reference_fields;
  ObjPtr<mirror::Class> super_class;
  ObjPtr<Class> klass;
  // Find the first class with non-zero instance reference fields.
  for (klass = this; klass != nullptr; klass = super_class) {
    super_class = klass->GetSuperClass();
    num_reference_fields = klass->NumReferenceInstanceFieldsDuringLinking();
    if (num_reference_fields != 0) {
      break;
    }
  }

  uint32_t ref_offsets = 0;
  // Leave the reference offsets as 0 for mirror::Object (the class field is handled specially).
  if (super_class != nullptr) {
    // All of the reference fields added by this class are guaranteed to be grouped in memory
    // starting at an appropriately aligned address after super class object data.
    uint32_t start_offset =
        RoundUp(super_class->GetObjectSize(), sizeof(mirror::HeapReference<mirror::Object>));
    uint32_t start_bit =
        (start_offset - mirror::kObjectHeaderSize) / sizeof(mirror::HeapReference<mirror::Object>);
    uint32_t end_bit = start_bit + num_reference_fields;
    bool overflowing = end_bit > 31;
    uint32_t* overflow_bitmap;  // Pointer to the last word of overflow bitmap to be written into.
    uint32_t overflow_words_to_write;  // Number of overflow bitmap words remaining to write.
    // Index in 'overflow_bitmap' from where to start writing bitmap words (in reverse order).
    int32_t overflow_bitmap_word_idx;
    if (overflowing) {
      // We will write overflow bitmap in reverse.
      overflow_bitmap =
          reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(this) + GetClassSize());
      DCHECK_ALIGNED(overflow_bitmap, sizeof(uint32_t));
      overflow_bitmap_word_idx = 0;
      overflow_words_to_write = RoundUp(end_bit, 32) / 32;
    }
    // TODO: Simplify by copying the bitmap from the super-class and then
    // appending the reference fields added by this class.
    while (true) {
      if (UNLIKELY(overflowing)) {
        // Write all the bitmap words which got skipped between previous
        // super-class and the current one.
        for (uint32_t new_words_to_write = RoundUp(end_bit, 32) / 32;
             overflow_words_to_write > new_words_to_write;
             overflow_words_to_write--) {
          overflow_bitmap[--overflow_bitmap_word_idx] = ref_offsets;
          ref_offsets = 0;
        }
        // Handle the references in the current super-class.
        if (num_reference_fields != 0u) {
          uint32_t aligned_end_bit = RoundDown(end_bit, 32);
          uint32_t aligned_start_bit = RoundUp(start_bit, 32);
          // Handle the case where a class' references are spanning across multiple 32-bit
          // words of the overflow bitmap.
          if (aligned_end_bit >= aligned_start_bit) {
            // handle the unaligned end first
            if (aligned_end_bit < end_bit) {
              ref_offsets |= 0xffffffffu >> (32 - (end_bit - aligned_end_bit));
              overflow_bitmap[--overflow_bitmap_word_idx] = ref_offsets;
              overflow_words_to_write--;
              ref_offsets = 0;
            }
            // store all the 32-bit bitmap words in between
            for (; aligned_end_bit > aligned_start_bit; aligned_end_bit -= 32) {
              overflow_bitmap[--overflow_bitmap_word_idx] = 0xffffffffu;
              overflow_words_to_write--;
            }
            CHECK_EQ(ref_offsets, 0u);
            // handle the unaligned start now
            if (aligned_start_bit > start_bit) {
              ref_offsets = 0xffffffffu << (32 - (aligned_start_bit - start_bit));
            }
          } else {
            DCHECK_EQ(aligned_start_bit - aligned_end_bit, 32u);
            ref_offsets |= (0xffffffffu << (32 - (aligned_start_bit - start_bit))) &
                           (0xffffffffu >> (32 - (end_bit - aligned_end_bit)));
          }
        }
      } else if (num_reference_fields != 0u) {
        ref_offsets |= (0xffffffffu << start_bit) & (0xffffffffu >> (32 - end_bit));
      }

      klass = super_class;
      super_class = klass->GetSuperClass();
      if (super_class == nullptr) {
        break;
      }
      num_reference_fields = klass->NumReferenceInstanceFieldsDuringLinking();
      start_offset =
          RoundUp(super_class->GetObjectSize(), sizeof(mirror::HeapReference<mirror::Object>));
      start_bit = (start_offset - mirror::kObjectHeaderSize) /
                  sizeof(mirror::HeapReference<mirror::Object>);
      end_bit = start_bit + num_reference_fields;
    }
    if (overflowing) {
      // We should not have more than one word left to write in the overflow bitmap.
      DCHECK_LE(overflow_words_to_write, 1u)
          << "overflow_bitmap_word_idx:" << -overflow_bitmap_word_idx;
      if (overflow_words_to_write > 0) {
        overflow_bitmap[--overflow_bitmap_word_idx] = ref_offsets;
      }
      ref_offsets = -overflow_bitmap_word_idx | kVisitReferencesSlowpathMask;
    }
  }
  // Record / value class flag is set before this method call, but that should not prevent a
  // class from being treated as normal.
  constexpr uint32_t kIgnoredFlags = kClassFlagStaticRefInfo | kClassFlagRecord | kClassFlagValue;
  if ((GetClassFlags() & ~kIgnoredFlags) == 0 && ref_offsets != 0) {
    AddRemoveClassFlags(kClassFlagNormal);
  }
  SetReferenceInstanceOffsets(ref_offsets);
}

class ReadBarrierOnNativeRootsVisitor {
 public:
  void operator()([[maybe_unused]] ObjPtr<Object> obj,
                  [[maybe_unused]] MemberOffset offset,
                  [[maybe_unused]] bool is_static) const {}

  void VisitRootIfNonNull(CompressedReference<Object>* root) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (!root->IsNull()) {
      VisitRoot(root);
    }
  }

  void VisitRoot(CompressedReference<Object>* root) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    ObjPtr<Object> old_ref = root->AsMirrorPtr();
    ObjPtr<Object> new_ref = ReadBarrier::BarrierForRoot(root);
    if (old_ref != new_ref) {
      // Update the field atomically. This may fail if mutator updates before us, but it's ok.
      auto* atomic_root =
          reinterpret_cast<Atomic<CompressedReference<Object>>*>(root);
      atomic_root->CompareAndSetStrongSequentiallyConsistent(
          CompressedReference<Object>::FromMirrorPtr(old_ref.Ptr()),
          CompressedReference<Object>::FromMirrorPtr(new_ref.Ptr()));
    }
  }
};

// The pre-fence visitor for Class::CopyOf().
class CopyClassVisitor {
 public:
  CopyClassVisitor(Thread* self,
                   Handle<Class>* orig,
                   size_t new_length,
                   size_t copy_bytes,
                   ImTable* imt,
                   PointerSize pointer_size)
      : self_(self), orig_(orig), new_length_(new_length),
        copy_bytes_(copy_bytes), imt_(imt), pointer_size_(pointer_size) {
  }

  void operator()(ObjPtr<Object> obj, [[maybe_unused]] size_t usable_size) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    StackHandleScope<1> hs(self_);
    Handle<mirror::Class> h_new_class_obj(hs.NewHandle(obj->AsClass()));
    Object::CopyObject(h_new_class_obj.Get(), orig_->Get(), copy_bytes_);
    Class::SetStatus(h_new_class_obj, ClassStatus::kResolving, self_);
    h_new_class_obj->PopulateEmbeddedVTable(pointer_size_);
    h_new_class_obj->SetImt(imt_, pointer_size_);
    h_new_class_obj->SetClassSize(new_length_);
    h_new_class_obj->PopulateReferenceOffsetBitmap();
    // Visit all of the references to make sure there is no from space references in the native
    // roots.
    h_new_class_obj->Object::VisitReferences(ReadBarrierOnNativeRootsVisitor(), VoidFunctor());
  }

 private:
  Thread* const self_;
  Handle<Class>* const orig_;
  const size_t new_length_;
  const size_t copy_bytes_;
  ImTable* imt_;
  const PointerSize pointer_size_;
  DISALLOW_COPY_AND_ASSIGN(CopyClassVisitor);
};

ObjPtr<ClassClass::CopyOf(Handle<Class> h_this,
                            Thread* self,
                            int32_t new_length,
                            ImTable* imt,
                            PointerSize pointer_size) {
  DCHECK_GE(new_length, static_cast<int32_t>(sizeof(Class)));
  // We may get copied by a compacting GC.
  Runtime* runtime = Runtime::Current();
  gc::Heap* heap = runtime->GetHeap();
  // The num_bytes (3rd param) is sizeof(Class) as opposed to SizeOf()
  // to skip copying the tail part that we will overwrite here.
  CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class), imt, pointer_size);
  ObjPtr<mirror::Class> java_lang_Class = GetClassRoot<mirror::Class>(runtime->GetClassLinker());
  ObjPtr<Object> new_class = kMovingClasses ?
      heap->AllocObject(self, java_lang_Class, new_length, visitor) :
      heap->AllocNonMovableObject(self, java_lang_Class, new_length, visitor);
  if (UNLIKELY(new_class == nullptr)) {
    self->AssertPendingOOMException();
    return nullptr;
  }
  return new_class->AsClass();
}

bool Class::DescriptorEquals(ObjPtr<mirror::Class> match) {
  DCHECK(match != nullptr);
  ObjPtr<mirror::Class> klass = this;
  while (klass->IsArrayClass()) {
    // No read barrier needed, we're reading a chain of constant references for comparison
    // with null. Then we follow up below with reading constant references to read constant
    // primitive data in both proxy and non-proxy paths. See ReadBarrierOption.
    klass = klass->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>();
    DCHECK(klass != nullptr);
    match = match->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>();
    if (match == nullptr){
      return false;
    }
  }
  if (match->IsArrayClass()) {
    return false;
  }

  if (UNLIKELY(klass->IsPrimitive()) || UNLIKELY(match->IsPrimitive())) {
    return klass->GetPrimitiveType() == match->GetPrimitiveType();
  }

  if (UNLIKELY(klass->IsProxyClass())) {
    return klass->ProxyDescriptorEquals(match);
  }
  if (UNLIKELY(match->IsProxyClass())) {
    return match->ProxyDescriptorEquals(klass);
  }

  const DexFile& klass_dex_file = klass->GetDexFile();
  const DexFile& match_dex_file = match->GetDexFile();
  dex::TypeIndex klass_type_index = klass->GetDexTypeIndex();
  dex::TypeIndex match_type_index = match->GetDexTypeIndex();
  if (&klass_dex_file == &match_dex_file) {
    return klass_type_index == match_type_index;
  }
  std::string_view klass_descriptor = klass_dex_file.GetTypeDescriptorView(klass_type_index);
  std::string_view match_descriptor = match_dex_file.GetTypeDescriptorView(match_type_index);
  return klass_descriptor == match_descriptor;
}

bool Class::ProxyDescriptorEquals(ObjPtr<mirror::Class> match) {
  DCHECK(IsProxyClass());
  ObjPtr<mirror::String> name = GetName<kVerifyNone, kWithoutReadBarrier>();
  DCHECK(name != nullptr);

  DCHECK(match != nullptr);
  DCHECK(!match->IsArrayClass());
  DCHECK(!match->IsPrimitive());
  if (match->IsProxyClass()) {
    ObjPtr<mirror::String> match_name = match->GetName<kVerifyNone, kWithoutReadBarrier>();
    DCHECK(name != nullptr);
    return name->Equals(match_name);
  }

  // Note: Proxy descriptor should never match a non-proxy descriptor but ART does not enforce that.
  std::string descriptor = DotToDescriptor(name->ToModifiedUtf8());
  std::string_view match_descriptor =
      match->GetDexFile().GetTypeDescriptorView(match->GetDexTypeIndex());
  return descriptor == match_descriptor;
}

bool Class::ProxyDescriptorEquals(std::string_view match) {
  DCHECK(IsProxyClass());
  std::string storage;
  const char* descriptor = GetDescriptor(&storage);
  DCHECK(descriptor == storage.c_str());
  return storage == match;
}

uint32_t Class::UpdateHashForProxyClass(uint32_t hash, ObjPtr<mirror::Class> proxy_class) {
  // No read barrier needed, the `name` field is constant for proxy classes and
  // the contents of the String are also constant. See ReadBarrierOption.
  // Note: The `proxy_class` can be a from-space reference.
  DCHECK(proxy_class->IsProxyClass());
  ObjPtr<mirror::String> name = proxy_class->GetName<kVerifyNone, kWithoutReadBarrier>();
  DCHECK(name != nullptr);
  // Update hash for characters we would get from `DotToDescriptor(name->ToModifiedUtf8())`.
  DCHECK_NE(name->GetLength(), 0);
  DCHECK_NE(name->CharAt(0), '[');
  hash = UpdateModifiedUtf8Hash(hash, 'L');
  if (name->IsCompressed()) {
    std::string_view dot_name(reinterpret_cast<const char*>(name->GetValueCompressed()),
                              name->GetLength());
    for (char c : dot_name) {
      hash = UpdateModifiedUtf8Hash(hash, (c != '.') ? c : '/');
    }
  } else {
    std::string dot_name = name->ToModifiedUtf8();
    for (char c : dot_name) {
      hash = UpdateModifiedUtf8Hash(hash, (c != '.') ? c : '/');
    }
  }
  hash = UpdateModifiedUtf8Hash(hash, ';');
  return hash;
}

// TODO: Move this to java_lang_Class.cc?
ArtMethod* Class::GetDeclaredConstructor(
    Thread* self, Handle<ObjectArray<Class>> args, PointerSize pointer_size) {
  for (auto& m : GetDeclaredMethods(pointer_size)) {
    if (m.IsInstanceConstructor()) {
      // May cause thread suspension and exceptions.
      if (m.GetInterfaceMethodIfProxy(kRuntimePointerSize)->EqualParameters(args)) {
        DCHECK(!self->IsExceptionPending());
        return &m;
      }
      if (UNLIKELY(self->IsExceptionPending())) {
        return nullptr;
      }
    }
  }
  return nullptr;
}

uint32_t Class::Depth() {
  uint32_t depth = 0;
  for (ObjPtr<Class> cls = this; cls->GetSuperClass() != nullptr; cls = cls->GetSuperClass()) {
    depth++;
  }
  return depth;
}

dex::TypeIndex Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) {
  std::string_view descriptor;
  std::optional<std::string> temp;
  if (IsPrimitive() || IsArrayClass() || IsProxyClass()) {
    temp.emplace();
    descriptor = GetDescriptor(&temp.value());
  } else {
    descriptor = GetDescriptorView();
  }
  const dex::TypeId* type_id = dex_file.FindTypeId(descriptor);
  return (type_id == nullptr) ? dex::TypeIndex() : dex_file.GetIndexForTypeId(*type_id);
}

ALWAYS_INLINE
static bool IsMethodPreferredOver(ArtMethod* orig_method,
                                  bool orig_method_hidden,
                                  ArtMethod* new_method,
                                  bool new_method_hidden) {
  DCHECK(new_method != nullptr);

  // Is this the first result?
  if (orig_method == nullptr) {
    return true;
  }

  // Original method is hidden, the new one is not?
  if (orig_method_hidden && !new_method_hidden) {
    return true;
  }

  // We iterate over virtual methods first and then over direct ones,
  // so we can never be in situation where `orig_method` is direct and
  // `new_method` is virtual.
  DCHECK_IMPLIES(orig_method->IsDirect(), new_method->IsDirect());

  // Original method is synthetic, the new one is not?
  if (orig_method->IsSynthetic() && !new_method->IsSynthetic()) {
    return true;
  }

  return false;
}

template <PointerSize kPointerSize>
ObjPtr<Method> Class::GetDeclaredMethodInternal(
    Thread* self,
    ObjPtr<Class> klass,
    ObjPtr<String> name,
    ObjPtr<ObjectArray<Class>> args,
    const std::function<hiddenapi::AccessContext()>& fn_get_access_context) {
  // Covariant return types (or smali) permit the class to define
  // multiple methods with the same name and parameter types.
  // Prefer (in decreasing order of importance):
  //  1) non-hidden method over hidden
  //  2) virtual methods over direct
  //  3) non-synthetic methods over synthetic
  // We never return miranda methods that were synthesized by the runtime.
  StackHandleScope<3> hs(self);
  auto h_method_name = hs.NewHandle(name);
  if (UNLIKELY(h_method_name == nullptr)) {
    ThrowNullPointerException("name == null");
    return nullptr;
  }
  auto h_args = hs.NewHandle(args);
  Handle<Class> h_klass = hs.NewHandle(klass);
  constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kCheckWithPolicy;
  ArtMethod* result = nullptr;
  bool result_hidden = false;
  for (auto& m : h_klass->GetDeclaredMethods(kPointerSize)) {
    if (!m.IsVirtual() || m.IsMiranda()) {
      continue;
    }
    ArtMethod* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
    if (!np_method->NameEquals(h_method_name.Get())) {
      continue;
    }
    // `ArtMethod::EqualParameters()` may throw when resolving types.
    if (!np_method->EqualParameters(h_args)) {
      if (UNLIKELY(self->IsExceptionPending())) {
        return nullptr;
      }
      continue;
    }
    bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
    if (!m_hidden && !m.IsSynthetic()) {
      // Non-hidden, virtual, non-synthetic. Best possible result, exit early.
      return Method::CreateFromArtMethod<kPointerSize>(self, &m);
    } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
      // Remember as potential result.
      result = &m;
      result_hidden = m_hidden;
    }
  }

  if ((result != nullptr) && !result_hidden) {
    // We have not found a non-hidden, virtual, non-synthetic method, but
    // if we have found a non-hidden, virtual, synthetic method, we cannot
    // do better than that later.
    DCHECK(!result->IsDirect());
    DCHECK(result->IsSynthetic());
  } else {
    for (auto& m : h_klass->GetDeclaredMethods(kPointerSize)) {
      if (m.IsVirtual() || m.IsConstructor()) {
        continue;
      }
      ArtMethod* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
      if (!np_method->NameEquals(h_method_name.Get())) {
        continue;
      }
      // `ArtMethod::EqualParameters()` may throw when resolving types.
      if (!np_method->EqualParameters(h_args)) {
        if (UNLIKELY(self->IsExceptionPending())) {
          return nullptr;
        }
        continue;
      }
      DCHECK(!m.IsMiranda());  // Direct methods cannot be miranda methods.
      bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
      if (!m_hidden && !m.IsSynthetic()) {
        // Non-hidden, direct, non-synthetic. Any virtual result could only have been
        // hidden, therefore this is the best possible match. Exit now.
        DCHECK((result == nullptr) || result_hidden);
        return Method::CreateFromArtMethod<kPointerSize>(self, &m);
      } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
        // Remember as potential result.
        result = &m;
        result_hidden = m_hidden;
      }
    }
  }

  return result != nullptr
      ? Method::CreateFromArtMethod<kPointerSize>(self, result)
      : nullptr;
}

template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32>(
    Thread* self,
    ObjPtr<Class> klass,
    ObjPtr<String> name,
    ObjPtr<ObjectArray<Class>> args,
    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64>(
    Thread* self,
    ObjPtr<Class> klass,
    ObjPtr<String> name,
    ObjPtr<ObjectArray<Class>> args,
    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);

template <PointerSize kPointerSize>
ObjPtr<Constructor> Class::GetDeclaredConstructorInternal(
    Thread* self,
    ObjPtr<Class> klass,
    ObjPtr<ObjectArray<Class>> args) {
  StackHandleScope<1> hs(self);
  ArtMethod* result = klass->GetDeclaredConstructor(self, hs.NewHandle(args), kPointerSize);
  return result != nullptr
      ? Constructor::CreateFromArtMethod<kPointerSize>(self, result)
      : nullptr;
}

// Constructor::CreateFromArtMethod<kTransactionActive>(self, result)

template
ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k32>(
    Thread* self,
    ObjPtr<Class> klass,
    ObjPtr<ObjectArray<Class>> args);
template
ObjPtr<Constructor> Class::GetDeclaredConstructorInternal<PointerSize::k64>(
    Thread* self,
    ObjPtr<Class> klass,
    ObjPtr<ObjectArray<Class>> args);

int32_t Class::GetInnerClassFlags(Handle<Class> h_this, int32_t default_value) {
  if (h_this->IsProxyClass() || h_this->GetDexCache() == nullptr) {
    return default_value;
  }
  uint32_t flags;
  if (!annotations::GetInnerClassFlags(h_this, &flags)) {
    return default_value;
  }
  return flags;
}

void Class::SetObjectSizeAllocFastPath(uint32_t new_object_size) {
  if (Runtime::Current()->IsActiveTransaction()) {
    SetField32Volatile<true>(ObjectSizeAllocFastPathOffset(), new_object_size);
  } else {
    SetField32Volatile<false>(ObjectSizeAllocFastPathOffset(), new_object_size);
  }
}

std::string Class::PrettyDescriptor(ObjPtr<mirror::Class> klass) {
  if (klass == nullptr) {
    return "null";
  }
  return klass->PrettyDescriptor();
}

std::string Class::PrettyDescriptor() {
  std::string temp;
  return art::PrettyDescriptor(GetDescriptor(&temp));
}

std::string Class::PrettyClass(ObjPtr<mirror::Class> c) {
  if (c == nullptr) {
    return "null";
  }
  return c->PrettyClass();
}

std::string Class::PrettyClass() {
  std::string result;
  if (IsObsoleteObject()) {
    result += "(Obsolete)";
  }
  if (IsRetired()) {
    result += "(Retired)";
  }
  result += "java.lang.Class<";
  result += PrettyDescriptor();
  result += ">";
  return result;
}

std::string Class::PrettyClassAndClassLoader(ObjPtr<mirror::Class> c) {
  if (c == nullptr) {
    return "null";
  }
  return c->PrettyClassAndClassLoader();
}

std::string Class::PrettyClassAndClassLoader() {
  std::string result;
  result += "java.lang.Class<";
  result += PrettyDescriptor();
  result += ",";
  result += mirror::Object::PrettyTypeOf(GetClassLoader());
  // TODO: add an identifying hash value for the loader
  result += ">";
  return result;
}

template<VerifyObjectFlags kVerifyFlags> void Class::GetAccessFlagsDCheck() {
  // Check class is loaded/retired or this is java.lang.String that has a
  // circularity issue during loading the names of its members
  DCHECK(IsIdxLoaded<kVerifyFlags>() || IsRetired<kVerifyFlags>() ||
         IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ||
         this == GetClassRoot<String>())
              << "IsIdxLoaded=" << IsIdxLoaded<kVerifyFlags>()
              << " IsRetired=" << IsRetired<kVerifyFlags>()
              << " IsErroneous=" <<
              IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()
              << " IsString=" << (this == GetClassRoot<String>())
              << " status= " << GetStatus<kVerifyFlags>()
              << " descriptor=" << PrettyDescriptor();
}
// Instantiate the common cases.
template void Class::GetAccessFlagsDCheck<kVerifyNone>();
template void Class::GetAccessFlagsDCheck<kVerifyThis>();
template void Class::GetAccessFlagsDCheck<kVerifyReads>();
template void Class::GetAccessFlagsDCheck<kVerifyWrites>();
template void Class::GetAccessFlagsDCheck<kVerifyAll>();

ObjPtr<Object> Class::GetMethodIds() {
  ObjPtr<ClassExt> ext(GetExtData());
  if (ext.IsNull()) {
    return nullptr;
  } else {
    return ext->GetJMethodIDs();
  }
}
bool Class::EnsureMethodIds(Handle<Class> h_this) {
  DCHECK_NE(Runtime::Current()->GetJniIdType(), JniIdType::kPointer) << "JNI Ids are pointers!";
  Thread* self = Thread::Current();
  ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self));
  if (ext.IsNull()) {
    self->AssertPendingOOMException();
    return false;
  }
  return ext->EnsureJMethodIDsArrayPresent(h_this->NumMethods());
}

ObjPtr<Object> Class::GetStaticFieldIds() {
  ObjPtr<ClassExt> ext(GetExtData());
  if (ext.IsNull()) {
    return nullptr;
  } else {
    return ext->GetStaticJFieldIDs();
  }
}
bool Class::EnsureStaticFieldIds(Handle<Class> h_this) {
  DCHECK_NE(Runtime::Current()->GetJniIdType(), JniIdType::kPointer) << "JNI Ids are pointers!";
  Thread* self = Thread::Current();
  ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self));
  if (ext.IsNull()) {
    self->AssertPendingOOMException();
    return false;
  }
  return ext->EnsureStaticJFieldIDsArrayPresent(h_this->NumFields());
}
ObjPtr<Object> Class::GetInstanceFieldIds() {
  ObjPtr<ClassExt> ext(GetExtData());
  if (ext.IsNull()) {
    return nullptr;
  } else {
    return ext->GetInstanceJFieldIDs();
  }
}
bool Class::EnsureInstanceFieldIds(Handle<Class> h_this) {
  DCHECK_NE(Runtime::Current()->GetJniIdType(), JniIdType::kPointer) << "JNI Ids are pointers!";
  Thread* self = Thread::Current();
  ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self));
  if (ext.IsNull()) {
    self->AssertPendingOOMException();
    return false;
  }
  return ext->EnsureInstanceJFieldIDsArrayPresent(h_this->NumFields());
}

size_t Class::GetStaticFieldIdOffset(ArtField* field) {
  DCHECK_LT(reinterpret_cast<uintptr_t>(field),
            reinterpret_cast<uintptr_t>(&*GetFieldsPtr()->end()))
      << "field not part of the current class. " << field->PrettyField() << " class is "
      << PrettyClass();
  DCHECK_GE(reinterpret_cast<uintptr_t>(field),
            reinterpret_cast<uintptr_t>(&*GetFieldsPtr()->begin()))
      << "field not part of the current class. " << field->PrettyField() << " class is "
      << PrettyClass();
  uintptr_t start = reinterpret_cast<uintptr_t>(&GetFieldsPtr()->At(0));
  uintptr_t fld = reinterpret_cast<uintptr_t>(field);
  size_t res = (fld - start) / sizeof(ArtField);
  DCHECK_EQ(&GetFieldsPtr()->At(res), field)
      << "Incorrect field computation expected: " << field->PrettyField()
      << " got: " << GetFieldsPtr()->At(res).PrettyField();
  return res;
}

size_t Class::GetInstanceFieldIdOffset(ArtField* field) {
  DCHECK_LT(reinterpret_cast<uintptr_t>(field),
            reinterpret_cast<uintptr_t>(&*GetFieldsPtr()->end()))
      << "field not part of the current class. " << field->PrettyField() << " class is "
      << PrettyClass();
  DCHECK_GE(reinterpret_cast<uintptr_t>(field),
            reinterpret_cast<uintptr_t>(&*GetFieldsPtr()->begin()))
      << "field not part of the current class. " << field->PrettyField() << " class is "
      << PrettyClass();
  uintptr_t start = reinterpret_cast<uintptr_t>(&GetFieldsPtr()->At(0));
  uintptr_t fld = reinterpret_cast<uintptr_t>(field);
  size_t res = (fld - start) / sizeof(ArtField);
  DCHECK_EQ(&GetFieldsPtr()->At(res), field)
      << "Incorrect field computation expected: " << field->PrettyField()
      << " got: " << GetFieldsPtr()->At(res).PrettyField();
  return res;
}

size_t Class::GetMethodIdOffset(ArtMethod* method, PointerSize pointer_size) {
  DCHECK(GetMethodsSlice(kRuntimePointerSize).Contains(method))
      << "method not part of the current class. " << method->PrettyMethod() << "( " << reinterpret_cast<void*>(method) << ")" << " class is "
      << PrettyClass() << [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
        std::ostringstream os;
        os << " Methods are [";
        for (ArtMethod& m : GetMethodsSlice(kRuntimePointerSize)) {
          os << m.PrettyMethod() << "( " << reinterpret_cast<void*>(&m) << "), ";
        }
        os << "]";
        return os.str();
      }();
  uintptr_t start = reinterpret_cast<uintptr_t>(&*GetMethodsSlice(pointer_size).begin());
  uintptr_t fld = reinterpret_cast<uintptr_t>(method);
  size_t art_method_size = ArtMethod::Size(pointer_size);
  size_t art_method_align = ArtMethod::Alignment(pointer_size);
  size_t res = (fld - start) / art_method_size;
  DCHECK_EQ(&GetMethodsPtr()->At(res, art_method_size, art_method_align), method)
      << "Incorrect method computation expected: " << method->PrettyMethod()
      << " got: " << GetMethodsPtr()->At(res, art_method_size, art_method_align).PrettyMethod();
  return res;
}

bool Class::CheckIsVisibleWithTargetSdk(Thread* self) {
  uint32_t targetSdkVersion = Runtime::Current()->GetTargetSdkVersion();
  if (IsSdkVersionSetAndAtMost(targetSdkVersion, SdkVersion::kT)) {
    ObjPtr<mirror::Class> java_lang_ClassValue =
        WellKnownClasses::ToClass(WellKnownClasses::java_lang_ClassValue);
    if (this == java_lang_ClassValue.Ptr()) {
      self->ThrowNewException("Ljava/lang/ClassNotFoundException;""java.lang.ClassValue");
      return false;
    }
  }
  return true;
}

ALWAYS_INLINE
static bool IsInterfaceMethodAccessible(ArtMethod* interface_method)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  // If the interface method is part of the public SDK, return it.
  if ((hiddenapi::GetRuntimeFlags(interface_method) & kAccPublicApi) != 0) {
    hiddenapi::ApiList api_list =
        hiddenapi::ApiList::FromDexFlags(hiddenapi::detail::GetDexFlags(interface_method));
    // The kAccPublicApi flag is also used as an optimization to avoid
    // other hiddenapi checks to always go on the slow path. Therefore, we
    // need to check here if the method is in the SDK list.
    if (api_list.IsSdkApi()) {
      return true;
    }
  }
  return false;
}

ArtMethod* Class::FindAccessibleInterfaceMethod(ArtMethod* implementation_method,
                                                PointerSize pointer_size)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ObjPtr<mirror::IfTable> iftable = GetIfTable();
  if (IsInterface()) {  // Interface class doesn't resolve methods into the iftable.
    for (int32_t i = 0, iftable_count = iftable->Count(); i < iftable_count; ++i) {
      ObjPtr<mirror::Class> iface = iftable->GetInterface(i);
      for (ArtMethod& interface_method : iface->GetMethodsSlice(pointer_size)) {
        if (interface_method.IsVirtual() &&
            implementation_method->HasSameNameAndSignature(&interface_method) &&
            IsInterfaceMethodAccessible(&interface_method)) {
          return &interface_method;
        }
      }
    }
  } else {
    for (int32_t i = 0, iftable_count = iftable->Count(); i < iftable_count; ++i) {
      ObjPtr<mirror::PointerArray> methods = iftable->GetMethodArrayOrNull(i);
      if (methods == nullptr) {
        continue;
      }
      ObjPtr<mirror::Class> iface = iftable->GetInterface(i);
      for (ArtMethod& m : iface->GetDeclaredMethods(pointer_size)) {
        if (m.IsVirtual() &&
            methods->GetElementPtrSize<ArtMethod*>(m.GetMethodIndex(), pointer_size)
                == implementation_method) {
          if (IsInterfaceMethodAccessible(&m)) {
            return &m;
          }
        }
      }
    }
  }
  return nullptr;
}

size_t Class::GetProxyThrowsIndex(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
  CHECK(IsProxyClass());
  size_t i = 0;
  for (const auto& m : GetDeclaredMethods(kRuntimePointerSize)) {
    if (m.IsVirtual()) {
      if (&m == method) {
        return i;
      }
      ++i;
    }
  }
  return static_cast<size_t>(-1);
}

template <PointerSize kPointerSize>
ArtMethod* Class::FindDeclaredClassMethodSlow(uint32_t dex_method_idx) {
  for (ArtMethod& m : GetDeclaredMethods(kPointerSize)) {
    if (m.GetDexMethodIndex() == dex_method_idx) {
      return &m;
    }
  }
  return nullptr;
}


}  // namespace mirror
}  // namespace art

Messung V0.5 in Prozent
C=88 H=95 G=91

¤ Dauer der Verarbeitung: 0.65 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik