Merge "Refactor JVMTI ClassFileLoadHook handling"
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 1868631..4d54d75 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -184,74 +184,27 @@
       return;
     }
 
-    // Strip the 'L' and ';' from the descriptor
-    std::string name(std::string(descriptor).substr(1, strlen(descriptor) - 2));
-
     art::Thread* self = art::Thread::Current();
-    art::JNIEnvExt* env = self->GetJniEnv();
-    ScopedLocalRef<jobject> loader(
-        env, class_loader.IsNull() ? nullptr : env->AddLocalReference<jobject>(class_loader.Get()));
-    std::unique_ptr<FixedUpDexFile> dex_file_copy(FixedUpDexFile::Create(initial_dex_file,
-                                                                         descriptor));
+    ArtClassDefinition def;
+    def.InitFirstLoad(descriptor, class_loader, initial_dex_file);
 
-    // Go back to native.
-    art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
-    // Call all Non-retransformable agents.
-    jint post_no_redefine_len = 0;
-    unsigned char* post_no_redefine_dex_data = nullptr;
-    std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>
-        post_no_redefine_unique_ptr(nullptr, FakeJvmtiDeleter<const unsigned char>());
-    event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
-        self,
-        static_cast<JNIEnv*>(env),
-        static_cast<jclass>(nullptr),  // The class doesn't really exist yet so send null.
-        loader.get(),
-        name.c_str(),
-        static_cast<jobject>(nullptr),  // Android doesn't seem to have protection domains
-        static_cast<jint>(dex_file_copy->Size()),
-        static_cast<const unsigned char*>(dex_file_copy->Begin()),
-        static_cast<jint*>(&post_no_redefine_len),
-        static_cast<unsigned char**>(&post_no_redefine_dex_data));
-    if (post_no_redefine_dex_data == nullptr) {
-      DCHECK_EQ(post_no_redefine_len, 0);
-      post_no_redefine_dex_data = const_cast<unsigned char*>(dex_file_copy->Begin());
-      post_no_redefine_len = dex_file_copy->Size();
-    } else {
-      post_no_redefine_unique_ptr =
-          std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>(
-              post_no_redefine_dex_data, FakeJvmtiDeleter<const unsigned char>());
-      DCHECK_GT(post_no_redefine_len, 0);
+    // Call all non-retransformable agents.
+    Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+        event_handler, self, &def);
+
+    std::vector<unsigned char> post_non_retransform;
+    if (def.IsModified()) {
+      // Copy the dex data after the non-retransformable events.
+      post_non_retransform.resize(def.GetDexData().size());
+      memcpy(post_non_retransform.data(), def.GetDexData().data(), post_non_retransform.size());
     }
+
     // Call all retransformable agents.
-    jint final_len = 0;
-    unsigned char* final_dex_data = nullptr;
-    std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>
-        final_dex_unique_ptr(nullptr, FakeJvmtiDeleter<const unsigned char>());
-    event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
-        self,
-        static_cast<JNIEnv*>(env),
-        static_cast<jclass>(nullptr),  // The class doesn't really exist yet so send null.
-        loader.get(),
-        name.c_str(),
-        static_cast<jobject>(nullptr),  // Android doesn't seem to have protection domains
-        static_cast<jint>(post_no_redefine_len),
-        static_cast<const unsigned char*>(post_no_redefine_dex_data),
-        static_cast<jint*>(&final_len),
-        static_cast<unsigned char**>(&final_dex_data));
-    if (final_dex_data == nullptr) {
-      DCHECK_EQ(final_len, 0);
-      final_dex_data = post_no_redefine_dex_data;
-      final_len = post_no_redefine_len;
-    } else {
-      final_dex_unique_ptr =
-          std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>(
-              final_dex_data, FakeJvmtiDeleter<const unsigned char>());
-      DCHECK_GT(final_len, 0);
-    }
+    Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+        event_handler, self, &def);
 
