Undo dex2dex compilation before invoking LoadHook

We need to undo any dex_to_dex compilation that might have taken place
before passing a dex file to any registered ClassFileLoadHooks to
ensure that no internal opcodes are present in any methods.

Test: ./test.py --host -j40
Bug: 36653594

Change-Id: Ia42c77312e685d69f6b3ea764fad01710b10ab45
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 35e9d5d..85100ae 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <sys/file.h>
 #include <sys/stat.h>
+#include <zlib.h>
 
 #include <memory>
 #include <sstream>
@@ -67,6 +68,12 @@
   {'0', '3', '8', '\0'}
 };
 
+uint32_t DexFile::CalculateChecksum() const {
+  const uint32_t non_sum = OFFSETOF_MEMBER(DexFile::Header, signature_);
+  const uint8_t* non_sum_ptr = Begin() + non_sum;
+  return adler32(adler32(0L, Z_NULL, 0), non_sum_ptr, Size() - non_sum);
+}
+
 struct DexFile::AnnotationValue {
   JValue value_;
   uint8_t type_;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 58b8e79..1b18d21 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1088,6 +1088,9 @@
   static int64_t ReadSignedLong(const uint8_t* ptr, int zwidth);
   static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right);
 
+  // Recalculates the checksum of the dex file. Does not use the current value in the header.
+  uint32_t CalculateChecksum() const;
+
   // Returns a human-readable form of the method at an index.
   std::string PrettyMethod(uint32_t method_idx, bool with_signature = true) const;
   // Returns a human-readable form of the field at an index.
@@ -1320,6 +1323,9 @@
   uint32_t NumVirtualMethods() const {
     return header_.virtual_methods_size_;
   }
+  bool IsAtMethod() const {
+    return pos_ >= EndOfInstanceFieldsPos();
+  }
   bool HasNextStaticField() const {
     return pos_ < EndOfStaticFieldsPos();
   }
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 0b3f16a..11b3cd0 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -17,7 +17,6 @@
 #include "dex_file_verifier.h"
 
 #include <inttypes.h>
-#include <zlib.h>
 
 #include <limits>
 #include <memory>
@@ -368,11 +367,8 @@
     return false;
   }
 
+  uint32_t adler_checksum = dex_file_->CalculateChecksum();
   // Compute and verify the checksum in the header.
