Class redefinition sometimes needs to update verification

In cases where class redefinition moves a class from having no
verification failures to having soft verification failures we need to
update the methods with new verification class flags. For example if
a method is modified to have unbalanced monitors we need to make sure
that future invokes of that method count locks and use the
interpreter.

Previously we would simply keep the same verification state as the
original implementation, causing us to try to compile in situations
the compiler cannot handle or leave monitors in inconsistent states.

Test: ./test.py --host
Bug: 142876078
Change-Id: I8adf59158639bdf237d691b20fad223f0a34db1f
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..66f5801 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=*/!Runtime::Current()->IsAotCompiler(),
+                     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();
+  }
+}