Faster Class::FindField().

Change `Class::FindField()` to take dex cache and type index
arguments instead of strings and optimize it similarly to
`Class::FindClassMethod()`, namely search by indexes when
possible (except when searching interfaces where we only add
a TODO comment for now) and avoid `strlen()` calls for ASCII
names and type descriptors.

Update `ClassLinker::FindResolvedFieldJLS()` to pass the new
arguments and replace all other `Class::FindField()` calls
with more direct calls as we know the classes where those
fields are defined.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Change-Id: Iba798819043eb1b53b0dbc41ef8d7fd1c5d2164d
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index c85751a..8b9e342 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -103,15 +103,28 @@
   return StringDataByIdx(type_id.descriptor_idx_);
 }
 
+inline std::string_view DexFile::GetTypeDescriptorView(const dex::TypeId& type_id) const {
+  return StringViewByIdx(type_id.descriptor_idx_);
+}
+
 inline const char* DexFile::GetFieldTypeDescriptor(const dex::FieldId& field_id) const {
   const dex::TypeId& type_id = GetTypeId(field_id.type_idx_);
   return GetTypeDescriptor(type_id);
 }
 
+inline std::string_view DexFile::GetFieldTypeDescriptorView(const dex::FieldId& field_id) const {
+  const dex::TypeId& type_id = GetTypeId(field_id.type_idx_);
+  return GetTypeDescriptorView(type_id);
+}
+
 inline const char* DexFile::GetFieldName(const dex::FieldId& field_id) const {
   return StringDataByIdx(field_id.name_idx_);
 }
 
