Do superclass validation at compile time and log with new class status.

Tries to perform superclass validation for classes that are resolved,
but not initialized at runtime. If successful, saves the result in the
oat file with a new class status. At runtime, the superclass validation
can be skipped during class initialization, saving some time and
reducing string accesses.

Results show savings of 50kB PSS in maps on startup, with slight
decrease in startup time.

Maps (average of 100 runs)
Before: dex 9941.3 odex 15159.8 total 25101.1 launch 908
After: dex 9897.4 odex 15155.7 total 25053.1 launch 906.6

Bug: 63456114
Test: mm test-art-host
Change-Id: If67a4a49d61781b6d561c26118d7e0c6b9cc0d6f
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index bb64755..e8b9150 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2245,7 +2245,7 @@
     const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage();
     const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage();
 
-    mirror::Class::Status old_status = klass->GetStatus();;
+    mirror::Class::Status old_status = klass->GetStatus();
     // Only try to initialize classes that were successfully verified.
     if (klass->IsVerified()) {
       // Don't initialize classes in boot space when compiling app image
@@ -2363,6 +2363,14 @@
             }
           }
         }
+        // If the class still isn't initialized, at least try some checks that initialization
+        // would do so they can be skipped at runtime.
+        if (!klass->IsInitialized() &&
+            manager_->GetClassLinker()->ValidateSuperClassDescriptors(klass)) {
+          old_status = mirror::Class::kStatusSuperclassValidated;
+        } else {
+          soa.Self()->ClearException();
+        }
         soa.Self()->AssertNoPendingException();
       }
     }
@@ -2860,13 +2868,14 @@
 
 void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
   switch (status) {
-    case mirror::Class::kStatusNotReady:
     case mirror::Class::kStatusErrorResolved:
     case mirror::Class::kStatusErrorUnresolved:
+    case mirror::Class::kStatusNotReady:
+    case mirror::Class::kStatusResolved:
     case mirror::Class::kStatusRetryVerificationAtRuntime:
     case mirror::Class::kStatusVerified:
+    case mirror::Class::kStatusSuperclassValidated:
     case mirror::Class::kStatusInitialized:
-    case mirror::Class::kStatusResolved:
       break;  // Expected states.
     default:
       LOG(FATAL) << "Unexpected class status for class "
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index a5d4540..1c3375c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4405,8 +4405,7 @@
 
   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 ||
-      oat_file_class_status == mirror::Class::kStatusInitialized) {
+  if (oat_file_class_status >= mirror::Class::kStatusVerified) {
     return true;
   }
   // If we only verified a subset of the classes at compile time, we can end up with classes that
@@ -4885,7 +4884,13 @@
       return WaitForInitializeClass(klass, self, lock);
     }
 
-    if (!ValidateSuperClassDescriptors(klass)) {
+    bool has_oat_class = false;
+    const OatFile::OatClass oat_class =
+        (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler())
+            ? OatFile::FindOatClass(klass->GetDexFile(), klass->GetDexClassDefIndex(), &has_oat_class)
+            : OatFile::OatClass::Invalid();
+    if (oat_class.GetStatus() < mirror::Class::kStatusSuperclassValidated &&
+        !ValidateSuperClassDescriptors(klass)) {
       mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
       return false;
     }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 8b8a6fd..864d37f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -675,6 +675,10 @@
   void VisitClassLoaders(ClassLoaderVisitor* visitor) const
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
 
+  // Checks that a class and its superclass from another class loader have the same virtual methods.
+  bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   struct DexCacheData {
     // Construct an invalid data object.
     DexCacheData()
@@ -907,8 +911,6 @@
   bool WaitForInitializeClass(Handle<mirror::Class> klass,
                               Thread* self,
                               ObjectLock<mirror::Class>& lock);
-  bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsSameDescriptorInDifferentClassContexts(Thread* self,
                                                 const char* descriptor,
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 3a7f21d..06e4704 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -48,7 +48,7 @@
 DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_CLASS_OFFSET), (static_cast<int32_t>(art::mirror::Object:: ClassOffset().Int32Value())))
 #define MIRROR_OBJECT_LOCK_WORD_OFFSET 4
 DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_LOCK_WORD_OFFSET), (static_cast<int32_t>(art::mirror::Object:: MonitorOffset().Int32Value())))
-#define MIRROR_CLASS_STATUS_INITIALIZED 0xa
+#define MIRROR_CLASS_STATUS_INITIALIZED 0xb
 DEFINE_CHECK_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), (static_cast<uint32_t>((art::mirror::Class::kStatusInitialized))))
 #define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000
 DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), (static_cast<uint32_t>((art::kAccClassIsFinalizable))))
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index b60ddcf..e516a06 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -138,9 +138,10 @@
     kStatusRetryVerificationAtRuntime = 6,  // Compile time verification failed, retry at runtime.
     kStatusVerifyingAtRuntime = 7,  // Retrying verification at runtime.
     kStatusVerified = 8,  // Logically part of linking; done pre-init.
