Revert "Revert "Implement RetransformClasses""

This reverts commit 52a2db50b76f2b981d21d5508c3d9e8ab4c5fe93.

Reason for revert: Issue with RedefineClasses was resolved.

Test: ART_TEST_JNI_FORCECOPY=true mma -j40 test-art-host

Change-Id: I06f198df4fb40c48647b358d241710329e0812e9
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 90467db..32e3948 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -631,7 +631,17 @@
   }
 
   static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) {
-    return ERR(NOT_IMPLEMENTED);
+    std::string error_msg;
+    jvmtiError res = Transformer::RetransformClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+                                                     art::Runtime::Current(),
+                                                     art::Thread::Current(),
+                                                     class_count,
+                                                     classes,
+                                                     &error_msg);
+    if (res != OK) {
+      LOG(WARNING) << "FAILURE TO RETRANFORM " << error_msg;
+    }
+    return res;
   }
 
   static jvmtiError RedefineClasses(jvmtiEnv* env,
@@ -1255,78 +1265,6 @@
     *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI;
     return ERR(NONE);
   }
-
-  // TODO Remove this once events are working.
-  static jvmtiError RetransformClassWithHook(jvmtiEnv* env,
-                                             jclass klass,
-                                             jvmtiEventClassFileLoadHook hook) {
-    std::vector<jclass> classes;
-    classes.push_back(klass);
-    return RetransformClassesWithHook(reinterpret_cast<ArtJvmTiEnv*>(env), classes, hook);
-  }
-
-  // TODO This will be called by the event handler for the art::ti Event Load Event
-  static jvmtiError RetransformClassesWithHook(ArtJvmTiEnv* env,
-                                               const std::vector<jclass>& classes,
-                                               jvmtiEventClassFileLoadHook hook) {
-    if (!IsValidEnv(env)) {
-      return ERR(INVALID_ENVIRONMENT);
-    }
-    jvmtiError res = OK;
-    std::string error;
-    for (jclass klass : classes) {
-      JNIEnv* jni_env = nullptr;
-      jobject loader = nullptr;
-      std::string name;
-      jobject protection_domain = nullptr;
-      jint data_len = 0;
-      unsigned char* dex_data = nullptr;
-      jvmtiError ret = OK;
-      std::string location;
-      if ((ret = GetTransformationData(env,
-                                       klass,
-                                       /*out*/&location,
-                                       /*out*/&jni_env,
-                                       /*out*/&loader,
-                                       /*out*/&name,
-                                       /*out*/&protection_domain,
-                                       /*out*/&data_len,
-                                       /*out*/&dex_data)) != OK) {
-        // TODO Do something more here? Maybe give log statements?
-        return ret;
-      }
-      jint new_data_len = 0;
-      unsigned char* new_dex_data = nullptr;
-      hook(env,
-           jni_env,
-           klass,
-           loader,
-           name.c_str(),
-           protection_domain,
-           data_len,
-           dex_data,
-           /*out*/&new_data_len,
-           /*out*/&new_dex_data);
-      // Check if anything actually changed.
-      if ((new_data_len != 0 || new_dex_data != nullptr) && new_dex_data != dex_data) {
-        jvmtiClassDefinition def = { klass, new_data_len, new_dex_data };
-        res = Redefiner::RedefineClasses(env,
-                                         art::Runtime::Current(),
-                                         art::Thread::Current(),
-                                         1,
-                                         &def,
-                                         &error);
-        env->Deallocate(new_dex_data);
-      }
-      // Deallocate the old dex data.
-      env->Deallocate(dex_data);
-      if (res != OK) {
-        LOG(ERROR) << "FAILURE TO REDEFINE " << error;
-        return res;
-      }
-    }
-    return OK;
-  }
 };
 
 static bool IsJvmtiVersion(jint version) {
@@ -1369,10 +1307,7 @@
 
 // The actual struct holding all of the entrypoints into the jvmti interface.
 const jvmtiInterface_1 gJvmtiInterface = {
-  // SPECIAL FUNCTION: RetransformClassWithHook Is normally reserved1
-  // TODO Remove once we have events working.
-  reinterpret_cast<void*>(JvmtiFunctions::RetransformClassWithHook),
-  // nullptr,  // reserved1
+  nullptr,  // reserved1
   JvmtiFunctions::SetEventNotificationMode,
   nullptr,  // reserved3
   JvmtiFunctions::GetAllThreads,
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 5eadc5a..1c84d4d 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -47,6 +47,7 @@
 namespace openjdkjvmti {
 
 extern const jvmtiInterface_1 gJvmtiInterface;
+extern EventHandler gEventHandler;
 
 // A structure that is a jvmtiEnv with additional information for the runtime.
 struct ArtJvmTiEnv : public jvmtiEnv {
@@ -124,6 +125,29 @@
   return ret;
 }
 
+struct ArtClassDefinition {
+  jclass klass;
+  jobject loader;
+  std::string name;
+  jobject protection_domain;
+  jint dex_len;
+  JvmtiUniquePtr dex_data;
+  bool modified;
+
+  ArtClassDefinition() = default;
+  ArtClassDefinition(ArtClassDefinition&& o) = default;
+
+  void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) {
+    if (new_dex_data == nullptr) {
+      return;
+    } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) {
+      modified = true;
+      dex_len = new_dex_len;
+      dex_data = MakeJvmtiUniquePtr(env, new_dex_data);
+    }
+  }
+};
+
 const jvmtiCapabilities kPotentialCapabilities = {
     .can_tag_objects                                 = 1,
     .can_generate_field_modification_events          = 0,
@@ -134,7 +158,7 @@
     .can_get_current_contended_monitor               = 0,
     .can_get_monitor_info                            = 0,
     .can_pop_frame                                   = 0,
-    .can_redefine_classes                            = 0,
+    .can_redefine_classes                            = 1,
     .can_signal_thread                               = 0,
     .can_get_source_file_name                        = 0,
     .can_get_line_numbers                            = 0,
@@ -162,7 +186,7 @@
     .can_get_owned_monitor_stack_depth_info          = 0,
     .can_get_constant_pool                           = 0,
     .can_set_native_method_prefix                    = 0,
-    .can_retransform_classes                         = 0,
+    .can_retransform_classes                         = 1,
     .can_retransform_any_class                       = 0,
     .can_generate_resource_exhaustion_heap_events    = 0,
     .can_generate_resource_exhaustion_threads_events = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 1e07bc6..21ec731 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -115,9 +115,95 @@
 }
 
 template <typename ...Args>
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread*,
+                                                         ArtJvmtiEvent event,
+                                                         Args... args ATTRIBUTE_UNUSED) const {
+  CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+        event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+  LOG(FATAL) << "Incorrect arguments to ClassFileLoadHook!";
+}
+
+// TODO Locking of some type!
+template <>
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
+                                                         ArtJvmtiEvent event,
+                                                         JNIEnv* jnienv,
+                                                         jclass class_being_redefined,
+                                                         jobject loader,
+                                                         const char* name,
+                                                         jobject protection_domain,
+                                                         jint class_data_len,
+                                                         const unsigned char* class_data,
+                                                         jint* new_class_data_len,
+                                                         unsigned char** new_class_data) const {
+  CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+        event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+  using FnType = void(jvmtiEnv*            /* jvmti_env */,
+                      JNIEnv*              /* jnienv */,
+                      jclass               /* class_being_redefined */,
+                      jobject              /* loader */,
+                      const char*          /* name */,
+                      jobject              /* protection_domain */,
+                      jint                 /* class_data_len */,
+                      const unsigned char* /* class_data */,
+                      jint*                /* new_class_data_len */,
+                      unsigned char**      /* new_class_data */);
+  jint current_len = class_data_len;
+  unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
+  ArtJvmTiEnv* last_env = nullptr;
+  for (ArtJvmTiEnv* env : envs) {
+    if (ShouldDispatch(event, env, thread)) {
+      jint new_len;
+      unsigned char* new_data;
+      FnType* callback = GetCallback<FnType>(env, event);
+      callback(env,
+               jnienv,
+               class_being_redefined,
+               loader,
+               name,
+               protection_domain,
+               current_len,
+               current_class_data,
+               &new_len,
+               &new_data);
+      if (new_data != nullptr && new_data != current_class_data) {
+        // Destroy the data the last transformer made. We skip this if the previous state was the
+        // initial one since we don't know here which jvmtiEnv allocated it.
+        // NB Currently this doesn't matter since all allocations just go to malloc but in the
+        // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
+        if (last_env != nullptr) {
+          last_env->Deallocate(current_class_data);
+        }
+        last_env = env;
+        current_class_data = new_data;
+        current_len = new_len;
+      }
+    }
+  }
+  if (last_env != nullptr) {
+    *new_class_data_len = current_len;
+    *new_class_data = current_class_data;
+  }
+}
+
+template <typename ...Args>
 inline void EventHandler::DispatchEvent(art::Thread* thread,
                                         ArtJvmtiEvent event,
                                         Args... args) const {
+  switch (event) {
+    case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+    case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
+      return DispatchClassFileLoadHookEvent(thread, event, args...);
+    default:
+      return GenericDispatchEvent(thread, event, args...);
+  }
+}
+
+// TODO Locking of some type!
+template <typename ...Args>
+inline void EventHandler::GenericDispatchEvent(art::Thread* thread,
+                                               ArtJvmtiEvent event,
+                                               Args... args) const {
   using FnType = void(jvmtiEnv*, Args...);
   for (ArtJvmTiEnv* env : envs) {
     if (ShouldDispatch(event, env, thread)) {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 7990141..08a8765 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -178,6 +178,15 @@
   ALWAYS_INLINE
   inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
 
+  template <typename ...Args>
+  ALWAYS_INLINE inline void GenericDispatchEvent(art::Thread* thread,
+                                                 ArtJvmtiEvent event,
+                                                 Args... args) const;
+  template <typename ...Args>
+  ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread,
+                                                           ArtJvmtiEvent event,
+                                                           Args... args) const;
+
   void HandleEventType(ArtJvmtiEvent event, bool enable);
 
   // List of all JvmTiEnv objects that have been created, in their creation order.
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 6af51c4..2db8a40 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -242,14 +242,12 @@
   }
 }
 
