Add more standard structural redefinition entrypoints
Add structural redefinition extension function and event that mirror
the 'RedefineClasses' function and 'ClassFileLoadHook' event. The new
extension function is called
'com.android.art.class.structurally_redefine_classes' and the new
extension event is called
'com.android.art.class.structural_dex_file_load_hook'.
These extensions are the preferred way to use structural redefinition.
Like the standard 'RedefineClasses' multiple classes may be redefined
at a time.
The structural_dex_file_load_hook is triggered prior to the
can_retransform_classes ClassFileLoadHook. It is triggered on all
classes, even ones that cannot be structurally changed by
class-loading, class redefinition or by calling the RetransformClasses
function.
Calling 'structurally_redefine_classes' with new definitions that do
not require structural changes will fall back to non-structural
redefinition.
Test: ./test.py --host
Bug: 134162467
Change-Id: If4810930470c5c6509cf6db779910006e114b39f
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 22822f8..23f7151 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -90,41 +90,42 @@
// Infrastructure to achieve type safety for event dispatch.
-#define FORALL_EVENT_TYPES(fn) \
- fn(VMInit, ArtJvmtiEvent::kVmInit) \
- fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
- fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
- fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
- fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
- fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
- fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
- fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
- fn(VMStart, ArtJvmtiEvent::kVmStart) \
- fn(Exception, ArtJvmtiEvent::kException) \
- fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
- fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
- fn(FramePop, ArtJvmtiEvent::kFramePop) \
- fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
- fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
- fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
- fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
- fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
- fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
- fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
- fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
- fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
- fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
- fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
- fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
- fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
- fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
- fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
- fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
- fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
- fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
- fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \
- fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) \
- fn(ObsoleteObjectCreated, ArtJvmtiEvent::kObsoleteObjectCreated)
+#define FORALL_EVENT_TYPES(fn) \
+ fn(VMInit, ArtJvmtiEvent::kVmInit) \
+ fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
+ fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
+ fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
+ fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
+ fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
+ fn(VMStart, ArtJvmtiEvent::kVmStart) \
+ fn(Exception, ArtJvmtiEvent::kException) \
+ fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
+ fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
+ fn(FramePop, ArtJvmtiEvent::kFramePop) \
+ fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
+ fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
+ fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
+ fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
+ fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
+ fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
+ fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
+ fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
+ fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
+ fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
+ fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
+ fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
+ fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
+ fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
+ fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
+ fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
+ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
+ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
+ fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \
+ fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) \
+ fn(ObsoleteObjectCreated, ArtJvmtiEvent::kObsoleteObjectCreated) \
+ fn(StructuralDexFileLoadHook, ArtJvmtiEvent::kStructuralDexFileLoadHook)
template <ArtJvmtiEvent kEvent>
struct EventFnType {
@@ -217,7 +218,8 @@
unsigned char** new_class_data) const {
art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
- kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable ||
+ kEvent == ArtJvmtiEvent::kStructuralDexFileLoadHook, "Unsupported event");
DCHECK(*new_class_data == nullptr);
jint current_len = class_data_len;
unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
@@ -588,6 +590,31 @@
new_class_data);
}
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ art::Thread* thread,
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+
template <ArtJvmtiEvent kEvent>
inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const {
bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 3f205eb..56406fc 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -100,6 +100,9 @@
case static_cast<jint>(ArtJvmtiEvent::kDdmPublishChunk):
DdmPublishChunk = reinterpret_cast<ArtJvmtiEventDdmPublishChunk>(cb);
return OK;
+ case static_cast<jint>(ArtJvmtiEvent::kStructuralDexFileLoadHook):
+ StructuralDexFileLoadHook = reinterpret_cast<ArtJvmtiEventStructuralDexFileLoadHook>(cb);
+ return OK;
default:
return ERR(ILLEGAL_ARGUMENT);
}
@@ -116,6 +119,7 @@
switch (e) {
case ArtJvmtiEvent::kDdmPublishChunk:
case ArtJvmtiEvent::kObsoleteObjectCreated:
+ case ArtJvmtiEvent::kStructuralDexFileLoadHook:
return true;
default:
return false;
@@ -1175,6 +1179,7 @@
case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
case ArtJvmtiEvent::kDdmPublishChunk:
case ArtJvmtiEvent::kObsoleteObjectCreated:
+ case ArtJvmtiEvent::kStructuralDexFileLoadHook:
return DeoptRequirement::kNone;
}
}
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index d5ab4fb..c9d587a 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -81,7 +81,8 @@
kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1,
kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2,
kObsoleteObjectCreated = JVMTI_MAX_EVENT_TYPE_VAL + 3,
- kMaxNormalEventTypeVal = kObsoleteObjectCreated,
+ kStructuralDexFileLoadHook = JVMTI_MAX_EVENT_TYPE_VAL + 4,
+ kMaxNormalEventTypeVal = kStructuralDexFileLoadHook,
// All that follow are events used to implement internal JVMTI functions. They are not settable
// directly by agents.
@@ -107,6 +108,17 @@
jlong* obsolete_tag,
jlong* new_tag);
+using ArtJvmtiEventStructuralDexFileLoadHook = void (*)(jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint dex_data_len,
+ const unsigned char* dex_data,
+ jint* new_dex_data_len,
+ unsigned char** new_dex_data);
+
// It is not enough to store a Thread pointer, as these may be reused. Use the pointer and the
// thread id.
// Note: We could just use the tid like tracing does.
@@ -119,7 +131,10 @@
};
struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks {
- ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr), ObsoleteObjectCreated(nullptr) {
+ ArtJvmtiEventCallbacks()
+ : DdmPublishChunk(nullptr),
+ ObsoleteObjectCreated(nullptr),
+ StructuralDexFileLoadHook(nullptr) {
memset(this, 0, sizeof(jvmtiEventCallbacks));
}
@@ -131,6 +146,7 @@
ArtJvmtiEventDdmPublishChunk DdmPublishChunk;
ArtJvmtiEventObsoleteObjectCreated ObsoleteObjectCreated;
+ ArtJvmtiEventStructuralDexFileLoadHook StructuralDexFileLoadHook;
};
bool IsExtensionEvent(jint e);
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 988274b..82ce916 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -204,6 +204,9 @@
memcpy(post_non_retransform.data(), def.GetDexData().data(), post_non_retransform.size());
}
+ // Call all structural transformation agents.
+ Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ event_handler, self, &def);
// Call all retransformable agents.
Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
event_handler, self, &def);
diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h
index 224e664..cb0853b 100644
--- a/openjdkjvmti/ti_class_definition.h
+++ b/openjdkjvmti/ti_class_definition.h
@@ -40,6 +40,7 @@
#include "base/array_ref.h"
#include "base/mem_map.h"
+#include "events.h"
namespace openjdkjvmti {
@@ -65,7 +66,8 @@
current_dex_file_(),
redefined_(false),
from_class_ext_(false),
- initialized_(false) {}
+ initialized_(false),
+ structural_transform_update_(false) {}
void InitFirstLoad(const char* descriptor,
art::Handle<art::mirror::ClassLoader> klass_loader,
@@ -76,7 +78,7 @@
ArtClassDefinition(ArtClassDefinition&& o) = default;
ArtClassDefinition& operator=(ArtClassDefinition&& o) = default;
- void SetNewDexData(jint new_dex_len, unsigned char* new_dex_data) {
+ void SetNewDexData(jint new_dex_len, unsigned char* new_dex_data, ArtJvmtiEvent event) {
DCHECK(IsInitialized());
if (new_dex_data == nullptr) {
return;
@@ -86,10 +88,17 @@
dex_data_memory_.resize(new_dex_len);
memcpy(dex_data_memory_.data(), new_dex_data, new_dex_len);
dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+ if (event == ArtJvmtiEvent::kStructuralDexFileLoadHook) {
+ structural_transform_update_ = true;
+ }
}
}
}
+ bool HasStructuralChanges() const {
+ return structural_transform_update_;
+ }
+
art::ArrayRef<const unsigned char> GetNewOriginalDexFile() const {
DCHECK(IsInitialized());
if (redefined_) {
@@ -187,6 +196,9 @@
bool initialized_;
+ // Set if we had a new dex from the given transform type.
+ bool structural_transform_update_;
+
DISALLOW_COPY_AND_ASSIGN(ArtClassDefinition);
};
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index 5dc7445..058a188 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -30,6 +30,7 @@
#include <vector>
+#include "jvmti.h"
#include "ti_extension.h"
#include "art_jvmti.h"
@@ -45,6 +46,7 @@
#include "ti_monitor.h"
#include "ti_redefine.h"
#include "ti_search.h"
+#include "transform.h"
#include "thread-inl.h"
@@ -416,7 +418,40 @@
return error;
}
- // StructurallyRedefineClass
+ // StructurallyRedefineClasses
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(Redefiner::StructurallyRedefineClasses),
+ "com.android.art.class.structurally_redefine_classes",
+ "Entrypoint for structural class redefinition. Has the same signature as RedefineClasses."
+ " Currently this only supports adding new static fields to a class without any instance"
+ " fields or methods. After calling this com.android.art.structural_dex_file_load_hook"
+ " events will be triggered, followed by re-transformable ClassFileLoadHook events. After"
+ " this method completes subsequent RetransformClasses calls will use the input to this"
+ " function as the initial class definition.",
+ {
+ { "num_classes", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
+ { "class_definitions", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CVOID, false },
+ },
+ {
+ ERR(CLASS_LOADER_UNSUPPORTED),
+ ERR(FAILS_VERIFICATION),
+ ERR(ILLEGAL_ARGUMENT),
+ ERR(INVALID_CLASS),
+ ERR(MUST_POSSESS_CAPABILITY),
+ ERR(MUST_POSSESS_CAPABILITY),
+ ERR(NULL_POINTER),
+ ERR(OUT_OF_MEMORY),
+ ERR(UNMODIFIABLE_CLASS),
+ ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED),
+ ERR(UNSUPPORTED_REDEFINITION_METHOD_ADDED),
+ ERR(UNSUPPORTED_REDEFINITION_METHOD_DELETED),
+ ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
+ // StructurallyRedefineClassDirect
error = add_extension(
reinterpret_cast<jvmtiExtensionFunction>(Redefiner::StructurallyRedefineClassDirect),
"com.android.art.UNSAFE.class.structurally_redefine_class_direct",
@@ -494,7 +529,7 @@
const char* id,
const char* short_description,
const std::vector<CParamInfo>& params) {
- DCHECK(IsExtensionEvent(extension_event_index));
+ DCHECK(IsExtensionEvent(extension_event_index)) << static_cast<jint>(extension_event_index);
jvmtiExtensionEventInfo event_info;
jvmtiError error;
@@ -592,7 +627,35 @@
if (error != OK) {
return error;
}
-
+ art::Runtime* runtime = art::Runtime::Current();
+ if (runtime->GetJniIdType() == art::JniIdType::kIndices &&
+ (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) {
+ error = add_extension(
+ ArtJvmtiEvent::kStructuralDexFileLoadHook,
+ "com.android.art.class.structural_dex_file_load_hook",
+ "Called during class load, after a 'RetransformClasses' call, or after a 'RedefineClasses'"
+ " call in order to allow the agent to modify the class. This event is called after any"
+ " non-can_retransform_classes ClassFileLoadHookEvents and before any"
+ " can_retransform_classes ClassFileLoadHookEvents. The transformations applied are"
+ " restricted in the same way that transformations applied via the "
+ " 'com.android.art.class.structurally_redefine_classes' extension function. The arguments"
+ " to the event are identical to the ones in the ClassFileLoadHook and have the same"
+ " semantics.",
+ {
+ { "jni_env", JVMTI_KIND_IN, JVMTI_TYPE_JNIENV, false },
+ { "class_being_redefined", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true },
+ { "loader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, false },
+ { "name", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CCHAR, false },
+ { "protection_domain", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, true },
+ { "dex_data_len", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
+ { "dex_data", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CCHAR, false },
+ { "new_dex_data_len", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
+ { "new_dex_data", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, true },
+ });
+ } else {
+ LOG(INFO) << "debuggable & jni-type indices are required to implement structural "
+ << "class redefinition extensions.";
+ }
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 05d7de7..22a3bc5 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -376,7 +376,7 @@
return ERR(INVALID_CLASS);
}
art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
- return Redefiner::GetClassRedefinitionError(h_klass, error_msg);
+ return Redefiner::GetClassRedefinitionError<kType>(h_klass, error_msg);
}
template <RedefinitionType kType>
@@ -574,9 +574,10 @@
}
}
-jvmtiError Redefiner::RedefineClasses(jvmtiEnv* jenv,
- jint class_count,
- const jvmtiClassDefinition* definitions) {
+template<RedefinitionType kType>
+jvmtiError Redefiner::RedefineClassesGeneric(jvmtiEnv* jenv,
+ jint class_count,
+ const jvmtiClassDefinition* definitions) {
art::Runtime* runtime = art::Runtime::Current();
art::Thread* self = art::Thread::Current();
ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
@@ -597,7 +598,8 @@
std::vector<ArtClassDefinition> def_vector;
def_vector.reserve(class_count);
for (jint i = 0; i < class_count; i++) {
- jvmtiError res = Redefiner::GetClassRedefinitionError(definitions[i].klass, &error_msg);
+ jvmtiError res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(
+ definitions[i].klass, &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO REDEFINE " << error_msg;
return res;
@@ -611,15 +613,35 @@
def_vector.push_back(std::move(def));
}
// Call all the transformation events.
- Transformer::RetransformClassesDirect(self, &def_vector);
- jvmtiError res = RedefineClassesDirect(
- env, runtime, self, def_vector, RedefinitionType::kNormal, &error_msg);
+ Transformer::RetransformClassesDirect<kType>(self, &def_vector);
+ if (kType == RedefinitionType::kStructural) {
+ Transformer::RetransformClassesDirect<RedefinitionType::kNormal>(self, &def_vector);
+ }
+ jvmtiError res = RedefineClassesDirect(env, runtime, self, def_vector, kType, &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO REDEFINE " << error_msg;
}
return res;
}
+jvmtiError Redefiner::StructurallyRedefineClasses(jvmtiEnv* jenv,
+ jint class_count,
+ const jvmtiClassDefinition* definitions) {
+ ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (art_env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (art_env->capabilities.can_redefine_classes != 1) {
+ return ERR(MUST_POSSESS_CAPABILITY);
+ }
+ return RedefineClassesGeneric<RedefinitionType::kStructural>(jenv, class_count, definitions);
+}
+
+jvmtiError Redefiner::RedefineClasses(jvmtiEnv* jenv,
+ jint class_count,
+ const jvmtiClassDefinition* definitions) {
+ return RedefineClassesGeneric<RedefinitionType::kNormal>(jenv, class_count, definitions);
+}
+
jvmtiError Redefiner::StructurallyRedefineClassDirect(jvmtiEnv* env,
jclass klass,
const unsigned char* data,
@@ -1154,13 +1176,10 @@
art::Handle<art::mirror::Class> h_klass(hs.NewHandle(GetMirrorClass()));
jvmtiError res;
- switch (driver_->type_) {
- case RedefinitionType::kNormal:
- res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(h_klass, &err);
- break;
- case RedefinitionType::kStructural:
+ if (driver_->type_ == RedefinitionType::kStructural && this->IsStructuralRedefinition()) {
res = Redefiner::GetClassRedefinitionError<RedefinitionType::kStructural>(h_klass, &err);
- break;
+ } else {
+ res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(h_klass, &err);
}
if (res != OK) {
RecordFailure(res, err);
@@ -1171,7 +1190,7 @@
}
bool Redefiner::ClassRedefinition::CheckRedefinitionIsValid() {
- return CheckRedefinable() && CheckClass() && CheckFields() && CheckMethods();
+ return CheckClass() && CheckFields() && CheckMethods() && CheckRedefinable();
}
class RedefinitionDataIter;
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index b028dee..58a688c 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -87,6 +87,9 @@
static jvmtiError RedefineClasses(jvmtiEnv* env,
jint class_count,
const jvmtiClassDefinition* definitions);
+ static jvmtiError StructurallyRedefineClasses(jvmtiEnv* env,
+ jint class_count,
+ const jvmtiClassDefinition* definitions);
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
static jvmtiError IsStructurallyModifiableClass(jvmtiEnv* env,
@@ -287,6 +290,11 @@
REQUIRES_SHARED(art::Locks::mutator_lock_);
template<RedefinitionType kType = RedefinitionType::kNormal>
+ static jvmtiError RedefineClassesGeneric(jvmtiEnv* env,
+ jint class_count,
+ const jvmtiClassDefinition* definitions);
+
+ template<RedefinitionType kType = RedefinitionType::kNormal>
static jvmtiError IsModifiableClassGeneric(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
template<RedefinitionType kType = RedefinitionType::kNormal>
diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc
index aa37793..6133685 100644
--- a/openjdkjvmti/transform.cc
+++ b/openjdkjvmti/transform.cc
@@ -255,13 +255,17 @@
template
void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
+template
+void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
template<ArtJvmtiEvent kEvent>
void Transformer::TransformSingleClassDirect(EventHandler* event_handler,
art::Thread* self,
/*in-out*/ArtClassDefinition* def) {
static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable ||
- kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable,
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ kEvent == ArtJvmtiEvent::kStructuralDexFileLoadHook,
"bad event type");
// We don't want to do transitions between calling the event and setting the new data so change to
// native state early. This also avoids any problems that the FaultHandler might have in
@@ -282,25 +286,30 @@
dex_data.data(),
/*out*/&new_len,
/*out*/&new_data);
- def->SetNewDexData(new_len, new_data);
+ def->SetNewDexData(new_len, new_data, kEvent);
}
+template <RedefinitionType kType>
void Transformer::RetransformClassesDirect(
- art::Thread* self,
- /*in-out*/std::vector<ArtClassDefinition>* definitions) {
+ art::Thread* self,
+ /*in-out*/ std::vector<ArtClassDefinition>* definitions) {
+ constexpr ArtJvmtiEvent kEvent = kType == RedefinitionType::kNormal
+ ? ArtJvmtiEvent::kClassFileLoadHookRetransformable
+ : ArtJvmtiEvent::kStructuralDexFileLoadHook;
for (ArtClassDefinition& def : *definitions) {
- TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
- gEventHandler, self, &def);
+ TransformSingleClassDirect<kEvent>(gEventHandler, self, &def);
}
}
+template void Transformer::RetransformClassesDirect<RedefinitionType::kNormal>(
+ art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions);
+template void Transformer::RetransformClassesDirect<RedefinitionType::kStructural>(
+ art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions);
+
jvmtiError Transformer::RetransformClasses(jvmtiEnv* env,
jint class_count,
const jclass* classes) {
- if (env == nullptr) {
- JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM env was null!";
- return ERR(INVALID_ENVIRONMENT);
- } else if (class_count < 0) {
+ if (class_count < 0) {
JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM class_count was less then 0";
return ERR(ILLEGAL_ARGUMENT);
} else if (class_count == 0) {
@@ -317,7 +326,7 @@
std::vector<ArtClassDefinition> definitions;
jvmtiError res = OK;
for (jint i = 0; i < class_count; i++) {
- res = Redefiner::GetClassRedefinitionError(classes[i], &error_msg);
+ res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(classes[i], &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM " << error_msg;
return res;
@@ -330,13 +339,16 @@
}
definitions.push_back(std::move(def));
}
- RetransformClassesDirect(self, &definitions);
- res = Redefiner::RedefineClassesDirect(ArtJvmTiEnv::AsArtJvmTiEnv(env),
- runtime,
- self,
- definitions,
- RedefinitionType::kNormal,
- &error_msg);
+ RetransformClassesDirect<RedefinitionType::kStructural>(self, &definitions);
+ RetransformClassesDirect<RedefinitionType::kNormal>(self, &definitions);
+ RedefinitionType redef_type =
+ std::any_of(definitions.cbegin(),
+ definitions.cend(),
+ [](const auto& it) { return it.HasStructuralChanges(); })
+ ? RedefinitionType::kStructural
+ : RedefinitionType::kNormal;
+ res = Redefiner::RedefineClassesDirect(
+ ArtJvmTiEnv::AsArtJvmTiEnv(env), runtime, self, definitions, redef_type, &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM " << error_msg;
}
diff --git a/openjdkjvmti/transform.h b/openjdkjvmti/transform.h
index 40c7267..a58b50e 100644
--- a/openjdkjvmti/transform.h
+++ b/openjdkjvmti/transform.h
@@ -39,6 +39,7 @@
#include "art_jvmti.h"
#include "ti_class_definition.h"
+#include "ti_redefine.h"
namespace openjdkjvmti {
@@ -56,6 +57,7 @@
art::Thread* self,
/*in-out*/ArtClassDefinition* def);
+ template<RedefinitionType kType>
static void RetransformClassesDirect(
art::Thread* self,
/*in-out*/std::vector<ArtClassDefinition>* definitions);