summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2019-09-24 14:36:27 -0700
committer Treehugger Robot <treehugger-gerrit@google.com> 2019-09-27 17:08:33 +0000
commitc18eba327c4e207ff7b38817f097ee2220b44b39 (patch)
tree1978e64abd3e9fd3df99179f4a2110a724a9565f
parent371390f775c90b8b3df11a9890585598a2a39da9 (diff)
Consolidate updating of reflective Field/Method references
Previously we used several different visitors to update Field & Method references for structural redefinition. We also did not visit or update JVMTI based references. This consolidates all the visitors to a single Runtime::VisitReflectiveTargets function with a single ReflectiveTargetVisitor type. This simplifies the code around structural redefinition and ensures that the reflective value holders are in charge of the actual replacement. Support was also added for walking internal openjdkjvmti references for things like field-read/modification events. Test: ./test.py --host Bug: 134162467 Change-Id: Ic5fc1db7db0a30f947a1a67259dc024e149ebd57
-rw-r--r--openjdkjvmti/OpenjdkJvmTi.cc4
-rw-r--r--openjdkjvmti/ti_breakpoint.cc63
-rw-r--r--openjdkjvmti/ti_breakpoint.h4
-rw-r--r--openjdkjvmti/ti_field.cc81
-rw-r--r--openjdkjvmti/ti_field.h3
-rw-r--r--openjdkjvmti/ti_redefine.cc192
-rw-r--r--runtime/Android.bp2
-rw-r--r--runtime/gc/heap.cc27
-rw-r--r--runtime/gc/heap.h4
-rw-r--r--runtime/jni/jni_id_manager.cc86
-rw-r--r--runtime/jni/jni_id_manager.h39
-rw-r--r--runtime/mirror/dex_cache.cc22
-rw-r--r--runtime/mirror/dex_cache.h3
-rw-r--r--runtime/mirror/executable-inl.h8
-rw-r--r--runtime/mirror/executable.h6
-rw-r--r--runtime/mirror/field-inl.h12
-rw-r--r--runtime/mirror/field.cc12
-rw-r--r--runtime/mirror/field.h4
-rw-r--r--runtime/mirror/method_handle_impl-inl.h14
-rw-r--r--runtime/mirror/method_handle_impl.cc15
-rw-r--r--runtime/mirror/method_handle_impl.h4
-rw-r--r--runtime/mirror/var_handle.cc11
-rw-r--r--runtime/mirror/var_handle.h4
-rw-r--r--runtime/reflective_value_visitor.cc45
-rw-r--r--runtime/reflective_value_visitor.h181
-rw-r--r--runtime/runtime.cc6
-rw-r--r--runtime/runtime.h12
-rw-r--r--runtime/runtime_callbacks.cc16
-rw-r--r--runtime/runtime_callbacks.h21
-rw-r--r--test/1984-structural-redefine-field-trace/expected.txt31
-rw-r--r--test/1984-structural-redefine-field-trace/info.txt1
-rwxr-xr-xtest/1984-structural-redefine-field-trace/run18
-rw-r--r--test/1984-structural-redefine-field-trace/src/Main.java (renamed from runtime/mirror/var_handle-inl.h)25
l---------test/1984-structural-redefine-field-trace/src/art/Redefinition.java1
-rw-r--r--test/1984-structural-redefine-field-trace/src/art/Test1984.java134
l---------test/1984-structural-redefine-field-trace/src/art/Trace.java1
36 files changed, 818 insertions, 294 deletions
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 0300c11a8b..b49762c577 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1536,6 +1536,8 @@ extern "C" bool ArtPlugin_Initialize() {
HeapExtensions::Register(gEventHandler);
SearchUtil::Register();
HeapUtil::Register();
+ FieldUtil::Register(gEventHandler);
+ BreakpointUtil::Register(gEventHandler);
Transformer::Setup();
{
@@ -1559,6 +1561,8 @@ extern "C" bool ArtPlugin_Deinitialize() {
MethodUtil::Unregister();
SearchUtil::Unregister();
HeapUtil::Unregister();
+ FieldUtil::Unregister();
+ BreakpointUtil::Unregister();
// TODO It would be good to delete the gEventHandler and gDeoptManager here but we cannot since
// daemon threads might be suspended and we want to make sure that even if they wake up briefly
diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc
index 813aa8eb83..13d8db7818 100644
--- a/openjdkjvmti/ti_breakpoint.cc
+++ b/openjdkjvmti/ti_breakpoint.cc
@@ -53,6 +53,69 @@
namespace openjdkjvmti {
+class JvmtiBreakpointReflectionSource : public art::ReflectionSourceInfo {
+ public:
+ JvmtiBreakpointReflectionSource(size_t pc, art::ArtMethod* m)
+ : art::ReflectionSourceInfo(art::ReflectionSourceType::kSourceMiscInternal),
+ pc_(pc),
+ m_(m) {}
+
+ void Describe(std::ostream& os) const override REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ReflectionSourceInfo::Describe(os);
+ os << " jvmti Breakpoint Method=" << m_->PrettyMethod() << " PC=" << pc_;
+ }
+
+ private:
+ size_t pc_;
+ art::ArtMethod* m_;
+};
+
+class BreakpointReflectiveValueCallback : public art::ReflectiveValueVisitCallback {
+ public:
+ void VisitReflectiveTargets(art::ReflectiveValueVisitor* visitor)
+ REQUIRES(art::Locks::mutator_lock_) {
+ art::Thread* self = art::Thread::Current();
+ eh_->ForEachEnv(self, [&](ArtJvmTiEnv* env) NO_THREAD_SAFETY_ANALYSIS {
+ art::Locks::mutator_lock_->AssertExclusiveHeld(self);
+ art::WriterMutexLock mu(self, env->event_info_mutex_);
+ std::vector<std::pair<Breakpoint, Breakpoint>> updated_breakpoints;
+ for (auto it : env->breakpoints) {
+ art::ArtMethod* orig_method = it.GetMethod();
+ art::ArtMethod* am = visitor->VisitMethod(
+ orig_method, JvmtiBreakpointReflectionSource(it.GetLocation(), orig_method));
+ if (am != orig_method) {
+ updated_breakpoints.push_back({ Breakpoint { am, it.GetLocation() }, it });
+ }
+ }
+ for (auto it : updated_breakpoints) {
+ DCHECK(env->breakpoints.find(it.second) != env->breakpoints.end());
+ env->breakpoints.erase(it.second);
+ env->breakpoints.insert(it.first);
+ }
+ });
+ }
+
+ EventHandler* eh_;
+};
+
+static BreakpointReflectiveValueCallback gReflectiveValueCallback;
+void BreakpointUtil::Register(EventHandler* eh) {
+ gReflectiveValueCallback.eh_ = eh;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add breakpoint reflective value visit callback");
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->AddReflectiveValueVisitCallback(&gReflectiveValueCallback);
+}
+
+void BreakpointUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove reflective value visit callback");
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->RemoveReflectiveValueVisitCallback(&gReflectiveValueCallback);
+}
+
size_t Breakpoint::hash() const {
return std::hash<uintptr_t> {}(reinterpret_cast<uintptr_t>(method_))
^ std::hash<jlocation> {}(location_);
diff --git a/openjdkjvmti/ti_breakpoint.h b/openjdkjvmti/ti_breakpoint.h
index 7aa33aeec0..96610c32c1 100644
--- a/openjdkjvmti/ti_breakpoint.h
+++ b/openjdkjvmti/ti_breakpoint.h
@@ -47,6 +47,7 @@ class Class;
namespace openjdkjvmti {
struct ArtJvmTiEnv;
+class EventHandler;
class Breakpoint {
public:
@@ -74,6 +75,9 @@ class Breakpoint {
class BreakpointUtil {
public:
+ static void Register(EventHandler* eh);
+ static void Unregister();
+
static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
// Used by class redefinition to remove breakpoints on redefined classes.
diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc
index 2a860d9f43..e5b96374b0 100644
--- a/openjdkjvmti/ti_field.cc
+++ b/openjdkjvmti/ti_field.cc
@@ -30,19 +30,100 @@
*/
#include "ti_field.h"
+#include <unordered_map>
+#include "android-base/thread_annotations.h"
#include "art_field-inl.h"
+#include "art_field.h"
#include "art_jvmti.h"
#include "base/enums.h"
+#include "base/locks.h"
#include "dex/dex_file_annotations.h"
#include "dex/modifiers.h"
#include "jni/jni_internal.h"
#include "mirror/object_array-inl.h"
+#include "reflective_value_visitor.h"
+#include "runtime.h"
+#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
namespace openjdkjvmti {
+class JvmtiFieldReflectionSource : public art::ReflectionSourceInfo {
+ public:
+ JvmtiFieldReflectionSource(bool is_access, art::ArtField* f)
+ : art::ReflectionSourceInfo(art::ReflectionSourceType::kSourceMiscInternal),
+ is_access_(is_access),
+ f_(f) {}
+ void Describe(std::ostream& os) const override REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ReflectionSourceInfo::Describe(os);
+ os << " jvmti Field" << (is_access_ ? "Access" : "Modification")
+ << "Watch Target=" << f_->PrettyField();
+ }
+
+ private:
+ bool is_access_;
+ art::ArtField* f_;
+};
+struct FieldReflectiveValueCallback : public art::ReflectiveValueVisitCallback {
+ public:
+ void VisitReflectiveTargets(art::ReflectiveValueVisitor* visitor)
+ REQUIRES(art::Locks::mutator_lock_) {
+ art::Thread* self = art::Thread::Current();
+ event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) NO_THREAD_SAFETY_ANALYSIS {
+ art::Locks::mutator_lock_->AssertExclusiveHeld(self);
+ art::WriterMutexLock mu(self, env->event_info_mutex_);
+ std::vector<std::pair<art::ArtField*, art::ArtField*>> updated_access_fields;
+ for (auto it : env->access_watched_fields) {
+ art::ArtField* af =
+ visitor->VisitField(it, JvmtiFieldReflectionSource(/*is_access=*/true, it));
+ if (af != it) {
+ updated_access_fields.push_back({ af, it });
+ }
+ }
+ for (auto it : updated_access_fields) {
+ DCHECK(env->access_watched_fields.find(it.second) != env->access_watched_fields.end());
+ env->access_watched_fields.erase(it.second);
+ env->access_watched_fields.insert(it.first);
+ }
+ std::vector<std::pair<art::ArtField*, art::ArtField*>> updated_modify_fields;
+ for (auto it : env->modify_watched_fields) {
+ art::ArtField* af =
+ visitor->VisitField(it, JvmtiFieldReflectionSource(/*is_access=*/false, it));
+ if (af != it) {
+ updated_modify_fields.push_back({ af, it });
+ }
+ }
+ for (auto it : updated_modify_fields) {
+ DCHECK(env->modify_watched_fields.find(it.second) != env->modify_watched_fields.end());
+ env->modify_watched_fields.erase(it.second);
+ env->modify_watched_fields.insert(it.first);
+ }
+ });
+ }
+
+ EventHandler* event_handler = nullptr;
+};
+
+static FieldReflectiveValueCallback gReflectiveValueCallback;
+
+void FieldUtil::Register(EventHandler* eh) {
+ gReflectiveValueCallback.event_handler = eh;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add reflective value visit callback");
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->AddReflectiveValueVisitCallback(&gReflectiveValueCallback);
+}
+
+void FieldUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove reflective value visit callback");
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->RemoveReflectiveValueVisitCallback(&gReflectiveValueCallback);
+}
// Note: For all these functions, we could do a check that the field actually belongs to the given
// class. But the spec seems to assume a certain encoding of the field ID, and so doesn't
// specify any errors.
diff --git a/openjdkjvmti/ti_field.h b/openjdkjvmti/ti_field.h
index 3cf29f099a..073c6cced4 100644
--- a/openjdkjvmti/ti_field.h
+++ b/openjdkjvmti/ti_field.h
@@ -71,6 +71,9 @@ class FieldUtil {
REQUIRES(!ArtJvmTiEnv::event_info_mutex_);
static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field)
REQUIRES(!ArtJvmTiEnv::event_info_mutex_);
+
+ static void Register(EventHandler* eh);
+ static void Unregister();
};
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 97cf98b83b..1ddc34bb7c 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -107,7 +107,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/object_array.h"
#include "mirror/string.h"
-#include "mirror/var_handle-inl.h"
+#include "mirror/var_handle.h"
#include "nativehelper/scoped_local_ref.h"
#include "non_debuggable_classes.h"
#include "obj_ptr.h"
@@ -1967,65 +1967,6 @@ void Redefiner::ClassRedefinition::CollectNewFieldAndMethodMappings(
}
}
-namespace {
-
-template <typename T>
-struct FuncVisitor : public art::ClassVisitor {
- public:
- explicit FuncVisitor(T f) : f_(f) {}
- bool operator()(art::ObjPtr<art::mirror::Class> k) override REQUIRES(art::Locks::mutator_lock_) {
- return f_(*this, k);
- }
-
- private:
- T f_;
-};
-
-// TODO We should put this in Runtime once we have full ArtMethod/ArtField updating.
-template <typename FieldVis, typename MethodVis>
-void VisitReflectiveObjects(art::Thread* self,
- art::gc::Heap* heap,
- FieldVis&& fv,
- MethodVis&& mv) REQUIRES(art::Locks::mutator_lock_) {
- // Horray for captures!
- auto get_visitor = [&mv, &fv](const char* desc) REQUIRES(art::Locks::mutator_lock_) {
- return [&mv, &fv, desc](auto* v) REQUIRES(art::Locks::mutator_lock_) {
- if constexpr (std::is_same_v<decltype(v), art::ArtMethod*>) {
- return mv(v, desc);
- } else {
- static_assert(std::is_same_v<decltype(v), art::ArtField*>,
- "Visitor called with unexpected type");
- return fv(v, desc);
- }
- };
- };
- heap->VisitObjectsPaused(
- [&](art::mirror::Object* ref) NO_THREAD_SAFETY_ANALYSIS {
- art::Locks::mutator_lock_->AssertExclusiveHeld(self);
- art::ObjPtr<art::mirror::Class> klass(ref->GetClass());
- // All these classes are in the BootstrapClassLoader.
- if (!klass->IsBootStrapClassLoaded()) {
- return;
- }
- if (art::GetClassRoot<art::mirror::Method>()->IsAssignableFrom(klass) ||
- art::GetClassRoot<art::mirror::Constructor>()->IsAssignableFrom(klass)) {
- art::down_cast<art::mirror::Executable*>(ref)->VisitTarget(
- get_visitor("java.lang.reflect.Executable"));
- } else if (art::GetClassRoot<art::mirror::Field>() == klass) {
- art::down_cast<art::mirror::Field*>(ref)->VisitTarget(
- get_visitor("java.lang.reflect.Field"));
- } else if (art::GetClassRoot<art::mirror::MethodHandle>()->IsAssignableFrom(klass)) {
- art::down_cast<art::mirror::MethodHandle*>(ref)->VisitTarget(
- get_visitor("java.lang.invoke.MethodHandle"));
- } else if (art::GetClassRoot<art::mirror::FieldVarHandle>()->IsAssignableFrom(klass)) {
- art::down_cast<art::mirror::FieldVarHandle*>(ref)->VisitTarget(
- get_visitor("java.lang.invoke.FieldVarHandle"));
- }
- });
-}
-
-} // namespace
-
void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDataIter& holder) {
DCHECK(IsStructuralRedefinition());
// LETS GO. We've got all new class structures so no need to do all the updating of the stacks.
@@ -2082,63 +2023,13 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat
m.SetDontCompile();
DCHECK_EQ(orig, m.GetDeclaringClass());
}
- // TODO Update live pointers in ART code. Currently we just assume there aren't any
- // ArtMethod/ArtField*s hanging around in the runtime that need to be updated to the new
- // non-obsolete versions. This isn't a totally safe assumption and we need to fix this oversight.
- // Update jni-ids
- driver_->runtime_->GetJniIdManager()->VisitIds(
- driver_->self_,
- [&](jmethodID mid, art::ArtMethod** meth) REQUIRES(art::Locks::mutator_lock_) {
- auto repl = method_map.find(*meth);
- if (repl != method_map.end()) {
- // Set the new method to have the same id.
- // TODO This won't be true when we do updates with actual instances.
- DCHECK_EQ(repl->second->GetDeclaringClass(), replacement)
- << "different classes! " << repl->second->GetDeclaringClass()->PrettyClass()
- << " vs " << replacement->PrettyClass();
- VLOG(plugin) << "Updating jmethodID " << reinterpret_cast<uintptr_t>(mid) << " from "
- << (*meth)->PrettyMethod() << " to " << repl->second->PrettyMethod();
- *meth = repl->second;
- replacement->GetExtData()->GetJMethodIDs()->SetElementPtrSize(
- replacement->GetMethodsSlice(art::kRuntimePointerSize).OffsetOf(repl->second),
- mid,
- art::kRuntimePointerSize);
- }
- },
- [&](jfieldID fid, art::ArtField** field) REQUIRES(art::Locks::mutator_lock_) {
- auto repl = field_map.find(*field);
- if (repl != field_map.end()) {
- // Set the new field to have the same id.
- // TODO This won't be true when we do updates with actual instances.
- DCHECK_EQ(repl->second->GetDeclaringClass(), replacement)
- << "different classes! " << repl->second->GetDeclaringClass()->PrettyClass()
- << " vs " << replacement->PrettyClass();
- VLOG(plugin) << "Updating jfieldID " << reinterpret_cast<uintptr_t>(fid) << " from "
- << (*field)->PrettyField() << " to " << repl->second->PrettyField();
- *field = repl->second;
- if (repl->second->IsStatic()) {
- replacement->GetExtData()->GetStaticJFieldIDs()->SetElementPtrSize(
- art::ArraySlice<art::ArtField>(replacement->GetSFieldsPtr()).OffsetOf(repl->second),
- fid,
- art::kRuntimePointerSize);
- } else {
- replacement->GetExtData()->GetInstanceJFieldIDs()->SetElementPtrSize(
- art::ArraySlice<art::ArtField>(replacement->GetIFieldsPtr()).OffsetOf(repl->second),
- fid,
- art::kRuntimePointerSize);
- }
- }
- });
// Copy the lock-word
replacement->SetLockWord(orig->GetLockWord(false), false);
orig->SetLockWord(art::LockWord::Default(), false);
- // Fix up java.lang.reflect.{Method,Field} and java.lang.invoke.{Method,FieldVar}Handle objects
+ // Update live pointers in ART code.
// TODO Performing 2 stack-walks back to back isn't the greatest. We might want to try to combine
// it with the one ReplaceReferences does. Doing so would be rather complicated though.
- // TODO We maybe should just give the Heap the ability to do this.
- VisitReflectiveObjects(
- driver_->self_,
- driver_->runtime_->GetHeap(),
+ driver_->runtime_->VisitReflectiveTargets(
[&](art::ArtField* f, const auto& info) REQUIRES(art::Locks::mutator_lock_) {
auto it = field_map.find(f);
if (it == field_map.end()) {
@@ -2152,7 +2043,8 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat
if (it == method_map.end()) {
return m;
}
- VLOG(plugin) << "Updating " << info << " object for (method) " << it->second->PrettyMethod();
+ VLOG(plugin) << "Updating " << info << " object for (method) "
+ << it->second->PrettyMethod();
return it->second;
});
@@ -2193,80 +2085,6 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat
}
}
- // Update dex-caches to point to new fields. We wait until here so that the new-class is known by
- // the linker. At the same time reset all methods to have interpreter entrypoints, anything jitted
- // might encode field/method offsets.
- FuncVisitor fv([&](art::ClassVisitor& thiz,
- art::ObjPtr<art::mirror::Class> klass) REQUIRES(art::Locks::mutator_lock_) {
- // Code to actually update a dex-cache. Since non-structural obsolete methods can lead to a
- // single class having several dex-caches associated with it we factor this out a bit.
- auto update_dex_cache = [&](art::ObjPtr<art::mirror::DexCache> dc,
- auto describe) REQUIRES(art::Locks::mutator_lock_) {
- // Clear dex-cache. We don't need to do anything with resolved-types since those are already
- // handled by ReplaceReferences.
- if (dc.IsNull()) {
- // We don't need to do anything if the class doesn't have a dex-cache. This is the case for
- // things like arrays and primitives.
- return;
- }
- for (size_t i = 0; art::kIsDebugBuild && i < dc->NumResolvedTypes(); i++) {
- DCHECK_NE(dc->GetResolvedTypes()[i].load().object.Read(), orig)
- << "Obsolete reference found in dex-cache of class " << klass->PrettyClass() << "!";
- }
- for (size_t i = 0; i < dc->NumResolvedFields(); i++) {
- auto pair(dc->GetNativePairPtrSize(dc->GetResolvedFields(), i, art::kRuntimePointerSize));
- auto new_val = field_map.find(pair.object);
- if (new_val != field_map.end()) {
- VLOG(plugin) << "Updating field dex-cache entry " << i << " of class "
- << klass->PrettyClass() << " dex cache " << describe();
- pair.object = new_val->second;
- dc->SetNativePairPtrSize(dc->GetResolvedFields(), i, pair, art::kRuntimePointerSize);
- }
- }
- for (size_t i = 0; i < dc->NumResolvedMethods(); i++) {
- auto pair(
- dc->GetNativePairPtrSize(dc->GetResolvedMethods(), i, art::kRuntimePointerSize));
- auto new_val = method_map.find(pair.object);
- if (new_val != method_map.end()) {
- VLOG(plugin) << "Updating method dex-cache entry " << i << " of class "
- << klass->PrettyClass() << " dex cache " << describe();
- pair.object = new_val->second;
- dc->SetNativePairPtrSize(dc->GetResolvedMethods(), i, pair, art::kRuntimePointerSize);
- }
- }
- };
- // Clear our own dex-cache.
- update_dex_cache(klass->GetDexCache(), []() { return "Primary"; });
- // Clear all the normal obsolete dex-caches.
- art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
- if (!ext.IsNull()) {
- art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_caches(
- ext->GetObsoleteDexCaches());
- // This contains the dex-cache associated with each obsolete method. Since each redefinition
- // could cause many methods to become obsolete a single dex-cache might be in the array
- // multiple times. We always add new obsoletes onto the end of this array so identical
- // dex-caches are all right next to one another.
- art::ObjPtr<art::mirror::DexCache> prev(nullptr);
- for (int32_t i = 0; !obsolete_caches.IsNull() && i < obsolete_caches->GetLength(); i++) {
- art::ObjPtr<art::mirror::DexCache> cur(obsolete_caches->Get(i));
- if (!cur.IsNull() && cur != prev) {
- prev = cur;
- VLOG(plugin) << "Clearing obsolete dex cache " << i << " of " << klass->PrettyClass();
- update_dex_cache(cur, [&i]() { return StringPrintf("Obsolete[%d]", i); });
- }
- }
- if (!ext->GetObsoleteClass().IsNull()) {
- VLOG(plugin) << "Recuring on obsolete class " << ext->GetObsoleteClass()->PrettyClass();
- // Recur on any obsolete-classes. These aren't known about by the class-linker anymore so
- // we need to visit it manually.
- thiz(ext->GetObsoleteClass());
- }
- }
- return true;
- });
- // TODO Rewrite VisitClasses to be able to take a lambda directly.
- driver_->runtime_->GetClassLinker()->VisitClasses(&fv);
-
art::jit::Jit* jit = driver_->runtime_->GetJit();
if (jit != nullptr) {
// Clear jit.
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 477362bd88..380954185e 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -189,6 +189,7 @@ libart_cc_defaults {
"read_barrier.cc",
"reference_table.cc",
"reflection.cc",
+ "reflective_value_visitor.cc",
"runtime.cc",
"runtime_callbacks.cc",
"runtime_common.cc",
@@ -498,6 +499,7 @@ gensrcs {
"oat.h",
"object_callbacks.h",
"process_state.h",
+ "reflective_value_visitor.h",
"stack.h",
"suspend_reason.h",
"thread.h",
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8f1508a969..b3a46ca045 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -41,6 +41,7 @@
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/utils.h"
+#include "class_root.h"
#include "common_throws.h"
#include "debugger.h"
#include "dex/dex_file-inl.h"
@@ -80,10 +81,14 @@
#include "jit/jit_code_cache.h"
#include "jni/java_vm_ext.h"
#include "mirror/class-inl.h"
+#include "mirror/executable-inl.h"
+#include "mirror/field.h"
+#include "mirror/method_handle_impl.h"
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/reference-inl.h"
+#include "mirror/var_handle.h"
#include "nativehelper/scoped_local_ref.h"
#include "obj_ptr-inl.h"
#include "reflection.h"
@@ -4193,5 +4198,27 @@ void Heap::PostForkChildAction(Thread* self) {
}
}
+void Heap::VisitReflectiveTargets(ReflectiveValueVisitor *visit) {
+ VisitObjectsPaused([&visit](mirror::Object* ref) NO_THREAD_SAFETY_ANALYSIS {
+ art::ObjPtr<mirror::Class> klass(ref->GetClass());
+ // All these classes are in the BootstrapClassLoader.
+ if (!klass->IsBootStrapClassLoaded()) {
+ return;
+ }
+ if (GetClassRoot<mirror::Method>()->IsAssignableFrom(klass) ||
+ GetClassRoot<mirror::Constructor>()->IsAssignableFrom(klass)) {
+ down_cast<mirror::Executable*>(ref)->VisitTarget(visit);
+ } else if (art::GetClassRoot<art::mirror::Field>() == klass) {
+ down_cast<mirror::Field*>(ref)->VisitTarget(visit);
+ } else if (art::GetClassRoot<art::mirror::MethodHandle>()->IsAssignableFrom(klass)) {
+ down_cast<mirror::MethodHandle*>(ref)->VisitTarget(visit);
+ } else if (art::GetClassRoot<art::mirror::FieldVarHandle>()->IsAssignableFrom(klass)) {
+ down_cast<mirror::FieldVarHandle*>(ref)->VisitTarget(visit);
+ } else if (art::GetClassRoot<art::mirror::DexCache>()->IsAssignableFrom(klass)) {
+ down_cast<mirror::DexCache*>(ref)->VisitReflectiveTargets(visit);
+ }
+ });
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 86d6b82d14..de94d379ef 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -51,6 +51,7 @@ class ConditionVariable;
enum class InstructionSet;
class IsMarkedVisitor;
class Mutex;
+class ReflectiveValueVisitor;
class RootVisitor;
class StackVisitor;
class Thread;
@@ -286,6 +287,9 @@ class Heap {
ALWAYS_INLINE void VisitObjectsPaused(Visitor&& visitor)
REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !*gc_complete_lock_);
+ void VisitReflectiveTargets(ReflectiveValueVisitor* visitor)
+ REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !*gc_complete_lock_);
+
void CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/jni/jni_id_manager.cc b/runtime/jni/jni_id_manager.cc
index 9ae8c89862..2553fdf993 100644
--- a/runtime/jni/jni_id_manager.cc
+++ b/runtime/jni/jni_id_manager.cc
@@ -28,11 +28,13 @@
#include "jni/jni_internal.h"
#include "jni_id_type.h"
#include "mirror/array-inl.h"
+#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class.h"
-#include "mirror/class_ext.h"
+#include "mirror/class_ext-inl.h"
#include "mirror/object-inl.h"
#include "obj_ptr-inl.h"
+#include "reflective_value_visitor.h"
#include "thread-inl.h"
#include "thread.h"
#include <algorithm>
@@ -310,18 +312,80 @@ jmethodID JniIdManager::EncodeMethodId(ArtMethod* method) {
return res;
}
-void JniIdManager::VisitIds(Thread* self, JniIdManager::IdVisitor* visitor) {
- art::WriterMutexLock mu(self, *Locks::jni_id_lock_);
- if (visitor->ShouldVisitFields()) {
- for (auto it = field_id_map_.begin(); it != field_id_map_.end(); ++it) {
- visitor->VisitFieldId(
- reinterpret_cast<jfieldID>(IndexToId(std::distance(field_id_map_.begin(), it))), &*it);
+void JniIdManager::VisitReflectiveTargets(ReflectiveValueVisitor* rvv) {
+ art::WriterMutexLock mu(Thread::Current(), *Locks::jni_id_lock_);
+ for (auto it = field_id_map_.begin(); it != field_id_map_.end(); ++it) {
+ ArtField* old_field = *it;
+ uintptr_t id = IndexToId(std::distance(field_id_map_.begin(), it));
+ ArtField* new_field =
+ rvv->VisitField(old_field, JniIdReflectiveSourceInfo(reinterpret_cast<jfieldID>(id)));
+ if (old_field != new_field) {
+ *it = new_field;
+ ObjPtr<mirror::Class> old_class(old_field->GetDeclaringClass());
+ ObjPtr<mirror::Class> new_class(new_field->GetDeclaringClass());
+ ObjPtr<mirror::ClassExt> old_ext_data(old_class->GetExtData());
+ ObjPtr<mirror::ClassExt> new_ext_data(new_class->GetExtData());
+ if (!old_ext_data.IsNull()) {
+ // Clear the old field mapping.
+ if (old_field->IsStatic()) {
+ size_t old_off = ArraySlice<ArtField>(old_class->GetSFieldsPtr()).OffsetOf(old_field);
+ ObjPtr<mirror::PointerArray> old_statics(old_ext_data->GetStaticJFieldIDs());
+ if (!old_statics.IsNull()) {
+ old_statics->SetElementPtrSize(old_off, 0, kRuntimePointerSize);
+ }
+ } else {
+ size_t old_off = ArraySlice<ArtField>(old_class->GetIFieldsPtr()).OffsetOf(old_field);
+ ObjPtr<mirror::PointerArray> old_instances(old_ext_data->GetInstanceJFieldIDs());
+ if (!old_instances.IsNull()) {
+ old_instances->SetElementPtrSize(old_off, 0, kRuntimePointerSize);
+ }
+ }
+ }
+ if (!new_ext_data.IsNull()) {
+ // Set the new field mapping.
+ if (new_field->IsStatic()) {
+ size_t new_off = ArraySlice<ArtField>(new_class->GetSFieldsPtr()).OffsetOf(new_field);
+ ObjPtr<mirror::PointerArray> new_statics(new_ext_data->GetStaticJFieldIDs());
+ if (!new_statics.IsNull()) {
+ new_statics->SetElementPtrSize(new_off, id, kRuntimePointerSize);
+ }
+ } else {
+ size_t new_off = ArraySlice<ArtField>(new_class->GetIFieldsPtr()).OffsetOf(new_field);
+ ObjPtr<mirror::PointerArray> new_instances(new_ext_data->GetInstanceJFieldIDs());
+ if (!new_instances.IsNull()) {
+ new_instances->SetElementPtrSize(new_off, id, kRuntimePointerSize);
+ }
+ }
+ }
}
}
- if (visitor->ShouldVisitMethods()) {
- for (auto it = method_id_map_.begin(); it != method_id_map_.end(); ++it) {
- visitor->VisitMethodId(
- reinterpret_cast<jmethodID>(IndexToId(std::distance(method_id_map_.begin(), it))), &*it);
+ for (auto it = method_id_map_.begin(); it != method_id_map_.end(); ++it) {
+ ArtMethod* old_method = *it;
+ uintptr_t id = IndexToId(std::distance(method_id_map_.begin(), it));
+ ArtMethod* new_method =
+ rvv->VisitMethod(old_method, JniIdReflectiveSourceInfo(reinterpret_cast<jmethodID>(id)));
+ if (old_method != new_method) {
+ *it = new_method;
+ ObjPtr<mirror::Class> old_class(old_method->GetDeclaringClass());
+ ObjPtr<mirror::Class> new_class(new_method->GetDeclaringClass());
+ ObjPtr<mirror::ClassExt> old_ext_data(old_class->GetExtData());
+ ObjPtr<mirror::ClassExt> new_ext_data(new_class->GetExtData());
+ if (!old_ext_data.IsNull()) {
+ // Clear the old method mapping.
+ size_t old_off = ArraySlice<ArtMethod>(old_class->GetMethodsPtr()).OffsetOf(old_method);
+ ObjPtr<mirror::PointerArray> old_methods(old_ext_data->GetJMethodIDs());
+ if (!old_methods.IsNull()) {
+ old_methods->SetElementPtrSize(old_off, 0, kRuntimePointerSize);
+ }
+ }
+ if (!new_ext_data.IsNull()) {
+ // Set the new method mapping.
+ size_t new_off = ArraySlice<ArtMethod>(new_class->GetMethodsPtr()).OffsetOf(new_method);
+ ObjPtr<mirror::PointerArray> new_methods(new_ext_data->GetJMethodIDs());
+ if (!new_methods.IsNull()) {
+ new_methods->SetElementPtrSize(new_off, id, kRuntimePointerSize);
+ }
+ }
}
}
}
diff --git a/runtime/jni/jni_id_manager.h b/runtime/jni/jni_id_manager.h
index 7b2f3c49bd..6b43534d18 100644
--- a/runtime/jni/jni_id_manager.h
+++ b/runtime/jni/jni_id_manager.h
@@ -25,6 +25,7 @@
#include "art_method.h"
#include "base/mutex.h"
#include "jni_id_type.h"
+#include "reflective_value_visitor.h"
namespace art {
namespace jni {
@@ -32,15 +33,6 @@ namespace jni {
class ScopedEnableSuspendAllJniIdQueries;
class JniIdManager {
public:
- class IdVisitor {
- public:
- virtual ~IdVisitor() {}
- virtual void VisitMethodId(jmethodID id, ArtMethod** method) = 0;
- virtual void VisitFieldId(jfieldID id, ArtField** field) = 0;
- virtual bool ShouldVisitFields() = 0;
- virtual bool ShouldVisitMethods() = 0;
- };
-
template <typename T,
typename = typename std::enable_if<std::is_same_v<T, jmethodID> ||
std::is_same_v<T, jfieldID>>>
@@ -55,33 +47,8 @@ class JniIdManager {
jfieldID EncodeFieldId(ArtField* field) REQUIRES(!Locks::jni_id_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- void VisitIds(Thread* self, IdVisitor* visitor);
-
- template<typename MethodVisitor, typename FieldVisitor>
- void VisitIds(Thread* self, MethodVisitor m, FieldVisitor f) REQUIRES(!Locks::jni_id_lock_) {
- struct FuncVisitor : public IdVisitor {
- public:
- FuncVisitor(MethodVisitor m, FieldVisitor f) : m_(m), f_(f) {}
- bool ShouldVisitFields() override {
- return true;
- }
- bool ShouldVisitMethods() override {
- return true;
- }
- void VisitMethodId(jmethodID mid, ArtMethod** am) NO_THREAD_SAFETY_ANALYSIS override {
- m_(mid, am);
- }
- void VisitFieldId(jfieldID fid, ArtField** af) NO_THREAD_SAFETY_ANALYSIS override {
- f_(fid, af);
- }
-
- private:
- MethodVisitor m_;
- FieldVisitor f_;
- };
- FuncVisitor fv(m, f);
- VisitIds(self, &fv);
- }
+ void VisitReflectiveTargets(ReflectiveValueVisitor* rvv)
+ REQUIRES(Locks::mutator_lock_, !Locks::jni_id_lock_);
private:
template <typename ArtType>
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index f97f5211f1..c2c47a5187 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -25,6 +25,7 @@
#include "object-inl.h"
#include "object.h"
#include "object_array-inl.h"
+#include "reflective_value_visitor.h"
#include "runtime.h"
#include "runtime_globals.h"
#include "string.h"
@@ -172,6 +173,27 @@ void DexCache::InitializeDexCache(Thread* self,
dex_file->NumCallSiteIds());
}
+void DexCache::VisitReflectiveTargets(ReflectiveValueVisitor* visitor) {
+ for (size_t i = 0; i < NumResolvedFields(); i++) {
+ auto pair(GetNativePairPtrSize(GetResolvedFields(), i, kRuntimePointerSize));
+ ArtField* new_val = visitor->VisitField(
+ pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedField, pair.index, this));
+ if (UNLIKELY(new_val != pair.object)) {
+ pair.object = new_val;
+ SetNativePairPtrSize(GetResolvedFields(), i, pair, kRuntimePointerSize);
+ }
+ }
+ for (size_t i = 0; i < NumResolvedMethods(); i++) {
+ auto pair(GetNativePairPtrSize(GetResolvedMethods(), i, kRuntimePointerSize));
+ ArtMethod* new_val = visitor->VisitMethod(
+ pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedMethod, pair.index, this));
+ if (UNLIKELY(new_val != pair.object)) {
+ pair.object = new_val;
+ SetNativePairPtrSize(GetResolvedMethods(), i, pair, kRuntimePointerSize);
+ }
+ }
+}
+
bool DexCache::AddPreResolvedStringsArray() {
DCHECK_EQ(NumPreResolvedStrings(), 0u);
Thread* const self = Thread::Current();
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index b41443e5fb..292db14587 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -37,6 +37,7 @@ struct DexCacheOffsets;
class DexFile;
union JValue;
class LinearAlloc;
+class ReflectiveValueVisitor;
class Thread;
namespace mirror {
@@ -476,6 +477,8 @@ class MANAGED DexCache final : public Object {
// Returns true if we succeeded in adding the pre-resolved string array.
bool AddPreResolvedStringsArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
+
private:
void Init(const DexFile* dex_file,
ObjPtr<String> location,
diff --git a/runtime/mirror/executable-inl.h b/runtime/mirror/executable-inl.h
index ce35d6ef20..f2d684a3d5 100644
--- a/runtime/mirror/executable-inl.h
+++ b/runtime/mirror/executable-inl.h
@@ -20,6 +20,7 @@
#include "executable.h"
#include "object-inl.h"
+#include "reflective_value_visitor.h"
#include "verify_object.h"
namespace art {
@@ -37,10 +38,11 @@ inline ObjPtr<mirror::Class> Executable::GetDeclaringClass() {
return GetFieldObject<mirror::Class>(DeclaringClassOffset());
}
-template<typename Visitor, VerifyObjectFlags kVerifiyFlags>
-inline void Executable::VisitTarget(Visitor&& v) {
+template<VerifyObjectFlags kVerifiyFlags>
+inline void Executable::VisitTarget(ReflectiveValueVisitor* v) {
+ HeapReflectiveSourceInfo hrsi(kSourceJavaLangReflectExecutable, this);
ArtMethod* orig = GetArtMethod<kVerifiyFlags>();
- ArtMethod* new_target = v(orig);
+ ArtMethod* new_target = v->VisitMethod(orig, hrsi);
if (orig != new_target) {
SetArtMethod(new_target);
SetDexMethodIndex(new_target->GetDexMethodIndex());
diff --git a/runtime/mirror/executable.h b/runtime/mirror/executable.h
index 8eca206d5a..750a16741d 100644
--- a/runtime/mirror/executable.h
+++ b/runtime/mirror/executable.h
@@ -25,6 +25,7 @@ namespace art {
struct ExecutableOffsets;
class ArtMethod;
+class ReflectiveValueVisitor;
namespace mirror {
@@ -41,9 +42,8 @@ class MANAGED Executable : public AccessibleObject {
return reinterpret_cast64<ArtMethod*>(GetField64<kVerifyFlags>(ArtMethodOffset()));
}
- template <typename Visitor,
- VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- inline void VisitTarget(Visitor&& v) REQUIRES(Locks::mutator_lock_);
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ inline void VisitTarget(ReflectiveValueVisitor* v) REQUIRES(Locks::mutator_lock_);
template <bool kTransactionActive = false,
bool kCheckTransaction = true,
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index 6e82d6d22f..ac11be1002 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -104,18 +104,6 @@ inline void Field::SetType(ObjPtr<mirror::Class> type) {
SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, type_), type);
}
-template<typename Visitor>
-inline void Field::VisitTarget(Visitor&& v) {
- ArtField* orig = GetArtField(/*use_dex_cache*/false);
- ArtField* new_value = v(orig);
- if (orig != new_value) {
- SetDexFieldIndex<false>(new_value->GetDexFieldIndex());
- SetOffset<false>(new_value->GetOffset().Int32Value());
- SetDeclaringClass<false>(new_value->GetDeclaringClass());
- }
- DCHECK_EQ(new_value, GetArtField(/*use_dex_cache*/false));
-}
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index 9a400065a9..aa071a8a8c 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -24,6 +24,18 @@
namespace art {
namespace mirror {
+void Field::VisitTarget(ReflectiveValueVisitor* v) {
+ HeapReflectiveSourceInfo hrsi(kSourceJavaLangReflectField, this);
+ ArtField* orig = GetArtField(/*use_dex_cache*/false);
+ ArtField* new_value = v->VisitField(orig, hrsi);
+ if (orig != new_value) {
+ SetDexFieldIndex<false>(new_value->GetDexFieldIndex());
+ SetOffset<false>(new_value->GetOffset().Int32Value());
+ SetDeclaringClass<false>(new_value->GetDeclaringClass());
+ }
+ DCHECK_EQ(new_value, GetArtField(/*use_dex_cache*/false));
+}
+
ArtField* Field::GetArtField(bool use_dex_cache) {
ObjPtr<mirror::Class> declaring_class = GetDeclaringClass();
if (UNLIKELY(declaring_class->IsProxyClass())) {
diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h
index 89c86e3339..2ed3452254 100644
--- a/runtime/mirror/field.h
+++ b/runtime/mirror/field.h
@@ -29,6 +29,7 @@ namespace art {
class ArtField;
struct FieldOffsets;
+class ReflectiveValueVisitor;
namespace mirror {
@@ -82,8 +83,7 @@ class MANAGED Field : public AccessibleObject {
// Used to modify the target of this Field object, if required for structural redefinition or some
// other purpose.
- template<typename Visitor>
- inline void VisitTarget(Visitor&& v) REQUIRES(Locks::mutator_lock_);
+ void VisitTarget(ReflectiveValueVisitor* v) REQUIRES(Locks::mutator_lock_);
private:
// Padding required for matching alignment with the Java peer.
diff --git a/runtime/mirror/method_handle_impl-inl.h b/runtime/mirror/method_handle_impl-inl.h
index 0085642220..932b4343f3 100644
--- a/runtime/mirror/method_handle_impl-inl.h
+++ b/runtime/mirror/method_handle_impl-inl.h
@@ -39,20 +39,6 @@ inline ObjPtr<mirror::Class> MethodHandle::GetTargetClass() {
GetTargetMethod()->GetDeclaringClass() : GetTargetField()->GetDeclaringClass();
}
-template<typename Visitor>
-inline void MethodHandle::VisitTarget(Visitor&& v) {
- void* target = GetTargetField();
- void* result;
- if (GetHandleKind() < kFirstAccessorKind) {
- result = v(GetTargetMethod());
- } else {
- result = v(GetTargetField());
- }
- if (result != target) {
- SetField64<false>(ArtFieldOrMethodOffset(), reinterpret_cast<uintptr_t>(result));
- }
-}
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index 433d4ba532..dd25fc9338 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -54,5 +54,20 @@ ObjPtr<mirror::MethodHandleImpl> MethodHandleImpl::Create(Thread* const self,
return mh.Get();
}
+void MethodHandle::VisitTarget(ReflectiveValueVisitor* v) {
+ void* target = GetTargetField();
+ void* result;
+ HeapReflectiveSourceInfo hrsi(kSourceJavaLangInvokeMethodHandle, this);
+ if (GetHandleKind() < kFirstAccessorKind) {
+ result = v->VisitMethod(GetTargetMethod(), hrsi);
+ } else {
+ result = v->VisitField(GetTargetField(), hrsi);
+ }
+ if (result != target) {
+ SetField64<false>(ArtFieldOrMethodOffset(), reinterpret_cast<uintptr_t>(result));
+ }
+}
+
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 357ec9d8f2..54aa0c9bc2 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -28,6 +28,7 @@ namespace art {
struct MethodHandleOffsets;
struct MethodHandleImplOffsets;
+class ReflectiveValueVisitor;
namespace mirror {
@@ -89,8 +90,7 @@ class MANAGED MethodHandle : public Object {
// Used when classes become structurally obsolete to change the MethodHandle to refer to the new
// method or field.
- template<typename Visitor>
- void VisitTarget(Visitor&& v) REQUIRES(Locks::mutator_lock_);
+ void VisitTarget(ReflectiveValueVisitor* v) REQUIRES(Locks::mutator_lock_);
protected:
void Initialize(uintptr_t art_field_or_method, Kind kind, Handle<MethodType> method_type)
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index d887b5a114..6d5ff2cfde 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -2032,5 +2032,16 @@ bool ByteBufferViewVarHandle::Access(AccessMode access_mode,
UNREACHABLE();
}
+void FieldVarHandle::VisitTarget(ReflectiveValueVisitor* v) {
+ ArtField* orig = GetField();
+ ArtField* new_value =
+ v->VisitField(orig, HeapReflectiveSourceInfo(kSourceJavaLangInvokeFieldVarHandle, this));
+ if (orig != new_value) {
+ SetField64</*kTransactionActive*/ false>(ArtFieldOffset(),
+ reinterpret_cast<uintptr_t>(new_value));
+ }
+}
+
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index ac78d984eb..02a0d8c361 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -36,6 +36,7 @@ struct ArrayElementVarHandleOffsets;
struct ByteArrayViewVarHandleOffsets;
struct ByteBufferViewVarHandleOffsets;
+class ReflectiveValueVisitor;
class ShadowFrameGetter;
namespace mirror {
@@ -198,8 +199,7 @@ class MANAGED FieldVarHandle : public VarHandle {
ArtField* GetField() REQUIRES_SHARED(Locks::mutator_lock_);
// Used for updating var-handles to obsolete fields.
- template<typename Visitor>
- inline void VisitTarget(Visitor&& v) REQUIRES(Locks::mutator_lock_);
+ void VisitTarget(ReflectiveValueVisitor* v) REQUIRES(Locks::mutator_lock_);
private:
static MemberOffset ArtFieldOffset() {
diff --git a/runtime/reflective_value_visitor.cc b/runtime/reflective_value_visitor.cc
new file mode 100644
index 0000000000..69fd51ff6e
--- /dev/null
+++ b/runtime/reflective_value_visitor.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "reflective_value_visitor.h"
+#include <sstream>
+
+#include "base/locks.h"
+#include "base/mutex-inl.h"
+#include "mirror/class.h"
+#include "mirror/object-inl.h"
+
+namespace art {
+
+void HeapReflectiveSourceInfo::Describe(std::ostream& os) const {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+ ReflectionSourceInfo::Describe(os);
+ os << " Class=" << src_->GetClass()->PrettyClass();
+}
+
+template<>
+void JniIdReflectiveSourceInfo<jfieldID>::Describe(std::ostream& os) const {
+ ReflectionSourceInfo::Describe(os);
+ os << " jfieldID=" << reinterpret_cast<uintptr_t>(id_);
+}
+
+template<>
+void JniIdReflectiveSourceInfo<jmethodID>::Describe(std::ostream& os) const {
+ ReflectionSourceInfo::Describe(os);
+ os << " jmethodID=" << reinterpret_cast<uintptr_t>(id_);
+}
+
+} // namespace art
diff --git a/runtime/reflective_value_visitor.h b/runtime/reflective_value_visitor.h
new file mode 100644
index 0000000000..8823fcba9c
--- /dev/null
+++ b/runtime/reflective_value_visitor.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ART_RUNTIME_REFLECTIVE_VALUE_VISITOR_H_
+#define ART_RUNTIME_REFLECTIVE_VALUE_VISITOR_H_
+
+#include <android-base/logging.h>
+
+#include <array>
+#include <compare>
+#include <functional>
+#include <stack>
+
+#include "android-base/macros.h"
+#include "base/enums.h"
+#include "base/globals.h"
+#include "base/locks.h"
+#include "base/macros.h"
+#include "base/value_object.h"
+#include "dex/dex_file.h"
+#include "jni.h"
+#include "mirror/dex_cache.h"
+#include "obj_ptr.h"
+
+namespace art {
+
+class ArtField;
+class ArtMethod;
+class BaseReflectiveHandleScope;
+class Thread;
+
+class ReflectionSourceInfo;
+
+class ReflectiveValueVisitor : public ValueObject {
+ public:
+ virtual ~ReflectiveValueVisitor() {}
+
+ virtual ArtMethod* VisitMethod(ArtMethod* in, const ReflectionSourceInfo& info)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ virtual ArtField* VisitField(ArtField* in, const ReflectionSourceInfo& info)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ // Give it an entrypoint through operator() to interact with things that expect lambda-like things
+ template <typename T,
+ typename = typename std::enable_if<std::is_same_v<T, ArtField> ||
+ std::is_same_v<T, ArtMethod>>>
+ T* operator()(T* t, const ReflectionSourceInfo& info) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if constexpr (std::is_same_v<T, ArtField>) {
+ return VisitField(t, info);
+ } else {
+ static_assert(std::is_same_v<T, ArtMethod>, "Expected ArtField or ArtMethod");
+ return VisitMethod(t, info);
+ }
+ }
+};
+
+template <typename FieldVis, typename MethodVis>
+class FunctionReflectiveValueVisitor : public ReflectiveValueVisitor {
+ public:
+ FunctionReflectiveValueVisitor(FieldVis fv, MethodVis mv) : fv_(fv), mv_(mv) {}
+ ArtField* VisitField(ArtField* in, const ReflectionSourceInfo& info) override
+ REQUIRES(Locks::mutator_lock_) {
+ return fv_(in, info);
+ }
+ ArtMethod* VisitMethod(ArtMethod* in, const ReflectionSourceInfo& info) override
+ REQUIRES(Locks::mutator_lock_) {
+ return mv_(in, info);
+ }
+
+ private:
+ FieldVis fv_;
+ MethodVis mv_;
+};
+
+enum ReflectionSourceType {
+ kSourceUnknown = 0,
+ kSourceJavaLangReflectExecutable,
+ kSourceJavaLangReflectField,
+ kSourceJavaLangInvokeMethodHandle,
+ kSourceJavaLangInvokeFieldVarHandle,
+ kSourceThreadHandleScope,
+ kSourceJniFieldId,
+ kSourceJniMethodId,
+ kSourceDexCacheResolvedMethod,
+ kSourceDexCacheResolvedField,
+ kSourceMiscInternal,
+};
+std::ostream& operator<<(std::ostream& os, const ReflectionSourceType& type);
+
+class ReflectionSourceInfo : public ValueObject {
+ public:
+ virtual ~ReflectionSourceInfo() {}
+ // Thread id 0 is for non thread roots.
+ explicit ReflectionSourceInfo(ReflectionSourceType type) : type_(type) {}
+ virtual void Describe(std::ostream& os) const {
+ os << "Type=" << type_;
+ }
+
+ private:
+ const ReflectionSourceType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReflectionSourceInfo);
+};
+inline std::ostream& operator<<(std::ostream& os, const ReflectionSourceInfo& info) {
+ info.Describe(os);
+ return os;
+}
+
+class ReflectiveHandleScopeSourceInfo : public ReflectionSourceInfo {
+ public:
+ explicit ReflectiveHandleScopeSourceInfo(BaseReflectiveHandleScope* source)
+ : ReflectionSourceInfo(kSourceThreadHandleScope), source_(source) {}
+
+ void Describe(std::ostream& os) const override {
+ ReflectionSourceInfo::Describe(os);
+ os << " source=" << source_;
+ }
+
+ private:
+ BaseReflectiveHandleScope* source_;
+};
+
+// TODO Maybe give this the ability to retrieve the type and ref, if it's useful.
+class HeapReflectiveSourceInfo : public ReflectionSourceInfo {
+ public:
+ HeapReflectiveSourceInfo(ReflectionSourceType t, mirror::Object* src)
+ : ReflectionSourceInfo(t), src_(src) {}
+ void Describe(std::ostream& os) const override;
+
+ private:
+ ObjPtr<mirror::Object> src_;
+};
+
+// TODO Maybe give this the ability to retrieve the id if it's useful.
+template <typename T,
+ typename = typename std::enable_if_t<std::is_same_v<T, jmethodID> ||
+ std::is_same_v<T, jfieldID>>>
+class JniIdReflectiveSourceInfo : public ReflectionSourceInfo {
+ public:
+ explicit JniIdReflectiveSourceInfo(T id)
+ : ReflectionSourceInfo(std::is_same_v<T, jmethodID> ? kSourceJniMethodId : kSourceJniFieldId),
+ id_(id) {}
+ void Describe(std::ostream& os) const override;
+
+ private:
+ T id_;
+};
+
+class DexCacheSourceInfo : public ReflectionSourceInfo {
+ public:
+ explicit DexCacheSourceInfo(ReflectionSourceType type,
+ size_t index,
+ ObjPtr<mirror::DexCache> cache)
+ : ReflectionSourceInfo(type), index_(index), cache_(cache) {}
+
+ void Describe(std::ostream& os) const override REQUIRES(Locks::mutator_lock_) {
+ ReflectionSourceInfo::Describe(os);
+ os << " index=" << index_ << " cache_=" << cache_.PtrUnchecked()
+ << " files=" << *cache_->GetDexFile();
+ }
+
+ private:
+ size_t index_;
+ ObjPtr<mirror::DexCache> cache_;
+};
+} // namespace art
+
+#endif // ART_RUNTIME_REFLECTIVE_VALUE_VISITOR_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 499dbf0a0f..9af2f41c63 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2197,6 +2197,12 @@ void Runtime::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
VisitConcurrentRoots(visitor, flags);
}
+void Runtime::VisitReflectiveTargets(ReflectiveValueVisitor *visitor) {
+ heap_->VisitReflectiveTargets(visitor);
+ jni_id_manager_->VisitReflectiveTargets(visitor);
+ callbacks_->VisitReflectiveTargets(visitor);
+}
+
void Runtime::VisitImageRoots(RootVisitor* visitor) {
for (auto* space : GetHeap()->GetContinuousSpaces()) {
if (space->IsImageSpace()) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 120ca66788..0b336c7345 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -43,6 +43,7 @@
#include "offsets.h"
#include "process_state.h"
#include "quick/quick_method_frame_info.h"
+#include "reflective_value_visitor.h"
#include "runtime_stats.h"
namespace art {
@@ -397,6 +398,17 @@ class Runtime {
void SweepSystemWeaks(IsMarkedVisitor* visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Walk all reflective objects and visit their targets as well as any method/fields held by the
+ // runtime threads that are marked as being reflective.
+ void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
+ // Helper for visiting reflective targets with lambdas for both field and method reflective
+ // targets.
+ template <typename FieldVis, typename MethodVis>
+ void VisitReflectiveTargets(FieldVis&& fv, MethodVis&& mv) REQUIRES(Locks::mutator_lock_) {
+ FunctionReflectiveValueVisitor frvv(fv, mv);
+ VisitReflectiveTargets(&frvv);
+ }
+
// Returns a special method that calls into a trampoline for runtime method resolution
ArtMethod* GetResolutionMethod();
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 40976c23ae..ac73364ff0 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -319,4 +319,20 @@ void RuntimeCallbacks::RegisterNativeMethod(ArtMethod* method,
}
}
+void RuntimeCallbacks::AddReflectiveValueVisitCallback(ReflectiveValueVisitCallback *cb) {
+ WriterMutexLock mu(Thread::Current(), *callback_lock_);
+ reflective_value_visit_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveReflectiveValueVisitCallback(ReflectiveValueVisitCallback *cb) {
+ WriterMutexLock mu(Thread::Current(), *callback_lock_);
+ Remove(cb, &reflective_value_visit_callbacks_);
+}
+
+void RuntimeCallbacks::VisitReflectiveTargets(ReflectiveValueVisitor *visitor) {
+ for (ReflectiveValueVisitCallback* cb : COPY(reflective_value_visit_callbacks_)) {
+ cb->VisitReflectiveTargets(visitor);
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index fe7bb0c1cd..7111ba0085 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -44,6 +44,7 @@ class MethodCallback;
class Monitor;
class ReaderWriterMutex;
class ThreadLifecycleCallback;
+class ReflectiveValueVisitor;
// Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must
// hold the exclusive lock to add or remove a listener. A thread must hold the shared lock
@@ -156,6 +157,17 @@ class MethodInspectionCallback {
virtual bool MethodNeedsDebugVersion(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
+// Callback to let something request to be notified when reflective objects are being visited and
+// updated to update any bare ArtMethod/ArtField pointers it might have.
+class ReflectiveValueVisitCallback {
+ public:
+ virtual ~ReflectiveValueVisitCallback() {}
+
+ // Called when something visits all reflective values with the update visitor.
+ virtual void VisitReflectiveTargets(ReflectiveValueVisitor* visitor)
+ REQUIRES(Locks::mutator_lock_) = 0;
+};
+
class RuntimeCallbacks {
public:
RuntimeCallbacks();
@@ -257,6 +269,13 @@ class RuntimeCallbacks {
void RemoveDebuggerControlCallback(DebuggerControlCallback* cb)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
+
+ void AddReflectiveValueVisitCallback(ReflectiveValueVisitCallback* cb)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveReflectiveValueVisitCallback(ReflectiveValueVisitCallback* cb)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
std::unique_ptr<ReaderWriterMutex> callback_lock_ BOTTOM_MUTEX_ACQUIRED_AFTER;
@@ -280,6 +299,8 @@ class RuntimeCallbacks {
GUARDED_BY(callback_lock_);
std::vector<DebuggerControlCallback*> debugger_control_callbacks_
GUARDED_BY(callback_lock_);
+ std::vector<ReflectiveValueVisitCallback*> reflective_value_visit_callbacks_
+ GUARDED_BY(callback_lock_);
};
} // namespace art
diff --git a/test/1984-structural-redefine-field-trace/expected.txt b/test/1984-structural-redefine-field-trace/expected.txt
new file mode 100644
index 0000000000..6153d7e789
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/expected.txt
@@ -0,0 +1,31 @@
+Dumping fields at start
+public static boolean art.Test1984$Transform.boom=false
+public static int art.Test1984$Transform.count_down=2
+public static boolean art.Test1984$Transform.tock=false
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() MODIFY: public static boolean art.Test1984$Transform.tock Set to: true
+method: public static void art.Test1984$Transform.tick() ACCESS: public static int art.Test1984$Transform.count_down
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() MODIFY: public static boolean art.Test1984$Transform.tock Set to: false
+method: public static void art.Test1984$Transform.tick() ACCESS: public static int art.Test1984$Transform.count_down
+method: public static void art.Test1984$Transform.tick() MODIFY: public static int art.Test1984$Transform.count_down Set to: 1
+method: public static void art.Test1984$Transform.tick() ACCESS: public static int art.Test1984$Transform.count_down
+REDEFINING TRANSFORM CLASS
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() MODIFY: public static boolean art.Test1984$Transform.tock Set to: true
+method: public static void art.Test1984$Transform.tick() ACCESS: public static int art.Test1984$Transform.count_down
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() ACCESS: public static boolean art.Test1984$Transform.tock
+method: public static void art.Test1984$Transform.tick() MODIFY: public static boolean art.Test1984$Transform.tock Set to: false
+method: public static void art.Test1984$Transform.tick() ACCESS: public static int art.Test1984$Transform.count_down
+method: public static void art.Test1984$Transform.tick() MODIFY: public static int art.Test1984$Transform.count_down Set to: 0
+method: public static void art.Test1984$Transform.tick() ACCESS: public static int art.Test1984$Transform.count_down
+method: public static void art.Test1984$Transform.tick() MODIFY: public static boolean art.Test1984$Transform.boom Set to: true
+Dumping fields at end
+public static int art.Test1984$Transform.aaa_INITIAL=0
+public static boolean art.Test1984$Transform.boom=true
+public static int art.Test1984$Transform.count_down=0
+public static boolean art.Test1984$Transform.tock=false
diff --git a/test/1984-structural-redefine-field-trace/info.txt b/test/1984-structural-redefine-field-trace/info.txt
new file mode 100644
index 0000000000..ded28c5423
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/info.txt
@@ -0,0 +1 @@
+Tests field access and modification watches in JVMTI when target is structurally redefined.
diff --git a/test/1984-structural-redefine-field-trace/run b/test/1984-structural-redefine-field-trace/run
new file mode 100755
index 0000000000..a36de16ea6
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true
diff --git a/runtime/mirror/var_handle-inl.h b/test/1984-structural-redefine-field-trace/src/Main.java
index d3f582d04e..415a85ea13 100644
--- a/runtime/mirror/var_handle-inl.h
+++ b/test/1984-structural-redefine-field-trace/src/Main.java
@@ -14,27 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_MIRROR_VAR_HANDLE_INL_H_
-#define ART_RUNTIME_MIRROR_VAR_HANDLE_INL_H_
-
-#include "var_handle.h"
-
-namespace art {
-class ArtField;
-
-namespace mirror {
-
-template<typename Visitor>
-inline void FieldVarHandle::VisitTarget(Visitor&& v) {
- ArtField* orig = GetField();
- ArtField* new_value = v(orig);
- if (orig != new_value) {
- SetField64</*kTransactionActive*/ false>(ArtFieldOffset(),
- reinterpret_cast<uintptr_t>(new_value));
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1984.run();
}
}
-
-} // namespace mirror
-} // namespace art
-
-#endif // ART_RUNTIME_MIRROR_VAR_HANDLE_INL_H_
diff --git a/test/1984-structural-redefine-field-trace/src/art/Redefinition.java b/test/1984-structural-redefine-field-trace/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1984-structural-redefine-field-trace/src/art/Test1984.java b/test/1984-structural-redefine-field-trace/src/art/Test1984.java
new file mode 100644
index 0000000000..a69d56e17a
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/src/art/Test1984.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 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.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.util.Base64;
+
+public class Test1984 {
+ public static void notifyFieldModify(
+ Executable method, long location, Class<?> f_klass, Object target, Field f, Object value) {
+ System.out.println("method: " + method + "\tMODIFY: " + f + "\tSet to: " + value);
+ }
+
+ public static void notifyFieldAccess(
+ Executable method, long location, Class<?> f_klass, Object target, Field f) {
+ System.out.println("method: " + method + "\tACCESS: " + f);
+ }
+
+ public static class Transform {
+ public static int count_down = 2;
+ public static boolean boom = false;
+ public static boolean tock = false;
+
+ public static void tick() {
+ boolean tocked = tock;
+ tock = !tock;
+ if (tocked) {
+ count_down--;
+ }
+ if (count_down == 0) {
+ boom = true;
+ }
+ }
+ }
+
+ /* Base64 encoded dex file for.
+ * // NB The addition of aaa_INITIAL means the fields all have different offsets
+ * public static class Transform {
+ * public static int aaa_INITIAL = 0;
+ * public static int count_down = 2;
+ * public static boolean boom = false;
+ * public static boolean tock = false;
+ * public static void tick() {
+ * boolean tocked = tock;
+ * tock = !tock;
+ * if (tocked) {
+ * count_down--;
+ * }
+ * if (count_down == 0) {
+ * boom = true;
+ * }
+ * }
+ * }
+ */
+ public static final byte[] REDEFINED_DEX_BYTES =
+ Base64.getDecoder()
+ .decode(
+ "ZGV4CjAzNQDejZufbnVbJEn1/OfB3XmJPtVbudlWkvnsAwAAcAAAAHhWNBIAAAAAAAAAADQDAAAU"
+ + "AAAAcAAAAAgAAADAAAAAAQAAAOAAAAAEAAAA7AAAAAQAAAAMAQAAAQAAACwBAACgAgAATAEAAPAB"
+ + "AAD6AQAAAgIAAAUCAAAfAgAALwIAAFMCAABzAgAAhwIAAJYCAAChAgAApAIAAKcCAAC0AgAAwQIA"
+ + "AMcCAADTAgAA2QIAAN8CAADlAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACgAAAAsAAAAKAAAA"
+ + "BgAAAAAAAAABAAAADAAAAAEABwAOAAAAAQAAAA8AAAABAAcAEgAAAAEAAAAAAAAAAQAAAAEAAAAB"
+ + "AAAAEQAAAAUAAAABAAAAAQAAAAEAAAAFAAAAAAAAAAgAAADgAQAAFgMAAAAAAAACAAAABwMAAA0D"
+ + "AAACAAAAAAAAAOwCAAALAAAAEgFnAQAAEiBnAAIAagEBAGoBAwAOAAAAAQABAAEAAAD0AgAABAAA"
+ + "AHAQAwAAAA4AAwAAAAAAAAD5AgAAGwAAABIRYwIDAGMAAwA5ABQAARBqAAMAOAIIAGAAAgDYAAD/"
+ + "ZwACAGAAAgA5AAQAagEBAA4AEgAo7gAATAEAAAAAAAAAAAAAAAAAAAg8Y2xpbml0PgAGPGluaXQ+"
+ + "AAFJABhMYXJ0L1Rlc3QxOTg0JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5ODQ7ACJMZGFsdmlrL2Fu"
+ + "bm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJDbGFzczsA"
+ + "EkxqYXZhL2xhbmcvT2JqZWN0OwANVGVzdDE5ODQuamF2YQAJVHJhbnNmb3JtAAFWAAFaAAthYWFf"
+ + "SU5JVElBTAALYWNjZXNzRmxhZ3MABGJvb20ACmNvdW50X2Rvd24ABG5hbWUABHRpY2sABHRvY2sA"
+ + "BXZhbHVlAAgABx0tPC0ABwAHDgANAAcdLXgtaksuAnkdAAIDARMYAgIEAg0ECRAXCQQAAwAACQEJ"
+ + "AQkBCQCIgATYAgGBgASAAwEJmAMAAA8AAAAAAAAAAQAAAAAAAAABAAAAFAAAAHAAAAACAAAACAAA"
+ + "AMAAAAADAAAAAQAAAOAAAAAEAAAABAAAAOwAAAAFAAAABAAAAAwBAAAGAAAAAQAAACwBAAADEAAA"
+ + "AQAAAEwBAAABIAAAAwAAAFgBAAAGIAAAAQAAAOABAAACIAAAFAAAAPABAAADIAAAAwAAAOwCAAAE"
+ + "IAAAAgAAAAcDAAAAIAAAAQAAABYDAAAAEAAAAQAAADQDAAA=");
+
+ public static void run() throws Exception {
+ System.out.println("Dumping fields at start");
+ for (Field f : Transform.class.getDeclaredFields()) {
+ System.out.println(f.toString() + "=" + f.get(null));
+ }
+ Trace.disableTracing(Thread.currentThread());
+ Trace.enableFieldTracing(
+ Test1984.class,
+ Test1984.class.getDeclaredMethod(
+ "notifyFieldAccess",
+ Executable.class,
+ Long.TYPE,
+ Class.class,
+ Object.class,
+ Field.class),
+ Test1984.class.getDeclaredMethod(
+ "notifyFieldModify",
+ Executable.class,
+ Long.TYPE,
+ Class.class,
+ Object.class,
+ Field.class,
+ Object.class),
+ Thread.currentThread());
+ for (Field f : Transform.class.getDeclaredFields()) {
+ Trace.watchFieldAccess(f);
+ Trace.watchFieldModification(f);
+ }
+ // count_down = 2
+ Transform.tick(); // count_down = 2
+ Transform.tick(); // count_down = 1
+ System.out.println("REDEFINING TRANSFORM CLASS");
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_BYTES);
+ Transform.tick(); // count_down = 1
+ Transform.tick(); // count_down = 0
+ System.out.println("Dumping fields at end");
+ for (Field f : Transform.class.getDeclaredFields()) {
+ System.out.println(f.toString() + "=" + f.get(null));
+ }
+ // Turn off tracing so we don't have to deal with print internals.
+ Trace.disableTracing(Thread.currentThread());
+ }
+}
diff --git a/test/1984-structural-redefine-field-trace/src/art/Trace.java b/test/1984-structural-redefine-field-trace/src/art/Trace.java
new file mode 120000
index 0000000000..5d9b44b463
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/src/art/Trace.java
@@ -0,0 +1 @@
+../../../jvmti-common/Trace.java \ No newline at end of file