-// TODO This should handle doing multiple classes at once so we need to do less cleanup when things
-// go wrong.
 jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env,
                                       art::Runtime* runtime,
                                       art::Thread* self,
                                       jint class_count,
                                       const jvmtiClassDefinition* definitions,
-                                      std::string* error_msg) {
+                                      /*out*/std::string* error_msg) {
   if (env == nullptr) {
     *error_msg = "env was null!";
     return ERR(INVALID_ENVIRONMENT);
@@ -263,46 +261,95 @@
     *error_msg = "null definitions!";
     return ERR(NULL_POINTER);
   }
+  std::vector<ArtClassDefinition> def_vector;
+  def_vector.reserve(class_count);
+  for (jint i = 0; i < class_count; i++) {
+    // We make a copy of the class_bytes to pass into the retransformation.
+    // This makes cleanup easier (since we unambiguously own the bytes) and also is useful since we
+    // will need to keep the original bytes around unaltered for subsequent RetransformClasses calls
+    // to get the passed in bytes.
+    // TODO Implement saving the original bytes.
+    unsigned char* class_bytes_copy = nullptr;
+    jvmtiError res = env->Allocate(definitions[i].class_byte_count, &class_bytes_copy);
+    if (res != OK) {
+      return res;
+    }
+    memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count);
+
+    ArtClassDefinition def;
+    def.dex_len = definitions[i].class_byte_count;
+    def.dex_data = MakeJvmtiUniquePtr(env, class_bytes_copy);
+    // We are definitely modified.
+    def.modified = true;
+    res = Transformer::FillInTransformationData(env, definitions[i].klass, &def);
+    if (res != OK) {
+      return res;
+    }
+    def_vector.push_back(std::move(def));
+  }
+  // Call all the transformation events.
+  jvmtiError res = Transformer::RetransformClassesDirect(env,
+                                                         self,
+                                                         &def_vector);
+  if (res != OK) {
+    // Something went wrong with transformation!
+    return res;
+  }
+  return RedefineClassesDirect(env, runtime, self, def_vector, error_msg);
+}
+
+jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
+                                            art::Runtime* runtime,
+                                            art::Thread* self,
+                                            const std::vector<ArtClassDefinition>& definitions,
+                                            std::string* error_msg) {
+  DCHECK(env != nullptr);
+  if (definitions.size() == 0) {
+    // We don't actually need to do anything. Just return OK.
+    return OK;
+  }
   // Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we
   // are going to redefine.
   art::jit::ScopedJitSuspend suspend_jit;
   // Get shared mutator lock so we can lock all the classes.
   art::ScopedObjectAccess soa(self);
   std::vector<Redefiner::ClassRedefinition> redefinitions;
-  redefinitions.reserve(class_count);
+  redefinitions.reserve(definitions.size());
   Redefiner r(runtime, self, error_msg);
-  for (jint i = 0; i < class_count; i++) {
-    jvmtiError res = r.AddRedefinition(env, definitions[i]);
-    if (res != OK) {
-      return res;
+  for (const ArtClassDefinition& def : definitions) {
+    // Only try to transform classes that have been modified.
+    if (def.modified) {
+      jvmtiError res = r.AddRedefinition(env, def);
+      if (res != OK) {
+        return res;
+      }
     }
   }
   return r.Run();
 }
 
-jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const jvmtiClassDefinition& def) {
+jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def) {
   std::string original_dex_location;
   jvmtiError ret = OK;
   if ((ret = GetClassLocation(env, def.klass, &original_dex_location))) {
     *error_msg_ = "Unable to get original dex file location!";
     return ret;
   }
-  std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
-                                                    def.class_byte_count,
-                                                    def.class_bytes,
-                                                    error_msg_));
-  std::ostringstream os;
   char* generic_ptr_unused = nullptr;
   char* signature_ptr = nullptr;