-    kStatusInitializing = 9,  // Class init in progress.
-    kStatusInitialized = 10,  // Ready to go.
-    kStatusMax = 11,
+    kStatusSuperclassValidated = 9,  // Superclass validation part of init done.
+    kStatusInitializing = 10,  // Class init in progress.
+    kStatusInitialized = 11,  // Ready to go.
+    kStatusMax = 12,
   };
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
diff --git a/runtime/oat.h b/runtime/oat.h
index 5e61907..f4edb16 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: update classpath key format.
-  static constexpr uint8_t kOatVersion[] = { '1', '2', '8', '\0' };
+  // Last oat version changed reason: add new class status to skip superclass validation.
+  static constexpr uint8_t kOatVersion[] = { '1', '2', '9', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 3619129..1ecdd0d 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -23,6 +23,7 @@
 
 #include "art_field-inl.h"
 #include "class_linker-inl.h"
+#include "common_runtime_test.h"
 #include "dexopt_test.h"
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
@@ -1059,7 +1060,7 @@
     const OatFile* oat_file = nullptr;
     dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
         dex_location_.c_str(),
-        /*class_loader*/nullptr,
+        Runtime::Current()->GetSystemClassLoader(),
         /*dex_elements*/nullptr,
         &oat_file,
         &error_msgs);
@@ -1089,6 +1090,10 @@
   std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
   std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
 
+  // Start the runtime to initialize the system's class loader.
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  runtime_->Start();
+
   // We use the lib core dex file, because it's large, and hopefully should
   // take a while to generate.
   Copy(GetLibCoreDexFileNames()[0], dex_location);
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index b166961..dc542d4 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -665,7 +665,10 @@
   // Get the oat file on disk.
   std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
 
