Revert^2 "Optimize FindClass ClassNotFoundException case"

With the current class loaders, there are many exceptions thrown for
each ClassNotFoundexception from FindClass calling
ClassLoader.loadClass. This CL reduces that by throwing directly from
FindClass to avoid these extra exceptions.

Numbers are from logging time taken by QuickDeliverException.
Time spent in exception throwing during startup goes down (Pixel 2 XL):
    Camera: 20ms -> 5ms
    Calculator: 5ms -> 2ms
    Maps: 20ms -> 10ms

Added option -XX:FastClassNotFoundException=<bool> to enable or
disable the feature.

This reverts commit d399f579cf2a8462ef6f1fbea360fe62be174377.

Bug: 130310316
Bug: 130293184
Bug: 130209120
Test: test-art-host
Test: art/test/testrunner/testrunner.py --host --no-prebuild -t 134
Change-Id: I059abce98fe876d89de35d099647ee673c27343b
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index 6832821..c9ca4c9 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -26,7 +26,8 @@
 
 namespace art {
 
-AotClassLinker::AotClassLinker(InternTable *intern_table) : ClassLinker(intern_table) {}
+AotClassLinker::AotClassLinker(InternTable* intern_table)
+    : ClassLinker(intern_table, /*fast_class_not_found_exceptions=*/ false) {}
 
 AotClassLinker::~AotClassLinker() {}
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f123246..b793d67 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -403,7 +403,7 @@
   }
 }
 
-ClassLinker::ClassLinker(InternTable* intern_table)
+ClassLinker::ClassLinker(InternTable* intern_table, bool fast_class_not_found_exceptions)
     : boot_class_table_(new ClassTable()),
       failed_dex_cache_class_lookups_(0),
       class_roots_(nullptr),
@@ -411,6 +411,7 @@
       init_done_(false),
       log_new_roots_(false),
       intern_table_(intern_table),
+      fast_class_not_found_exceptions_(fast_class_not_found_exceptions),
       quick_resolution_trampoline_(nullptr),
       quick_imt_conflict_trampoline_(nullptr),
       quick_generic_jni_trampoline_(nullptr),
@@ -2978,31 +2979,42 @@
 
       std::string class_name_string(descriptor + 1, descriptor_length - 2);
       std::replace(class_name_string.begin(), class_name_string.end(), '/', '.');
-      ScopedLocalRef<jobject> class_loader_object(
-          soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
-      ScopedLocalRef<jobject> result(soa.Env(), nullptr);
-      {
-        ScopedThreadStateChange tsc(self, kNative);
-        ScopedLocalRef<jobject> class_name_object(
-            soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
-        if (class_name_object.get() == nullptr) {
-          DCHECK(self->IsExceptionPending());  // OOME.
+      if (known_hierarchy &&
+          fast_class_not_found_exceptions_ &&
+          !Runtime::Current()->IsJavaDebuggable()) {
+        // For known hierarchy, we know that the class is going to throw an exception. If we aren't
+        // debuggable, optimize this path by throwing directly here without going back to Java
+        // language. This reduces how many ClassNotFoundExceptions happen.
+        self->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
+                                 "%s",
+                                 class_name_string.c_str());
+      } else {
+        ScopedLocalRef<jobject> class_loader_object(
+            soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
+        ScopedLocalRef<jobject> result(soa.Env(), nullptr);
+        {
+          ScopedThreadStateChange tsc(self, kNative);
+          ScopedLocalRef<jobject> class_name_object(
+              soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
+          if (class_name_object.get() == nullptr) {
+            DCHECK(self->IsExceptionPending());  // OOME.
+            return nullptr;
+          }
+          CHECK(class_loader_object.get() != nullptr);
+          result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
+                                                   WellKnownClasses::java_lang_ClassLoader_loadClass,
+                                                   class_name_object.get()));
+        }
+        if (result.get() == nullptr && !self->IsExceptionPending()) {
+          // broken loader - throw NPE to be compatible with Dalvik
+          ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
+                                                 class_name_string.c_str()).c_str());
           return nullptr;
         }
-        CHECK(class_loader_object.get() != nullptr);
-        result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
-                                                 WellKnownClasses::java_lang_ClassLoader_loadClass,
-                                                 class_name_object.get()));
+        result_ptr = soa.Decode<mirror::Class>(result.get());
+        // Check the name of the returned class.
+        descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
       }
-      if (result.get() == nullptr && !self->IsExceptionPending()) {
-        // broken loader - throw NPE to be compatible with Dalvik
-        ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
-                                               class_name_string.c_str()).c_str());
-        return nullptr;
-      }
-      result_ptr = soa.Decode<mirror::Class>(result.get());
-      // Check the name of the returned class.
-      descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
     } else {
       DCHECK(!MatchesDexFileCaughtExceptions(self->GetException(), this));
     }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5c3e620..f3c3ef8 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -120,7 +120,8 @@
  public:
   static constexpr bool kAppImageMayContainStrings = true;
 
-  explicit ClassLinker(InternTable* intern_table);
+  explicit ClassLinker(InternTable* intern_table,
+                       bool fast_class_not_found_exceptions = true);
   virtual ~ClassLinker();
 
   // Initialize class linker by bootstraping from dex files.
@@ -1367,6 +1368,8 @@
 
   InternTable* intern_table_;
 
+  const bool fast_class_not_found_exceptions_;
+
   // Trampolines within the image the bounce to runtime entrypoints. Done so that there is a single
   // patch point within the image. TODO: make these proper relocations.
   const void* quick_resolution_trampoline_;
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 24fe634..96c8e10 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -358,6 +358,10 @@
       .Define("-Xverifier-logging-threshold=_")
           .WithType<unsigned int>()
           .IntoKey(M::VerifierLoggingThreshold)
+      .Define("-XX:FastClassNotFoundException=_")
+          .WithType<bool>()
+          .WithValueMap({{"false", false}, {"true", true}})
+          .IntoKey(M::FastClassNotFoundException)
       .Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 68c4cb9..c21b2e4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1486,10 +1486,13 @@
   GetHeap()->EnableObjectValidation();
 
   CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);
+
   if (UNLIKELY(IsAotCompiler())) {
     class_linker_ = new AotClassLinker(intern_table_);
   } else {
-    class_linker_ = new ClassLinker(intern_table_);
+    class_linker_ = new ClassLinker(
+        intern_table_,
+        runtime_options.GetOrDefault(Opt::FastClassNotFoundException));
   }
   if (GetHeap()->HasBootImageSpace()) {
     bool result = class_linker_->InitFromBootImage(&error_msg);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 0c332a2..4488680 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -165,4 +165,6 @@
                      ImageSpaceLoadingOrder, \
                      gc::space::ImageSpaceLoadingOrder::kSystemFirst)
 
+RUNTIME_OPTIONS_KEY (bool,                FastClassNotFoundException,     true)
+
 #undef RUNTIME_OPTIONS_KEY
diff --git a/test/134-nodex2oat-nofallback/run b/test/134-nodex2oat-nofallback/run
index 359d22d..bf61d45 100755
--- a/test/134-nodex2oat-nofallback/run
+++ b/test/134-nodex2oat-nofallback/run
@@ -17,6 +17,6 @@
 flags="${@}"
 
 # Make sure we cannot run without an oat file without fallback.
-${RUN} ${flags} --runtime-option -Xno-dex-file-fallback
+${RUN} ${flags} --runtime-option -Xno-dex-file-fallback --runtime-option -XX:FastClassNotFoundException=false
 # Suppress the exit value. This isn't expected to be successful.
 echo "Exit status:" $?