-  if (env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused) != OK) {
-    *error_msg_ = "A jclass passed in does not seem to be valid";
-    return ERR(INVALID_CLASS);
+  if ((ret = env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused)) != OK) {
+    *error_msg_ = "Unable to get class signature!";
+    return ret;
   }
-  // These will make sure we deallocate the signature.
-  JvmtiUniquePtr sig_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
   JvmtiUniquePtr generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused));
+  JvmtiUniquePtr signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
+  std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
+                                                    def.dex_len,
+                                                    def.dex_data.get(),
+                                                    error_msg_));
+  std::ostringstream os;
   if (map.get() == nullptr) {
-    os << "Failed to create anonymous mmap for modified dex file of class " << signature_ptr
+    os << "Failed to create anonymous mmap for modified dex file of class " << def.name
        << "in dex file " << original_dex_location << " because: " << *error_msg_;
     *error_msg_ = os.str();
     return ERR(OUT_OF_MEMORY);
@@ -319,7 +366,7 @@
                                                                   /*verify_checksum*/true,
                                                                   error_msg_));
   if (dex_file.get() == nullptr) {
-    os << "Unable to load modified dex file for " << signature_ptr << ": " << *error_msg_;
+    os << "Unable to load modified dex file for " << def.name << ": " << *error_msg_;
     *error_msg_ = os.str();
     return ERR(INVALID_CLASS_FORMAT);
   }
