8007037: JSR 292: the VM_RedefineClasses::append_entry() should do cross-checks with indy operands
Summary: References from operands to CP entries and back must be correct after CP merge
Reviewed-by: coleenp, twisti
Contributed-by: serguei.spitsyn@oracle.com
--- a/hotspot/src/share/vm/oops/constantPool.cpp Wed Apr 24 15:57:17 2013 -0700
+++ b/hotspot/src/share/vm/oops/constantPool.cpp Thu Apr 25 03:58:53 2013 -0700
@@ -1043,24 +1043,13 @@
case JVM_CONSTANT_InvokeDynamic:
{
- int k1 = invoke_dynamic_bootstrap_method_ref_index_at(index1);
- int k2 = cp2->invoke_dynamic_bootstrap_method_ref_index_at(index2);
- bool match = compare_entry_to(k1, cp2, k2, CHECK_false);
- if (!match) return false;
- k1 = invoke_dynamic_name_and_type_ref_index_at(index1);
- k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2);
- match = compare_entry_to(k1, cp2, k2, CHECK_false);
- if (!match) return false;
- int argc = invoke_dynamic_argument_count_at(index1);
- if (argc == cp2->invoke_dynamic_argument_count_at(index2)) {
- for (int j = 0; j < argc; j++) {
- k1 = invoke_dynamic_argument_index_at(index1, j);
- k2 = cp2->invoke_dynamic_argument_index_at(index2, j);
- match = compare_entry_to(k1, cp2, k2, CHECK_false);
- if (!match) return false;
- }
- return true; // got through loop; all elements equal
- }
+ int k1 = invoke_dynamic_name_and_type_ref_index_at(index1);
+ int k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2);
+ int i1 = invoke_dynamic_bootstrap_specifier_index(index1);
+ int i2 = cp2->invoke_dynamic_bootstrap_specifier_index(index2);
+ bool match = compare_entry_to(k1, cp2, k2, CHECK_false) &&
+ compare_operand_to(i1, cp2, i2, CHECK_false);
+ return match;
} break;
case JVM_CONSTANT_String:
@@ -1095,6 +1084,80 @@
} // end compare_entry_to()
+// Resize the operands array with delta_len and delta_size.
+// Used in RedefineClasses for CP merge.
+void ConstantPool::resize_operands(int delta_len, int delta_size, TRAPS) {
+ int old_len = operand_array_length(operands());
+ int new_len = old_len + delta_len;
+ int min_len = (delta_len > 0) ? old_len : new_len;
+
+ int old_size = operands()->length();
+ int new_size = old_size + delta_size;
+ int min_size = (delta_size > 0) ? old_size : new_size;
+
+ ClassLoaderData* loader_data = pool_holder()->class_loader_data();
+ Array<u2>* new_ops = MetadataFactory::new_array<u2>(loader_data, new_size, CHECK);
+
+ // Set index in the resized array for existing elements only
+ for (int idx = 0; idx < min_len; idx++) {
+ int offset = operand_offset_at(idx); // offset in original array
+ operand_offset_at_put(new_ops, idx, offset + 2*delta_len); // offset in resized array
+ }
+ // Copy the bootstrap specifiers only
+ Copy::conjoint_memory_atomic(operands()->adr_at(2*old_len),
+ new_ops->adr_at(2*new_len),
+ (min_size - 2*min_len) * sizeof(u2));
+ // Explicitly deallocate old operands array.
+ // Note, it is not needed for 7u backport.
+ if ( operands() != NULL) { // the safety check
+ MetadataFactory::free_array<u2>(loader_data, operands());
+ }
+ set_operands(new_ops);
+} // end resize_operands()
+
+
+// Extend the operands array with the length and size of the ext_cp operands.
+// Used in RedefineClasses for CP merge.
+void ConstantPool::extend_operands(constantPoolHandle ext_cp, TRAPS) {
+ int delta_len = operand_array_length(ext_cp->operands());
+ if (delta_len == 0) {
+ return; // nothing to do
+ }
+ int delta_size = ext_cp->operands()->length();
+
+ assert(delta_len > 0 && delta_size > 0, "extended operands array must be bigger");
+
+ if (operand_array_length(operands()) == 0) {
+ ClassLoaderData* loader_data = pool_holder()->class_loader_data();
+ Array<u2>* new_ops = MetadataFactory::new_array<u2>(loader_data, delta_size, CHECK);
+ // The first element index defines the offset of second part
+ operand_offset_at_put(new_ops, 0, 2*delta_len); // offset in new array
+ set_operands(new_ops);
+ } else {
+ resize_operands(delta_len, delta_size, CHECK);
+ }
+
+} // end extend_operands()
+
+
+// Shrink the operands array to a smaller array with new_len length.
+// Used in RedefineClasses for CP merge.
+void ConstantPool::shrink_operands(int new_len, TRAPS) {
+ int old_len = operand_array_length(operands());
+ if (new_len == old_len) {
+ return; // nothing to do
+ }
+ assert(new_len < old_len, "shrunken operands array must be smaller");
+
+ int free_base = operand_next_offset_at(new_len - 1);
+ int delta_len = new_len - old_len;
+ int delta_size = 2*delta_len + free_base - operands()->length();
+
+ resize_operands(delta_len, delta_size, CHECK);
+
+} // end shrink_operands()
+
+
void ConstantPool::copy_operands(constantPoolHandle from_cp,
constantPoolHandle to_cp,
TRAPS) {
@@ -1357,6 +1420,46 @@
} // end find_matching_entry()
+// Compare this constant pool's bootstrap specifier at idx1 to the constant pool
+// cp2's bootstrap specifier at idx2.
+bool ConstantPool::compare_operand_to(int idx1, constantPoolHandle cp2, int idx2, TRAPS) {
+ int k1 = operand_bootstrap_method_ref_index_at(idx1);
+ int k2 = cp2->operand_bootstrap_method_ref_index_at(idx2);
+ bool match = compare_entry_to(k1, cp2, k2, CHECK_false);
+
+ if (!match) {
+ return false;
+ }
+ int argc = operand_argument_count_at(idx1);
+ if (argc == cp2->operand_argument_count_at(idx2)) {
+ for (int j = 0; j < argc; j++) {
+ k1 = operand_argument_index_at(idx1, j);
+ k2 = cp2->operand_argument_index_at(idx2, j);
+ match = compare_entry_to(k1, cp2, k2, CHECK_false);
+ if (!match) {
+ return false;
+ }
+ }
+ return true; // got through loop; all elements equal
+ }
+ return false;
+} // end compare_operand_to()
+
+// Search constant pool search_cp for a bootstrap specifier that matches
+// this constant pool's bootstrap specifier at pattern_i index.
+// Return the index of a matching bootstrap specifier or (-1) if there is no match.
+int ConstantPool::find_matching_operand(int pattern_i,
+ constantPoolHandle search_cp, int search_len, TRAPS) {
+ for (int i = 0; i < search_len; i++) {
+ bool found = compare_operand_to(pattern_i, search_cp, i, CHECK_(-1));
+ if (found) {
+ return i;
+ }
+ }
+ return -1; // bootstrap specifier not found; return unused index (-1)
+} // end find_matching_operand()
+
+
#ifndef PRODUCT
const char* ConstantPool::printable_name_at(int which) {
--- a/hotspot/src/share/vm/oops/constantPool.hpp Wed Apr 24 15:57:17 2013 -0700
+++ b/hotspot/src/share/vm/oops/constantPool.hpp Thu Apr 25 03:58:53 2013 -0700
@@ -567,6 +567,47 @@
_indy_argc_offset = 1, // u2 argc
_indy_argv_offset = 2 // u2 argv[argc]
};
+
+ // These functions are used in RedefineClasses for CP merge
+
+ int operand_offset_at(int bootstrap_specifier_index) {
+ assert(0 <= bootstrap_specifier_index &&
+ bootstrap_specifier_index < operand_array_length(operands()),
+ "Corrupted CP operands");
+ return operand_offset_at(operands(), bootstrap_specifier_index);
+ }
+ int operand_bootstrap_method_ref_index_at(int bootstrap_specifier_index) {
+ int offset = operand_offset_at(bootstrap_specifier_index);
+ return operands()->at(offset + _indy_bsm_offset);
+ }
+ int operand_argument_count_at(int bootstrap_specifier_index) {
+ int offset = operand_offset_at(bootstrap_specifier_index);
+ int argc = operands()->at(offset + _indy_argc_offset);
+ return argc;
+ }
+ int operand_argument_index_at(int bootstrap_specifier_index, int j) {
+ int offset = operand_offset_at(bootstrap_specifier_index);
+ return operands()->at(offset + _indy_argv_offset + j);
+ }
+ int operand_next_offset_at(int bootstrap_specifier_index) {
+ int offset = operand_offset_at(bootstrap_specifier_index) + _indy_argv_offset
+ + operand_argument_count_at(bootstrap_specifier_index);
+ return offset;
+ }
+ // Compare a bootsrap specifier in the operands arrays
+ bool compare_operand_to(int bootstrap_specifier_index1, constantPoolHandle cp2,
+ int bootstrap_specifier_index2, TRAPS);
+ // Find a bootsrap specifier in the operands array
+ int find_matching_operand(int bootstrap_specifier_index, constantPoolHandle search_cp,
+ int operands_cur_len, TRAPS);
+ // Resize the operands array with delta_len and delta_size
+ void resize_operands(int delta_len, int delta_size, TRAPS);
+ // Extend the operands array with the length and size of the ext_cp operands
+ void extend_operands(constantPoolHandle ext_cp, TRAPS);
+ // Shrink the operands array to a smaller array with new_len length
+ void shrink_operands(int new_len, TRAPS);
+
+
int invoke_dynamic_bootstrap_method_ref_index_at(int which) {
assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
int op_base = invoke_dynamic_operand_base(which);
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed Apr 24 15:57:17 2013 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Thu Apr 25 03:58:53 2013 -0700
@@ -415,20 +415,26 @@
// this is an indirect CP entry so it needs special handling
case JVM_CONSTANT_InvokeDynamic:
{
- // TBD: cross-checks and possible extra appends into CP and bsm operands
- // are needed as well. This issue is tracked by a separate bug 8007037.
- int bss_idx = scratch_cp->invoke_dynamic_bootstrap_specifier_index(scratch_i);
-
- int ref_i = scratch_cp->invoke_dynamic_name_and_type_ref_index_at(scratch_i);
- int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p,
+ // Index of the bootstrap specifier in the operands array
+ int old_bs_i = scratch_cp->invoke_dynamic_bootstrap_specifier_index(scratch_i);
+ int new_bs_i = find_or_append_operand(scratch_cp, old_bs_i, merge_cp_p,
+ merge_cp_length_p, THREAD);
+ // The bootstrap method NameAndType_info index
+ int old_ref_i = scratch_cp->invoke_dynamic_name_and_type_ref_index_at(scratch_i);
+ int new_ref_i = find_or_append_indirect_entry(scratch_cp, old_ref_i, merge_cp_p,
merge_cp_length_p, THREAD);
- if (new_ref_i != ref_i) {
+ if (new_bs_i != old_bs_i) {
RC_TRACE(0x00080000,
- ("InvokeDynamic entry@%d name_and_type ref_index change: %d to %d",
- *merge_cp_length_p, ref_i, new_ref_i));
+ ("InvokeDynamic entry@%d bootstrap_method_attr_index change: %d to %d",
+ *merge_cp_length_p, old_bs_i, new_bs_i));
}
-
- (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, bss_idx, new_ref_i);
+ if (new_ref_i != old_ref_i) {
+ RC_TRACE(0x00080000,
+ ("InvokeDynamic entry@%d name_and_type_index change: %d to %d",
+ *merge_cp_length_p, old_ref_i, new_ref_i));
+ }
+
+ (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
if (scratch_i != *merge_cp_length_p) {
// The new entry in *merge_cp_p is at a different index than
// the new entry in scratch_cp so we need to map the index values.
@@ -492,6 +498,105 @@
} // end find_or_append_indirect_entry()
+// Append a bootstrap specifier into the merge_cp operands that is semantically equal
+// to the scratch_cp operands bootstrap specifier passed by the old_bs_i index.
+// Recursively append new merge_cp entries referenced by the new bootstrap specifier.
+void VM_RedefineClasses::append_operand(constantPoolHandle scratch_cp, int old_bs_i,
+ constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS) {
+
+ int old_ref_i = scratch_cp->operand_bootstrap_method_ref_index_at(old_bs_i);
+ int new_ref_i = find_or_append_indirect_entry(scratch_cp, old_ref_i, merge_cp_p,
+ merge_cp_length_p, THREAD);
+ if (new_ref_i != old_ref_i) {
+ RC_TRACE(0x00080000,
+ ("operands entry@%d bootstrap method ref_index change: %d to %d",
+ _operands_cur_length, old_ref_i, new_ref_i));
+ }
+
+ Array<u2>* merge_ops = (*merge_cp_p)->operands();
+ int new_bs_i = _operands_cur_length;
+ // We have _operands_cur_length == 0 when the merge_cp operands is empty yet.
+ // However, the operand_offset_at(0) was set in the extend_operands() call.
+ int new_base = (new_bs_i == 0) ? (*merge_cp_p)->operand_offset_at(0)
+ : (*merge_cp_p)->operand_next_offset_at(new_bs_i - 1);
+ int argc = scratch_cp->operand_argument_count_at(old_bs_i);
+
+ ConstantPool::operand_offset_at_put(merge_ops, _operands_cur_length, new_base);
+ merge_ops->at_put(new_base++, new_ref_i);
+ merge_ops->at_put(new_base++, argc);
+
+ for (int i = 0; i < argc; i++) {
+ int old_arg_ref_i = scratch_cp->operand_argument_index_at(old_bs_i, i);
+ int new_arg_ref_i = find_or_append_indirect_entry(scratch_cp, old_arg_ref_i, merge_cp_p,
+ merge_cp_length_p, THREAD);
+ merge_ops->at_put(new_base++, new_arg_ref_i);
+ if (new_arg_ref_i != old_arg_ref_i) {
+ RC_TRACE(0x00080000,
+ ("operands entry@%d bootstrap method argument ref_index change: %d to %d",
+ _operands_cur_length, old_arg_ref_i, new_arg_ref_i));
+ }
+ }
+ if (old_bs_i != _operands_cur_length) {
+ // The bootstrap specifier in *merge_cp_p is at a different index than
+ // that in scratch_cp so we need to map the index values.
+ map_operand_index(old_bs_i, new_bs_i);
+ }
+ _operands_cur_length++;
+} // end append_operand()
+
+
+int VM_RedefineClasses::find_or_append_operand(constantPoolHandle scratch_cp,
+ int old_bs_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS) {
+
+ int new_bs_i = old_bs_i; // bootstrap specifier index
+ bool match = (old_bs_i < _operands_cur_length) &&
+ scratch_cp->compare_operand_to(old_bs_i, *merge_cp_p, old_bs_i, THREAD);
+
+ if (!match) {
+ // forward reference in *merge_cp_p or not a direct match
+ int found_i = scratch_cp->find_matching_operand(old_bs_i, *merge_cp_p,
+ _operands_cur_length, THREAD);
+ if (found_i != -1) {
+ guarantee(found_i != old_bs_i, "compare_operand_to() and find_matching_operand() disagree");
+ // found a matching operand somewhere else in *merge_cp_p so just need a mapping
+ new_bs_i = found_i;
+ map_operand_index(old_bs_i, found_i);
+ } else {
+ // no match found so we have to append this bootstrap specifier to *merge_cp_p
+ append_operand(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p, THREAD);
+ new_bs_i = _operands_cur_length - 1;
+ }
+ }
+ return new_bs_i;
+} // end find_or_append_operand()
+
+
+void VM_RedefineClasses::finalize_operands_merge(constantPoolHandle merge_cp, TRAPS) {
+ if (merge_cp->operands() == NULL) {
+ return;
+ }
+ // Shrink the merge_cp operands
+ merge_cp->shrink_operands(_operands_cur_length, CHECK);
+
+ if (RC_TRACE_ENABLED(0x00040000)) {
+ // don't want to loop unless we are tracing
+ int count = 0;
+ for (int i = 1; i < _operands_index_map_p->length(); i++) {
+ int value = _operands_index_map_p->at(i);
+ if (value != -1) {
+ RC_TRACE_WITH_THREAD(0x00040000, THREAD,
+ ("operands_index_map[%d]: old=%d new=%d", count, i, value));
+ count++;
+ }
+ }
+ }
+ // Clean-up
+ _operands_index_map_p = NULL;
+ _operands_cur_length = 0;
+ _operands_index_map_count = 0;
+} // end finalize_operands_merge()
+
+
jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions(
instanceKlassHandle the_class,
instanceKlassHandle scratch_class) {
@@ -765,6 +870,31 @@
} // end find_new_index()
+// Find new bootstrap specifier index value for old bootstrap specifier index
+// value by seaching the index map. Returns unused index (-1) if there is
+// no mapped value for the old bootstrap specifier index.
+int VM_RedefineClasses::find_new_operand_index(int old_index) {
+ if (_operands_index_map_count == 0) {
+ // map is empty so nothing can be found
+ return -1;
+ }
+
+ if (old_index == -1 || old_index >= _operands_index_map_p->length()) {
+ // The old_index is out of range so it is not mapped.
+ // This should not happen in regular constant pool merging use.
+ return -1;
+ }
+
+ int value = _operands_index_map_p->at(old_index);
+ if (value == -1) {
+ // the old_index is not mapped
+ return -1;
+ }
+
+ return value;
+} // end find_new_operand_index()
+
+
// Returns true if the current mismatch is due to a resolved/unresolved
// class pair. Otherwise, returns false.
bool VM_RedefineClasses::is_unresolved_class_mismatch(constantPoolHandle cp1,
@@ -1014,6 +1144,25 @@
} // end map_index()
+// Map old_index to new_index as needed.
+void VM_RedefineClasses::map_operand_index(int old_index, int new_index) {
+ if (find_new_operand_index(old_index) != -1) {
+ // old_index is already mapped
+ return;
+ }
+
+ if (old_index == new_index) {
+ // no mapping is needed
+ return;
+ }
+
+ _operands_index_map_p->at_put(old_index, new_index);
+ _operands_index_map_count++;
+
+ RC_TRACE(0x00040000, ("mapped bootstrap specifier at index %d to %d", old_index, new_index));
+} // end map_index()
+
+
// Merge old_cp and scratch_cp and return the results of the merge via
// merge_cp_p. The number of entries in *merge_cp_p is returned via
// merge_cp_length_p. The entries in old_cp occupy the same locations
@@ -1086,6 +1235,7 @@
} // end for each old_cp entry
ConstantPool::copy_operands(old_cp, *merge_cp_p, CHECK_0);
+ (*merge_cp_p)->extend_operands(scratch_cp, CHECK_0);
// We don't need to sanity check that *merge_cp_length_p is within
// *merge_cp_p bounds since we have the minimum on-entry check above.
@@ -1198,6 +1348,8 @@
CHECK_0);
}
+ finalize_operands_merge(*merge_cp_p, THREAD);
+
RC_TRACE_WITH_THREAD(0x00020000, THREAD,
("after pass 1b: merge_cp_len=%d, scratch_i=%d, index_map_len=%d",
*merge_cp_length_p, scratch_i, _index_map_count));
@@ -1270,6 +1422,11 @@
_index_map_count = 0;
_index_map_p = new intArray(scratch_cp->length(), -1);
+ _operands_cur_length = ConstantPool::operand_array_length(old_cp->operands());
+ _operands_index_map_count = 0;
+ _operands_index_map_p = new intArray(
+ ConstantPool::operand_array_length(scratch_cp->operands()), -1);
+
// reference to the cp holder is needed for copy_operands()
merge_cp->set_pool_holder(scratch_class());
bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp,
@@ -1400,7 +1557,6 @@
return true;
} // end rewrite_cp_refs()
-
// Rewrite constant pool references in the methods.
bool VM_RedefineClasses::rewrite_cp_refs_in_methods(
instanceKlassHandle scratch_class, TRAPS) {
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed Apr 24 15:57:17 2013 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Thu Apr 25 03:58:53 2013 -0700
@@ -359,8 +359,15 @@
// _index_map_p contains any entries.
int _index_map_count;
intArray * _index_map_p;
+
+ // _operands_index_map_count is just an optimization for knowing if
+ // _operands_index_map_p contains any entries.
+ int _operands_cur_length;
+ int _operands_index_map_count;
+ intArray * _operands_index_map_p;
+
// ptr to _class_count scratch_classes
- Klass** _scratch_classes;
+ Klass** _scratch_classes;
jvmtiError _res;
// Performance measurement support. These timers do not cover all
@@ -422,12 +429,19 @@
// Support for constant pool merging (these routines are in alpha order):
void append_entry(constantPoolHandle scratch_cp, int scratch_i,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS);
+ void append_operand(constantPoolHandle scratch_cp, int scratch_bootstrap_spec_index,
+ constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS);
+ void finalize_operands_merge(constantPoolHandle merge_cp, TRAPS);
int find_or_append_indirect_entry(constantPoolHandle scratch_cp, int scratch_i,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS);
+ int find_or_append_operand(constantPoolHandle scratch_cp, int scratch_bootstrap_spec_index,
+ constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS);
int find_new_index(int old_index);
+ int find_new_operand_index(int old_bootstrap_spec_index);
bool is_unresolved_class_mismatch(constantPoolHandle cp1, int index1,
constantPoolHandle cp2, int index2);
void map_index(constantPoolHandle scratch_cp, int old_index, int new_index);
+ void map_operand_index(int old_bootstrap_spec_index, int new_bootstrap_spec_index);
bool merge_constant_pools(constantPoolHandle old_cp,
constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p,
int *merge_cp_length_p, TRAPS);