Revert^2 "Class redefinition sometimes needs to update verification"

We were incorrectly preventing dex2oat from suspending during
verification. This caused a major regression in memory use and
compilation speed.

This reverts commit 2cf00ede148bd9d77c291d4c0cb23edd5a9c36b4.

Reason for revert: Fixed issue causing AOT slowdown.
Test: go/lem
Test: go/lem-allight-unrevert-verify-check
Test: ./test.py --host
Bug: 142876078

Change-Id: If699f71a06818856358859ed5e4e01e0ffc1c1a4
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 67d10b6..df25261 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1496,7 +1496,14 @@
                                                 &error);
   switch (failure) {
     case art::verifier::FailureKind::kNoFailure:
+      // TODO It is possible that by doing redefinition previous NO_COMPILE verification failures
+      // were fixed. It would be nice to reflect this in the new implementations.
+      return true;
     case art::verifier::FailureKind::kSoftFailure:
+      // Soft failures might require interpreter on some methods. It won't prevent redefinition but
+      // it does mean we need to run the verifier again and potentially update method flags after
+      // performing the swap.
+      needs_reverify_ = true;
       return true;
     case art::verifier::FailureKind::kHardFailure: {
       RecordFailure(ERR(FAILS_VERIFICATION), "Failed to verify class. Error was: " + error);
@@ -1881,9 +1888,37 @@
   // TODO Do the dex_file release at a more reasonable place. This works but it muddles who really
   // owns the DexFile and when ownership is transferred.
   ReleaseAllDexFiles();
+  // By now the class-linker knows about all the classes so we can safetly retry verification and
+  // update method flags.
+  ReverifyClasses(holder);
   return OK;
 }
 
+void Redefiner::ReverifyClasses(RedefinitionDataHolder& holder) {
+  art::ScopedAssertNoThreadSuspension nts("Updating method flags");
+  for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) {
+    data.GetRedefinition().ReverifyClass(data);
+  }
+}
+
+void Redefiner::ClassRedefinition::ReverifyClass(const RedefinitionDataIter &cur_data) {
+  if (!needs_reverify_) {
+    return;
+  }
+  VLOG(plugin) << "Reverifying " << class_sig_ << " due to soft failures";
+  std::string error;
+  // TODO Make verification log level lower
+  art::verifier::FailureKind failure =
+      art::verifier::ClassVerifier::ReverifyClass(driver_->self_,
+                                                  cur_data.GetMirrorClass(),
+                                                  /*log_level=*/
+                                                  art::verifier::HardFailLogMode::kLogWarning,
+                                                  /*api_level=*/
+                                                  art::Runtime::Current()->GetTargetSdkVersion(),
+                                                  &error);
+  CHECK_NE(failure, art::verifier::FailureKind::kHardFailure);
+}
+
 void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
                                                  const art::dex::ClassDef& class_def) {
   art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index d10758c..dad085d 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -209,6 +209,9 @@
     void UpdateClass(const RedefinitionDataIter& cur_data)
         REQUIRES(art::Locks::mutator_lock_);
 
+    void ReverifyClass(const RedefinitionDataIter& cur_data)
+        REQUIRES(art::Locks::mutator_lock_);
+
     void CollectNewFieldAndMethodMappings(const RedefinitionDataIter& data,
                                           std::map<art::ArtMethod*, art::ArtMethod*>* method_map,
                                           std::map<art::ArtField*, art::ArtField*>* field_map)
@@ -248,6 +251,10 @@
 
     bool added_fields_ = false;
     bool added_methods_ = false;
+
+    // Does the class need to be reverified due to verification soft-fails possibly forcing
+    // interpreter or lock-counting?
+    bool needs_reverify_ = false;
   };
 
   ArtJvmTiEnv* env_;
@@ -294,6 +301,7 @@
   bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder)
       REQUIRES_SHARED(art::Locks::mutator_lock_);
   void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_);
+  void ReverifyClasses(RedefinitionDataHolder& holder) REQUIRES(art::Locks::mutator_lock_);
   void UnregisterAllBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
   // Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no
   // new obsolete methods).
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d84ea7c..d4fb5d7 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -335,6 +335,11 @@
     DCHECK(!IsNative());
     AddAccessFlags(kAccSkipAccessChecks);
   }