-    if (final_dex_data != dex_file_copy->Begin()) {
+    if (def.IsModified()) {
       LOG(WARNING) << "Changing class " << descriptor;
-      art::ScopedObjectAccess soa(self);
       art::StackHandleScope<2> hs(self);
       // Save the results of all the non-retransformable agents.
       // First allocate the ClassExt
@@ -268,7 +221,7 @@
 
       // Allocate the byte array to store the dex file bytes in.
       art::MutableHandle<art::mirror::Object> arr(hs.NewHandle<art::mirror::Object>(nullptr));
-      if (post_no_redefine_dex_data == dex_file_copy->Begin() && name != "java/lang/Long") {
+      if (post_non_retransform.empty() && strcmp(descriptor, "Ljava/lang/Long;") != 0) {
         // we didn't have any non-retransformable agents. We can just cache a pointer to the
         // initial_dex_file. It will be kept live by the class_loader.
         jlong dex_ptr = reinterpret_cast<uintptr_t>(&initial_dex_file);
@@ -278,8 +231,8 @@
       } else {
         arr.Assign(art::mirror::ByteArray::AllocateAndFill(
             self,
-            reinterpret_cast<const signed char*>(post_no_redefine_dex_data),
-            post_no_redefine_len));
+            reinterpret_cast<const signed char*>(post_non_retransform.data()),
+            post_non_retransform.size()));
       }
       if (arr.IsNull()) {
         LOG(WARNING) << "Unable to allocate memory for initial dex-file. Aborting transformation";
@@ -290,8 +243,8 @@
       std::unique_ptr<const art::DexFile> dex_file(MakeSingleDexFile(self,
                                                                      descriptor,
                                                                      initial_dex_file.GetLocation(),
-                                                                     final_len,
-                                                                     final_dex_data));
+                                                                     def.GetDexData().size(),
+                                                                     def.GetDexData().data()));
       if (dex_file.get() == nullptr) {
         return;
       }
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index c8a3047..7f2f800 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -46,29 +46,33 @@
 namespace openjdkjvmti {
 
 bool ArtClassDefinition::IsModified() const {
-  // RedefineClasses calls always are 'modified' since they need to change the original_dex_file of
+  // RedefineClasses calls always are 'modified' since they need to change the current_dex_file of
   // the class.
   if (redefined_) {
     return true;
   }
+
+  // Check to see if any change has taken place.
+  if (current_dex_file_.data() == dex_data_.data()) {
+    // no change at all.
+    return false;
+  }
+
   // Check if the dex file we want to set is the same as the current one.
   // Unfortunately we need to do this check even if no modifications have been done since it could
   // be that agents were removed in the mean-time so we still have a different dex file. The dex
   // checksum means this is likely to be fairly fast.
-  return static_cast<jint>(original_dex_file_.size()) != dex_len_ ||
-      memcmp(original_dex_file_.data(), dex_data_.get(), dex_len_) != 0;
+  return current_dex_file_.size() != dex_data_.size() ||
+      memcmp(current_dex_file_.data(), dex_data_.data(), current_dex_file_.size()) != 0;
 }
 
-jvmtiError ArtClassDefinition::InitCommon(ArtJvmTiEnv* env, jclass klass) {
-  JNIEnv* jni_env = GetJniEnv(env);
-  if (jni_env == nullptr) {
-    return ERR(INTERNAL);
-  }
-  art::ScopedObjectAccess soa(jni_env);
+jvmtiError ArtClassDefinition::InitCommon(art::Thread* self, jclass klass) {
+  art::ScopedObjectAccess soa(self);
   art::ObjPtr<art::mirror::Class> m_klass(soa.Decode<art::mirror::Class>(klass));
   if (m_klass.IsNull()) {
     return ERR(INVALID_CLASS);
   }
+  initialized_ = true;
   klass_ = klass;
   loader_ = soa.AddLocalReference<jobject>(m_klass->GetClassLoader());
   std::string descriptor_store;
@@ -79,11 +83,18 @@
   return OK;
 }
 
+static void DequickenDexFile(const art::DexFile* dex_file,
+                             const char* descriptor,
+                             /*out*/std::vector<unsigned char>* dex_data)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file, descriptor));
+  dex_data->resize(fixed_dex_file->Size());
+  memcpy(dex_data->data(), fixed_dex_file->Begin(), fixed_dex_file->Size());
+}
+
 // Gets the data surrounding the given class.
-static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
-                                                art::Handle<art::mirror::Class> klass,
-                                                /*out*/jint* dex_data_len,
-                                                /*out*/unsigned char** dex_data)
+static void GetDexDataForRetransformation(art::Handle<art::mirror::Class> klass,
+                                          /*out*/std::vector<unsigned char>* dex_data)
     REQUIRES_SHARED(art::Locks::mutator_lock_) {
   art::StackHandleScope<3> hs(art::Thread::Current());
   art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
@@ -95,12 +106,9 @@
         DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
         art::Handle<art::mirror::ByteArray> orig_dex_bytes(
             hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray())));