@@ -989,17 +1036,16 @@
 // Performs updates to class that will allow us to verify it.
 void Redefiner::ClassRedefinition::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
                                                art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
-  const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
-      *dex_file_, class_sig_.c_str(), art::ComputeModifiedUtf8Hash(class_sig_.c_str()));
-  DCHECK(class_def != nullptr);
-  UpdateMethods(mclass, new_dex_cache, *class_def);
+  DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
+  const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0);
+  UpdateMethods(mclass, new_dex_cache, class_def);
   UpdateFields(mclass);
 
   // Update the class fields.
   // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
   // to call GetReturnTypeDescriptor and GetParameterTypeList above).
   mclass->SetDexCache(new_dex_cache.Ptr());
-  mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
+  mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(class_def));
   mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str())));
 }
 
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 8626bc5..f8d51ad 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -72,13 +72,25 @@
  public:
   // Redefine the given classes with the given dex data. Note this function does not take ownership
   // of the dex_data pointers. It is not used after this call however and may be freed if desired.
+  // The caller is responsible for freeing it. The runtime makes its own copy of the data. This
+  // function does not call the transformation events.
+  // TODO Check modified flag of the definitions.
+  static jvmtiError RedefineClassesDirect(ArtJvmTiEnv* env,
+                                          art::Runtime* runtime,
+                                          art::Thread* self,
+                                          const std::vector<ArtClassDefinition>& definitions,
+                                          /*out*/std::string* error_msg);
+
+  // Redefine the given classes with the given dex data. Note this function does not take ownership
+  // of the dex_data pointers. It is not used after this call however and may be freed if desired.
   // The caller is responsible for freeing it. The runtime makes its own copy of the data.
+  // TODO This function should call the transformation events.
   static jvmtiError RedefineClasses(ArtJvmTiEnv* env,
                                     art::Runtime* runtime,
                                     art::Thread* self,
                                     jint class_count,
                                     const jvmtiClassDefinition* definitions,
-                                    std::string* error_msg);
+                                    /*out*/std::string* error_msg);
 
   static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
 
@@ -209,7 +221,7 @@
         redefinitions_(),
         error_msg_(error_msg) { }
 