+  void ClearSkipAccessChecks() {
+    // SkipAccessChecks() is applicable only to non-native methods.
+    DCHECK(!IsNative());
+    ClearAccessFlags(kAccSkipAccessChecks);
+  }
 
   bool PreviouslyWarm() {
     if (IsIntrinsic()) {
@@ -363,6 +368,7 @@
 
   void SetMustCountLocks() {
     AddAccessFlags(kAccMustCountLocks);
+    ClearAccessFlags(kAccSkipAccessChecks);
   }
 
   // Returns true if this method could be overridden by a default method.
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 455f98d..a0e8a23 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1031,6 +1031,15 @@
   return nullptr;
 }
 
+void Class::ClearSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
+  DCHECK(IsVerified());
+  for (auto& m : GetMethods(pointer_size)) {
+    if (!m.IsNative() && m.IsInvokable()) {
+      m.ClearSkipAccessChecks();
+    }
+  }
+}
+
 void Class::SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
   DCHECK(IsVerified());
   for (auto& m : GetMethods(pointer_size)) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 6ed20ed..d925a96 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1134,6 +1134,9 @@
   static ObjPtr<mirror::Class> GetPrimitiveClass(ObjPtr<mirror::String> name)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Clear the kAccSkipAccessChecks flag on each method, for class redefinition.
+  void ClearSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   // When class is verified, set the kAccSkipAccessChecks flag on each method.
   void SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/verifier/class_verifier.cc b/runtime/verifier/class_verifier.cc
index 1df11ad..fca9e16 100644
--- a/runtime/verifier/class_verifier.cc
+++ b/runtime/verifier/class_verifier.cc
@@ -20,6 +20,7 @@
 #include <android-base/stringprintf.h>
 
 #include "art_method-inl.h"
+#include "base/enums.h"
 #include "base/systrace.h"
 #include "base/utils.h"
 #include "class_linker.h"
@@ -28,11 +29,13 @@
 #include "dex/class_reference.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
+#include "handle.h"
 #include "handle_scope-inl.h"
 #include "method_verifier-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "runtime.h"