+inline std::string_view DexFile::GetFieldNameView(const dex::FieldId& field_id) const {
+  return StringViewByIdx(field_id.name_idx_);
+}
+
 inline const char* DexFile::GetMethodDeclaringClassDescriptor(const dex::MethodId& method_id)
     const {
   const dex::TypeId& type_id = GetTypeId(method_id.class_idx_);
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index ca5e73b..c2a2d64 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -307,6 +307,7 @@
 
   // Returns the type descriptor string of a type id.
   const char* GetTypeDescriptor(const dex::TypeId& type_id) const;
+  std::string_view GetTypeDescriptorView(const dex::TypeId& type_id) const;
 
   // Looks up a type for the given string index
   const dex::TypeId* FindTypeId(dex::StringIndex string_idx) const;
@@ -354,9 +355,11 @@
 
   // Returns the class descriptor string of a field id.
   const char* GetFieldTypeDescriptor(const dex::FieldId& field_id) const;
+  std::string_view GetFieldTypeDescriptorView(const dex::FieldId& field_id) const;
 
   // Returns the name of a field id.
   const char* GetFieldName(const dex::FieldId& field_id) const;
+  std::string_view GetFieldNameView(const dex::FieldId& field_id) const;
 
   // Returns the number of method identifiers in the .dex file.
   size_t NumMethodIds() const {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5f4f0ab..50b5357 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -9346,14 +9346,8 @@
                                             ObjPtr<mirror::DexCache> dex_cache,
                                             ObjPtr<mirror::ClassLoader> class_loader,
                                             uint32_t field_idx) {
-  ArtField* resolved = nullptr;
   Thread* self = Thread::Current();
-  const DexFile& dex_file = *dex_cache->GetDexFile();
-  const dex::FieldId& field_id = dex_file.GetFieldId(field_idx);
-
-  const char* name = dex_file.GetFieldName(field_id);
-  const char* type = dex_file.GetFieldTypeDescriptor(field_id);
-  resolved = mirror::Class::FindField(self, klass, name, type);
+  ArtField* resolved = mirror::Class::FindField(self, klass, dex_cache, field_idx);
 
   if (resolved != nullptr &&
       hiddenapi::ShouldDenyAccessToMember(resolved,
@@ -10030,10 +10024,7 @@
   // Make a pretend boot-classpath.
   // TODO: Should we scan the image?
   ArtField* const parent_field =
-      mirror::Class::FindField(self,
-                               h_class_loader->GetClass(),
-                               "parent",
-                               "Ljava/lang/ClassLoader;");
+      jni::DecodeArtField(WellKnownClasses::java_lang_ClassLoader_parent);
   DCHECK(parent_field != nullptr);
   if (parent_loader.Get() == nullptr) {
     ScopedObjectAccessUnchecked soa(self);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 8064bcc..e6397cb 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -908,27 +908,28 @@
 }
 
 // Custom binary search to avoid double comparisons from std::binary_search.
-static ArtField* FindFieldByNameAndType(LengthPrefixedArray<ArtField>* fields,
+static ArtField* FindFieldByNameAndType(const DexFile& dex_file,
+                                        LengthPrefixedArray<ArtField>* fields,
                                         std::string_view name,
                                         std::string_view type)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (fields == nullptr) {
-    return nullptr;
-  }
+  DCHECK(fields != nullptr);
   size_t low = 0;
   size_t high = fields->size();
   ArtField* ret = nullptr;
   while (low < high) {
     size_t mid = (low + high) / 2;
     ArtField& field = fields->At(mid);
+    DCHECK(field.GetDexFile() == &dex_file);
+    const dex::FieldId& field_id = dex_file.GetFieldId(field.GetDexFieldIndex());
     // 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 in the same class name 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.
-    int result = std::string_view(field.GetName()).compare(name);
+    int result = dex_file.GetFieldNameView(field_id).compare(name);
     if (result == 0) {
-      result = std::string_view(field.GetTypeDescriptor()).compare(type);
+      result = dex_file.GetFieldTypeDescriptorView(field_id).compare(type);
     }
     if (result < 0) {
       low = mid + 1;
@@ -954,7 +955,12 @@
 
 ArtField* Class::FindDeclaredInstanceField(std::string_view name, std::string_view type) {
   // Binary search by name. Interfaces are not relevant because they can't contain instance fields.
-  return FindFieldByNameAndType(GetIFieldsPtr(), name, type);
+  LengthPrefixedArray<ArtField>* ifields = GetIFieldsPtr();
+  if (ifields == nullptr) {
+    return nullptr;
+  }
+  DCHECK(!IsProxyClass());
+  return FindFieldByNameAndType(GetDexFile(), ifields, name, type);
 }
 
 ArtField* Class::FindDeclaredInstanceField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) {
@@ -994,7 +1000,28 @@
 
 ArtField* Class::FindDeclaredStaticField(std::string_view name, std::string_view type) {
   DCHECK(!type.empty());
-  return FindFieldByNameAndType(GetSFieldsPtr(), name, type);
+  LengthPrefixedArray<ArtField>* sfields = GetSFieldsPtr();
+  if (sfields == 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(sfields->size(), 2u);
+    DCHECK_EQ(strcmp(sfields->At(0).GetName(), "interfaces"), 0);
+    DCHECK_EQ(strcmp(sfields->At(0).GetTypeDescriptor(), "[Ljava/lang/Class;"), 0);
+    DCHECK_EQ(strcmp(sfields->At(1).GetName(), "throws"), 0);
+    DCHECK_EQ(strcmp(sfields->At(1).GetTypeDescriptor(), "[[Ljava/lang/Class;"), 0);
+    if (name == "interfaces") {
+      return (type == "[Ljava/lang/Class;") ? &sfields->At(0) : nullptr;
+    } else if (name == "throws") {
+      return (type == "[[Ljava/lang/Class;") ? &sfields->At(1) : nullptr;
+    } else {
+      return nullptr;
+    }
+  }
+  return FindFieldByNameAndType(GetDexFile(), sfields, name, type);
 }
 
 ArtField* Class::FindDeclaredStaticField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx) {
@@ -1059,30 +1086,124 @@
   return nullptr;
 }
 
+// Find a field using the JLS field resolution order
+FLATTEN
 ArtField* Class::FindField(Thread* self,
                            ObjPtr<Class> klass,
-                           std::string_view name,
-                           std::string_view type) {
-  // Find a field using the JLS field resolution order
-  for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) {
+                           ObjPtr<mirror::DexCache> dex_cache,
+                           uint32_t field_idx) {
+  // FIXME: Hijacking a proxy class by a custom class loader can break this assumption.
+  DCHECK(!klass->IsProxyClass());
+
+  // 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->dex_type_idx_,
+              klass_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_.index_);
+    ArtField* f = klass->FindDeclaredInstanceField(klass_dex_cache, field_idx);
+    if (f == nullptr) {
+      f = klass->FindDeclaredStaticField(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 = GetDirectInterface(self, k, i);
+        DCHECK(interface != nullptr);
+        ArtField* f = FindStaticField(self, interface, name, type);
+        if (f != nullptr) {
+          return f;
+        }
+      }
+    }
+    return static_cast<ArtField*>(nullptr);
+  };
+
+  // If we had a dex cache mismatch, search declared fields by name and type.
+  if (klass_dex_cache != dex_cache &&
+      (klass->GetIFieldsPtr() != nullptr || klass->GetSFieldsPtr() != nullptr)) {
+    ensure_name_and_type_initialized();
+    ArtField* f = klass->FindDeclaredInstanceField(name, type);
+    if (f == nullptr) {
+      f = klass->FindDeclaredStaticField(name, type);
+    }
+    if (f != nullptr) {
+      return f;
+    }
+  }
+
+  // Search direct interfaces.
+  {
+    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?
-    ArtField* f = k->FindDeclaredInstanceField(name, type);
-    if (f != nullptr) {
-      return f;
-    }
-    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 = GetDirectInterface(self, k, i);
-      DCHECK(interface != nullptr);
-      f = FindStaticField(self, interface, name, type);
+    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->GetIFields()) {
+        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;
+        }
+      }
+      for (ArtField& field : k->GetSFields()) {
+        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 if (k->GetIFieldsPtr() != nullptr || k->GetSFieldsPtr() != nullptr) {
+      ensure_name_and_type_initialized();
+      ArtField* f = k->FindDeclaredInstanceField(name, type);
+      if (f == nullptr) {
+        f = k->FindDeclaredStaticField(name, type);
+      }
       if (f != nullptr) {
         return f;
       }
     }
+    // Is this field in any of this class' interfaces?
+    ArtField* f = search_direct_interfaces(k);
+    if (f != nullptr) {
+      return f;
+    }
   }
   return nullptr;
 }
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index cc2924e..aff98ca 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1094,8 +1094,8 @@
   // Find a static or instance field using the JLS resolution order
   static ArtField* FindField(Thread* self,
                              ObjPtr<Class> klass,
-                             std::string_view name,
-                             std::string_view type)
+                             ObjPtr<mirror::DexCache> dex_cache,
+                             uint32_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Finds the given instance field in this class or a superclass.
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index 5e763dd..c32c702 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -278,7 +278,7 @@
   ScopedObjectAccess soa(self);
 
   ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
-  ArtField* value = mirror::Class::FindField(self, i->GetClass(), "value", "I");
+  ArtField* value = i->GetClass()->FindDeclaredInstanceField("value", "I");
   int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
                                     VarHandle::AccessMode::kGetAndSet,
                                     VarHandle::AccessMode::kGetAndBitwiseXor);
@@ -481,7 +481,7 @@
   ScopedObjectAccess soa(self);
 
   ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
-  ArtField* value = mirror::Class::FindField(self, i->GetClass(), "MIN_VALUE", "I");
+  ArtField* value = i->GetClass()->FindDeclaredStaticField("MIN_VALUE", "I");
   int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kSet,
                                     VarHandle::AccessMode::kGetOpaque,
                                     VarHandle::AccessMode::kGetAndBitwiseAndRelease);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 16a5f93..61a751c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3158,14 +3158,14 @@
     return nullptr;
   }
 
