--- a/src/hotspot/share/oops/constantPool.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/oops/constantPool.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -607,7 +607,6 @@
return symbol_at(signature_index);
}
-
int ConstantPool::impl_name_and_type_ref_index_at(int which, bool uncached) {
int i = which;
if (!uncached && cache() != NULL) {
@@ -621,14 +620,18 @@
// change byte-ordering and go via cache
i = remap_instruction_operand_from_cache(which);
} else {
- if (tag_at(which).is_invoke_dynamic()) {
+ if (tag_at(which).is_invoke_dynamic() ||
+ tag_at(which).is_dynamic_constant() ||
+ tag_at(which).is_dynamic_constant_in_error()) {
int pool_index = invoke_dynamic_name_and_type_ref_index_at(which);
assert(tag_at(pool_index).is_name_and_type(), "");
return pool_index;
}
}
assert(tag_at(i).is_field_or_method(), "Corrupted constant pool");
- assert(!tag_at(i).is_invoke_dynamic(), "Must be handled above");
+ assert(!tag_at(i).is_invoke_dynamic() &&
+ !tag_at(i).is_dynamic_constant() &&
+ !tag_at(i).is_dynamic_constant_in_error(), "Must be handled above");
jint ref_index = *int_at_addr(i);
return extract_high_short_from_int(ref_index);
}
@@ -672,16 +675,12 @@
void ConstantPool::verify_constant_pool_resolve(const constantPoolHandle& this_cp, Klass* k, TRAPS) {
- if (k->is_instance_klass() || k->is_objArray_klass()) {
- InstanceKlass* holder = this_cp->pool_holder();
- Klass* elem = k->is_instance_klass() ? k : ObjArrayKlass::cast(k)->bottom_klass();
-
- // The element type could be a typeArray - we only need the access check if it is
- // an reference to another class
- if (elem->is_instance_klass()) {
- LinkResolver::check_klass_accessability(holder, elem, CHECK);
- }
+ if (!(k->is_instance_klass() || k->is_objArray_klass())) {
+ return; // short cut, typeArray klass is always accessible
}
+ Klass* holder = this_cp->pool_holder();
+ bool fold_type_to_class = true;
+ LinkResolver::check_klass_accessability(holder, k, fold_type_to_class, CHECK);
}
@@ -769,8 +768,8 @@
THROW_MSG(error, message->as_C_string());
}
-// If resolution for Class, MethodHandle or MethodType fails, save the exception
-// in the resolution error table, so that the same exception is thrown again.
+// If resolution for Class, Dynamic constant, MethodHandle or MethodType fails, save the
+// exception in the resolution error table, so that the same exception is thrown again.
void ConstantPool::save_and_throw_exception(const constantPoolHandle& this_cp, int which,
constantTag tag, TRAPS) {
Symbol* error = PENDING_EXCEPTION->klass()->name();
@@ -806,16 +805,31 @@
}
}
+BasicType ConstantPool::basic_type_for_constant_at(int which) {
+ constantTag tag = tag_at(which);
+ if (tag.is_dynamic_constant() ||
+ tag.is_dynamic_constant_in_error()) {
+ // have to look at the signature for this one
+ Symbol* constant_type = uncached_signature_ref_at(which);
+ return FieldType::basic_type(constant_type);
+ }
+ return tag.basic_type();
+}
+
// Called to resolve constants in the constant pool and return an oop.
// Some constant pool entries cache their resolved oop. This is also
// called to create oops from constants to use in arguments for invokedynamic
-oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index, TRAPS) {
+oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
+ int index, int cache_index,
+ bool* status_return, TRAPS) {
oop result_oop = NULL;
Handle throw_exception;
if (cache_index == _possible_index_sentinel) {
// It is possible that this constant is one which is cached in the objects.
// We'll do a linear search. This should be OK because this usage is rare.
+ // FIXME: If bootstrap specifiers stress this code, consider putting in
+ // a reverse index. Binary search over a short array should do it.
assert(index > 0, "valid index");
cache_index = this_cp->cp_to_object_index(index);
}
@@ -825,6 +839,12 @@
if (cache_index >= 0) {
result_oop = this_cp->resolved_references()->obj_at(cache_index);
if (result_oop != NULL) {
+ if (result_oop == Universe::the_null_sentinel()) {
+ DEBUG_ONLY(int temp_index = (index >= 0 ? index : this_cp->object_to_cp_index(cache_index)));
+ assert(this_cp->tag_at(temp_index).is_dynamic_constant(), "only condy uses the null sentinel");
+ result_oop = NULL;
+ }
+ if (status_return != NULL) (*status_return) = true;
return result_oop;
// That was easy...
}
@@ -835,6 +855,35 @@
constantTag tag = this_cp->tag_at(index);
+ if (status_return != NULL) {
+ // don't trigger resolution if the constant might need it
+ switch (tag.value()) {
+ case JVM_CONSTANT_Class:
+ {
+ CPKlassSlot kslot = this_cp->klass_slot_at(index);
+ int resolved_klass_index = kslot.resolved_klass_index();
+ if (this_cp->resolved_klasses()->at(resolved_klass_index) == NULL) {
+ (*status_return) = false;
+ return NULL;
+ }
+ // the klass is waiting in the CP; go get it
+ break;
+ }
+ case JVM_CONSTANT_String:
+ case JVM_CONSTANT_Integer:
+ case JVM_CONSTANT_Float:
+ case JVM_CONSTANT_Long:
+ case JVM_CONSTANT_Double:
+ // these guys trigger OOM at worst
+ break;
+ default:
+ (*status_return) = false;
+ return NULL;
+ }
+ // from now on there is either success or an OOME
+ (*status_return) = true;
+ }
+
switch (tag.value()) {
case JVM_CONSTANT_UnresolvedClass:
@@ -848,6 +897,63 @@
break;
}
+ case JVM_CONSTANT_Dynamic:
+ {
+ Klass* current_klass = this_cp->pool_holder();
+ Symbol* constant_name = this_cp->uncached_name_ref_at(index);
+ Symbol* constant_type = this_cp->uncached_signature_ref_at(index);
+
+ // The initial step in resolving an unresolved symbolic reference to a
+ // dynamically-computed constant is to resolve the symbolic reference to a
+ // method handle which will be the bootstrap method for the dynamically-computed
+ // constant. If resolution of the java.lang.invoke.MethodHandle for the bootstrap
+ // method fails, then a MethodHandleInError is stored at the corresponding
+ // bootstrap method's CP index for the CONSTANT_MethodHandle_info. No need to
+ // set a DynamicConstantInError here since any subsequent use of this
+ // bootstrap method will encounter the resolution of MethodHandleInError.
+ oop bsm_info = this_cp->resolve_bootstrap_specifier_at(index, THREAD);
+ Exceptions::wrap_dynamic_exception(CHECK_NULL);
+ assert(bsm_info != NULL, "");
+ // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_Dynamic.
+ Handle bootstrap_specifier = Handle(THREAD, bsm_info);
+
+ // Resolve the Dynamically-Computed constant to invoke the BSM in order to obtain the resulting oop.
+ Handle value = SystemDictionary::link_dynamic_constant(current_klass,
+ index,
+ bootstrap_specifier,
+ constant_name,
+ constant_type,
+ THREAD);
+ result_oop = value();
+ Exceptions::wrap_dynamic_exception(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ // Resolution failure of the dynamically-computed constant, save_and_throw_exception
+ // will check for a LinkageError and store a DynamicConstantInError.
+ save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
+ }
+ BasicType type = FieldType::basic_type(constant_type);
+ if (!is_reference_type(type)) {
+ // Make sure the primitive value is properly boxed.
+ // This is a JDK responsibility.
+ const char* fail = NULL;
+ if (result_oop == NULL) {
+ fail = "null result instead of box";
+ } else if (!is_java_primitive(type)) {
+ // FIXME: support value types via unboxing
+ fail = "can only handle references and primitives";
+ } else if (!java_lang_boxing_object::is_instance(result_oop, type)) {
+ fail = "primitive is not properly boxed";
+ }
+ if (fail != NULL) {
+ // Since this exception is not a LinkageError, throw exception
+ // but do not save a DynamicInError resolution result.
+ // See section 5.4.3 of the VM spec.
+ THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), fail);
+ }
+ }
+ break;
+ }
+
case JVM_CONSTANT_String:
assert(cache_index != _no_index_sentinel, "should have been set");
if (this_cp->is_pseudo_string_at(index)) {
@@ -857,6 +963,7 @@
result_oop = string_at_impl(this_cp, index, cache_index, CHECK_NULL);
break;
+ case JVM_CONSTANT_DynamicInError:
case JVM_CONSTANT_MethodHandleInError:
case JVM_CONSTANT_MethodTypeInError:
{
@@ -957,15 +1064,20 @@
// The important thing here is that all threads pick up the same result.
// It doesn't matter which racing thread wins, as long as only one
// result is used by all threads, and all future queries.
- oop old_result = this_cp->resolved_references()->atomic_compare_exchange_oop(cache_index, result_oop, NULL);
+ oop new_result = (result_oop == NULL ? Universe::the_null_sentinel() : result_oop);
+ oop old_result = this_cp->resolved_references()
+ ->atomic_compare_exchange_oop(cache_index, new_result, NULL);
if (old_result == NULL) {
return result_oop; // was installed
} else {
// Return the winning thread's result. This can be different than
// the result here for MethodHandles.
+ if (old_result == Universe::the_null_sentinel())
+ old_result = NULL;
return old_result;
}
} else {
+ assert(result_oop != Universe::the_null_sentinel(), "");
return result_oop;
}
}
@@ -979,13 +1091,14 @@
oop ConstantPool::resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS) {
- assert(this_cp->tag_at(index).is_invoke_dynamic(), "Corrupted constant pool");
-
+ assert((this_cp->tag_at(index).is_invoke_dynamic() ||
+ this_cp->tag_at(index).is_dynamic_constant()), "Corrupted constant pool");
Handle bsm;
int argc;
{
- // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&type], plus optional arguments
- // The bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
+ // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&mtype], plus optional arguments
+ // JVM_CONSTANT_Dynamic is an ordered pair of [bootm, name&ftype], plus optional arguments
+ // In both cases, the bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
// It is accompanied by the optional arguments.
int bsm_index = this_cp->invoke_dynamic_bootstrap_method_ref_index_at(index);
oop bsm_oop = this_cp->resolve_possibly_cached_constant_at(bsm_index, CHECK_NULL);
@@ -995,30 +1108,142 @@
// Extract the optional static arguments.
argc = this_cp->invoke_dynamic_argument_count_at(index);
- if (argc == 0) return bsm_oop;
+
+ // if there are no static arguments, return the bsm by itself:
+ if (argc == 0 && UseBootstrapCallInfo < 2) return bsm_oop;
bsm = Handle(THREAD, bsm_oop);
}
+ // We are going to return an ordered pair of {bsm, info}, using a 2-array.
objArrayHandle info;
{
- objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1+argc, CHECK_NULL);
+ objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 2, CHECK_NULL);
info = objArrayHandle(THREAD, info_oop);
}
info->obj_at_put(0, bsm());
- for (int i = 0; i < argc; i++) {
- int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
- oop arg_oop = this_cp->resolve_possibly_cached_constant_at(arg_index, CHECK_NULL);
- info->obj_at_put(1+i, arg_oop);
+
+ bool use_BSCI;
+ switch (UseBootstrapCallInfo) {
+ default: use_BSCI = true; break; // stress mode
+ case 0: use_BSCI = false; break; // stress mode
+ case 1: // normal mode
+ // If we were to support an alternative mode of BSM invocation,
+ // we'd convert to pull mode here if the BSM could be a candidate
+ // for that alternative mode. We can't easily test for things
+ // like varargs here, but we can get away with approximate testing,
+ // since the JDK runtime will make up the difference either way.
+ // For now, exercise the pull-mode path if the BSM is of arity 2,
+ // or if there is a potential condy loop (see below).
+ oop mt_oop = java_lang_invoke_MethodHandle::type(bsm());
+ use_BSCI = (java_lang_invoke_MethodType::ptype_count(mt_oop) == 2);
+ break;
+ }
+
+ // Here's a reason to use BSCI even if it wasn't requested:
+ // If a condy uses a condy argument, we want to avoid infinite
+ // recursion (condy loops) in the C code. It's OK in Java,
+ // because Java has stack overflow checking, so we punt
+ // potentially cyclic cases from C to Java.
+ if (!use_BSCI && this_cp->tag_at(index).is_dynamic_constant()) {
+ bool found_unresolved_condy = false;
+ for (int i = 0; i < argc; i++) {
+ int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
+ if (this_cp->tag_at(arg_index).is_dynamic_constant()) {
+ // potential recursion point condy -> condy
+ bool found_it = false;
+ this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
+ if (!found_it) { found_unresolved_condy = true; break; }
+ }
+ }
+ if (found_unresolved_condy)
+ use_BSCI = true;
+ }
+
+ const int SMALL_ARITY = 5;
+ if (use_BSCI && argc <= SMALL_ARITY && UseBootstrapCallInfo <= 2) {
+ // If there are only a few arguments, and none of them need linking,
+ // push them, instead of asking the JDK runtime to turn around and
+ // pull them, saving a JVM/JDK transition in some simple cases.
+ bool all_resolved = true;
+ for (int i = 0; i < argc; i++) {
+ bool found_it = false;
+ int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
+ this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
+ if (!found_it) { all_resolved = false; break; }
+ }
+ if (all_resolved)
+ use_BSCI = false;
}
+ if (!use_BSCI) {
+ // return {bsm, {arg...}}; resolution of arguments is done immediately, before JDK code is called
+ objArrayOop args_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), argc, CHECK_NULL);
+ info->obj_at_put(1, args_oop); // may overwrite with args[0] below
+ objArrayHandle args(THREAD, args_oop);
+ copy_bootstrap_arguments_at_impl(this_cp, index, 0, argc, args, 0, true, Handle(), CHECK_NULL);
+ if (argc == 1) {
+ // try to discard the singleton array
+ oop arg_oop = args->obj_at(0);
+ if (arg_oop != NULL && !arg_oop->is_array()) {
+ // JVM treats arrays and nulls specially in this position,
+ // but other things are just single arguments
+ info->obj_at_put(1, arg_oop);
+ }
+ }
+ } else {
+ // return {bsm, {arg_count, pool_index}}; JDK code must pull the arguments as needed
+ typeArrayOop ints_oop = oopFactory::new_typeArray(T_INT, 2, CHECK_NULL);
+ ints_oop->int_at_put(0, argc);
+ ints_oop->int_at_put(1, index);
+ info->obj_at_put(1, ints_oop);
+ }
return info();
}
+void ConstantPool::copy_bootstrap_arguments_at_impl(const constantPoolHandle& this_cp, int index,
+ int start_arg, int end_arg,
+ objArrayHandle info, int pos,
+ bool must_resolve, Handle if_not_available,
+ TRAPS) {
+ int argc;
+ int limit = pos + end_arg - start_arg;
+ // checks: index in range [0..this_cp->length),
+ // tag at index, start..end in range [0..argc],
+ // info array non-null, pos..limit in [0..info.length]
+ if ((0 >= index || index >= this_cp->length()) ||
+ !(this_cp->tag_at(index).is_invoke_dynamic() ||
+ this_cp->tag_at(index).is_dynamic_constant()) ||
+ (0 > start_arg || start_arg > end_arg) ||
+ (end_arg > (argc = this_cp->invoke_dynamic_argument_count_at(index))) ||
+ (0 > pos || pos > limit) ||
+ (info.is_null() || limit > info->length())) {
+ // An index or something else went wrong; throw an error.
+ // Since this is an internal API, we don't expect this,
+ // so we don't bother to craft a nice message.
+ THROW_MSG(vmSymbols::java_lang_LinkageError(), "bad BSM argument access");
+ }
+ // now we can loop safely
+ int info_i = pos;
+ for (int i = start_arg; i < end_arg; i++) {
+ int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
+ oop arg_oop;
+ if (must_resolve) {
+ arg_oop = this_cp->resolve_possibly_cached_constant_at(arg_index, CHECK);
+ } else {
+ bool found_it = false;
+ arg_oop = this_cp->find_cached_constant_at(arg_index, found_it, CHECK);
+ if (!found_it) arg_oop = if_not_available();
+ }
+ info->obj_at_put(info_i++, arg_oop);
+ }
+}
+
oop ConstantPool::string_at_impl(const constantPoolHandle& this_cp, int which, int obj_index, TRAPS) {
// If the string has already been interned, this entry will be non-null
oop str = this_cp->resolved_references()->obj_at(obj_index);
+ assert(str != Universe::the_null_sentinel(), "");
if (str != NULL) return str;
Symbol* sym = this_cp->unresolved_string_at(which);
str = StringTable::intern(sym, CHECK_(NULL));
@@ -1199,6 +1424,18 @@
}
} break;
+ case JVM_CONSTANT_Dynamic:
+ {
+ 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);
+ // separate statements and variables because CHECK_false is used
+ bool match_entry = compare_entry_to(k1, cp2, k2, CHECK_false);
+ bool match_operand = compare_operand_to(i1, cp2, i2, CHECK_false);
+ return (match_entry && match_operand);
+ } break;
+
case JVM_CONSTANT_InvokeDynamic:
{
int k1 = invoke_dynamic_name_and_type_ref_index_at(index1);
@@ -1525,6 +1762,15 @@
to_cp->method_handle_index_at_put(to_i, k1, k2);
} break;
+ case JVM_CONSTANT_Dynamic:
+ case JVM_CONSTANT_DynamicInError:
+ {
+ int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i);
+ int k2 = from_cp->invoke_dynamic_name_and_type_ref_index_at(from_i);
+ k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands
+ to_cp->dynamic_constant_at_put(to_i, k1, k2);
+ } break;
+
case JVM_CONSTANT_InvokeDynamic:
{
int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i);
@@ -1786,6 +2032,8 @@
case JVM_CONSTANT_NameAndType:
return 5;
+ case JVM_CONSTANT_Dynamic:
+ case JVM_CONSTANT_DynamicInError:
case JVM_CONSTANT_InvokeDynamic:
// u1 tag, u2 bsm, u2 nt
return 5;
@@ -1971,6 +2219,17 @@
DBG(printf("JVM_CONSTANT_MethodType: %hd", idx1));
break;
}
+ case JVM_CONSTANT_Dynamic:
+ case JVM_CONSTANT_DynamicInError: {
+ *bytes = tag;
+ idx1 = extract_low_short_from_int(*int_at_addr(idx));
+ idx2 = extract_high_short_from_int(*int_at_addr(idx));
+ assert(idx2 == invoke_dynamic_name_and_type_ref_index_at(idx), "correct half of u4");
+ Bytes::put_Java_u2((address) (bytes+1), idx1);
+ Bytes::put_Java_u2((address) (bytes+3), idx2);
+ DBG(printf("JVM_CONSTANT_Dynamic: %hd %hd", idx1, idx2));
+ break;
+ }
case JVM_CONSTANT_InvokeDynamic: {
*bytes = tag;
idx1 = extract_low_short_from_int(*int_at_addr(idx));
@@ -2176,6 +2435,21 @@
case JVM_CONSTANT_MethodTypeInError :
st->print("signature_index=%d", method_type_index_at(index));
break;
+ case JVM_CONSTANT_Dynamic :
+ case JVM_CONSTANT_DynamicInError :
+ {
+ st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index));
+ st->print(" type_index=%d", invoke_dynamic_name_and_type_ref_index_at(index));
+ int argc = invoke_dynamic_argument_count_at(index);
+ if (argc > 0) {
+ for (int arg_i = 0; arg_i < argc; arg_i++) {
+ int arg = invoke_dynamic_argument_index_at(index, arg_i);
+ st->print((arg_i == 0 ? " arguments={%d" : ", %d"), arg);
+ }
+ st->print("}");
+ }
+ }
+ break;
case JVM_CONSTANT_InvokeDynamic :
{
st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index));