+#include "thread.h"
 
 namespace art {
 namespace verifier {
@@ -43,6 +46,30 @@
 // sure we only print this once.
 static bool gPrintedDxMonitorText = false;
 
+FailureKind ClassVerifier::ReverifyClass(Thread* self,
+                                         ObjPtr<mirror::Class> klass,
+                                         HardFailLogMode log_level,
+                                         uint32_t api_level,
+                                         std::string* error) {
+  DCHECK(!Runtime::Current()->IsAotCompiler());
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Class> h_klass(hs.NewHandle(klass));
+  ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+  FailureKind res = CommonVerifyClass(self,
+                                      h_klass.Get(),
+                                      /*callbacks=*/nullptr,
+                                      /*allow_soft_failures=*/false,
+                                      log_level,
+                                      api_level,
+                                      /*can_allocate=*/ false,
+                                      error);
+  if (res == FailureKind::kSoftFailure) {
+    // We cannot skip access checks since there was a soft failure.
+    h_klass->ClearSkipAccessChecksFlagOnAllMethods(kRuntimePointerSize);
+  }
+  return res;
+}
+
 FailureKind ClassVerifier::VerifyClass(Thread* self,
                                        ObjPtr<mirror::Class> klass,
                                        CompilerCallbacks* callbacks,
@@ -53,6 +80,23 @@
   if (klass->IsVerified()) {
     return FailureKind::kNoFailure;
   }
+  return CommonVerifyClass(self,
+                           klass,
+                           callbacks,
+                           allow_soft_failures,
+                           log_level,
+                           api_level,
+                           /*can_allocate=*/ true,
+                           error);
+}
+FailureKind ClassVerifier::CommonVerifyClass(Thread* self,
+                                             ObjPtr<mirror::Class> klass,
+                                             CompilerCallbacks* callbacks,
+                                             bool allow_soft_failures,
+                                             HardFailLogMode log_level,
+                                             uint32_t api_level,
+                                             bool can_allocate,
+                                             std::string* error) {
   bool early_failure = false;
   std::string failure_message;
   const DexFile& dex_file = klass->GetDexFile();
@@ -89,6 +133,7 @@
                      allow_soft_failures,
                      log_level,
                      api_level,
+                     can_allocate,
                      error);
 }
 
@@ -102,6 +147,30 @@
                                        HardFailLogMode log_level,
                                        uint32_t api_level,
                                        std::string* error) {
+  return VerifyClass(self,
+                     dex_file,
+                     dex_cache,
+                     class_loader,
+                     class_def,
+                     callbacks,
+                     allow_soft_failures,
+                     log_level,
+                     api_level,
+                     /*can_allocate=*/ true,
+                     error);
+}
+
+FailureKind ClassVerifier::VerifyClass(Thread* self,
+                                       const DexFile* dex_file,
+                                       Handle<mirror::DexCache> dex_cache,
+                                       Handle<mirror::ClassLoader> class_loader,
+                                       const dex::ClassDef& class_def,
+                                       CompilerCallbacks* callbacks,
+                                       bool allow_soft_failures,
+                                       HardFailLogMode log_level,
+                                       uint32_t api_level,
+                                       bool can_allocate,
+                                       std::string* error) {
   // A class must not be abstract and final.
   if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) {
     *error = "Verifier rejected class ";
@@ -156,6 +225,7 @@
                                      /*need_precise_constants=*/ false,
                                      api_level,
                                      Runtime::Current()->IsAotCompiler(),
+                                     can_allocate,
                                      &hard_failure_msg);
     if (result.kind == FailureKind::kHardFailure) {
       if (failure_data.kind == FailureKind::kHardFailure) {
diff --git a/runtime/verifier/class_verifier.h b/runtime/verifier/class_verifier.h
index db5e4f5..c97ea24 100644
--- a/runtime/verifier/class_verifier.h
+++ b/runtime/verifier/class_verifier.h
@@ -50,6 +50,14 @@
 // Verifier that ensures the complete class is OK.
 class ClassVerifier {
  public:
+  // Redo verification on a loaded class. This is for use by class redefinition. Since the class is
+  // already loaded and in use this can only be performed with the mutator lock held.
+  static FailureKind ReverifyClass(Thread* self,
+                                   ObjPtr<mirror::Class> klass,
+                                   HardFailLogMode log_level,
+                                   uint32_t api_level,
+                                   std::string* error)
+      REQUIRES(Locks::mutator_lock_);
   // Verify a class. Returns "kNoFailure" on success.
   static FailureKind VerifyClass(Thread* self,
                                  ObjPtr<mirror::Class> klass,
@@ -70,6 +78,18 @@
                                  uint32_t api_level,
                                  std::string* error)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  static FailureKind VerifyClass(Thread* self,
+                                 const DexFile* dex_file,
+                                 Handle<mirror::DexCache> dex_cache,
+                                 Handle<mirror::ClassLoader> class_loader,
+                                 const dex::ClassDef& class_def,
+                                 CompilerCallbacks* callbacks,
+                                 bool allow_soft_failures,
+                                 HardFailLogMode log_level,
+                                 uint32_t api_level,
+                                 bool can_allocate,
+                                 std::string* error)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   static void Init(ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_);
   static void Shutdown();
@@ -78,6 +98,16 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
+  static FailureKind CommonVerifyClass(Thread* self,
+                                       ObjPtr<mirror::Class> klass,
+                                       CompilerCallbacks* callbacks,
+                                       bool allow_soft_failures,
+                                       HardFailLogMode log_level,
+                                       uint32_t api_level,
+                                       bool can_allocate,
+                                       std::string* error)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   DISALLOW_COPY_AND_ASSIGN(ClassVerifier);
 };
 
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 839491e..29bc40c 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -5112,6 +5112,7 @@
                                                          bool need_precise_constants,
                                                          uint32_t api_level,
                                                          bool aot_mode,
+                                                         bool allow_suspension,
                                                          std::string* hard_failure_msg) {
   if (VLOG_IS_ON(verifier_debug)) {
     return VerifyMethod<true>(self,
@@ -5131,6 +5132,7 @@
                               need_precise_constants,
                               api_level,
                               aot_mode,
+                              allow_suspension,
                               hard_failure_msg);
   } else {
     return VerifyMethod<false>(self,
@@ -5150,6 +5152,7 @@
                                need_precise_constants,
                                api_level,
                                aot_mode,
+                               allow_suspension,
                                hard_failure_msg);
   }
 }
@@ -5172,6 +5175,7 @@
                                                          bool need_precise_constants,
                                                          uint32_t api_level,
                                                          bool aot_mode,
+                                                         bool allow_suspension,
                                                          std::string* hard_failure_msg) {
   MethodVerifier::FailureData result;
   uint64_t start_ns = kTimeVerifyMethod ? NanoTime() : 0;
@@ -5182,8 +5186,8 @@
                                                 dex_file,
                                                 code_item,
                                                 method_idx,
-                                                /* can_load_classes= */ true,
-                                                /* allow_thread_suspension= */ true,
+                                                /* can_load_classes= */ allow_suspension,
+                                                /* allow_thread_suspension= */ allow_suspension,
                                                 allow_soft_failures,
                                                 aot_mode,
                                                 dex_cache,
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index aab6ee5..09d384a 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -253,6 +253,7 @@
                                   bool need_precise_constants,
                                   uint32_t api_level,
                                   bool aot_mode,
+                                  bool allow_suspension,
                                   std::string* hard_failure_msg)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -274,6 +275,7 @@
                                   bool need_precise_constants,
                                   uint32_t api_level,
                                   bool aot_mode,