-  uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
-  const uint32_t non_sum = sizeof(header_->magic_) + sizeof(header_->checksum_);
-  const uint8_t* non_sum_ptr = reinterpret_cast<const uint8_t*>(header_) + non_sum;
-  adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
   if (adler_checksum != header_->checksum_) {
     if (verify_checksum_) {
       ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index dd49ad0..e38f265 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -24,6 +24,7 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: ["events.cc",
+           "fixed_up_dex_file.cc",
            "object_tagging.cc",
            "OpenjdkJvmTi.cc",
            "ti_class.cc",
@@ -56,7 +57,10 @@
 art_cc_library {
     name: "libopenjdkjvmti",
     defaults: ["libopenjdkjvmti_defaults"],
-    shared_libs: ["libart"],
+    shared_libs: [
+        "libart",
+        "libart-compiler",
+    ],
 }
 
 art_cc_library {
@@ -65,5 +69,8 @@
         "art_debug_defaults",
         "libopenjdkjvmti_defaults",
     ],
-    shared_libs: ["libartd"],
+    shared_libs: [
+        "libartd",
+        "libartd-compiler",
+    ],
 }
diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.cc b/runtime/openjdkjvmti/fixed_up_dex_file.cc
new file mode 100644
index 0000000..3338358
--- /dev/null
+++ b/runtime/openjdkjvmti/fixed_up_dex_file.cc
@@ -0,0 +1,145 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "fixed_up_dex_file.h"
+#include "dex_file-inl.h"
+
+// Compiler includes.
+#include "dex/dex_to_dex_decompiler.h"
+
+// Runtime includes.
+#include "oat_file.h"
+#include "vdex_file.h"
+
+namespace openjdkjvmti {
+
+static void RecomputeDexChecksum(art::DexFile* dex_file)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  reinterpret_cast<art::DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_ =
+      dex_file->CalculateChecksum();
+}
+
+// TODO This is more complicated then it seems like it should be.
+// The fact we don't keep around the data of where in the flat binary log of dex-quickening changes
+// each dex file starts means we need to search for it. Since JVMTI is the exception though we are
+// not going to put in the effort to optimize for it.
+static void DoDexUnquicken(const art::DexFile& new_dex_file,
+                           const art::DexFile& original_dex_file)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  const art::OatDexFile* oat_dex = original_dex_file.GetOatDexFile();
+  if (oat_dex == nullptr) {
+    return;
+  }
+  const art::OatFile* oat_file = oat_dex->GetOatFile();
+  if (oat_file == nullptr) {
+    return;
+  }
+  const art::VdexFile* vdex = oat_file->GetVdexFile();
+  if (vdex == nullptr || vdex->GetQuickeningInfo().size() == 0) {
+    return;
+  }
+  const art::ArrayRef<const uint8_t> quickening_info(vdex->GetQuickeningInfo());
+  const uint8_t* quickening_info_ptr = quickening_info.data();
+  for (const art::OatDexFile* cur_oat_dex : oat_file->GetOatDexFiles()) {
+    std::string error;
+    std::unique_ptr<const art::DexFile> cur_dex_file(cur_oat_dex->OpenDexFile(&error));
+    DCHECK(cur_dex_file.get() != nullptr);
+    // Is this the dex file we are looking for?
+    if (UNLIKELY(cur_dex_file->Begin() == original_dex_file.Begin())) {
+      // Simple sanity check.
+      CHECK_EQ(new_dex_file.NumClassDefs(), original_dex_file.NumClassDefs());
+      for (uint32_t i = 0; i < new_dex_file.NumClassDefs(); ++i) {
+        const art::DexFile::ClassDef& class_def = new_dex_file.GetClassDef(i);
+        const uint8_t* class_data = new_dex_file.GetClassData(class_def);
+        if (class_data == nullptr) {
+          continue;
+        }
+        for (art::ClassDataItemIterator it(new_dex_file, class_data); it.HasNext(); it.Next()) {
+          if (it.IsAtMethod() && it.GetMethodCodeItem() != nullptr) {
+            uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+            quickening_info_ptr += sizeof(uint32_t);
+            art::optimizer::ArtDecompileDEX(
+                *it.GetMethodCodeItem(),
+                art::ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+                /*decompile_return_instruction*/true);
+            quickening_info_ptr += quickening_size;
+          }
+        }
+      }
+      // We don't need to bother looking through the rest of the dex-files.
+      break;
+    } else {
+      // Not the dex file we want. Skip over all the quickening info for all its classes.
+      for (uint32_t i = 0; i < cur_dex_file->NumClassDefs(); ++i) {
+        const art::DexFile::ClassDef& class_def = cur_dex_file->GetClassDef(i);
+        const uint8_t* class_data = cur_dex_file->GetClassData(class_def);
+        if (class_data == nullptr) {
+          continue;
+        }
+        for (art::ClassDataItemIterator it(*cur_dex_file, class_data); it.HasNext(); it.Next()) {
+          if (it.IsAtMethod() && it.GetMethodCodeItem() != nullptr) {
+            uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+            quickening_info_ptr += sizeof(uint32_t);
+            quickening_info_ptr += quickening_size;
+          }
+        }
+      }
+    }
+  }
+}
+
+std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) {
+  // Copy the data into mutable memory.
+  std::vector<unsigned char> data;
+  data.resize(original.Size());
+  memcpy(data.data(), original.Begin(), original.Size());
+  std::string error;
+  std::unique_ptr<const art::DexFile> new_dex_file(art::DexFile::Open(
+      data.data(),
+      data.size(),
+      /*location*/"Unquickening_dexfile.dex",
+      /*location_checksum*/0,
+      /*oat_dex_file*/nullptr,
+      /*verify*/false,
+      /*verify_checksum*/false,
+      &error));
+  if (new_dex_file.get() == nullptr) {
+    LOG(ERROR) << "Unable to open dex file from memory for unquickening! error: " << error;
+    return nullptr;
+  }
+
+  DoDexUnquicken(*new_dex_file, original);
+  RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get()));
+  std::unique_ptr<FixedUpDexFile> ret(new FixedUpDexFile(std::move(new_dex_file), std::move(data)));
+  return ret;
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.h b/runtime/openjdkjvmti/fixed_up_dex_file.h
new file mode 100644
index 0000000..db12f48
--- /dev/null
+++ b/runtime/openjdkjvmti/fixed_up_dex_file.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
+#define ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
+
+#include <memory>
+#include <vector>
+
+#include "jni.h"
+#include "jvmti.h"
+#include "base/mutex.h"
+#include "dex_file.h"
+
+namespace openjdkjvmti {
+
+// A holder for a DexFile that has been 'fixed up' to ensure it is fully compliant with the
+// published standard (no internal/quick opcodes, all fields are the defined values, etc). This is
+// used to ensure that agents get a consistent dex file regardless of what version of android they
+// are running on.
+class FixedUpDexFile {
+ public:
+  static std::unique_ptr<FixedUpDexFile> Create(const art::DexFile& original)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+  const art::DexFile& GetDexFile() {
+    return *dex_file_;
+  }
+
+  const unsigned char* Begin() {
+    return data_.data();
+  }
+
+  size_t Size() {
+    return data_.size();
+  }
+
+ private:
+  explicit FixedUpDexFile(std::unique_ptr<const art::DexFile> fixed_up_dex_file,
+                          std::vector<unsigned char> data)
+      : dex_file_(std::move(fixed_up_dex_file)),
+        data_(std::move(data)) {}
+
+  // the fixed up DexFile
+  std::unique_ptr<const art::DexFile> dex_file_;
+  // The backing data for dex_file_.
+  const std::vector<unsigned char> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(FixedUpDexFile);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 38fd1d4..907ab02 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -43,6 +43,7 @@
 #include "common_throws.h"
 #include "dex_file_annotations.h"
 #include "events-inl.h"
+#include "fixed_up_dex_file.h"
 #include "gc/heap.h"
 #include "gc_root.h"
 #include "handle.h"
@@ -161,6 +162,8 @@
     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));
