Add ClassPreDefine hook.

This hook is called prior to defining a class for the first time. It
allows callbacks to modify the dex-file used to load the class if they
wish. The event handler is responsible for ensuring that the returned
dex-file is set-up correctly.

Bug: 31684920
Test: mma -j40 test-art-host
Change-Id: Iaed79c1597913148ead795e033a0a10a4ebe6b2b
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 14918df..ddb3245 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2626,13 +2626,26 @@
     self->AssertPendingOOMException();
     return nullptr;
   }
-  ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(dex_file, class_loader.Get());
+  // Get the real dex file. This will return the input if there aren't any callbacks or they do
+  // nothing.
+  DexFile const* new_dex_file = nullptr;
+  DexFile::ClassDef const* new_class_def = nullptr;
+  // TODO We should ideally figure out some way to move this after we get a lock on the klass so it
+  // will only be called once.
+  Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor,
+                                                            klass,
+                                                            class_loader,
+                                                            dex_file,
+                                                            dex_class_def,
+                                                            &new_dex_file,
+                                                            &new_class_def);
+  ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
   if (dex_cache == nullptr) {
     self->AssertPendingOOMException();
     return nullptr;
   }
   klass->SetDexCache(dex_cache);
-  SetupClass(dex_file, dex_class_def, klass, class_loader.Get());
+  SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());
 
   // Mark the string class by setting its access flag.
   if (UNLIKELY(!init_done_)) {
@@ -2658,7 +2671,7 @@
   // end up allocating unfree-able linear alloc resources and then lose the race condition. The
   // other reason is that the field roots are only visited from the class table. So we need to be
   // inserted before we allocate / fill in these fields.
-  LoadClass(self, dex_file, dex_class_def, klass);
+  LoadClass(self, *new_dex_file, *new_class_def, klass);
   if (self->IsExceptionPending()) {
     VLOG(class_linker) << self->GetException()->Dump();
     // An exception occured during load, set status to erroneous while holding klass' lock in case
@@ -2671,7 +2684,7 @@
 
   // Finish loading (if necessary) by finding parents
   CHECK(!klass->IsLoaded());
-  if (!LoadSuperAndInterfaces(klass, dex_file)) {
+  if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
     // Loading failed.
     if (!klass->IsErroneous()) {
       mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 8da979b..d3bb58d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1199,6 +1199,23 @@
  public:
   virtual ~ClassLoadCallback() {}
 
+  // If set we will replace initial_class_def & initial_dex_file with the final versions. The
+  // callback author is responsible for ensuring these are allocated in such a way they can be
+  // cleaned up if another transformation occurs. Note that both must be set or null/unchanged on
+  // return.
+  // Note: the class may be temporary, in which case a following ClassPrepare event will be a
+  //       different object. It is the listener's responsibility to handle this.
+  // Note: This callback is rarely useful so a default implementation has been given that does
+  //       nothing.
+  virtual void ClassPreDefine(const char* descriptor ATTRIBUTE_UNUSED,
+                              Handle<mirror::Class> klass ATTRIBUTE_UNUSED,
+                              Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED,
+                              const DexFile& initial_dex_file ATTRIBUTE_UNUSED,
+                              const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+                              /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
+                              /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED)
+      REQUIRES_SHARED(Locks::mutator_lock_) {}
+
   // A class has been loaded.
   // Note: the class may be temporary, in which case a following ClassPrepare event will be a
   //       different object. It is the listener's responsibility to handle this.
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 7b15a4f..25324b5 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -67,6 +67,36 @@
   }
 }
 
+void RuntimeCallbacks::ClassPreDefine(const char* descriptor,
+                                      Handle<mirror::Class> temp_class,
+                                      Handle<mirror::ClassLoader> loader,
+                                      const DexFile& initial_dex_file,
+                                      const DexFile::ClassDef& initial_class_def,
+                                      /*out*/DexFile const** final_dex_file,
+                                      /*out*/DexFile::ClassDef const** final_class_def) {
+  DexFile const* current_dex_file = &initial_dex_file;
+  DexFile::ClassDef const* current_class_def = &initial_class_def;
+  for (ClassLoadCallback* cb : class_callbacks_) {
+    DexFile const* new_dex_file = nullptr;
+    DexFile::ClassDef const* new_class_def = nullptr;
+    cb->ClassPreDefine(descriptor,
+                       temp_class,
+                       loader,
+                       *current_dex_file,
+                       *current_class_def,
+                       &new_dex_file,
+                       &new_class_def);
+    if ((new_dex_file != nullptr && new_dex_file != current_dex_file) ||
+        (new_class_def != nullptr && new_class_def != current_class_def)) {
+      DCHECK(new_dex_file != nullptr && new_class_def != nullptr);
+      current_dex_file = new_dex_file;
+      current_class_def = new_class_def;
+    }
+  }
+  *final_dex_file = current_dex_file;
+  *final_class_def = current_class_def;
+}
+
 void RuntimeCallbacks::ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) {
   for (ClassLoadCallback* cb : class_callbacks_) {
     cb->ClassPrepare(temp_klass, klass);
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index e580e78..d321254 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -21,12 +21,14 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "dex_file.h"
 #include "handle.h"
 
 namespace art {
 
 namespace mirror {
 class Class;
+class ClassLoader;
 }  // namespace mirror
 
 class ClassLoadCallback;
@@ -99,6 +101,15 @@
   void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void ClassPreDefine(const char* descriptor,
+                      Handle<mirror::Class> temp_class,
+                      Handle<mirror::ClassLoader> loader,
+                      const DexFile& initial_dex_file,
+                      const DexFile::ClassDef& initial_class_def,
+                      /*out*/DexFile const** final_dex_file,
+                      /*out*/DexFile::ClassDef const** final_class_def)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   std::vector<ThreadLifecycleCallback*> thread_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 8974b59..66eb2ec 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -249,6 +249,21 @@
   }
 
   struct Callback : public ClassLoadCallback {
+    virtual void ClassPreDefine(const char* descriptor,
+                                Handle<mirror::Class> klass ATTRIBUTE_UNUSED,
+                                Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED,
+                                const DexFile& initial_dex_file,
+                                const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+                                /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
+                                /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED)
+        OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+      std::string location(initial_dex_file.GetLocation());
+      std::string event =
+          std::string("PreDefine:") + descriptor + " <" +
+          location.substr(location.rfind("/") + 1, location.size()) + ">";
+      data.push_back(event);
+    }
+
     void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
       std::string tmp;
       std::string event = std::string("Load:") + klass->GetDescriptor(&tmp);
@@ -281,14 +296,21 @@
       hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
   ASSERT_TRUE(h_Y.Get() != nullptr);
 
-  bool expect1 = Expect({ "Load:LX;", "Prepare:LX;[LX;]", "Load:LY;", "Prepare:LY;[LY;]" });
+  bool expect1 = Expect({ "PreDefine:LY; <art-gtest-XandY.jar>",
+                          "PreDefine:LX; <art-gtest-XandY.jar>",
+                          "Load:LX;",
+                          "Prepare:LX;[LX;]",
+                          "Load:LY;",
+                          "Prepare:LY;[LY;]" });
   EXPECT_TRUE(expect1);
 
   cb_.data.clear();
 
   ASSERT_TRUE(class_linker_->EnsureInitialized(Thread::Current(), h_Y, true, true));
 
-  bool expect2 = Expect({ "Load:LY$Z;", "Prepare:LY$Z;[LY$Z;]" });
+  bool expect2 = Expect({ "PreDefine:LY$Z; <art-gtest-XandY.jar>",
+                          "Load:LY$Z;",
+                          "Prepare:LY$Z;[LY$Z;]" });
   EXPECT_TRUE(expect2);
 }