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();
+ }
+}