mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-8850 DCEVM: Enable interface replacement
This commit is contained in:
@@ -34,17 +34,31 @@
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
#include "utilities/copy.hpp"
|
||||
#include "runtime/fieldDescriptor.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
|
||||
DcevmSharedGC* DcevmSharedGC::_static_instance = nullptr;
|
||||
|
||||
void DcevmSharedGC::create_static_instance() {
|
||||
_static_instance = new DcevmSharedGC();
|
||||
}
|
||||
|
||||
void DcevmSharedGC::destroy_static_instance() {
|
||||
if (_static_instance != nullptr) {
|
||||
delete _static_instance;
|
||||
_static_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, bool must_be_new) {
|
||||
if (rescued_oops != NULL) {
|
||||
if (rescued_oops != nullptr) {
|
||||
copy_rescued_objects_back(rescued_oops, 0, rescued_oops->length(), must_be_new);
|
||||
}
|
||||
}
|
||||
|
||||
// (DCEVM) Copy the rescued objects to their destination address after compaction.
|
||||
void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, int from, int to, bool must_be_new) {
|
||||
|
||||
if (rescued_oops != NULL) {
|
||||
if (rescued_oops != nullptr) {
|
||||
for (int i=from; i < to; i++) {
|
||||
HeapWord* rescued_ptr = rescued_oops->at(i);
|
||||
oop rescued_obj = cast_to_oop(rescued_ptr);
|
||||
@@ -52,10 +66,10 @@ void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_
|
||||
size_t size = rescued_obj->size();
|
||||
oop new_obj = rescued_obj->forwardee();
|
||||
|
||||
assert(!must_be_new || rescued_obj->klass()->new_version() != NULL, "Just checking");
|
||||
assert(!must_be_new || rescued_obj->klass()->new_version() != nullptr, "Just checking");
|
||||
Klass* new_klass = rescued_obj->klass()->new_version();
|
||||
if (new_klass!= NULL) {
|
||||
if (new_klass->update_information() != NULL) {
|
||||
if (new_klass!= nullptr) {
|
||||
if (new_klass->update_information() != nullptr) {
|
||||
DcevmSharedGC::update_fields(rescued_obj, new_obj);
|
||||
} else {
|
||||
rescued_obj->set_klass(new_klass);
|
||||
@@ -69,11 +83,10 @@ void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_
|
||||
assert(oopDesc::is_oop(new_obj), "must be a valid oop");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DcevmSharedGC::clear_rescued_objects_resource(GrowableArray<HeapWord*>* rescued_oops) {
|
||||
if (rescued_oops != NULL) {
|
||||
if (rescued_oops != nullptr) {
|
||||
for (int i=0; i < rescued_oops->length(); i++) {
|
||||
HeapWord* rescued_ptr = rescued_oops->at(i);
|
||||
size_t size = cast_to_oop(rescued_ptr)->size();
|
||||
@@ -84,7 +97,7 @@ void DcevmSharedGC::clear_rescued_objects_resource(GrowableArray<HeapWord*>* res
|
||||
}
|
||||
|
||||
void DcevmSharedGC::clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued_oops) {
|
||||
if (rescued_oops != NULL) {
|
||||
if (rescued_oops != nullptr) {
|
||||
for (int i=0; i < rescued_oops->length(); i++) {
|
||||
HeapWord* rescued_ptr = rescued_oops->at(i);
|
||||
FREE_C_HEAP_ARRAY(HeapWord, rescued_ptr);
|
||||
@@ -96,7 +109,7 @@ void DcevmSharedGC::clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued
|
||||
// (DCEVM) Update instances of a class whose fields changed.
|
||||
void DcevmSharedGC::update_fields(oop q, oop new_location) {
|
||||
|
||||
assert(q->klass()->new_version() != NULL, "class of old object must have new version");
|
||||
assert(q->klass()->new_version() != nullptr, "class of old object must have new version");
|
||||
|
||||
Klass* old_klass_oop = q->klass();
|
||||
Klass* new_klass_oop = q->klass()->new_version();
|
||||
@@ -107,7 +120,7 @@ void DcevmSharedGC::update_fields(oop q, oop new_location) {
|
||||
size_t size = q->size_given_klass(old_klass);
|
||||
size_t new_size = q->size_given_klass(new_klass);
|
||||
|
||||
HeapWord* tmp = NULL;
|
||||
HeapWord* tmp = nullptr;
|
||||
oop tmp_obj = q;
|
||||
|
||||
// Save object somewhere, there is an overlap in fields
|
||||
@@ -122,39 +135,141 @@ void DcevmSharedGC::update_fields(oop q, oop new_location) {
|
||||
|
||||
q->set_klass(new_klass_oop);
|
||||
int *cur = new_klass_oop->update_information();
|
||||
assert(cur != NULL, "just checking");
|
||||
DcevmSharedGC::update_fields(new_location, q, cur);
|
||||
assert(cur != nullptr, "just checking");
|
||||
_static_instance->update_fields(new_location, q, cur, false);
|
||||
|
||||
if (tmp != NULL) {
|
||||
if (tmp != nullptr) {
|
||||
FREE_RESOURCE_ARRAY(HeapWord, tmp, size);
|
||||
}
|
||||
}
|
||||
|
||||
void DcevmSharedGC::update_fields(oop new_location, oop tmp_obj, int *cur) {
|
||||
assert(cur != NULL, "just checking");
|
||||
char* to = (char*)cast_from_oop<HeapWord*>(new_location);
|
||||
void DcevmSharedGC::update_fields(oop new_obj, oop old_obj, int* cur, bool do_compat_check) {
|
||||
assert(cur != nullptr, "just checking");
|
||||
char* to = (char*)cast_from_oop<HeapWord*>(new_obj);
|
||||
char* src_base = (char *)cast_from_oop<HeapWord*>(old_obj);
|
||||
while (*cur != 0) {
|
||||
int size = *cur;
|
||||
if (size > 0) {
|
||||
int raw = *cur;
|
||||
if (raw > 0) {
|
||||
cur++;
|
||||
int offset = *cur;
|
||||
HeapWord* from = (HeapWord*)(((char *)cast_from_oop<HeapWord*>(tmp_obj)) + offset);
|
||||
if (size == HeapWordSize) {
|
||||
*((HeapWord*)to) = *from;
|
||||
} else if (size == HeapWordSize * 2) {
|
||||
*((HeapWord*)to) = *from;
|
||||
*(((HeapWord*)to) + 1) = *(from + 1);
|
||||
int src_offset = *cur;
|
||||
HeapWord* from = (HeapWord*)(src_base + src_offset);
|
||||
|
||||
bool compat_check = do_compat_check && ((raw & UpdateInfoCompatFlag) != 0);
|
||||
int size = (raw & UpdateInfoLengthMask);
|
||||
|
||||
if (!compat_check) {
|
||||
if (size == HeapWordSize) {
|
||||
*((HeapWord *) to) = *from;
|
||||
} else if (size == HeapWordSize * 2) {
|
||||
*((HeapWord *) to) = *from;
|
||||
*(((HeapWord *) to) + 1) = *(from + 1);
|
||||
} else {
|
||||
Copy::conjoint_jbytes(from, to, size);
|
||||
}
|
||||
} else {
|
||||
Copy::conjoint_jbytes(from, to, size);
|
||||
assert(size == heapOopSize, "Must be one oop");
|
||||
int dst_offset = (int)(to - (char*)cast_from_oop<HeapWord*>(new_obj));
|
||||
|
||||
oop obj = old_obj->obj_field(src_offset);
|
||||
|
||||
if (obj == nullptr) {
|
||||
new_obj->obj_field_put(dst_offset, nullptr);
|
||||
} else {
|
||||
bool compatible = is_compatible(new_obj, dst_offset, obj);
|
||||
new_obj->obj_field_put(dst_offset, compatible ? obj : (oop)nullptr);
|
||||
}
|
||||
}
|
||||
to += size;
|
||||
cur++;
|
||||
} else {
|
||||
assert(size < 0, "");
|
||||
int skip = -*cur;
|
||||
Copy::fill_to_bytes(to, skip, 0);
|
||||
to += skip;
|
||||
Copy::fill_to_bytes(to, -raw, 0);
|
||||
to += -raw;
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DcevmSharedGC::update_fields_in_old(oop old_obj, int* cur) {
|
||||
assert(cur != nullptr, "just checking");
|
||||
int dst_offset = 0;
|
||||
while (*cur != 0) {
|
||||
int raw = *cur;
|
||||
if (raw > 0) {
|
||||
cur++;
|
||||
int size = (raw & UpdateInfoLengthMask);
|
||||
|
||||
if ((raw & UpdateInfoCompatFlag) != 0) {
|
||||
assert(size == heapOopSize, "Must be one oop");
|
||||
int src_offset = *cur;
|
||||
oop obj = old_obj->obj_field(src_offset);
|
||||
if (obj != nullptr) {
|
||||
bool compatible = is_compatible(old_obj, dst_offset, obj);
|
||||
if (!compatible) {
|
||||
old_obj->obj_field_put(src_offset, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
dst_offset += size;
|
||||
cur++;
|
||||
} else {
|
||||
dst_offset += -raw;
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool signature_matches_name(Symbol* sig, Symbol* name) {
|
||||
const int sig_len = sig->utf8_length();
|
||||
const int name_len = name->utf8_length();
|
||||
if (sig_len != name_len + 2) {
|
||||
return false;
|
||||
}
|
||||
const u1* s = sig ->bytes();
|
||||
const u1* n = name->bytes();
|
||||
return (s[0] == 'L' && s[sig_len - 1] == ';' && memcmp(s + 1, n, name_len) == 0);
|
||||
}
|
||||
|
||||
bool DcevmSharedGC::is_compatible(oop fld_holder, int fld_offset, oop fld_val) {
|
||||
assert(oopDesc::is_oop(fld_val), "val has corrupted header");
|
||||
|
||||
bool result = false;
|
||||
Symbol *sig_wanted;
|
||||
|
||||
InstanceKlass* holder_ik = InstanceKlass::cast(fld_holder->klass()->newest_version());
|
||||
Symbol** sig_cached = _field_sig_table->get({holder_ik, fld_offset});
|
||||
|
||||
if (sig_cached != nullptr) {
|
||||
sig_wanted = *sig_cached;
|
||||
} else {
|
||||
fieldDescriptor fd_new;
|
||||
bool ok = holder_ik->find_field_from_offset(fld_offset, false, &fd_new);
|
||||
assert(ok, "Must exist");
|
||||
sig_wanted = fd_new.signature();
|
||||
_field_sig_table->put({holder_ik, fld_offset}, sig_wanted);
|
||||
}
|
||||
|
||||
InstanceKlass *ik = InstanceKlass::cast(fld_val->klass()->newest_version());
|
||||
bool* hit = _compat_table->get({ik, sig_wanted });
|
||||
|
||||
if (hit != nullptr) {
|
||||
result = *hit;
|
||||
} else {
|
||||
InstanceKlass* scan = ik;
|
||||
while (scan != nullptr && !result) {
|
||||
if (signature_matches_name(sig_wanted, scan->name())) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
Array<InstanceKlass*>* ifaces = scan->local_interfaces();
|
||||
for (int j = 0; j < ifaces->length(); ++j) {
|
||||
if (signature_matches_name(sig_wanted, ifaces->at(j)->name())) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
scan = (scan->super() != nullptr) ? InstanceKlass::cast(scan->super()) : nullptr;
|
||||
}
|
||||
_compat_table->put({ik, sig_wanted }, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -33,16 +33,76 @@
|
||||
#include "runtime/timer.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/stack.hpp"
|
||||
#include "utilities/resourceHash.hpp"
|
||||
|
||||
// Shared GC code used from different GC (Serial, CMS, G1) on enhanced redefinition
|
||||
class DcevmSharedGC : AllStatic {
|
||||
public:
|
||||
class DcevmSharedGC : public CHeapObj<mtInternal> {
|
||||
private:
|
||||
struct SymbolKey {
|
||||
InstanceKlass* ik;
|
||||
Symbol* dst_sig;
|
||||
};
|
||||
static unsigned symbol_hash(const SymbolKey& k) {
|
||||
return (uintptr_t)k.ik ^ (uintptr_t)k.dst_sig;
|
||||
}
|
||||
static bool symbol_eq(const SymbolKey& a, const SymbolKey& b) {
|
||||
return a.ik == b.ik && a.dst_sig == b.dst_sig;
|
||||
}
|
||||
typedef ResourceHashtable<SymbolKey, bool, 512,
|
||||
AnyObj::C_HEAP, mtInternal,
|
||||
&symbol_hash, &symbol_eq> CompatTable;
|
||||
|
||||
struct OffsetKey {
|
||||
InstanceKlass* ik;
|
||||
int offset;
|
||||
};
|
||||
static unsigned offset_hash(const OffsetKey& k) {
|
||||
return uintptr_t(k.ik) ^ k.offset;
|
||||
}
|
||||
static bool offset_eq(const OffsetKey& a, const OffsetKey& b) {
|
||||
return a.ik == b.ik && a.offset == b.offset;
|
||||
}
|
||||
typedef ResourceHashtable<OffsetKey, Symbol*, 512,
|
||||
AnyObj::C_HEAP, mtInternal,
|
||||
&offset_hash, &offset_eq> FieldSigTable;
|
||||
|
||||
CompatTable* _compat_table;
|
||||
FieldSigTable* _field_sig_table;
|
||||
static DcevmSharedGC* _static_instance;
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------
|
||||
// update info flags
|
||||
//
|
||||
// bit 31 : sign bit (<0 = fill, >0 = copy)
|
||||
// bit 30 : UpdateInfoCompatFlag – copy segment requires per-oop compatibility check
|
||||
// bits 0-29 : raw byte length of the segment
|
||||
// ------------------------------------------------------------------
|
||||
static const int UpdateInfoCompatFlag = 1U << 30;
|
||||
static const int UpdateInfoLengthMask = ~(1U << 31 | UpdateInfoCompatFlag);
|
||||
|
||||
DcevmSharedGC() {
|
||||
_compat_table = new (mtInternal) CompatTable();
|
||||
_field_sig_table = new (mtInternal) FieldSigTable();
|
||||
}
|
||||
|
||||
~DcevmSharedGC() {
|
||||
delete _compat_table;
|
||||
delete _field_sig_table;
|
||||
}
|
||||
|
||||
static void create_static_instance();
|
||||
static void destroy_static_instance();
|
||||
|
||||
static void copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, bool must_be_new);
|
||||
static void copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, int from, int to, bool must_be_new);
|
||||
static void clear_rescued_objects_resource(GrowableArray<HeapWord*>* rescued_oops);
|
||||
static void clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued_oops);
|
||||
static void update_fields(oop q, oop new_location);
|
||||
static void update_fields(oop new_location, oop tmp_obj, int *cur);
|
||||
|
||||
bool is_compatible(oop fld_holder, int fld_offset, oop fld_val);
|
||||
void update_fields(oop new_obj, oop old_obj, int *cur, bool do_compat_check);
|
||||
void update_fields_in_old(oop old_obj, int *cur);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1787,6 +1787,18 @@ bool InstanceKlass::find_field_from_offset(int offset, bool is_static, fieldDesc
|
||||
}
|
||||
|
||||
|
||||
bool InstanceKlass::find_local_field_by_name(Symbol* name, fieldDescriptor* fd) const {
|
||||
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
|
||||
Symbol* f_name = fs.name();
|
||||
if (f_name == name) {
|
||||
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.index());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void InstanceKlass::methods_do(void f(Method* method)) {
|
||||
// Methods aren't stable until they are loaded. This can be read outside
|
||||
// a lock through the ClassLoaderData for profiling
|
||||
|
||||
@@ -566,6 +566,7 @@ public:
|
||||
bool find_local_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const;
|
||||
bool find_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const;
|
||||
|
||||
bool find_local_field_by_name(Symbol* name, fieldDescriptor* fd) const;
|
||||
private:
|
||||
inline static int quick_search(const Array<Method*>* methods, const Symbol* name);
|
||||
|
||||
|
||||
@@ -196,13 +196,14 @@ void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word
|
||||
// The constructor is also used from CppVtableCloner,
|
||||
// which doesn't zero out the memory before calling the constructor.
|
||||
Klass::Klass(KlassKind kind) : _kind(kind),
|
||||
_old_version(NULL),
|
||||
_new_version(NULL),
|
||||
_old_version(nullptr),
|
||||
_new_version(nullptr),
|
||||
_redefinition_flags(Klass::NoRedefinition),
|
||||
_is_redefining(false),
|
||||
_update_information(NULL),
|
||||
_update_information(nullptr),
|
||||
_is_copying_backwards(false),
|
||||
_is_rolled_back(false),
|
||||
_compat_check_field_offsets(nullptr),
|
||||
_shared_class_path_index(-1) {
|
||||
CDS_ONLY(_shared_class_flags = 0;)
|
||||
CDS_JAVA_HEAP_ONLY(_archived_mirror_index = -1;)
|
||||
|
||||
@@ -167,18 +167,18 @@ class Klass : public Metadata {
|
||||
|
||||
JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
|
||||
|
||||
// Advanced class redefinition
|
||||
|
||||
// Enhanced class redefinition
|
||||
// Old version (used in advanced class redefinition)
|
||||
Klass* _old_version;
|
||||
// New version (used in advanced class redefinition)
|
||||
Klass* _new_version;
|
||||
|
||||
int _redefinition_flags; // Level of class redefinition
|
||||
bool _is_redefining;
|
||||
int* _update_information;
|
||||
bool _is_copying_backwards; // Does the class need to copy fields backwards? => possibly overwrite itself?
|
||||
bool _is_rolled_back; // true if class was rolled back in redefinition
|
||||
// offsets of fields used in compatibility check
|
||||
GrowableArray<int>* volatile _compat_check_field_offsets;
|
||||
|
||||
private:
|
||||
// This is an index into FileMapHeader::_shared_path_table[], to
|
||||
@@ -419,6 +419,13 @@ protected:
|
||||
void set_copying_backwards(bool b) { _is_copying_backwards = b; }
|
||||
bool is_rolled_back() { return _is_rolled_back; }
|
||||
void set_rolled_back(bool b) { _is_rolled_back = b;}
|
||||
GrowableArray<int>* compat_check_field_offsets() const { return Atomic::load_acquire(&_compat_check_field_offsets); }
|
||||
GrowableArray<int>* set_compat_check_field_offsets(GrowableArray<int>* offsets) {
|
||||
return Atomic::cmpxchg(&_compat_check_field_offsets, (GrowableArray<int>*) nullptr, offsets);
|
||||
}
|
||||
void clear_compat_check_field_offsets() {
|
||||
_compat_check_field_offsets = nullptr;
|
||||
}
|
||||
|
||||
protected: // internal accessors
|
||||
void set_subklass(Klass* s);
|
||||
@@ -431,7 +438,8 @@ protected:
|
||||
ModifyClassSize = ModifyClass << 1, // The size of the class meta data changes.
|
||||
ModifyInstances = ModifyClassSize << 1, // There are change to the instance format.
|
||||
ModifyInstanceSize = ModifyInstances << 1, // The size of instances changes.
|
||||
RemoveSuperType = ModifyInstanceSize << 1, // A super type of this class is removed.
|
||||
RemoveInterface = ModifyInstanceSize << 1, // A super type of this class is removed.
|
||||
RemoveSuperType = RemoveInterface << 1, // A super type of this class is removed.
|
||||
MarkedAsAffected = RemoveSuperType << 1, // This class has been marked as an affected class.
|
||||
PrimaryRedefine = MarkedAsAffected << 1 // This class is from primary redefinition set
|
||||
};
|
||||
|
||||
@@ -108,6 +108,7 @@ VM_EnhancedRedefineClasses::VM_EnhancedRedefineClasses(jint class_count, const j
|
||||
VM_GC_Operation(Universe::heap()->total_collections(), GCCause::_heap_inspection, Universe::heap()->total_full_collections(), true) {
|
||||
_new_classes = nullptr;
|
||||
_affected_klasses = nullptr;
|
||||
_removed_interfaces = nullptr;
|
||||
_class_count = class_count;
|
||||
_class_defs = class_defs;
|
||||
_class_load_kind = class_load_kind;
|
||||
@@ -128,7 +129,6 @@ static inline InstanceKlass* get_ik(jclass def) {
|
||||
// - Start mark&sweep GC.
|
||||
// - true if success, otherwise all chnages are rollbacked.
|
||||
bool VM_EnhancedRedefineClasses::doit_prologue() {
|
||||
|
||||
if (_class_count == 0) {
|
||||
_res = JVMTI_ERROR_NONE;
|
||||
return false;
|
||||
@@ -202,7 +202,36 @@ bool VM_EnhancedRedefineClasses::doit_prologue() {
|
||||
|
||||
// Closer for static fields - copy value from old class to the new class.
|
||||
class FieldCopier : public FieldClosure {
|
||||
public:
|
||||
private:
|
||||
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
|
||||
public:
|
||||
FieldCopier(VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
|
||||
: _removed_interfaces(removed_interfaces) {}
|
||||
|
||||
bool is_compatible(oop fld_holder, int fld_offset, Symbol* sig_new) {
|
||||
oop oop = fld_holder->obj_field(fld_offset);
|
||||
if (oop != nullptr) {
|
||||
Klass* k = oop->klass();
|
||||
if (k->is_instance_klass() && k->is_redefining()) {
|
||||
InstanceKlass *scan = InstanceKlass::cast(k);
|
||||
while (scan != nullptr) {
|
||||
if (sig_new->equals(scan->signature_name())) {
|
||||
return true;
|
||||
}
|
||||
Array<InstanceKlass*>* local_interfaces = scan->local_interfaces();
|
||||
for (int j = 0; j < local_interfaces->length(); j++) {
|
||||
Klass* iface = local_interfaces->at(j);
|
||||
if (sig_new->equals(iface->signature_name())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
scan = (scan->super() != nullptr) ? InstanceKlass::cast(scan->super()) : nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void do_field(fieldDescriptor* fd) {
|
||||
InstanceKlass* cur = InstanceKlass::cast(fd->field_holder());
|
||||
oop cur_oop = cur->java_mirror();
|
||||
@@ -211,19 +240,36 @@ class FieldCopier : public FieldClosure {
|
||||
oop old_oop = old->java_mirror();
|
||||
|
||||
fieldDescriptor result;
|
||||
bool found = old->find_local_field(fd->name(), fd->signature(), &result);
|
||||
Symbol* sig_new = fd->signature();
|
||||
bool found = old->find_local_field_by_name(fd->name(), &result);
|
||||
if (found && result.is_static()) {
|
||||
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
|
||||
fd->name()->as_C_string(), result.offset(), fd->offset());
|
||||
memcpy(cur_oop->field_addr<HeapWord>(fd->offset()),
|
||||
old_oop->field_addr<HeapWord>(result.offset()),
|
||||
type2aelembytes(fd->field_type()));
|
||||
Symbol* sig_old = result.signature();
|
||||
bool compatible = false;
|
||||
|
||||
// Static fields may have references to java.lang.Class
|
||||
if (fd->field_type() == T_OBJECT) {
|
||||
oop oop = cur_oop->obj_field(fd->offset());
|
||||
if (oop != nullptr && oop->is_instance() && InstanceKlass::cast(oop->klass())->is_mirror_instance_klass()) {
|
||||
Klass* klass = java_lang_Class::as_Klass(oop);
|
||||
if (sig_new == sig_old) {
|
||||
if (_removed_interfaces != nullptr && _removed_interfaces->contains(sig_old) && fd->field_type() == T_OBJECT && result.field_type() == T_OBJECT) {
|
||||
compatible = is_compatible(old_oop, result.offset(), sig_new);
|
||||
} else {
|
||||
compatible = true;
|
||||
}
|
||||
} else {
|
||||
if (fd->field_type() == T_OBJECT && result.field_type() == T_OBJECT) {
|
||||
compatible = is_compatible(old_oop, result.offset(), sig_new);
|
||||
}
|
||||
}
|
||||
|
||||
if (compatible) {
|
||||
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
|
||||
fd->name()->as_C_string(), result.offset(), fd->offset());
|
||||
memcpy(cur_oop->field_addr<HeapWord>(fd->offset()),
|
||||
old_oop->field_addr<HeapWord>(result.offset()),
|
||||
type2aelembytes(fd->field_type()));
|
||||
|
||||
// Static fields may have references to java.lang.Class
|
||||
if (fd->field_type() == T_OBJECT) {
|
||||
oop oop = cur_oop->obj_field(fd->offset());
|
||||
if (oop != nullptr && oop->is_instance() && InstanceKlass::cast(oop->klass())->is_mirror_instance_klass()) {
|
||||
Klass *klass = java_lang_Class::as_Klass(oop);
|
||||
if (klass != nullptr && klass->is_instance_klass()) {
|
||||
assert(oop == InstanceKlass::cast(klass)->java_mirror(), "just checking");
|
||||
if (klass->new_version() != nullptr) {
|
||||
@@ -233,10 +279,36 @@ class FieldCopier : public FieldClosure {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_trace(redefine,class, obsolete, metadata)("Skipping incompatible static field %s, old_signature=%s, new_signature=%s",
|
||||
fd->name()->as_C_string(), sig_old->as_C_string(), sig_new->as_C_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FieldCompatibilityChecker : public FieldClosure {
|
||||
private:
|
||||
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
|
||||
GrowableArray<int>* _compat_check_field_offsets;
|
||||
public:
|
||||
FieldCompatibilityChecker(VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
|
||||
: _removed_interfaces(removed_interfaces), _compat_check_field_offsets(nullptr) {}
|
||||
|
||||
void do_field(fieldDescriptor* fd) {
|
||||
Symbol *sig_new = fd->signature();
|
||||
if (_removed_interfaces->contains(fd->signature())) {
|
||||
if (_compat_check_field_offsets == nullptr) {
|
||||
_compat_check_field_offsets = new (mtInternal) GrowableArray<int>(2, mtInternal);
|
||||
}
|
||||
_compat_check_field_offsets->append(fd->offset());
|
||||
}
|
||||
}
|
||||
|
||||
GrowableArray<int>* compat_check_field_offsets() {
|
||||
return _compat_check_field_offsets;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: review...
|
||||
void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
|
||||
@@ -442,15 +514,23 @@ class ChangePointersOopClosure : public BasicOopIterateClosure {
|
||||
// - otherwise set the _needs_instance_update flag, we need to do full GC
|
||||
// and reshuffle object positions durring mark&sweep
|
||||
class ChangePointersObjectClosure : public ObjectClosure {
|
||||
private:
|
||||
|
||||
private:
|
||||
OopIterateClosure *_closure;
|
||||
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
|
||||
DcevmSharedGC* _dcevm_shared_gc;
|
||||
bool _needs_instance_update;
|
||||
oop _tmp_obj;
|
||||
size_t _tmp_obj_size;
|
||||
|
||||
public:
|
||||
ChangePointersObjectClosure(OopIterateClosure *closure) : _closure(closure), _needs_instance_update(false), _tmp_obj(nullptr), _tmp_obj_size(0) {}
|
||||
ChangePointersObjectClosure(OopIterateClosure *closure, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
|
||||
: _closure(closure), _removed_interfaces(removed_interfaces), _needs_instance_update(false), _tmp_obj(nullptr), _tmp_obj_size(0) {
|
||||
_dcevm_shared_gc = new DcevmSharedGC();
|
||||
}
|
||||
|
||||
~ChangePointersObjectClosure() {
|
||||
delete _dcevm_shared_gc;
|
||||
}
|
||||
|
||||
bool needs_instance_update() {
|
||||
return _needs_instance_update;
|
||||
@@ -465,6 +545,36 @@ public:
|
||||
Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), cast_from_oop<HeapWord*>(_tmp_obj), size);
|
||||
}
|
||||
|
||||
void do_compat_check_field_offsets(oop obj) {
|
||||
// Non-redefined class may store fields of redefined types
|
||||
// check field-level compatibility to avoid invalid accesses.
|
||||
GrowableArray<int>* fields = obj->klass()->compat_check_field_offsets();
|
||||
if (fields == nullptr) {
|
||||
FieldCompatibilityChecker fld_compat_check(_removed_interfaces);
|
||||
InstanceKlass::cast(obj->klass())->do_nonstatic_fields(&fld_compat_check);
|
||||
fields = fld_compat_check.compat_check_field_offsets();
|
||||
if (fields != nullptr) {
|
||||
GrowableArray<int>* old = obj->klass()->set_compat_check_field_offsets(fields);
|
||||
if (old != nullptr) {
|
||||
delete fields;
|
||||
fields = old;
|
||||
}
|
||||
} else {
|
||||
fields = reinterpret_cast<GrowableArray<int>*>(-1);
|
||||
obj->klass()->set_compat_check_field_offsets(fields);
|
||||
}
|
||||
}
|
||||
if (reinterpret_cast<intptr_t>(fields) != -1) {
|
||||
for (int i = 0; i < fields->length(); i++) {
|
||||
int fld_offset = fields->at(i);
|
||||
oop fld_val = obj->obj_field(fld_offset);
|
||||
if (fld_val != nullptr && !_dcevm_shared_gc->is_compatible(obj, fld_offset, fld_val)) {
|
||||
obj->obj_field_put(fld_offset, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void do_object(oop obj) {
|
||||
if (obj->is_instance() && InstanceKlass::cast(obj->klass())->is_mirror_instance_klass()) {
|
||||
// static fields may have references to old java.lang.Class instances, update them
|
||||
@@ -473,6 +583,9 @@ public:
|
||||
//instanceMirrorKlass::oop_fields_iterate(obj, _closure);
|
||||
} else {
|
||||
obj->oop_iterate(_closure);
|
||||
if (_removed_interfaces != nullptr && obj->klass()->is_instance_klass() && obj->klass()->new_version() == nullptr) {
|
||||
do_compat_check_field_offsets(obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->klass()->new_version() != nullptr) {
|
||||
@@ -482,6 +595,7 @@ public:
|
||||
if (obj->size() - obj->size_given_klass(new_klass) != 0) {
|
||||
// We need an instance update => set back to old klass
|
||||
_needs_instance_update = true;
|
||||
_dcevm_shared_gc->update_fields_in_old(obj, new_klass->update_information());
|
||||
} else {
|
||||
// Either new size is bigger or gap is too small to be filled
|
||||
oop src = obj;
|
||||
@@ -492,7 +606,7 @@ public:
|
||||
src->set_klass(obj->klass()->new_version());
|
||||
// FIXME: instance updates...
|
||||
//guarantee(false, "instance updates!");
|
||||
DcevmSharedGC::update_fields(obj, src, new_klass->update_information());
|
||||
_dcevm_shared_gc->update_fields(obj, src, new_klass->update_information(), true);
|
||||
}
|
||||
} else {
|
||||
obj->set_klass(obj->klass()->new_version());
|
||||
@@ -505,14 +619,15 @@ class ChangePointersObjectTask : public WorkerTask {
|
||||
private:
|
||||
ChangePointersOopClosure<StoreBarrier>* _cl;
|
||||
ParallelObjectIterator* _poi;
|
||||
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
|
||||
bool _needs_instance_update;
|
||||
public:
|
||||
ChangePointersObjectTask(ChangePointersOopClosure<StoreBarrier>* cl, ParallelObjectIterator* poi) : WorkerTask("IterateObject Closure"),
|
||||
_cl(cl), _poi(poi), _needs_instance_update(false) { }
|
||||
ChangePointersObjectTask(ChangePointersOopClosure<StoreBarrier>* cl, ParallelObjectIterator* poi, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
|
||||
: WorkerTask("IterateObject Closure"), _cl(cl), _poi(poi), _removed_interfaces(removed_interfaces), _needs_instance_update(false) { }
|
||||
|
||||
virtual void work(uint worker_id) {
|
||||
HandleMark hm(Thread::current()); // make sure any handles created are deleted
|
||||
ChangePointersObjectClosure objectClosure(_cl);
|
||||
ChangePointersObjectClosure objectClosure(_cl, _removed_interfaces);
|
||||
_poi->object_iterate(&objectClosure, worker_id);
|
||||
_needs_instance_update = _needs_instance_update || objectClosure.needs_instance_update();
|
||||
}
|
||||
@@ -521,6 +636,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class ClearCompatCheckFields : public KlassClosure {
|
||||
public:
|
||||
ClearCompatCheckFields() {}
|
||||
void do_klass(Klass* k) {
|
||||
if (k->compat_check_field_offsets() != nullptr) {
|
||||
if (reinterpret_cast<intptr_t>(k->compat_check_field_offsets()) != -1) {
|
||||
delete k->compat_check_field_offsets();
|
||||
}
|
||||
k->clear_compat_check_field_offsets();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Main transformation method - runs in VM thread.
|
||||
// - for each scratch class call redefine_single_class
|
||||
// - clear code cache (flush_dependent_code)
|
||||
@@ -552,6 +680,7 @@ void VM_EnhancedRedefineClasses::doit() {
|
||||
}
|
||||
|
||||
Universe::set_inside_redefinition(true);
|
||||
DcevmSharedGC::create_static_instance();
|
||||
|
||||
// Mark methods seen on stack and everywhere else so old methods are not
|
||||
// cleaned up if they're on the stack.
|
||||
@@ -639,11 +768,11 @@ void VM_EnhancedRedefineClasses::doit() {
|
||||
WorkerThreads* workers = Universe::heap()->safepoint_workers();
|
||||
if (workers != nullptr && workers->active_workers() > 1) {
|
||||
ParallelObjectIterator poi(workers->active_workers());
|
||||
ChangePointersObjectTask objectTask(&oopClosure, &poi);
|
||||
ChangePointersObjectTask objectTask(&oopClosure, &poi, _removed_interfaces);
|
||||
workers->run_task(&objectTask);
|
||||
needs_instance_update = objectTask.needs_instance_update();
|
||||
} else {
|
||||
ChangePointersObjectClosure objectClosure(&oopClosure);
|
||||
ChangePointersObjectClosure objectClosure(&oopClosure, _removed_interfaces);
|
||||
Universe::heap()->object_iterate(&objectClosure);
|
||||
needs_instance_update = objectClosure.needs_instance_update();
|
||||
}
|
||||
@@ -679,7 +808,7 @@ void VM_EnhancedRedefineClasses::doit() {
|
||||
// Initialize the new class! Special static initialization that does not execute the
|
||||
// static constructor but copies static field values from the old class if name
|
||||
// and signature of a static field match.
|
||||
FieldCopier copier;
|
||||
FieldCopier copier(_removed_interfaces);
|
||||
cur->do_local_static_fields(&copier); // TODO (tw): What about internal static fields??
|
||||
//java_lang_Class::set_klass(old->java_mirror(), cur); // FIXME-isd (from JDK8): is that correct?
|
||||
//FIXME-isd (from JDK8): do we need this: ??? old->set_java_mirror(cur->java_mirror());
|
||||
@@ -753,6 +882,12 @@ void VM_EnhancedRedefineClasses::doit() {
|
||||
cur->clear_update_information();
|
||||
}
|
||||
|
||||
// delete compat_check_fields
|
||||
if (_removed_interfaces != nullptr) {
|
||||
ClearCompatCheckFields compat_check_fields;
|
||||
ClassLoaderDataGraph::classes_do(&compat_check_fields);
|
||||
}
|
||||
|
||||
// TODO: explain...
|
||||
LoaderConstraintTable::update_after_redefinition();
|
||||
|
||||
@@ -790,6 +925,7 @@ void VM_EnhancedRedefineClasses::doit() {
|
||||
}
|
||||
#endif
|
||||
|
||||
DcevmSharedGC::destroy_static_instance();
|
||||
Universe::set_inside_redefinition(false);
|
||||
_timer_vm_op_doit.stop();
|
||||
|
||||
@@ -881,9 +1017,10 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
|
||||
_max_redefinition_flags = Klass::NoRedefinition;
|
||||
|
||||
GrowableArray<Klass*>* prev_affected_klasses = new (mtInternal) GrowableArray<Klass*>(_class_count, mtInternal);
|
||||
GrowableArray<int> klass_redefinition_flags(_class_count, mtInternal);
|
||||
|
||||
do {
|
||||
err = load_new_class_versions_single_step(&old_2_new_klass_map, THREAD);
|
||||
err = load_new_class_versions_single_step(&old_2_new_klass_map, &klass_redefinition_flags, THREAD);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
delete prev_affected_klasses;
|
||||
return err;
|
||||
@@ -906,6 +1043,15 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
|
||||
delete _affected_klasses;
|
||||
_affected_klasses = prev_affected_klasses;
|
||||
|
||||
// Calculate instance update information after all new classes are resolved
|
||||
for (int i = 0; i < _new_classes->length(); i++) {
|
||||
int redefinition_flags = klass_redefinition_flags.at(i);
|
||||
if ((redefinition_flags & Klass::ModifyInstances) != 0) {
|
||||
Klass *new_class = _new_classes->at(i);
|
||||
calculate_instance_update_information(new_class);
|
||||
}
|
||||
}
|
||||
|
||||
// Link and verify new classes _after_ all classes have been updated in the system dictionary!
|
||||
for (int i = 0; i < _affected_klasses->length(); i++) {
|
||||
Klass* the_class = _affected_klasses->at(i);
|
||||
@@ -928,7 +1074,8 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
|
||||
return JVMTI_ERROR_NONE;
|
||||
}
|
||||
|
||||
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, TRAPS) {
|
||||
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map,
|
||||
GrowableArray<int>* klass_redefinition_flags, TRAPS) {
|
||||
|
||||
// thread local state - used to transfer class_being_redefined object to SystemDictonery::resolve_from_stream
|
||||
JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current());
|
||||
@@ -1146,9 +1293,7 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2N
|
||||
|
||||
_max_redefinition_flags = _max_redefinition_flags | redefinition_flags;
|
||||
|
||||
if ((redefinition_flags & Klass::ModifyInstances) != 0) {
|
||||
calculate_instance_update_information(new_class);
|
||||
}
|
||||
klass_redefinition_flags->append(redefinition_flags);
|
||||
|
||||
if (the_class == vmClasses::Object_klass()) {
|
||||
_object_klass_redefined = true;
|
||||
@@ -1195,7 +1340,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
|
||||
cur_klass = new_class->super();
|
||||
while (cur_klass != nullptr) {
|
||||
if (!the_class->is_subclass_of(cur_klass->is_redefining() ? cur_klass->old_version() : cur_klass)) {
|
||||
log_info(redefine, class, load)("added super class %s", cur_klass->name()->as_C_string());
|
||||
log_debug(redefine, class, load)("added super class %s", cur_klass->name()->as_C_string());
|
||||
result = result | Klass::ModifyClass | Klass::ModifyInstances;
|
||||
}
|
||||
cur_klass = cur_klass->super();
|
||||
@@ -1209,8 +1354,13 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
|
||||
for (i = 0; i < old_interfaces->length(); i++) {
|
||||
InstanceKlass* old_interface = InstanceKlass::cast(old_interfaces->at(i));
|
||||
if (!new_class->implements_interface_dcevm(old_interface, old_2_new_klass_map)) {
|
||||
result = result | Klass::RemoveSuperType | Klass::ModifyClass;
|
||||
log_info(redefine, class, load)("removed interface %s", old_interface->name()->as_C_string());
|
||||
result = result | Klass::RemoveInterface | Klass::ModifyClass;
|
||||
log_debug(redefine, class, load)("removed interface %s", old_interface->name()->as_C_string());
|
||||
if (_removed_interfaces == nullptr) {
|
||||
_removed_interfaces = new (mtInternal) SymbolSet();
|
||||
}
|
||||
Symbol* interf_sign_sym = SymbolTable::new_symbol(old_interface->signature_name());
|
||||
_removed_interfaces->put(interf_sign_sym, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,7 +1369,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
|
||||
for (i = 0; i<new_interfaces->length(); i++) {
|
||||
if (!the_class->implements_interface_dcevm(new_interfaces->at(i), old_2_new_klass_map)) {
|
||||
result = result | Klass::ModifyClass;
|
||||
log_info(redefine, class, load)("added interface %s", new_interfaces->at(i)->name()->as_C_string());
|
||||
log_debug(redefine, class, load)("added interface %s", new_interfaces->at(i)->name()->as_C_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1495,7 +1645,7 @@ jvmtiError VM_EnhancedRedefineClasses::find_class_bytes(InstanceKlass* the_class
|
||||
|
||||
// Calculate difference between non static fields of old and new class and store the info into new class:
|
||||
// instanceKlass->store_update_information
|
||||
// instanceKlass->copy_backwards
|
||||
// instanceKlass->copying_backwards
|
||||
void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* new_version) {
|
||||
|
||||
class CalculateFieldUpdates : public FieldClosure {
|
||||
@@ -1503,17 +1653,24 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
|
||||
private:
|
||||
InstanceKlass* _old_ik;
|
||||
GrowableArray<int> _update_info;
|
||||
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
|
||||
int _position;
|
||||
bool _copy_backwards;
|
||||
bool _copying_backwards;
|
||||
bool _compat_check;
|
||||
|
||||
public:
|
||||
|
||||
bool does_copy_backwards() {
|
||||
return _copy_backwards;
|
||||
bool is_copying_backwards() {
|
||||
return _copying_backwards;
|
||||
}
|
||||
|
||||
CalculateFieldUpdates(InstanceKlass* old_ik) :
|
||||
_old_ik(old_ik), _position(instanceOopDesc::base_offset_in_bytes()), _copy_backwards(false) {
|
||||
bool is_compat_check() {
|
||||
return _compat_check;
|
||||
}
|
||||
|
||||
CalculateFieldUpdates(InstanceKlass* old_ik, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces) :
|
||||
_old_ik(old_ik), _removed_interfaces(removed_interfaces), _position(instanceOopDesc::base_offset_in_bytes()),
|
||||
_copying_backwards(false), _compat_check() {
|
||||
_update_info.append(_position);
|
||||
_update_info.append(0);
|
||||
}
|
||||
@@ -1535,43 +1692,78 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
|
||||
assert(_position == fd->offset(), "must be correct offset!");
|
||||
|
||||
InstanceKlass* holder = fd->field_holder();
|
||||
InstanceKlass* maybe_old_holder = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
|
||||
if (fd->index() < holder->java_fields_count()) {
|
||||
fieldDescriptor old_fd;
|
||||
if (_old_ik->find_field(fd->name(), fd->signature(), false, &old_fd) != nullptr) {
|
||||
// Found field in the old class, copy
|
||||
copy(old_fd.offset(), type2aelembytes(fd->field_type()));
|
||||
|
||||
if (old_fd.offset() < fd->offset()) {
|
||||
_copy_backwards = true;
|
||||
bool found = false;
|
||||
if (_old_ik->find_field(fd->name(), fd->signature(), false, &old_fd) != nullptr) {
|
||||
found = true;
|
||||
} else {
|
||||
if (maybe_old_holder->find_local_field_by_name(fd->name(), &old_fd) && !old_fd.is_static()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
// Found field in the old class, copy
|
||||
Symbol *sig_new = fd->signature();
|
||||
Symbol *sig_old = old_fd.signature();
|
||||
int compat_flag;
|
||||
|
||||
if (sig_new == sig_old) {
|
||||
if (_removed_interfaces != nullptr && _removed_interfaces->contains(sig_old) && fd->field_type() == T_OBJECT && old_fd.field_type() == T_OBJECT) {
|
||||
compat_flag = 0;
|
||||
_compat_check = true;
|
||||
} else {
|
||||
compat_flag = 1;
|
||||
}
|
||||
} else {
|
||||
if (fd->field_type() == T_OBJECT && old_fd.field_type() == T_OBJECT) {
|
||||
compat_flag = 0;
|
||||
_compat_check = true;
|
||||
} else {
|
||||
compat_flag = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer special flags
|
||||
fd->set_is_field_modification_watched(old_fd.is_field_modification_watched());
|
||||
fd->set_is_field_access_watched(old_fd.is_field_access_watched());
|
||||
if (compat_flag != -1) {
|
||||
copy(old_fd.offset(), type2aelembytes(fd->field_type()), (compat_flag == 0));
|
||||
|
||||
if (old_fd.offset() < fd->offset()) {
|
||||
_copying_backwards = true;
|
||||
}
|
||||
|
||||
// Transfer special flags
|
||||
fd->set_is_field_modification_watched(old_fd.is_field_modification_watched());
|
||||
fd->set_is_field_access_watched(old_fd.is_field_access_watched());
|
||||
} else {
|
||||
fill(type2aelembytes(fd->field_type()));
|
||||
}
|
||||
} else {
|
||||
// New field, fill
|
||||
fill(type2aelembytes(fd->field_type()));
|
||||
}
|
||||
} else {
|
||||
FieldInfo internal_field = holder->field(fd->index());
|
||||
InstanceKlass* maybe_old_klass = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
|
||||
int java_fields_count = maybe_old_klass->java_fields_count();
|
||||
|
||||
int java_fields_count = maybe_old_holder->java_fields_count();
|
||||
int num_injected;
|
||||
const InjectedField* const injected = JavaClasses::get_injected(maybe_old_klass->name(), &num_injected);
|
||||
for (int i = java_fields_count; i < java_fields_count+num_injected; i++) {
|
||||
FieldInfo maybe_old_field = maybe_old_klass->field(i);
|
||||
const InjectedField *const injected = JavaClasses::get_injected(maybe_old_holder->name(), &num_injected);
|
||||
for (int i = java_fields_count; i < java_fields_count + num_injected; i++) {
|
||||
FieldInfo maybe_old_field = maybe_old_holder->field(i);
|
||||
if (maybe_old_field.field_flags().is_injected() &&
|
||||
internal_field.field_flags().is_injected() &&
|
||||
maybe_old_field.lookup_symbol(maybe_old_field.name_index()) == internal_field.lookup_symbol(internal_field.name_index())) {
|
||||
copy(maybe_old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())));
|
||||
copy(maybe_old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())), false);
|
||||
if (maybe_old_field.offset() < internal_field.offset()) {
|
||||
_copy_backwards = true;
|
||||
_copying_backwards = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void fill(int size) {
|
||||
@@ -1583,19 +1775,24 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
|
||||
_position += size;
|
||||
}
|
||||
|
||||
void copy(int offset, int size) {
|
||||
int prev_end = -1;
|
||||
if (_update_info.length() > 0 && _update_info.at(_update_info.length() - 1) > 0) {
|
||||
prev_end = _update_info.at(_update_info.length() - 2) + _update_info.at(_update_info.length() - 1);
|
||||
}
|
||||
void copy(int offset, int size, bool needs_compat_check) {
|
||||
if (!needs_compat_check && _update_info.length() >= 2) {
|
||||
int last_size = _update_info.at(_update_info.length() - 2);
|
||||
int last_offset = _update_info.at(_update_info.length() - 1);
|
||||
|
||||
if (prev_end == offset) {
|
||||
(*_update_info.adr_at(_update_info.length() - 2)) += size;
|
||||
} else {
|
||||
_update_info.append(size);
|
||||
_update_info.append(offset);
|
||||
if (last_offset > 0 && (last_size & DcevmSharedGC::UpdateInfoCompatFlag) == 0) {
|
||||
int last_len = last_size & DcevmSharedGC::UpdateInfoLengthMask;
|
||||
int prev_end = last_offset + last_len;
|
||||
if (prev_end == offset) {
|
||||
(*_update_info.adr_at(_update_info.length() - 2)) += size;
|
||||
_position += size;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int tagged_size = needs_compat_check ? (size | DcevmSharedGC::UpdateInfoCompatFlag) : size;
|
||||
_update_info.append(tagged_size);
|
||||
_update_info.append(offset);
|
||||
_position += size;
|
||||
}
|
||||
};
|
||||
@@ -1604,15 +1801,16 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
|
||||
InstanceKlass* old_ik = InstanceKlass::cast(new_version->old_version());
|
||||
|
||||
//
|
||||
CalculateFieldUpdates cl(old_ik);
|
||||
CalculateFieldUpdates cl(old_ik, _removed_interfaces);
|
||||
ik->do_nonstatic_fields_dcevm(&cl);
|
||||
|
||||
GrowableArray<int> result = cl.finish();
|
||||
ik->store_update_information(result);
|
||||
ik->set_copying_backwards(cl.does_copy_backwards());
|
||||
ik->set_copying_backwards(cl.is_copying_backwards());
|
||||
|
||||
if (log_is_enabled(Trace, redefine, class, obsolete, metadata)) {
|
||||
log_trace(redefine, class, obsolete, metadata)("Instance update information for %s:", new_version->name()->as_C_string());
|
||||
if (cl.does_copy_backwards()) {
|
||||
if (cl.is_copying_backwards()) {
|
||||
log_trace(redefine, class, obsolete, metadata)("\tDoes copy backwards!");
|
||||
}
|
||||
for (int i=0; i<result.length(); i++) {
|
||||
@@ -1777,21 +1975,24 @@ u8 VM_EnhancedRedefineClasses::next_id() {
|
||||
}
|
||||
|
||||
// Clean method data for this class
|
||||
void VM_EnhancedRedefineClasses::MethodDataCleaner::do_klass(Klass* k) {
|
||||
if (k->is_instance_klass()) {
|
||||
InstanceKlass *ik = InstanceKlass::cast(k);
|
||||
// Clean MethodData of this class's methods so they don't refer to
|
||||
// old methods that are no longer running.
|
||||
Array<Method*>* methods = ik->methods();
|
||||
int num_methods = methods->length();
|
||||
for (int index = 0; index < num_methods; ++index) {
|
||||
if (methods->at(index)->method_data() != nullptr) {
|
||||
methods->at(index)->method_data()->clean_weak_method_links();
|
||||
class MethodDataCleaner : public KlassClosure {
|
||||
public:
|
||||
MethodDataCleaner() {}
|
||||
void do_klass(Klass* k) {
|
||||
if (k->is_instance_klass()) {
|
||||
InstanceKlass *ik = InstanceKlass::cast(k);
|
||||
// Clean MethodData of this class's methods so they don't refer to
|
||||
// old methods that are no longer running.
|
||||
Array<Method*>* methods = ik->methods();
|
||||
int num_methods = methods->length();
|
||||
for (int index = 0; index < num_methods; ++index) {
|
||||
if (methods->at(index)->method_data() != nullptr) {
|
||||
methods->at(index)->method_data()->clean_weak_method_links();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void VM_EnhancedRedefineClasses::update_jmethod_ids(Thread *current) {
|
||||
for (int j = 0; j < _matching_methods_length; ++j) {
|
||||
|
||||
@@ -47,7 +47,11 @@
|
||||
// - doit() - main redefition, adjust existing objects on the heap, clear caches
|
||||
// - doit_epilogue() - cleanup
|
||||
class VM_EnhancedRedefineClasses: public VM_GC_Operation {
|
||||
private:
|
||||
public:
|
||||
static unsigned int sym_hash (Symbol* const& s) { return (int)(uintptr_t)s; }
|
||||
static bool sym_equals(Symbol* const& a, Symbol* const& b) { return a == b; }
|
||||
typedef ResourceHashtable<Symbol*, char, 37, AnyObj::C_HEAP, mtInternal, &sym_hash, &sym_equals> SymbolSet;
|
||||
private:
|
||||
// These static fields are needed by ClassLoaderDataGraph::classes_do()
|
||||
// facility and the AdjustCpoolCacheAndVtable helper:
|
||||
static Array<Method*>* _old_methods;
|
||||
@@ -71,7 +75,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
|
||||
// RetransformClasses. Indicate which.
|
||||
JvmtiClassLoadKind _class_load_kind;
|
||||
|
||||
GrowableArray<InstanceKlass*>* _new_classes;
|
||||
GrowableArray<InstanceKlass*>* _new_classes;
|
||||
jvmtiError _res;
|
||||
|
||||
// Set if any of the InstanceKlasses have entries in the ResolvedMethodTable
|
||||
@@ -90,6 +94,8 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
|
||||
|
||||
int _max_redefinition_flags;
|
||||
|
||||
SymbolSet* _removed_interfaces;
|
||||
|
||||
// Performance measurement support. These timers do not cover all
|
||||
// the work done for JVM/TI RedefineClasses() but they do cover
|
||||
// the heavy lifting.
|
||||
@@ -113,7 +119,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
|
||||
//
|
||||
// The result is sotred in _affected_klasses(old definitions) and _new_classes(new definitions) arrays.
|
||||
jvmtiError load_new_class_versions(TRAPS);
|
||||
jvmtiError load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, TRAPS);
|
||||
jvmtiError load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, GrowableArray<int>* klass_redefinition_flags, TRAPS);
|
||||
|
||||
// Searches for all affected classes and performs a sorting such tha
|
||||
// a supertype is always before a subtype.
|
||||
@@ -171,13 +177,6 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
|
||||
ClearCpoolCacheAndUnpatch(Thread* t) : _thread(t) {}
|
||||
void do_klass(Klass* k);
|
||||
};
|
||||
|
||||
// Clean MethodData out
|
||||
class MethodDataCleaner : public KlassClosure {
|
||||
public:
|
||||
MethodDataCleaner() {}
|
||||
void do_klass(Klass* k);
|
||||
};
|
||||
public:
|
||||
VM_EnhancedRedefineClasses(jint class_count,
|
||||
const jvmtiClassDefinition *class_defs,
|
||||
|
||||
Reference in New Issue
Block a user