-  if (oat_file != nullptr) {
+  // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
+  // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
+  // could load oat files without checking the classpath, which would be incorrect.
+  if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Take the file only if it has no collisions, or we must take it because of preopting.
     bool accept_oat_file =
         !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
diff --git a/test/138-duplicate-classes-check2/src/FancyLoader.java b/test/138-duplicate-classes-check2/src/FancyLoader.java
deleted file mode 100644
index 58b7ec4..0000000
--- a/test/138-duplicate-classes-check2/src/FancyLoader.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 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 java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * A class loader with atypical behavior: we try to load a private
- * class implementation before asking the system or boot loader.  This
- * is used to create multiple classes with identical names in a single VM.
- *
- * If DexFile is available, we use that; if not, we assume we're not in
- * Dalvik and instantiate the class with defineClass().
- *
- * The location of the DEX files and class data is dependent upon the
- * test framework.
- */
-public class FancyLoader extends ClassLoader {
-    /* this is where the "alternate" .class files live */
-    static final String CLASS_PATH = "classes-ex/";
-
-    /* this is the "alternate" DEX/Jar file */
-    static final String DEX_FILE = System.getenv("DEX_LOCATION") +
-            "/138-duplicate-classes-check2-ex.jar";
-
-    /* on Dalvik, this is a DexFile; otherwise, it's null */
-    private Class<?> mDexClass;
-
-    private Object mDexFile;
-
-    /**
-     * Construct FancyLoader, grabbing a reference to the DexFile class
-     * if we're running under Dalvik.
-     */
-    public FancyLoader(ClassLoader parent) {
-        super(parent);
-
-        try {
-            mDexClass = parent.loadClass("dalvik.system.DexFile");
-        } catch (ClassNotFoundException cnfe) {
-            // ignore -- not running Dalvik
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name.
-     *
-     * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
-     * If we don't find a match, we throw an exception.
-     */
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (mDexClass != null) {
-            return findClassDalvik(name);
-        } else {
-            return findClassNonDalvik(name);
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name, from a DEX file.
-     */
-    private Class<?> findClassDalvik(String name)
-        throws ClassNotFoundException {
-
-        if (mDexFile == null) {
-            synchronized (FancyLoader.class) {
-                Constructor<?> ctor;
-                /*
-                 * Construct a DexFile object through reflection.
-                 */
-                try {
-                    ctor = mDexClass.getConstructor(String.class);
-                } catch (NoSuchMethodException nsme) {
-                    throw new ClassNotFoundException("getConstructor failed",
-                        nsme);
-                }
-
-                try {
-                    mDexFile = ctor.newInstance(DEX_FILE);
-                } catch (InstantiationException ie) {
-                    throw new ClassNotFoundException("newInstance failed", ie);
-                } catch (IllegalAccessException iae) {
-                    throw new ClassNotFoundException("newInstance failed", iae);
-                } catch (InvocationTargetException ite) {
-                    throw new ClassNotFoundException("newInstance failed", ite);
-                }
-            }
-        }
-
-        /*
-         * Call DexFile.loadClass(String, ClassLoader).
-         */
-        Method meth;
-
-        try {
-            meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class);
-        } catch (NoSuchMethodException nsme) {
-            throw new ClassNotFoundException("getMethod failed", nsme);
-        }
-
-        try {
-            meth.invoke(mDexFile, name, this);
-        } catch (IllegalAccessException iae) {
-            throw new ClassNotFoundException("loadClass failed", iae);
-        } catch (InvocationTargetException ite) {
-            throw new ClassNotFoundException("loadClass failed",
-                ite.getCause());
-        }
-
-        return null;
-    }
-
-    /**
-     * Finds the class with the specified binary name, from .class files.
-     */
-    private Class<?> findClassNonDalvik(String name)
-        throws ClassNotFoundException {
-
-        String pathName = CLASS_PATH + name + ".class";
-        //System.out.println("--- Fancy: looking for " + pathName);
-
-        File path = new File(pathName);
-        RandomAccessFile raf;
-
-        try {
-            raf = new RandomAccessFile(path, "r");
-        } catch (FileNotFoundException fnfe) {
-            throw new ClassNotFoundException("Not found: " + pathName);
-        }
-
-        /* read the entire file in */
-        byte[] fileData;
-        try {
-            fileData = new byte[(int) raf.length()];
-            raf.readFully(fileData);
-        } catch (IOException ioe) {
-            throw new ClassNotFoundException("Read error: " + pathName);
-        } finally {
-            try {
-                raf.close();
-            } catch (IOException ioe) {
-                // drop
-            }
-        }
-
-        /* create the class */
-        //System.out.println("--- Fancy: defining " + name);
-        try {
-            return defineClass(name, fileData, 0, fileData.length);
-        } catch (Throwable th) {
-            throw new ClassNotFoundException("defineClass failed", th);
-        }
-    }
-
-    /**
-     * Load a class.
-     *
-     * Normally a class loader wouldn't override this, but we want our
-     * version of the class to take precedence over an already-loaded
-     * version.
-     *
-     * We still want the system classes (e.g. java.lang.Object) from the
-     * bootstrap class loader.
-     */
-    protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        Class<?> res;
-
-        /*
-         * 1. Invoke findLoadedClass(String) to check if the class has
-         * already been loaded.
-         *
-         * This doesn't change.
-         */
-        res = findLoadedClass(name);
-        if (res != null) {
-            System.out.println("FancyLoader.loadClass: "
-                + name + " already loaded");
-            if (resolve)
-                resolveClass(res);
-            return res;
-        }
-
-        /*
-         * 3. Invoke the findClass(String) method to find the class.
-         */
-        try {
-            res = findClass(name);
-            if (resolve)
-                resolveClass(res);
-        }
-        catch (ClassNotFoundException e) {
-            // we couldn't find it, so eat the exception and keep going
-        }
-
-        /*
-         * 2. Invoke the loadClass method on the parent class loader.  If
-         * the parent loader is null the class loader built-in to the
-         * virtual machine is used, instead.
-         *
-         * (Since we're not in java.lang, we can't actually invoke the
-         * parent's loadClass() method, but we passed our parent to the
-         * super-class which can take care of it for us.)
-         */
-        res = super.loadClass(name, resolve);   // returns class or throws
-        return res;
-    }
-}
diff --git a/test/138-duplicate-classes-check2/src/Main.java b/test/138-duplicate-classes-check2/src/Main.java
index faf8b5d..588e5eb 100644
--- a/test/138-duplicate-classes-check2/src/Main.java
+++ b/test/138-duplicate-classes-check2/src/Main.java
@@ -15,12 +15,30 @@
  */
 
 import java.io.File;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 
 /**
  * Structural hazard test.
  */
 public class Main {
+    public static String TEST_NAME = "138-duplicate-classes-check2";
+
+    public static ClassLoader getClassLoaderFor(String location) throws Exception {
+        try {
+            Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+            Constructor<?> ctor =
+                    class_loader_class.getConstructor(String.class, ClassLoader.class);
+            /* on Dalvik, this is a DexFile; otherwise, it's null */
+            return (ClassLoader) ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar",
+                                                  Main.class.getClassLoader());
+        } catch (ClassNotFoundException e) {
+            // Running on RI. Use URLClassLoader.
+            return new java.net.URLClassLoader(
+                    new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
+        }
+    }
+
     public static void main(String[] args) {
         new Main().run();
     }
@@ -29,15 +47,18 @@
         System.out.println(new A().i);
 
         // Now run the class from the -ex file.
-
-        FancyLoader loader = new FancyLoader(getClass().getClassLoader());
-
         try {
-            Class<?> testEx = loader.loadClass("TestEx");
-            Method test = testEx.getDeclaredMethod("test");
-            test.invoke(null);
-        } catch (Exception exc) {
-            exc.printStackTrace(System.out);
+            /* this is the "alternate" DEX/Jar file */
+            ClassLoader new_loader = getClassLoaderFor(System.getenv("DEX_LOCATION"));
+            Class<?> klass = (Class<?>) new_loader.loadClass("TestEx");
+            if (klass == null) {
+                throw new AssertionError("loadClass failed");
+            }
+            Method run_test = klass.getMethod("test");
+            run_test.invoke(null);
+        } catch (Exception e) {
+            System.out.println(e.toString());
+            e.printStackTrace(System.out);
         }
     }
 }