-  ArtField* stack_trace_element_field = h_aste_class->FindField(
-      soa.Self(), h_aste_class.Get(), "stackTraceElement", "Ljava/lang/StackTraceElement;");
+  ArtField* stack_trace_element_field =
+      h_aste_class->FindDeclaredInstanceField("stackTraceElement", "Ljava/lang/StackTraceElement;");
   DCHECK(stack_trace_element_field != nullptr);
-  ArtField* held_locks_field = h_aste_class->FindField(
-        soa.Self(), h_aste_class.Get(), "heldLocks", "[Ljava/lang/Object;");
+  ArtField* held_locks_field =
+      h_aste_class->FindDeclaredInstanceField("heldLocks", "[Ljava/lang/Object;");
   DCHECK(held_locks_field != nullptr);
-  ArtField* blocked_on_field = h_aste_class->FindField(
-        soa.Self(), h_aste_class.Get(), "blockedOn", "Ljava/lang/Object;");
+  ArtField* blocked_on_field =
+      h_aste_class->FindDeclaredInstanceField("blockedOn", "Ljava/lang/Object;");
   DCHECK(blocked_on_field != nullptr);
 
   int32_t length = static_cast<int32_t>(dumper.stack_trace_elements_.size());
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index ee7c591..f606d02 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -627,15 +627,13 @@
       class_linker_->FindClass(soa.Self(), "Ljava/lang/Boolean;", class_loader));
   ASSERT_TRUE(boolean_class != nullptr);
   ASSERT_TRUE(heap->ObjectIsInBootImageSpace(boolean_class.Get()));