-  jvmtiError AddRedefinition(ArtJvmTiEnv* env, const jvmtiClassDefinition& def)
+  jvmtiError AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def)
       REQUIRES_SHARED(art::Locks::mutator_lock_);
 
   static jvmtiError GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index f545125..2809cb6 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -38,6 +38,7 @@
 #include "class_linker.h"
 #include "dex_file.h"
 #include "dex_file_types.h"
+#include "events-inl.h"
 #include "gc_root-inl.h"
 #include "globals.h"
 #include "jni_env_ext-inl.h"
@@ -52,12 +53,76 @@
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread_list.h"
+#include "ti_redefine.h"
 #include "transform.h"
 #include "utf.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 
 namespace openjdkjvmti {
 
+jvmtiError Transformer::RetransformClassesDirect(
+      ArtJvmTiEnv* env,
+      art::Thread* self,
+      /*in-out*/std::vector<ArtClassDefinition>* definitions) {
+  for (ArtClassDefinition& def : *definitions) {
+    jint new_len = -1;
+    unsigned char* new_data = nullptr;
+    // Static casts are so that we get the right template initialization for the special event
+    // handling code required by the ClassFileLoadHooks.
+    gEventHandler.DispatchEvent(self,
+                                ArtJvmtiEvent::kClassFileLoadHookRetransformable,
+                                GetJniEnv(env),
+                                static_cast<jclass>(def.klass),
+                                static_cast<jobject>(def.loader),
+                                static_cast<const char*>(def.name.c_str()),
+                                static_cast<jobject>(def.protection_domain),
+                                static_cast<jint>(def.dex_len),
+                                static_cast<const unsigned char*>(def.dex_data.get()),
+                                static_cast<jint*>(&new_len),
+                                static_cast<unsigned char**>(&new_data));
+    def.SetNewDexData(env, new_len, new_data);
+  }
+  return OK;
+}
+
+jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env,
+                                           art::Runtime* runtime,
+                                           art::Thread* self,
+                                           jint class_count,
+                                           const jclass* classes,
+                                           /*out*/std::string* error_msg) {
+  if (env == nullptr) {
+    *error_msg = "env was null!";
+    return ERR(INVALID_ENVIRONMENT);
+  } else if (class_count < 0) {
+    *error_msg = "class_count was less then 0";
+    return ERR(ILLEGAL_ARGUMENT);
+  } else if (class_count == 0) {
+    // We don't actually need to do anything. Just return OK.
+    return OK;
+  } else if (classes == nullptr) {
+    *error_msg = "null classes!";
+    return ERR(NULL_POINTER);
+  }
+  // A holder that will Deallocate all the class bytes buffers on destruction.
+  std::vector<ArtClassDefinition> definitions;
+  jvmtiError res = OK;
+  for (jint i = 0; i < class_count; i++) {
+    ArtClassDefinition def;
+    res = FillInTransformationData(env, classes[i], &def);
+    if (res != OK) {
+      return res;
+    }
+    definitions.push_back(std::move(def));
+  }
+  res = RetransformClassesDirect(env, self, &definitions);
+  if (res != OK) {
+    return res;
+  }
+  return Redefiner::RedefineClassesDirect(env, runtime, self, definitions, error_msg);
+}
+
+// TODO Move this somewhere else, ti_class?
 jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location) {
   JNIEnv* jni_env = nullptr;
   jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(&jni_env), JNI_VERSION_1_1);
@@ -73,42 +138,61 @@
   return OK;
 }
 
