Add fast path for FindClass using the type dex file.

If we are using the PathClassLoader with a BootClassLoader
parent, we can handle the common case in the FindClass function
without needing to go back to java code.

Around 10% speedup measured of maps launch, could be noise due to
large variation of app launch times. Eliminates defineClassNative
from being anywhere near the top of sampling profiles.

Bug: 17397179
Bug: 16828525

(cherry picked from commit 8a39e7fe02e9a81853dc7a75cb50d9ece07a9b37)

Change-Id: I1f31de76c1b1a53e32173b1e61a59b0e2f267c98
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1e64c00..9c2cc6c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2093,7 +2093,6 @@
     // The boot class loader, search the boot class path.
     ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
     if (pair.second != nullptr) {
-      StackHandleScope<1> hs(self);
       return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
     } else {
       // The boot class loader is searched ahead of the application class loader, failures are
@@ -2132,6 +2131,87 @@
     }
   } else {
     ScopedObjectAccessUnchecked soa(self);
+    if (class_loader->GetClass() ==
+            soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) &&
+        class_loader->GetParent()->GetClass() ==
+            soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
+      ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+      // Check if this would be found in the parent boot class loader.
+      if (pair.second != nullptr) {
+        mirror::Class* klass = LookupClass(descriptor, nullptr);
+        if (klass != nullptr) {
+          return klass;
+        }
+        klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+                            *pair.second);
+        if (klass == nullptr) {
+          CHECK(self->IsExceptionPending()) << descriptor;
+          self->ClearException();
+        } else {
+          return klass;
+        }
+      } else {
+        // RegisterDexFile may allocate dex caches (and cause thread suspension).
+        StackHandleScope<3> hs(self);
+        // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
+        // We need to get the DexPathList and loop through it.
+        Handle<mirror::ArtField> cookie_field =
+            hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
+        Handle<mirror::ArtField> dex_file_field =
+            hs.NewHandle(
+                soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
+        mirror::Object* dex_path_list =
+            soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+            GetObject(class_loader.Get());
+        if (dex_path_list != nullptr && dex_file_field.Get() != nullptr &&
+            cookie_field.Get() != nullptr) {
+          // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+          mirror::Object* dex_elements_obj =
+              soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+              GetObject(dex_path_list);
+          // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+          // at the mCookie which is a DexFile vector.
+          if (dex_elements_obj != nullptr) {
+            Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+                hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+            for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+              mirror::Object* element = dex_elements->GetWithoutChecks(i);
+              if (element == nullptr) {
+                // Should never happen, fall back to java code to throw a NPE.
+                break;
+              }
+              mirror::Object* dex_file = dex_file_field->GetObject(element);
+              if (dex_file != nullptr) {
+                const uint64_t cookie = cookie_field->GetLong(dex_file);
+                auto* dex_files =
+                    reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie));
+                if (dex_files == nullptr) {
+                  // This should never happen so log a warning.
+                  LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
+                  break;
+                }
+                for (const DexFile* dex_file : *dex_files) {
+                  const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+                  if (dex_class_def != nullptr) {
+                    RegisterDexFile(*dex_file);
+                    mirror::Class* klass =
+                        DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+                    if (klass == nullptr) {
+                      CHECK(self->IsExceptionPending()) << descriptor;
+                      self->ClearException();
+                      // Exit the loop to make the java code generate an exception.
+                      i = dex_elements->GetLength();
+                      break;
+                    }
+                    return klass;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
     ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                 soa.AddLocalReference<jobject>(class_loader.Get()));
     std::string class_name_string(DescriptorToDot(descriptor));