-        *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength());
-        return CopyDataIntoJvmtiBuffer(
-            env,
-            reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()),
-            *dex_data_len,
-            /*out*/dex_data);
+        dex_data->resize(orig_dex_bytes->GetLength());
+        memcpy(dex_data->data(), orig_dex_bytes->GetData(), dex_data->size());
+        return;
       } else if (orig_dex->IsDexCache()) {
         dex_file = orig_dex->AsDexCache()->GetDexFile();
       } else {
@@ -113,7 +121,7 @@
         art::JValue val;
         if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) {
           // This should never happen.
-          return ERR(INTERNAL);
+          LOG(FATAL) << "Unable to unbox a primitive long value!";
         }
         dex_file = reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
       }
@@ -123,59 +131,153 @@
     dex_file = &klass->GetDexFile();
   }
   std::string storage;
-  std::unique_ptr<FixedUpDexFile> fixed_dex_file(
-      FixedUpDexFile::Create(*dex_file, klass->GetDescriptor(&storage)));
-  *dex_data_len = static_cast<jint>(fixed_dex_file->Size());
-  return CopyDataIntoJvmtiBuffer(env,
-                                 fixed_dex_file->Begin(),
-                                 fixed_dex_file->Size(),
-                                 /*out*/dex_data);
+  DequickenDexFile(dex_file, klass->GetDescriptor(&storage), dex_data);
 }
 
-jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, jclass klass) {
-  jvmtiError res = InitCommon(env, klass);
+static bool DexNeedsDequickening(art::Handle<art::mirror::Class> klass,
+                                 /*out*/ bool* from_class_ext)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
+  if (ext.IsNull()) {
+    // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
+    *from_class_ext = false;
+    return true;
+  }
+  art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
+  if (orig_dex.IsNull()) {
+    // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
+    *from_class_ext = false;
+    return true;
+  } else if (!orig_dex->IsArrayInstance()) {
+    // We were redefined but the original is held in a dex-cache or dex file. This means that the
+    // original dex file is the one from the disk, which might be quickened.
+    DCHECK(orig_dex->IsDexCache() || orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"));
+    *from_class_ext = true;
+    return true;
+  } else {
+    // An array instance means the original-dex-file is from a redefineClasses which cannot have any
+    // quickening, so it's fine to use directly.
+    DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
+    *from_class_ext = true;
+    return false;
+  }
+}
+
+static const art::DexFile* GetQuickenedDexFile(art::Handle<art::mirror::Class> klass)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
+  if (ext.IsNull() || ext->GetOriginalDexFile() == nullptr) {
+    return &klass->GetDexFile();
+  }
+
+  art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
+  DCHECK(!orig_dex->IsArrayInstance());
+  if (orig_dex->IsDexCache()) {
+    return orig_dex->AsDexCache()->GetDexFile();
+  }
+
+  DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
+      << "Expected java/lang/Long but found object of type "
+      << orig_dex->GetClass()->PrettyClass();
+  art::ObjPtr<art::mirror::Class> prim_long_class(
+      art::Runtime::Current()->GetClassLinker()->GetClassRoot(
+          art::ClassLinker::kPrimitiveLong));
+  art::JValue val;
+  if (!art::UnboxPrimitiveForResult(orig_dex.Ptr(), prim_long_class, &val)) {
+    LOG(FATAL) << "Unable to unwrap a long value!";
+  }
+  return reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
+}
+
+template<typename GetOriginalDexFile>
+void ArtClassDefinition::InitWithDex(GetOriginalDexFile get_original,
+                                     const art::DexFile* quick_dex) {
+  art::Thread* self = art::Thread::Current();
+  DCHECK(quick_dex != nullptr);
+  get_original(/*out*/&dex_data_memory_);
+  dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+  if (from_class_ext_) {
+    // We got initial from class_ext so the current one must have undergone redefinition so no
+    // cdex or quickening stuff.
+    // We can only do this if it's not a first load.
+    DCHECK(klass_ != nullptr);
+    const art::DexFile& cur_dex = self->DecodeJObject(klass_)->AsClass()->GetDexFile();
+    current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+  } else {
+    // No redefinition must have ever happened so the (dequickened) cur_dex is the same as the
+    // initial dex_data. We need to copy it into another buffer to keep it around if we have a
+    // real redefinition.
+    current_dex_memory_.resize(dex_data_.size());
+    memcpy(current_dex_memory_.data(), dex_data_.data(), current_dex_memory_.size());
+    current_dex_file_ = art::ArrayRef<const unsigned char>(current_dex_memory_);
+  }
+}
+
+jvmtiError ArtClassDefinition::Init(art::Thread* self, jclass klass) {
+  jvmtiError res = InitCommon(self, klass);
   if (res != OK) {
     return res;
   }
-  unsigned char* new_data = nullptr;
-  art::Thread* self = art::Thread::Current();
   art::ScopedObjectAccess soa(self);
   art::StackHandleScope<1> hs(self);
   art::Handle<art::mirror::Class> m_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass()));