-// TODO Move this function somewhere more appropriate.
-// Gets the data surrounding the given class.
-jvmtiError GetTransformationData(ArtJvmTiEnv* env,
-                                 jclass klass,
-                                 /*out*/std::string* location,
-                                 /*out*/JNIEnv** jni_env_ptr,
-                                 /*out*/jobject* loader,
-                                 /*out*/std::string* name,
-                                 /*out*/jobject* protection_domain,
-                                 /*out*/jint* data_len,
-                                 /*out*/unsigned char** dex_data) {
-  jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(jni_env_ptr), JNI_VERSION_1_1);
-  if (ret != JNI_OK) {
-    // TODO Different error might be better?
-    return ERR(INTERNAL);
-  }
-  JNIEnv* jni_env = *jni_env_ptr;
-  art::ScopedObjectAccess soa(jni_env);
-  art::StackHandleScope<3> hs(art::Thread::Current());
-  art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass)));
-  *loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
-  *name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
-  // TODO is this always null?
-  *protection_domain = nullptr;
-  const art::DexFile& dex = hs_klass->GetDexFile();
-  *location = dex.GetLocation();
-  *data_len = static_cast<jint>(dex.Size());
-  // TODO We should maybe change env->Allocate to allow us to mprotect this memory and stop writes.
-  jvmtiError alloc_error = env->Allocate(*data_len, dex_data);
+// TODO Implement this for real once transformed dex data is actually saved.
+jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env,
+                                                      art::Handle<art::mirror::Class> klass,
+                                                      /*out*/jint* dex_data_len,
+                                                      /*out*/unsigned char** dex_data) {
+  // TODO De-quicken the dex file before passing it to the agents.
+  LOG(WARNING) << "Dex file is not de-quickened yet! Quickened dex instructions might be present";
+  LOG(WARNING) << "Caching of initial dex data is not yet performed! Dex data might have been "
+               << "transformed by agent already";
+  const art::DexFile& dex = klass->GetDexFile();
+  *dex_data_len = static_cast<jint>(dex.Size());
+  unsigned char* new_dex_data = nullptr;
+  jvmtiError alloc_error = env->Allocate(*dex_data_len, &new_dex_data);
   if (alloc_error != OK) {
     return alloc_error;
   }
   // Copy the data into a temporary buffer.
-  memcpy(reinterpret_cast<void*>(*dex_data),
-          reinterpret_cast<const void*>(dex.Begin()),
-          *data_len);
+  memcpy(reinterpret_cast<void*>(new_dex_data),
+         reinterpret_cast<const void*>(dex.Begin()),
+         *dex_data_len);
+  *dex_data = new_dex_data;
+  return OK;
+}
+
+// TODO Move this function somewhere more appropriate.
+// Gets the data surrounding the given class.
+// TODO Make this less magical.
+jvmtiError Transformer::FillInTransformationData(ArtJvmTiEnv* env,
+                                                 jclass klass,
+                                                 ArtClassDefinition* def) {
+  JNIEnv* jni_env = GetJniEnv(env);
+  if (jni_env == nullptr) {
+    // TODO Different error might be better?
+    return ERR(INTERNAL);
+  }
+  art::ScopedObjectAccess soa(jni_env);
+  art::StackHandleScope<3> hs(art::Thread::Current());
+  art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass)));
+  if (hs_klass.IsNull()) {
+    return ERR(INVALID_CLASS);
+  }
+  def->klass = klass;
+  def->loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
+  def->name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
+  // TODO is this always null?
+  def->protection_domain = nullptr;
+  if (def->dex_data.get() == nullptr) {
+    unsigned char* new_data;
+    jvmtiError res = GetDexDataForRetransformation(env, hs_klass, &def->dex_len, &new_data);
+    if (res == OK) {
+      def->dex_data = MakeJvmtiUniquePtr(env, new_data);
+    } else {
+      return res;
+    }
+  }
   return OK;
 }
 
diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h
index 0ad5099..0ff2bd1 100644
--- a/runtime/openjdkjvmti/transform.h
+++ b/runtime/openjdkjvmti/transform.h
@@ -43,16 +43,30 @@
 
 jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location);
 
-// Gets the data surrounding the given class.
-jvmtiError GetTransformationData(ArtJvmTiEnv* env,
-                                 jclass klass,
-                                 /*out*/std::string* location,
-                                 /*out*/JNIEnv** jni_env_ptr,
-                                 /*out*/jobject* loader,
-                                 /*out*/std::string* name,
-                                 /*out*/jobject* protection_domain,
-                                 /*out*/jint* data_len,
-                                 /*out*/unsigned char** dex_data);
+class Transformer {
+ public:
+  static jvmtiError RetransformClassesDirect(
+      ArtJvmTiEnv* env, art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions);
+
+  static jvmtiError RetransformClasses(ArtJvmTiEnv* env,
+                                       art::Runtime* runtime,
+                                       art::Thread* self,
+                                       jint class_count,
+                                       const jclass* classes,
+                                       /*out*/std::string* error_msg);
+
+  // Gets the data surrounding the given class.
+  static jvmtiError FillInTransformationData(ArtJvmTiEnv* env,
+                                             jclass klass,
+                                             ArtClassDefinition* def);
+
+ private:
+  static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
+                                                  art::Handle<art::mirror::Class> klass,
+                                                  /*out*/jint* dex_data_length,
+                                                  /*out*/unsigned char** dex_data)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
+};
 
 }  // namespace openjdkjvmti