Fix java.lang.Void.TYPE (void.class) initialization.

Do not rely on the DexCache resolved types now that entries
can be evicted.

Test: 157-void-class
Test: testrunner.py --host
Bug: 35652776
Bug: 30627598
Change-Id: Ic384174ae1849072568dd6d2ff9cc60d7f7f1880
diff --git a/runtime/Android.bp b/runtime/Android.bp
index b4c7b9c..9958814 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -156,6 +156,7 @@
         "native/java_lang_Thread.cc",
         "native/java_lang_Throwable.cc",
         "native/java_lang_VMClassLoader.cc",
+        "native/java_lang_Void.cc",
         "native/java_lang_invoke_MethodHandleImpl.cc",
         "native/java_lang_ref_FinalizerReference.cc",
         "native/java_lang_ref_Reference.cc",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9b0ffaf..9fefed6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -752,22 +752,6 @@
                FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
   mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
 
-  // Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly
-  // initialized.
-  {
-    const DexFile& dex_file = java_lang_Object->GetDexFile();
-    const DexFile::TypeId* void_type_id = dex_file.FindTypeId("V");
-    CHECK(void_type_id != nullptr);
-    dex::TypeIndex void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
-    // Now we resolve void type so the dex cache contains it. We use java.lang.Object class
-    // as referrer so the used dex cache is core's one.
-    ObjPtr<mirror::Class> resolved_type = ResolveType(dex_file,
-                                                      void_type_idx,
-                                                      java_lang_Object.Get());
-    CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid));
-    self->AssertNoPendingException();
-  }
-
   // Create conflict tables that depend on the class linker.
   runtime->FixupConflictTables();
 
@@ -4162,19 +4146,6 @@
     return false;
   }
 
