Fix image bug around clinit and nterp.

nterp trampoline doesn't do clinit checks, so we should install the
resolution stub if a method needs a clinit check before being entered.

Test: 818-clinit-nterp
Bug: 178992601
Change-Id: I9d6b023a6c6620adfcd2a6ed5cd1e042a7cb449b
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 440b677..0e5914b 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -3175,18 +3175,27 @@
     quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
   }
 
+  bool needs_clinit_check = NeedsClinitCheckBeforeCall(method) &&
+      !method->GetDeclaringClass()->IsVisiblyInitialized();
+
   if (quick_code == nullptr) {
-    // If we don't have code, use generic jni / interpreter bridge.
-    // Both perform class initialization check if needed.
+    // If we don't have code, use generic jni / interpreter.
     if (method->IsNative()) {
+      // The generic JNI trampolines performs class initialization check if needed.
       quick_code = GetOatAddress(StubType::kQuickGenericJNITrampoline);
     } else if (CanMethodUseNterp(method, compiler_options_.GetInstructionSet())) {
-      quick_code = GetOatAddress(StubType::kNterpTrampoline);
+      // The nterp trampoline doesn't do initialization checks, so install the
+      // resolution stub if needed.
+      if (needs_clinit_check) {
+        quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
+      } else {
+        quick_code = GetOatAddress(StubType::kNterpTrampoline);
+      }
     } else {
+      // The interpreter brige performs class initialization check if needed.
       quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge);
     }
-  } else if (NeedsClinitCheckBeforeCall(method) &&
-             !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+  } else if (needs_clinit_check) {
     // If we do have code but the method needs a class initialization check before calling
     // that code, install the resolution stub that will perform the check.
     quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9cde319..8080eca 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1979,6 +1979,8 @@
       // nterp entry point.
       if (method.GetEntryPointFromQuickCompiledCode() == nterp_trampoline_) {
         if (can_use_nterp) {
+          DCHECK(!NeedsClinitCheckBeforeCall(&method) ||
+                 method.GetDeclaringClass()->IsVisiblyInitialized());
           method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpEntryPoint());
         } else {
           method.SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
diff --git a/runtime/image.cc b/runtime/image.cc
index 97bb0ad..57a2972 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -29,8 +29,8 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-// Last change: Rewrite ClassLinker::LinkFields().
-const uint8_t ImageHeader::kImageVersion[] = { '0', '9', '2', '\0' };
+// Last change: Fix clinit with nterp.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '9', '3', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_reservation_size,
                          uint32_t component_count,
diff --git a/test/818-clinit-nterp/expected-stderr.txt b/test/818-clinit-nterp/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/818-clinit-nterp/expected-stderr.txt
diff --git a/test/818-clinit-nterp/expected-stdout.txt b/test/818-clinit-nterp/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/818-clinit-nterp/expected-stdout.txt
diff --git a/test/818-clinit-nterp/info.txt b/test/818-clinit-nterp/info.txt
new file mode 100644
index 0000000..7313189
--- /dev/null
+++ b/test/818-clinit-nterp/info.txt
@@ -0,0 +1,2 @@
+Regression test for image creation, which used to install the nterp trampoline
+on methods that required a clinit check.
diff --git a/test/818-clinit-nterp/profile b/test/818-clinit-nterp/profile
new file mode 100644
index 0000000..df4346e
--- /dev/null
+++ b/test/818-clinit-nterp/profile
@@ -0,0 +1,2 @@
+LMain;
+LClinit;
diff --git a/test/818-clinit-nterp/run b/test/818-clinit-nterp/run
new file mode 100644
index 0000000..52d2b5f
--- /dev/null
+++ b/test/818-clinit-nterp/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+exec ${RUN} $@ --profile
diff --git a/test/818-clinit-nterp/src/Main.java b/test/818-clinit-nterp/src/Main.java
new file mode 100644
index 0000000..5342cec
--- /dev/null
+++ b/test/818-clinit-nterp/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    Clinit.run();
+    if (!clinitDidRun) {
+      throw new Error("Expected Clinit.<clinit> to have run");
+    }
+  }
+  static boolean clinitDidRun = false;
+}
+
+class Clinit {
+  public static void run() {
+  }
+
+  static {
+    Main.clinitDidRun = true;
+  }
+}