-  res = GetDexDataForRetransformation(env, m_klass, &dex_len_, &new_data);
-  if (res != OK) {
-    return res;
+  if (!DexNeedsDequickening(m_klass, &from_class_ext_)) {
+    // We don't need to do any dequickening. We want to copy the data just so we don't need to deal
+    // with the GC moving it around.
+    art::ObjPtr<art::mirror::ByteArray> orig_dex(
+        m_klass->GetExtData()->GetOriginalDexFile()->AsByteArray());
+    dex_data_memory_.resize(orig_dex->GetLength());
+    memcpy(dex_data_memory_.data(), orig_dex->GetData(), dex_data_memory_.size());
+    dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+
+    // Since we are here we must not have any quickened instructions since we were redefined.
+    const art::DexFile& cur_dex = m_klass->GetDexFile();
+    DCHECK(from_class_ext_);
+    current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+    return OK;
   }
-  dex_data_ = MakeJvmtiUniquePtr(env, new_data);
-  if (m_klass->GetExtData() == nullptr || m_klass->GetExtData()->GetOriginalDexFile() == nullptr) {
-    // We have never redefined class this yet. Keep track of what the (de-quickened) dex file looks
-    // like so we can tell if anything has changed. Really we would like to just always do the
-    // 'else' block but the fact that we de-quickened stuff screws us over.
-    unsigned char* original_data_memory = nullptr;
-    res = CopyDataIntoJvmtiBuffer(env, dex_data_.get(), dex_len_, &original_data_memory);
-    original_dex_file_memory_ = MakeJvmtiUniquePtr(env, original_data_memory);
-    original_dex_file_ = art::ArrayRef<const unsigned char>(original_data_memory, dex_len_);
-  } else {
-    // We know that we have been redefined at least once (there is an original_dex_file set in
-    // the class) so we can just use the current dex file directly.
-    const art::DexFile& dex_file = m_klass->GetDexFile();
-    original_dex_file_ = art::ArrayRef<const unsigned char>(dex_file.Begin(), dex_file.Size());
-  }
-  return res;
+
+  // We need to dequicken stuff. This is often super slow (10's of ms). Instead we will do it
+  // dynamically.
+  const art::DexFile* quick_dex = GetQuickenedDexFile(m_klass);
+  auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    GetDexDataForRetransformation(m_klass, dex_data);
+  };
+  InitWithDex(get_original, quick_dex);
+  return OK;
 }
 
-jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def) {
-  jvmtiError res = InitCommon(env, def.klass);
+jvmtiError ArtClassDefinition::Init(art::Thread* self, const jvmtiClassDefinition& def) {
+  jvmtiError res = InitCommon(self, def.klass);
   if (res != OK) {
     return res;
   }
-  unsigned char* new_data = nullptr;
-  original_dex_file_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
+  // We are being directly redefined.
   redefined_ = true;
-  dex_len_ = def.class_byte_count;
-  res = CopyDataIntoJvmtiBuffer(env, def.class_bytes, def.class_byte_count, /*out*/ &new_data);
-  dex_data_ = MakeJvmtiUniquePtr(env, new_data);
-  return res;
+  current_dex_file_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
+  dex_data_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
+  return OK;
+}
+
+void ArtClassDefinition::InitFirstLoad(const char* descriptor,
+                                       art::Handle<art::mirror::ClassLoader> klass_loader,
+                                       const art::DexFile& dex_file) {
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);
+  initialized_ = true;
+  // No Class
+  klass_ = nullptr;
+  loader_ = soa.AddLocalReference<jobject>(klass_loader.Get());
+  std::string descriptor_str(descriptor);
+  name_ = descriptor_str.substr(1, descriptor_str.size() - 2);
+  // Android doesn't really have protection domains.
+  protection_domain_ = nullptr;
+  auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    DequickenDexFile(&dex_file, descriptor, dex_data);
+  };
+  InitWithDex(get_original, &dex_file);
 }
 
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h
index accc456..0084739 100644
--- a/openjdkjvmti/ti_class_definition.h
+++ b/openjdkjvmti/ti_class_definition.h
@@ -48,41 +48,49 @@
         loader_(nullptr),
         name_(),
         protection_domain_(nullptr),