+                                  bool allow_suspension,
                                   std::string* hard_failure_msg)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/test/1989-transform-bad-monitor/expected.txt b/test/1989-transform-bad-monitor/expected.txt
new file mode 100644
index 0000000..65ec72d
--- /dev/null
+++ b/test/1989-transform-bad-monitor/expected.txt
@@ -0,0 +1,6 @@
+hello without locks
+Goodbye before unlock
+Goodbye after unlock
+Got exception of type class java.lang.IllegalMonitorStateException
+Make sure locks aren't held
+Locks are good.
diff --git a/test/1989-transform-bad-monitor/info.txt b/test/1989-transform-bad-monitor/info.txt
new file mode 100644
index 0000000..0056464
--- /dev/null
+++ b/test/1989-transform-bad-monitor/info.txt
@@ -0,0 +1,6 @@
+Tests basic functions in the jvmti plugin.
+
+b/142876078
+
+This tests that redefining a method to have unbalanced locks doesn't cause issues and the method
+is given lock-counting and not compiled.
diff --git a/test/1989-transform-bad-monitor/run b/test/1989-transform-bad-monitor/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1989-transform-bad-monitor/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1989-transform-bad-monitor/src/Main.java b/test/1989-transform-bad-monitor/src/Main.java
new file mode 100644
index 0000000..9c61e89
--- /dev/null
+++ b/test/1989-transform-bad-monitor/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1989.run();
+  }
+}
diff --git a/test/1989-transform-bad-monitor/src/art/Redefinition.java b/test/1989-transform-bad-monitor/src/art/Redefinition.java
new file mode 120000
index 0000000..81eaf31
--- /dev/null
+++ b/test/1989-transform-bad-monitor/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java
\ No newline at end of file
diff --git a/test/1989-transform-bad-monitor/src/art/Test1989.java b/test/1989-transform-bad-monitor/src/art/Test1989.java
new file mode 100644
index 0000000..fb16c22
--- /dev/null
+++ b/test/1989-transform-bad-monitor/src/art/Test1989.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Base64;
+public class Test1989 {
+
+  static class Transform {
+    public void sayHi(Object l_first, Object l_second) {
+      System.out.println("hello without locks");
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform {
+   *   public void sayHi(Object l_first, Object l_second) {
+   *    monitor-enter l_first
+   *    monitor-enter l_second
+   *    System.out.println("Goodbye before unlock");
+   *    monitor-exit l_second
+   *    System.out.println("Goodbye after unlock");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+"yv66vgADAC0AHgEAFEdvb2RieWUgYWZ0ZXIgdW5sb2NrDAAYAB0BABBqYXZhL2xhbmcvT2JqZWN0" +
+"AQAGPGluaXQ+BwADDAAEAAkHABEBABZhcnQvVGVzdDE5ODkkVHJhbnNmb3JtAQADKClWBwAVAQAE" +
+"Q29kZQgAHAkACgACAQANVGVzdDE5ODkuamF2YQEAClNvdXJjZUZpbGUMABIAGwEAE2phdmEvaW8v" +
+"UHJpbnRTdHJlYW0BAAdwcmludGxuCgAFAAYBAAVzYXlIaQEAEGphdmEvbGFuZy9TeXN0ZW0IAAEK" +
+"AAcAEAEAA291dAcACAEAJyhMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspVgEA" +
+"FShMamF2YS9sYW5nL1N0cmluZzspVgEAFUdvb2RieWUgYmVmb3JlIHVubG9jawEAFUxqYXZhL2lv" +
+"L1ByaW50U3RyZWFtOwAgABkABQAAAAAAAgAAAAQACQABAAsAAAARAAEAAQAAAAUqtwATsQAAAAAA" +
+"AQAUABoAAQALAAAAIwACAAMAAAAXK8IswrIADRIMtgAXLMOyAA0SFrYAF7EAAAAAAAEADwAAAAIA" +
+"Dg==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+"ZGV4CjAzNQB5oAZwVUwJoMSgbr1BNffRcXjpPMVhYzgYBAAAcAAAAHhWNBIAAAAAAAAAAFQDAAAW" +
+"AAAAcAAAAAkAAADIAAAAAwAAAOwAAAABAAAAEAEAAAQAAAAYAQAAAQAAADgBAADAAgAAWAEAAFgB" +
+"AABgAQAAdgEAAI0BAACnAQAAtwEAANsBAAD7AQAAEgIAACYCAAA6AgAATgIAAF0CAABoAgAAawIA" +
+"AG8CAAB0AgAAgQIAAIcCAACMAgAAlQIAAJwCAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA" +
+"CgAAAA0AAAANAAAACAAAAAAAAAAPAAAACAAAAKQCAAAOAAAACAAAAKwCAAAHAAQAEgAAAAAAAAAA" +
+"AAAAAAABABQAAAAEAAIAEwAAAAUAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAsAAADYAgAARAMAAAAA" +
+"AAAGPGluaXQ+ABRHb29kYnllIGFmdGVyIHVubG9jawAVR29vZGJ5ZSBiZWZvcmUgdW5sb2NrABhM" +
+"YXJ0L1Rlc3QxOTg5JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5ODk7ACJMZGFsdmlrL2Fubm90YXRp" +
+"b24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJDbGFzczsAFUxqYXZh" +
+"L2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" +
+"EkxqYXZhL2xhbmcvU3lzdGVtOwANVGVzdDE5ODkuamF2YQAJVHJhbnNmb3JtAAFWAAJWTAADVkxM" +
+"AAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAAVzYXlIaQAFdmFsdWUAAAIAAAAFAAUA" +
+"AQAAAAYAAgMCEAQIERcMAgIBFRgBAAAAAAAAAAAAAAACAAAAuwIAALICAADMAgAAAAAAAAAAAAAA" +
+"AAAABgAOAAgCAAAOHh54H3gAAAEAAQABAAAA6AIAAAQAAABwEAMAAAAOAAUAAwACAAAA7AIAABIA" +
+"AAAdAx0EYgAAABoBAgBuIAIAEAAeBGIDAAAaBAEAbiACAEMADgAAAAEBAICABPgFAQGQBgAAEAAA" +
+"AAAAAAABAAAAAAAAAAEAAAAWAAAAcAAAAAIAAAAJAAAAyAAAAAMAAAADAAAA7AAAAAQAAAABAAAA" +
+"EAEAAAUAAAAEAAAAGAEAAAYAAAABAAAAOAEAAAIgAAAWAAAAWAEAAAEQAAACAAAApAIAAAQgAAAC" +
+"AAAAsgIAAAMQAAADAAAAxAIAAAYgAAABAAAA2AIAAAMgAAACAAAA6AIAAAEgAAACAAAA+AIAAAAg" +
+"AAABAAAARAMAAAAQAAABAAAAVAMAAA==");
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) throws Exception {
+    Object a = new Object();
+    Object b = new Object();
+    t.sayHi(a, b);
+    Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    try {
+      t.sayHi(a, b);
+    } catch (Throwable e) {
+      System.out.println("Got exception of type " + e.getClass());
+    }
+    System.out.println("Make sure locks aren't held");
+    Thread thr = new Thread(() -> {
+      synchronized(a) {
+        synchronized (b) {
+          System.out.println("Locks are good.");
+        }
+      }
+    });
+    thr.start();
+    thr.join();
+  }
+}