-  // We may be running with a preopted oat file but without image. In this case,
-  // we don't skip verification of skip_access_checks classes to ensure we initialize
-  // dex caches with all types resolved during verification.
-  // We need to trust image classes, as these might be coming out of a pre-opted, quickened boot
-  // image (that we just failed loading), and the verifier can't be run on quickened opcodes when
-  // the runtime isn't started. On the other hand, app classes can be re-verified even if they are
-  // already pre-opted, as then the runtime is started.
-  if (!Runtime::Current()->IsAotCompiler() &&
-      !Runtime::Current()->GetHeap()->HasBootImageSpace() &&
-      klass->GetClassLoader() != nullptr) {
-    return false;
-  }
-
   uint16_t class_def_index = klass->GetDexClassDefIndex();
   oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
   if (oat_file_class_status == mirror::Class::kStatusVerified ||
diff --git a/runtime/native/java_lang_Void.cc b/runtime/native/java_lang_Void.cc
new file mode 100644
index 0000000..96bfd1b
--- /dev/null
+++ b/runtime/native/java_lang_Void.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "java_lang_Void.h"
+
+#include "class_linker.h"
+#include "jni_internal.h"
+#include "runtime.h"
+#include "scoped_fast_native_object_access-inl.h"
+
+namespace art {
+
+static jclass Void_lookupType(JNIEnv* env, jclass) {
+  ScopedFastNativeObjectAccess soa(env);
+  return soa.AddLocalReference<jclass>(
+      Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kPrimitiveVoid));
+}
+
+static JNINativeMethod gMethods[] = {
+  FAST_NATIVE_METHOD(Void, lookupType, "()Ljava/lang/Class;"),
+};
+
+void register_java_lang_Void(JNIEnv* env) {
+  REGISTER_NATIVE_METHODS("java/lang/Void");
+}
+
+}  // namespace art
diff --git a/runtime/native/java_lang_Void.h b/runtime/native/java_lang_Void.h
new file mode 100644
index 0000000..8777d80
--- /dev/null
+++ b/runtime/native/java_lang_Void.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_java_lang_Void(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 69dcfeb..42a0ca9 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -114,6 +114,7 @@
 #include "native/java_lang_Thread.h"
 #include "native/java_lang_Throwable.h"
 #include "native/java_lang_VMClassLoader.h"
+#include "native/java_lang_Void.h"
 #include "native/java_lang_invoke_MethodHandleImpl.h"
 #include "native/java_lang_ref_FinalizerReference.h"
 #include "native/java_lang_ref_Reference.h"
@@ -1556,6 +1557,7 @@
   register_java_lang_Thread(env);
   register_java_lang_Throwable(env);
   register_java_lang_VMClassLoader(env);
+  register_java_lang_Void(env);
   register_java_util_concurrent_atomic_AtomicLong(env);
   register_libcore_util_CharsetUtils(env);
   register_org_apache_harmony_dalvik_ddmc_DdmServer(env);
diff --git a/test/157-void-class/expected.txt b/test/157-void-class/expected.txt
new file mode 100644
index 0000000..3f61c0b
--- /dev/null
+++ b/test/157-void-class/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+void.class = void
diff --git a/test/157-void-class/info.txt b/test/157-void-class/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/157-void-class/info.txt
diff --git a/test/157-void-class/run b/test/157-void-class/run
new file mode 100755
index 0000000..59e852c
--- /dev/null
+++ b/test/157-void-class/run
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Let the test build its own core image with --no-image and use verify-profile,
+# so that the compiler does not try to initialize classes. This leaves the
+# java.lang.Void compile-time verified but uninitialized.
+./default-run "$@" --no-image \
+    --runtime-option -Ximage-compiler-option \
+    --runtime-option --compiler-filter=verify-profile
diff --git a/test/157-void-class/src/Main.java b/test/157-void-class/src/Main.java
new file mode 100644
index 0000000..322b705
--- /dev/null
+++ b/test/157-void-class/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import libcore.util.EmptyArray;
+
+public class Main {
+    public static void main(String[] args) {
+        try {
+            // Check if we're running dalvik or RI.
+            Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+            System.loadLibrary(args[0]);
+        } catch (ClassNotFoundException e) {
+            usingRI = true;
+            // Add expected JNI_OnLoad log line to match expected.txt.
+            System.out.println("JNI_OnLoad called");
+        }
+        try {
+            // Initialize all classes needed for old java.lang.Void.TYPE initialization.
+            Runnable.class.getMethod("run", EmptyArray.CLASS).getReturnType();
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+        // Clear the resolved types of the ojluni dex file to make sure there is no entry
+        // for "V", i.e. void.
+        clearResolvedTypes(Integer.class);
+        // With java.lang.Void being compile-time verified but uninitialized, initialize
+        // it now. Previously, this would indirectly initialize TYPE with the current,
+        // i.e. zero-initialized, value of TYPE. The only thing that could prevent the
+        // series of calls leading to this was a cache hit in Class.getDexCacheType()
+        // which we have prevented by clearing the cache above.
+        Class<?> voidClass = void.class;
+        System.out.println("void.class = " + voidClass);
+    }
+
+    public static void clearResolvedTypes(Class<?> c) {
+        if (!usingRI) {
+            nativeClearResolvedTypes(c);
+        }
+    }
+
+    public static native void nativeClearResolvedTypes(Class<?> c);
+
+    static boolean usingRI = false;
+}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index f3d4332..99926aa 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -371,6 +371,22 @@
 
 
 if [ "$HAVE_IMAGE" = "n" ]; then
+    if [ "${HOST}" = "y" ]; then
+        framework="${ANDROID_HOST_OUT}/framework"
+        bpath_suffix="-hostdex"
+    else
+        framework="${ANDROID_ROOT}/framework"
+        bpath_suffix=""
+    fi
+    # TODO If the target was compiled WITH_DEXPREOPT=true then these tests will
+    # fail since these jar files will be stripped.
+    bpath="${framework}/core-libart${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/core-oj${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/conscrypt${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/okhttp${bpath_suffix}.jar"
+    bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar"
+    # Pass down the bootclasspath
+    FLAGS="${FLAGS} -Xbootclasspath:${bpath}"
     # Add 5 minutes to give some time to generate the boot image.
     TIME_OUT_VALUE=$((${TIME_OUT_VALUE} + 300))
     DALVIKVM_BOOT_OPT="-Ximage:/system/non-existant/core.art"
diff --git a/test/run-test b/test/run-test
index e808dee..92d7d88 100755
--- a/test/run-test
+++ b/test/run-test
@@ -525,22 +525,6 @@
         err_echo "--no-image is only supported on the art runtime"
         exit 1
     fi
-    if [ "$target_mode" = "no" ]; then
-        framework="${ANDROID_HOST_OUT}/framework"
-        bpath_suffix="-hostdex"
-    else
-        framework="${android_root}/framework"
-        bpath_suffix=""
-    fi
-    # TODO If the target was compiled WITH_DEXPREOPT=true then these tests will
-    # fail since these jar files will be stripped.
-    bpath="${framework}/core-libart${bpath_suffix}.jar"
-    bpath="${bpath}:${framework}/core-oj${bpath_suffix}.jar"
-    bpath="${bpath}:${framework}/conscrypt${bpath_suffix}.jar"
-    bpath="${bpath}:${framework}/okhttp${bpath_suffix}.jar"
-    bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar"
-    # Pass down the bootclasspath
-    run_args="${run_args} --runtime-option -Xbootclasspath:${bpath}"
     run_args="${run_args} --no-image"
 fi