-        dex_len_(0),
-        dex_data_(nullptr),
-        original_dex_file_memory_(nullptr),
-        original_dex_file_(),
-        redefined_(false) {}
+        dex_data_memory_(),
+        dex_data_(),
+        current_dex_file_(),
+        redefined_(false),
+        from_class_ext_(false),
+        initialized_(false) {}
 
-  jvmtiError Init(ArtJvmTiEnv* env, jclass klass);
-  jvmtiError Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def);
+  void InitFirstLoad(const char* descriptor,
+                     art::Handle<art::mirror::ClassLoader> klass_loader,
+                     const art::DexFile& dex_file);
+  jvmtiError Init(art::Thread* self, jclass klass);
+  jvmtiError Init(art::Thread* self, const jvmtiClassDefinition& def);
 
   ArtClassDefinition(ArtClassDefinition&& o) = default;
   ArtClassDefinition& operator=(ArtClassDefinition&& o) = default;
 
-  void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) {
+  void SetNewDexData(jint new_dex_len, unsigned char* new_dex_data) {
     DCHECK(IsInitialized());
     if (new_dex_data == nullptr) {
       return;
-    } else if (new_dex_data != dex_data_.get() || new_dex_len != dex_len_) {
-      dex_len_ = new_dex_len;
-      dex_data_ = MakeJvmtiUniquePtr(env, new_dex_data);
+    } else {
+      art::ArrayRef<const unsigned char> new_data(new_dex_data, new_dex_len);
+      if (new_data != dex_data_) {
+        dex_data_memory_.resize(new_dex_len);
+        memcpy(dex_data_memory_.data(), new_dex_data, new_dex_len);
+        dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+      }
     }
   }
 
   art::ArrayRef<const unsigned char> GetNewOriginalDexFile() const {
     DCHECK(IsInitialized());
     if (redefined_) {
-      return original_dex_file_;
+      return current_dex_file_;
     } else {
       return art::ArrayRef<const unsigned char>();
     }
   }
 
-  bool IsModified() const;
+  bool IsModified() const REQUIRES_SHARED(art::Locks::mutator_lock_);
 
   bool IsInitialized() const {
-    return klass_ != nullptr;
+    return initialized_;
   }
 
   jclass GetClass() const {
@@ -107,22 +115,43 @@
 
   art::ArrayRef<const unsigned char> GetDexData() const {
     DCHECK(IsInitialized());
-    return art::ArrayRef<const unsigned char>(dex_data_.get(), dex_len_);
+    return dex_data_;
   }
 
  private:
-  jvmtiError InitCommon(ArtJvmTiEnv* env, jclass klass);
+  jvmtiError InitCommon(art::Thread* self, jclass klass);
+
+  template<typename GetOriginalDexFile>
+  void InitWithDex(GetOriginalDexFile get_original, const art::DexFile* quick_dex)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
 
   jclass klass_;
   jobject loader_;
   std::string name_;
   jobject protection_domain_;
-  jint dex_len_;
-  JvmtiUniquePtr<unsigned char> dex_data_;
-  JvmtiUniquePtr<unsigned char> original_dex_file_memory_;
-  art::ArrayRef<const unsigned char> original_dex_file_;
+
+  // A unique_ptr to the current dex_data if it needs to be cleaned up.
+  std::vector<unsigned char> dex_data_memory_;
+
+  // A ref to the current dex data. This is either dex_data_memory_, or current_dex_file_. This is
+  // what the dex file will be turned into.
+  art::ArrayRef<const unsigned char> dex_data_;
+
+  // This is only used if we failed to create a mmap to store the dequickened data
+  std::vector<unsigned char> current_dex_memory_;
+
+  // This is a dequickened version of what is loaded right now. It is either current_dex_memory_ (if
+  // no other redefinition has ever happened to this) or the current dex_file_ directly (if this
+  // class has been redefined, thus it cannot have any quickened stuff).
+  art::ArrayRef<const unsigned char> current_dex_file_;
+
   bool redefined_;
 
+  // If we got the initial dex_data_ from a class_ext
+  bool from_class_ext_;
+
+  bool initialized_;
+
   DISALLOW_COPY_AND_ASSIGN(ArtClassDefinition);
 };
 
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 717b2ba..c3fb946 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -351,15 +351,14 @@
     memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count);
 
     ArtClassDefinition def;