-  ArtField* true_field =
-      mirror::Class::FindField(soa.Self(), boolean_class.Get(), "TRUE", "Ljava/lang/Boolean;");
+  ArtField* true_field = boolean_class->FindDeclaredStaticField("TRUE", "Ljava/lang/Boolean;");
   ASSERT_TRUE(true_field != nullptr);
   ASSERT_TRUE(true_field->IsStatic());
   Handle<mirror::Object> true_value = hs.NewHandle(true_field->GetObject(boolean_class.Get()));
   ASSERT_TRUE(true_value != nullptr);
   ASSERT_TRUE(heap->ObjectIsInBootImageSpace(true_value.Get()));
-  ArtField* value_field =
-      mirror::Class::FindField(soa.Self(), boolean_class.Get(), "value", "Z");
+  ArtField* value_field = boolean_class->FindDeclaredInstanceField("value", "Z");
   ASSERT_TRUE(value_field != nullptr);
   ASSERT_FALSE(value_field->IsStatic());
 
@@ -643,8 +641,7 @@
       class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;", class_loader)));
   ASSERT_TRUE(static_field_class != nullptr);
   ASSERT_FALSE(heap->ObjectIsInBootImageSpace(static_field_class.Get()));
-  ArtField* int_field =
-      mirror::Class::FindField(soa.Self(), static_field_class.Get(), "intField", "I");
+  ArtField* int_field = static_field_class->FindDeclaredStaticField("intField", "I");
   ASSERT_TRUE(int_field != nullptr);
 
   Handle<mirror::Class> static_fields_test_class(hs.NewHandle(
@@ -652,7 +649,7 @@
   ASSERT_TRUE(static_fields_test_class != nullptr);
   ASSERT_FALSE(heap->ObjectIsInBootImageSpace(static_fields_test_class.Get()));
   ArtField* static_fields_test_int_field =
-      mirror::Class::FindField(soa.Self(), static_fields_test_class.Get(), "intField", "I");
+      static_fields_test_class->FindDeclaredStaticField("intField", "I");
   ASSERT_TRUE(static_fields_test_int_field != nullptr);
 
   Handle<mirror::Class> instance_fields_test_class(hs.NewHandle(
@@ -660,7 +657,7 @@
   ASSERT_TRUE(instance_fields_test_class != nullptr);
   ASSERT_FALSE(heap->ObjectIsInBootImageSpace(instance_fields_test_class.Get()));
   ArtField* instance_fields_test_int_field =
-      mirror::Class::FindField(soa.Self(), instance_fields_test_class.Get(), "intField", "I");
+      instance_fields_test_class->FindDeclaredInstanceField("intField", "I");
   ASSERT_TRUE(instance_fields_test_int_field != nullptr);
   Handle<mirror::Object> instance_fields_test_object = hs.NewHandle(
       instance_fields_test_class->Alloc(soa.Self(), heap->GetCurrentAllocator()));
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3b3a1a3..8414aa4 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -136,6 +136,7 @@
 jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
 jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
 jfieldID WellKnownClasses::java_io_FileDescriptor_descriptor;
+jfieldID WellKnownClasses::java_lang_ClassLoader_parent;
 jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
 jfieldID WellKnownClasses::java_lang_Thread_daemon;
 jfieldID WellKnownClasses::java_lang_Thread_group;
@@ -429,30 +430,46 @@
   ScopedLocalRef<jclass> java_io_FileDescriptor(env, env->FindClass("java/io/FileDescriptor"));
   java_io_FileDescriptor_descriptor = CacheField(env, java_io_FileDescriptor.get(), false, "descriptor", "I");
 
-  java_lang_Thread_parkBlocker = CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
+  java_lang_ClassLoader_parent =
+      CacheField(env, java_lang_ClassLoader, false, "parent", "Ljava/lang/ClassLoader;");
+  java_lang_Thread_parkBlocker =
+      CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
   java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
-  java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
+  java_lang_Thread_group =
+      CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
   java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
   java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
   java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
   java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
   java_lang_Thread_systemDaemon = CacheField(env, java_lang_Thread, false, "systemDaemon", "Z");
-  java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
-  java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
+  java_lang_Thread_unparkedBeforeStart =
+      CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
+  java_lang_ThreadGroup_groups =
+      CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
   java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
-  java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
-  java_lang_ThreadGroup_name = CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
-  java_lang_ThreadGroup_parent = CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
-  java_lang_ThreadGroup_systemThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
-  java_lang_Throwable_cause = CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
-  java_lang_Throwable_detailMessage = CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
-  java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
-  java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
-  java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
+  java_lang_ThreadGroup_mainThreadGroup =
+      CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
+  java_lang_ThreadGroup_name =
+      CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
+  java_lang_ThreadGroup_parent =
+      CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
+  java_lang_ThreadGroup_systemThreadGroup =
+      CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
+  java_lang_Throwable_cause =
+      CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
+  java_lang_Throwable_detailMessage =
+      CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
+  java_lang_Throwable_stackTrace =
+      CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
+  java_lang_Throwable_stackState =
+      CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
+  java_lang_Throwable_suppressedExceptions =
+      CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
 
   java_nio_Buffer_address = CacheField(env, java_nio_Buffer, false, "address", "J");
   java_nio_Buffer_capacity = CacheField(env, java_nio_Buffer, false, "capacity", "I");
-  java_nio_Buffer_elementSizeShift = CacheField(env, java_nio_Buffer, false, "_elementSizeShift", "I");
+  java_nio_Buffer_elementSizeShift =
+      CacheField(env, java_nio_Buffer, false, "_elementSizeShift", "I");
   java_nio_Buffer_limit = CacheField(env, java_nio_Buffer, false, "limit", "I");
   java_nio_Buffer_position = CacheField(env, java_nio_Buffer, false, "position", "I");
 
@@ -461,12 +478,18 @@
   java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z");
   java_nio_ByteBuffer_limit = CacheField(env, java_nio_ByteBuffer, false, "limit", "I");
   java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I");
-  java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
-  libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
-  org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
-  org_apache_harmony_dalvik_ddmc_Chunk_length = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I");
-  org_apache_harmony_dalvik_ddmc_Chunk_offset = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I");
-  org_apache_harmony_dalvik_ddmc_Chunk_type = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "type", "I");
+  java_util_Collections_EMPTY_LIST =
+      CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
+  libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(
+      env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
+  org_apache_harmony_dalvik_ddmc_Chunk_data =
+      CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
+  org_apache_harmony_dalvik_ddmc_Chunk_length =
+      CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I");
+  org_apache_harmony_dalvik_ddmc_Chunk_offset =
+      CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I");
+  org_apache_harmony_dalvik_ddmc_Chunk_type =
+      CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "type", "I");
 
   java_lang_Boolean_valueOf = CachePrimitiveBoxingMethod(env, 'Z', "java/lang/Boolean");
   java_lang_Byte_valueOf = CachePrimitiveBoxingMethod(env, 'B', "java/lang/Byte");
@@ -602,6 +625,7 @@
   dalvik_system_DexPathList_dexElements = nullptr;
   dalvik_system_DexPathList__Element_dexFile = nullptr;
   dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr;
+  java_lang_ClassLoader_parent = nullptr;
   java_lang_Thread_parkBlocker = nullptr;
   java_lang_Thread_daemon = nullptr;
   java_lang_Thread_group = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index b2e01ae..434d041 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -149,6 +149,7 @@
   static jfieldID dalvik_system_DexPathList__Element_dexFile;
   static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
   static jfieldID java_io_FileDescriptor_descriptor;
+  static jfieldID java_lang_ClassLoader_parent;
   static jfieldID java_lang_Thread_parkBlocker;
   static jfieldID java_lang_Thread_daemon;
   static jfieldID java_lang_Thread_group;