Boot image: only use the resolution stub when there is AOT code.

This avoids dirtying the page when the class becomes initialized.

Test: test.py
Bug: 119800099
Change-Id: I5cd7ae56b6637583179dbb766da251977b7ac961
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index e1839fe..dd29fa2 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -3371,12 +3371,9 @@
     quick_code = method->IsNative()
         ? GetOatAddress(StubType::kQuickGenericJNITrampoline)
         : GetOatAddress(StubType::kQuickToInterpreterBridge);
-  }
-
-  if (!method->GetDeclaringClass()->IsInitialized() &&
-      method->IsStatic() &&
-      !method->IsConstructor()) {
-    // We need to go through the resolution stub for class initialization.
+  } else if (method->NeedsInitializationCheck()) {
+    // If we do have code and the method needs an initialization check, go through
+    // the resolution stub.
     quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
   }
   return quick_code;
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 646f73d..6cfb015 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -881,4 +881,8 @@
         method->GetDeclaringClass<kReadBarrierOption>()->IsErroneous());
 }
 
+bool ArtMethod::NeedsInitializationCheck() {
+  return IsStatic() && !IsConstructor() && !GetDeclaringClass()->IsVisiblyInitialized();
+}
+
 }  // namespace art
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d84ea7c..46cc1ed 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -365,6 +365,10 @@
     AddAccessFlags(kAccMustCountLocks);
   }
 
+  // Returns whether the method can be invoked directly or needs a
+  // class initialization check.
+  bool NeedsInitializationCheck() REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Returns true if this method could be overridden by a default method.
   bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 93fb8a3..a1372ec 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3725,6 +3725,10 @@
       // Only update static methods.
       continue;
     }
+    if (!IsQuickResolutionStub(method->GetEntryPointFromQuickCompiledCode())) {
+      // Only update methods whose entrypoint is the resolution stub.
+      continue;
+    }
     const void* quick_code = nullptr;
     if (has_oat_class) {
       OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index d0901c4..58f263e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2381,6 +2381,21 @@
     jit->MethodEntered(self, called);
   }
 
+  // We can set the entrypoint of a native method to generic JNI even when the
+  // class hasn't been initialized, so we need to do the initialization check
+  // before invoking the native code.
+  if (called->NeedsInitializationCheck()) {
+    // Ensure static method's class is initialized.
+    StackHandleScope<1> hs(self);
+    Handle<mirror::Class> h_class(hs.NewHandle(called->GetDeclaringClass()));
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+      DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod();
+      self->PopHandleScope();
+      // A negative value denotes an error.
+      return GetTwoWordFailureValue();
+    }
+  }
+
   uint32_t cookie;
   uint32_t* sp32;
   // Skip calling JniMethodStart for @CriticalNative.
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 08883fc..d6d0b87 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -1023,12 +1023,15 @@
     return false;
   }
   if (UNLIKELY(method->IsPreCompiled()) && !with_backedges /* don't check for OSR */) {
-    const void* entry_point = code_cache_->GetSavedEntryPointOfPreCompiledMethod(method);
-    if (entry_point != nullptr) {
-      Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(method, entry_point);
-      return true;
+    if (!method->NeedsInitializationCheck()) {
+      const void* entry_point = code_cache_->GetSavedEntryPointOfPreCompiledMethod(method);
+      if (entry_point != nullptr) {
+        Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(method, entry_point);
+        return true;
+      }
     }
   }
+
   if (IgnoreSamplesForMethod(method)) {
     return false;
   }
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index ecfe9b6..665b8be 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -757,7 +757,7 @@
       data->SetCode(code_ptr);
       instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation();
       for (ArtMethod* m : data->GetMethods()) {
-        if (!class_linker->IsQuickResolutionStub(m->GetEntryPointFromQuickCompiledCode())) {
+        if (!m->NeedsInitializationCheck()) {
           instrum->UpdateMethodsCode(m, method_header->GetEntryPoint());
         }
       }
@@ -770,8 +770,7 @@
       if (osr) {
         number_of_osr_compilations_++;
         osr_code_map_.Put(method, code_ptr);
-      } else if (class_linker->IsQuickResolutionStub(
-          method->GetEntryPointFromQuickCompiledCode())) {
+      } else if (method->NeedsInitializationCheck()) {
         // This situation currently only occurs in the jit-zygote mode.
         DCHECK(Runtime::Current()->IsUsingApexBootImageLocation());
         DCHECK(!garbage_collect_code_);
@@ -1572,18 +1571,15 @@
     return false;
   }
 
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  if (class_linker->IsQuickResolutionStub(method->GetEntryPointFromQuickCompiledCode())) {
-    if (!prejit) {
-      // Unless we're pre-jitting, we currently don't save the JIT compiled code if we cannot
-      // update the entrypoint due to having the resolution stub.
-      VLOG(jit) << "Not compiling "
-                << method->PrettyMethod()
-                << " because it has the resolution stub";
-      // Give it a new chance to be hot.
-      ClearMethodCounter(method, /*was_warm=*/ false);
-      return false;
-    }
+  if (method->NeedsInitializationCheck() && !prejit) {
+    // Unless we're pre-jitting, we currently don't save the JIT compiled code if we cannot
+    // update the entrypoint due to needing an initialization check.
+    VLOG(jit) << "Not compiling "
+              << method->PrettyMethod()
+              << " because it has the resolution stub";
+    // Give it a new chance to be hot.
+    ClearMethodCounter(method, /*was_warm=*/ false);
+    return false;
   }
 
   MutexLock mu(self, *Locks::jit_lock_);
@@ -1614,7 +1610,7 @@
       for (ArtMethod* m : data->GetMethods()) {
         // Call the dedicated method instead of the more generic UpdateMethodsCode, because
         // `m` might be in the process of being deleted.
-        if (!class_linker->IsQuickResolutionStub(m->GetEntryPointFromQuickCompiledCode())) {
+        if (!m->NeedsInitializationCheck()) {
           instrumentation->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
         }
       }