-    res = def.Init(env, definitions[i]);
+    res = def.Init(self, definitions[i]);
     if (res != OK) {
       return res;
     }
     def_vector.push_back(std::move(def));
   }
   // Call all the transformation events.
-  jvmtiError res = Transformer::RetransformClassesDirect(env,
-                                                         event_handler,
+  jvmtiError res = Transformer::RetransformClassesDirect(event_handler,
                                                          self,
                                                          &def_vector);
   if (res != OK) {
diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc
index 3a5fccc..8445eca 100644
--- a/openjdkjvmti/transform.cc
+++ b/openjdkjvmti/transform.cc
@@ -63,27 +63,46 @@
 
 namespace openjdkjvmti {
 
+// Initialize templates.
+template
+void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+    EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
+template
+void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+    EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
+
+template<ArtJvmtiEvent kEvent>
+void Transformer::TransformSingleClassDirect(EventHandler* event_handler,
+                                             art::Thread* self,
+                                             /*in-out*/ArtClassDefinition* def) {
+  static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable ||
+                kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable,
+                "bad event type");
+  jint new_len = -1;
+  unsigned char* new_data = nullptr;
+  art::ArrayRef<const unsigned char> dex_data = def->GetDexData();
+  event_handler->DispatchEvent<kEvent>(
+      self,
+      static_cast<JNIEnv*>(self->GetJniEnv()),
+      def->GetClass(),
+      def->GetLoader(),
+      def->GetName().c_str(),
+      def->GetProtectionDomain(),
+      static_cast<jint>(dex_data.size()),
+      dex_data.data(),
+      /*out*/&new_len,
+      /*out*/&new_data);
+  def->SetNewDexData(new_len, new_data);
+}
+
 jvmtiError Transformer::RetransformClassesDirect(
-      ArtJvmTiEnv* env,
       EventHandler* event_handler,
       art::Thread* self,
       /*in-out*/std::vector<ArtClassDefinition>* definitions) {
   for (ArtClassDefinition& def : *definitions) {
-    jint new_len = -1;
-    unsigned char* new_data = nullptr;
-    art::ArrayRef<const unsigned char> dex_data = def.GetDexData();
-    event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
-        self,
-        GetJniEnv(env),
-        def.GetClass(),
-        def.GetLoader(),
-        def.GetName().c_str(),
-        def.GetProtectionDomain(),
-        static_cast<jint>(dex_data.size()),
-        dex_data.data(),
-        /*out*/&new_len,
-        /*out*/&new_data);
-    def.SetNewDexData(env, new_len, new_data);
+    TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(event_handler,
+                                                                                 self,
+                                                                                 &def);
   }
   return OK;
 }
@@ -120,13 +139,13 @@
       return ERR(UNMODIFIABLE_CLASS);
     }
     ArtClassDefinition def;
-    res = def.Init(env, classes[i]);
+    res = def.Init(self, classes[i]);
     if (res != OK) {
       return res;
     }
     definitions.push_back(std::move(def));
   }
-  res = RetransformClassesDirect(env, event_handler, self, &definitions);
+  res = RetransformClassesDirect(event_handler, self, &definitions);
   if (res != OK) {
     return res;
   }
diff --git a/openjdkjvmti/transform.h b/openjdkjvmti/transform.h
index 6bbe60a..f43af17 100644
--- a/openjdkjvmti/transform.h
+++ b/openjdkjvmti/transform.h
@@ -48,8 +48,13 @@
 
 class Transformer {
  public:
+  template<ArtJvmtiEvent kEvent>
+  static void TransformSingleClassDirect(
+      EventHandler* event_handler,
+      art::Thread* self,
+      /*in-out*/ArtClassDefinition* def);
+
   static jvmtiError RetransformClassesDirect(
-      ArtJvmTiEnv* env,
       EventHandler* event_handler,
       art::Thread* self,
       /*in-out*/std::vector<ArtClassDefinition>* definitions);