--- old/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2018-05-18 03:44:27.554168720 -0400 +++ new/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2018-05-18 03:44:26.014079574 -0400 @@ -683,6 +683,13 @@ _operands_index_map_count = 0; } // end finalize_operands_merge() +// Symbol* comparator for qsort +// The caller must have an active ResourceMark. +static int symcmp(const void* a, const void* b) { + char* astr = (*(Symbol**)a)->as_C_string(); + char* bstr = (*(Symbol**)b)->as_C_string(); + return strcmp(astr, bstr); +} jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( InstanceKlass* the_class, @@ -725,6 +732,100 @@ return JVMTI_ERROR_INVALID_CLASS; } + // Check whether the class NestHost attribute has been changed. + { + Thread* thread = Thread::current(); + ResourceMark rm(thread); + JvmtiThreadState *state = JvmtiThreadState::state_for((JavaThread*)thread); + RedefineVerifyMark rvm(the_class, scratch_class, state); + u2 the_nest_host_idx = the_class->nest_host_index(); + u2 scr_nest_host_idx = scratch_class->nest_host_index(); + + if (the_nest_host_idx != 0 && scr_nest_host_idx != 0) { + Symbol* the_sym = the_class->constants()->klass_name_at(the_nest_host_idx); + Symbol* scr_sym = scratch_class->constants()->klass_name_at(scr_nest_host_idx); + if (the_sym != scr_sym) { + log_trace(redefine, class, nestmates) + ("redefined class %s attribute change error: NestHost class: %s replaced with: %s", + the_class->external_name(), the_sym->as_C_string(), scr_sym->as_C_string()); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + } else if ((the_nest_host_idx == 0) ^ (scr_nest_host_idx == 0)) { + const char* action_str = (the_nest_host_idx != 0) ? "removed" : "added"; + log_trace(redefine, class, nestmates) + ("redefined class %s attribute change error: NestHost attribute %s", + the_class->external_name(), action_str); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + + // Check whether the class NestMembers attribute has been changed. + Array* the_nest_members = the_class->nest_members(); + Array* scr_nest_members = scratch_class->nest_members(); + bool the_members_exists = the_nest_members != Universe::the_empty_short_array(); + bool scr_members_exists = scr_nest_members != Universe::the_empty_short_array(); + + int members_len = the_nest_members->length(); + if (the_members_exists && scr_members_exists) { + if (members_len != scr_nest_members->length()) { + log_trace(redefine, class, nestmates) + ("redefined class %s attribute change error: NestMember len=%d changed to len=%d", + the_class->external_name(), members_len, scr_nest_members->length()); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + + // The order of entries in the NestMembers array is not specified so we + // have to explicitly check for the same contents. We do this by copying + // the referenced symbols into their own arrays, sorting them and then + // comparing each element pair. + + Symbol** the_syms = NEW_RESOURCE_ARRAY_RETURN_NULL(Symbol*, members_len); + Symbol** scr_syms = NEW_RESOURCE_ARRAY_RETURN_NULL(Symbol*, members_len); + + if (the_syms == NULL || scr_syms == NULL) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } + + for (int i = 0; i < members_len; i++) { + int the_cp_index = the_nest_members->at(i); + int scr_cp_index = scr_nest_members->at(i); + the_syms[i] = the_class->constants()->klass_name_at(the_cp_index); + scr_syms[i] = scratch_class->constants()->klass_name_at(scr_cp_index); + } + + qsort(the_syms, members_len, sizeof(Symbol*), symcmp); + qsort(scr_syms, members_len, sizeof(Symbol*), symcmp); + + for (int i = 0; i < members_len; i++) { + if (the_syms[i] != scr_syms[i]) { + log_trace(redefine, class, nestmates) + ("redefined class %s attribute change error: NestMembers[%d]: %s changed to %s", + the_class->external_name(), i, the_syms[i]->as_C_string(), scr_syms[i]->as_C_string()); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + } + /* + for (int i = 0; i < members_len; i++) { + int the_cp_index = the_nest_members->at(i); + int scr_cp_index = scr_nest_members->at(i); + Symbol* the_sym = the_class->constants()->klass_name_at(the_cp_index); + Symbol* scr_sym = scratch_class->constants()->klass_name_at(scr_cp_index); + if (the_sym != scr_sym) { + log_trace(redefine, class, nestmates) + ("redefined class %s attribute change error: NestMembers[%d]: %s changed to %s", + the_class->external_name(), i, the_sym->as_C_string(), scr_sym->as_C_string()); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + } + */ + } else if (the_members_exists ^ scr_members_exists) { + const char* action_str = (the_members_exists) ? "removed" : "added"; + log_trace(redefine, class, nestmates) + ("redefined class %s attribute change error: NestMembers attribute %s", + the_class->external_name(), action_str); + return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; + } + } + // Check whether class modifiers are the same. jushort old_flags = (jushort) the_class->access_flags().get_flags(); jushort new_flags = (jushort) scratch_class->access_flags().get_flags(); @@ -1598,6 +1699,12 @@ bool VM_RedefineClasses::rewrite_cp_refs(InstanceKlass* scratch_class, TRAPS) { + // rewrite constant pool references in the nest attributes: + if (!rewrite_cp_refs_in_nest_attributes(scratch_class)) { + // propagate failure back to caller + return false; + } + // rewrite constant pool references in the methods: if (!rewrite_cp_refs_in_methods(scratch_class, THREAD)) { // propagate failure back to caller @@ -1680,6 +1787,22 @@ return true; } // end rewrite_cp_refs() +// Rewrite constant pool references in the NestHost and NestMembers attributes. +bool VM_RedefineClasses::rewrite_cp_refs_in_nest_attributes( + InstanceKlass* scratch_class) { + + u2 cp_index = scratch_class->nest_host_index(); + if (cp_index != 0) { + scratch_class->set_nest_host_index(find_new_index(cp_index)); + } + Array* nest_members = scratch_class->nest_members(); + for (int i = 0; i < nest_members->length(); i++) { + u2 cp_index = nest_members->at(i); + nest_members->at_put(i, find_new_index(cp_index)); + } + return true; +} + // Rewrite constant pool references in the methods. bool VM_RedefineClasses::rewrite_cp_refs_in_methods( InstanceKlass* scratch_class, TRAPS) {