+
     // Go back to native.
     art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
     // Call all Non-retransformable agents.
@@ -174,14 +177,14 @@
         loader.get(),
         name.c_str(),
         static_cast<jobject>(nullptr),  // Android doesn't seem to have protection domains
-        static_cast<jint>(initial_dex_file.Size()),
-        static_cast<const unsigned char*>(initial_dex_file.Begin()),
+        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*>(initial_dex_file.Begin());
-      post_no_redefine_len = initial_dex_file.Size();
+      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>(post_no_redefine_dex_data);
       DCHECK_GT(post_no_redefine_len, 0);
@@ -210,7 +213,7 @@
       DCHECK_GT(final_len, 0);
     }
 
-    if (final_dex_data != initial_dex_file.Begin()) {
+    if (final_dex_data != dex_file_copy->Begin()) {
       LOG(WARNING) << "Changing class " << descriptor;
       art::ScopedObjectAccess soa(self);
       art::StackHandleScope<2> hs(self);
diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/runtime/openjdkjvmti/ti_class_definition.cc
index 2c2a79b..de8d8fe 100644
--- a/runtime/openjdkjvmti/ti_class_definition.cc
+++ b/runtime/openjdkjvmti/ti_class_definition.cc
@@ -40,16 +40,18 @@
 
 namespace openjdkjvmti {
 
-bool ArtClassDefinition::IsModified(art::Thread* self) const {
-  if (modified) {
+bool ArtClassDefinition::IsModified() const {
+  // RedefineClasses calls always are 'modified' since they need to change the original_dex_file of
+  // the class.
+  if (redefined) {
     return true;
   }
   // Check if the dex file we want to set is the same as the current one.
-  art::StackHandleScope<1> hs(self);
-  art::Handle<art::mirror::Class> h_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass()));
-  const art::DexFile& cur_dex_file = h_klass->GetDexFile();
-  return static_cast<jint>(cur_dex_file.Size()) != dex_len ||
-      memcmp(cur_dex_file.Begin(), dex_data.get(), dex_len) != 0;
+  // 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.At(0), dex_data.get(), dex_len) != 0;
 }
 
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h
index 9ca1c07..7a2e922 100644
--- a/runtime/openjdkjvmti/ti_class_definition.h
+++ b/runtime/openjdkjvmti/ti_class_definition.h
@@ -47,6 +47,7 @@
   jobject protection_domain;
   jint dex_len;
   JvmtiUniquePtr<unsigned char> dex_data;
+  JvmtiUniquePtr<unsigned char> original_dex_file_memory;
   art::ArraySlice<const unsigned char> original_dex_file;
 
   ArtClassDefinition()
@@ -56,8 +57,9 @@
         protection_domain(nullptr),
         dex_len(0),
         dex_data(nullptr),
+        original_dex_file_memory(nullptr),
         original_dex_file(),
-        modified(false) {}
+        redefined(false) {}
 
   ArtClassDefinition(ArtClassDefinition&& o) = default;
 
@@ -65,20 +67,27 @@
     if (new_dex_data == nullptr) {
       return;
     } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) {
-      SetModified();
       dex_len = new_dex_len;
       dex_data = MakeJvmtiUniquePtr(env, new_dex_data);
     }
   }
 
-  void SetModified() {
-    modified = true;
+  void SetRedefined() {
+    redefined = true;
   }
 
-  bool IsModified(art::Thread* self) const REQUIRES_SHARED(art::Locks::mutator_lock_);
+  art::ArraySlice<const unsigned char> GetNewOriginalDexFile() const {
+    if (redefined) {
+      return original_dex_file;
+    } else {
+      return art::ArraySlice<const unsigned char>();
+    }
+  }
+
+  bool IsModified() const;
 
  private:
-  bool modified;
+  bool redefined;
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 7faddfb..95a1b00 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -347,7 +347,7 @@
     def.dex_len = definitions[i].class_byte_count;
     def.dex_data = MakeJvmtiUniquePtr(env, class_bytes_copy);
     // We are definitely modified.
-    def.SetModified();
+    def.SetRedefined();
     def.original_dex_file = art::ArraySlice<const unsigned char>(definitions[i].class_bytes,
                                                                  definitions[i].class_byte_count);
     res = Transformer::FillInTransformationData(env, definitions[i].klass, &def);
@@ -386,7 +386,7 @@
   Redefiner r(runtime, self, error_msg);
   for (const ArtClassDefinition& def : definitions) {
     // Only try to transform classes that have been modified.
-    if (def.IsModified(self)) {
+    if (def.IsModified()) {
       jvmtiError res = r.AddRedefinition(env, def);
       if (res != OK) {
         return res;
@@ -443,7 +443,7 @@
                                    def.klass,
                                    dex_file.release(),
                                    signature_ptr,
-                                   def.original_dex_file));
+                                   def.GetNewOriginalDexFile()));
   return OK;
 }
 
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index 06aecba..a1883b4 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -39,6 +39,7 @@
 #include "dex_file.h"
 #include "dex_file_types.h"
 #include "events-inl.h"
+#include "fixed_up_dex_file.h"
 #include "gc_root-inl.h"
 #include "globals.h"
 #include "jni_env_ext-inl.h"
@@ -152,6 +153,7 @@
                                                       /*out*/unsigned char** dex_data) {
   art::StackHandleScope<3> hs(art::Thread::Current());
   art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
+  const art::DexFile* dex_file = nullptr;
   if (!ext.IsNull()) {
     art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile()));
     if (!orig_dex.IsNull()) {
@@ -167,17 +169,20 @@
             /*out*/dex_data);
       } else {
         DCHECK(orig_dex->IsDexCache());
-        const art::DexFile* dex_file = orig_dex->AsDexCache()->GetDexFile();
+        dex_file = orig_dex->AsDexCache()->GetDexFile();
         *dex_data_len = static_cast<jint>(dex_file->Size());
-        return CopyDataIntoJvmtiBuffer(env, dex_file->Begin(), dex_file->Size(), /*out*/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";
-  const art::DexFile& dex = klass->GetDexFile();
-  *dex_data_len = static_cast<jint>(dex.Size());
-  return CopyDataIntoJvmtiBuffer(env, dex.Begin(), *dex_data_len, /*out*/dex_data);
+  if (dex_file == nullptr) {
+    dex_file = &klass->GetDexFile();
+    *dex_data_len = static_cast<jint>(dex_file->Size());
+  }
+  std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file));
+  return CopyDataIntoJvmtiBuffer(env,
+                                 fixed_dex_file->Begin(),
+                                 fixed_dex_file->Size(),
+                                 /*out*/dex_data);
 }
 
 // TODO Move this function somewhere more appropriate.
@@ -209,6 +214,31 @@
     jvmtiError res = GetDexDataForRetransformation(env, hs_klass, &def->dex_len, &new_data);
     if (res == OK) {
       def->dex_data = MakeJvmtiUniquePtr(env, new_data);
+      // TODO This whole thing is a bit of a mess.
+      // We need to keep track of what the runtime should think an unmodified dex file is since
+      // we need to be able to tell if anything changes. This might be different then the currently
+      // loaded dex file since we need to un-quicken stuff.
+      if (hs_klass->GetExtData() == nullptr ||
+          hs_klass->GetExtData()->GetOriginalDexFile() == nullptr) {
+        // We have never redefined 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 = env->Allocate(def->dex_len, &original_data_memory);
+        if (res != OK) {
+          return res;
+        }
+        memcpy(original_data_memory, new_data, def->dex_len);
+        def->original_dex_file_memory = MakeJvmtiUniquePtr(env, original_data_memory);
+        def->original_dex_file = art::ArraySlice<const unsigned char>(original_data_memory,
+                                                                      def->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.
+        def->original_dex_file = art::ArraySlice<const unsigned char>(
+            hs_klass->GetDexFile().Begin(), hs_klass->GetDexFile().Size());
+      }
     } else {
       return res;
     }