mirror of
https://github.com/openjdk/jdk18u.git
synced 2025-12-12 04:52:54 -06:00
8279822: CI: Constant pool entries in error state are not supported
Reviewed-by: dlong, kvn Backport-of: c5c8c0644d9442846de15422285fffeb91c3e0a1
This commit is contained in:
parent
29c7f1c427
commit
038983353a
@ -416,11 +416,11 @@ void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, Growabl
|
|||||||
// Avoid calling get_constant() which will try to allocate
|
// Avoid calling get_constant() which will try to allocate
|
||||||
// unloaded constant. We need only constant's type.
|
// unloaded constant. We need only constant's type.
|
||||||
int index = s.get_constant_pool_index();
|
int index = s.get_constant_pool_index();
|
||||||
constantTag tag = s.get_constant_pool_tag(index);
|
BasicType con_bt = s.get_basic_type_for_constant_at(index);
|
||||||
if (tag.is_long() || tag.is_double()) {
|
if (con_bt == T_LONG || con_bt == T_DOUBLE) {
|
||||||
// Only longs and doubles use 2 stack slots.
|
// Only longs and doubles use 2 stack slots.
|
||||||
state.lpush();
|
state.lpush();
|
||||||
} else if (tag.basic_type() == T_OBJECT) {
|
} else if (con_bt == T_OBJECT) {
|
||||||
state.apush(unknown_obj);
|
state.apush(unknown_obj);
|
||||||
} else {
|
} else {
|
||||||
state.spush();
|
state.spush();
|
||||||
|
|||||||
@ -127,6 +127,17 @@ public:
|
|||||||
bool is_valid() const {
|
bool is_valid() const {
|
||||||
return basic_type() != T_ILLEGAL;
|
return basic_type() != T_ILLEGAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_loaded() const {
|
||||||
|
if (is_valid()) {
|
||||||
|
if (is_reference_type(basic_type())) {
|
||||||
|
return as_object()->is_loaded();
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Debugging output
|
// Debugging output
|
||||||
void print();
|
void print();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -503,13 +503,6 @@ ciKlass* ciEnv::get_klass_by_name_impl(ciKlass* accessing_klass,
|
|||||||
domain = Handle(current, accessing_klass->protection_domain());
|
domain = Handle(current, accessing_klass->protection_domain());
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup up the proper type to return on OOM
|
|
||||||
ciKlass* fail_type;
|
|
||||||
if (sym->char_at(0) == JVM_SIGNATURE_ARRAY) {
|
|
||||||
fail_type = _unloaded_ciobjarrayklass;
|
|
||||||
} else {
|
|
||||||
fail_type = _unloaded_ciinstance_klass;
|
|
||||||
}
|
|
||||||
Klass* found_klass;
|
Klass* found_klass;
|
||||||
{
|
{
|
||||||
ttyUnlocker ttyul; // release tty lock to avoid ordering problems
|
ttyUnlocker ttyul; // release tty lock to avoid ordering problems
|
||||||
@ -591,7 +584,6 @@ ciKlass* ciEnv::get_klass_by_index_impl(const constantPoolHandle& cpool,
|
|||||||
int index,
|
int index,
|
||||||
bool& is_accessible,
|
bool& is_accessible,
|
||||||
ciInstanceKlass* accessor) {
|
ciInstanceKlass* accessor) {
|
||||||
EXCEPTION_CONTEXT;
|
|
||||||
Klass* klass = NULL;
|
Klass* klass = NULL;
|
||||||
Symbol* klass_name = NULL;
|
Symbol* klass_name = NULL;
|
||||||
|
|
||||||
@ -599,7 +591,7 @@ ciKlass* ciEnv::get_klass_by_index_impl(const constantPoolHandle& cpool,
|
|||||||
klass_name = cpool->symbol_at(index);
|
klass_name = cpool->symbol_at(index);
|
||||||
} else {
|
} else {
|
||||||
// Check if it's resolved if it's not a symbol constant pool entry.
|
// Check if it's resolved if it's not a symbol constant pool entry.
|
||||||
klass = ConstantPool::klass_at_if_loaded(cpool, index);
|
klass = ConstantPool::klass_at_if_loaded(cpool, index);
|
||||||
// Try to look it up by name.
|
// Try to look it up by name.
|
||||||
if (klass == NULL) {
|
if (klass == NULL) {
|
||||||
klass_name = cpool->klass_name_at(index);
|
klass_name = cpool->klass_name_at(index);
|
||||||
@ -666,7 +658,6 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
|||||||
int pool_index, int cache_index,
|
int pool_index, int cache_index,
|
||||||
ciInstanceKlass* accessor) {
|
ciInstanceKlass* accessor) {
|
||||||
bool ignore_will_link;
|
bool ignore_will_link;
|
||||||
EXCEPTION_CONTEXT;
|
|
||||||
int index = pool_index;
|
int index = pool_index;
|
||||||
if (cache_index >= 0) {
|
if (cache_index >= 0) {
|
||||||
assert(index < 0, "only one kind of index at a time");
|
assert(index < 0, "only one kind of index at a time");
|
||||||
@ -677,12 +668,14 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
|||||||
return ciConstant(T_OBJECT, get_object(NULL));
|
return ciConstant(T_OBJECT, get_object(NULL));
|
||||||
}
|
}
|
||||||
BasicType bt = T_OBJECT;
|
BasicType bt = T_OBJECT;
|
||||||
if (cpool->tag_at(index).is_dynamic_constant())
|
if (cpool->tag_at(index).is_dynamic_constant()) {
|
||||||
bt = Signature::basic_type(cpool->uncached_signature_ref_at(index));
|
bt = Signature::basic_type(cpool->uncached_signature_ref_at(index));
|
||||||
if (is_reference_type(bt)) {
|
}
|
||||||
} else {
|
if (!is_reference_type(bt)) {
|
||||||
// we have to unbox the primitive value
|
// we have to unbox the primitive value
|
||||||
if (!is_java_primitive(bt)) return ciConstant();
|
if (!is_java_primitive(bt)) {
|
||||||
|
return ciConstant();
|
||||||
|
}
|
||||||
jvalue value;
|
jvalue value;
|
||||||
BasicType bt2 = java_lang_boxing_object::get_value(obj, &value);
|
BasicType bt2 = java_lang_boxing_object::get_value(obj, &value);
|
||||||
assert(bt2 == bt, "");
|
assert(bt2 == bt, "");
|
||||||
@ -717,6 +710,7 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
|||||||
} else if (tag.is_double()) {
|
} else if (tag.is_double()) {
|
||||||
return ciConstant((jdouble)cpool->double_at(index));
|
return ciConstant((jdouble)cpool->double_at(index));
|
||||||
} else if (tag.is_string()) {
|
} else if (tag.is_string()) {
|
||||||
|
EXCEPTION_CONTEXT;
|
||||||
oop string = NULL;
|
oop string = NULL;
|
||||||
assert(cache_index >= 0, "should have a cache index");
|
assert(cache_index >= 0, "should have a cache index");
|
||||||
string = cpool->string_at(index, cache_index, THREAD);
|
string = cpool->string_at(index, cache_index, THREAD);
|
||||||
@ -733,24 +727,18 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
|||||||
return ciConstant(T_OBJECT, constant);
|
return ciConstant(T_OBJECT, constant);
|
||||||
}
|
}
|
||||||
} else if (tag.is_unresolved_klass_in_error()) {
|
} else if (tag.is_unresolved_klass_in_error()) {
|
||||||
return ciConstant();
|
return ciConstant(T_OBJECT, get_unloaded_klass_mirror(NULL));
|
||||||
} else if (tag.is_klass() || tag.is_unresolved_klass()) {
|
} else if (tag.is_klass() || tag.is_unresolved_klass()) {
|
||||||
// 4881222: allow ldc to take a class type
|
|
||||||
ciKlass* klass = get_klass_by_index_impl(cpool, index, ignore_will_link, accessor);
|
ciKlass* klass = get_klass_by_index_impl(cpool, index, ignore_will_link, accessor);
|
||||||
if (HAS_PENDING_EXCEPTION) {
|
|
||||||
CLEAR_PENDING_EXCEPTION;
|
|
||||||
record_out_of_memory_failure();
|
|
||||||
return ciConstant();
|
|
||||||
}
|
|
||||||
assert (klass->is_instance_klass() || klass->is_array_klass(),
|
assert (klass->is_instance_klass() || klass->is_array_klass(),
|
||||||
"must be an instance or array klass ");
|
"must be an instance or array klass ");
|
||||||
return ciConstant(T_OBJECT, klass->java_mirror());
|
return ciConstant(T_OBJECT, klass->java_mirror());
|
||||||
} else if (tag.is_method_type()) {
|
} else if (tag.is_method_type() || tag.is_method_type_in_error()) {
|
||||||
// must execute Java code to link this CP entry into cache[i].f1
|
// must execute Java code to link this CP entry into cache[i].f1
|
||||||
ciSymbol* signature = get_symbol(cpool->method_type_signature_at(index));
|
ciSymbol* signature = get_symbol(cpool->method_type_signature_at(index));
|
||||||
ciObject* ciobj = get_unloaded_method_type_constant(signature);
|
ciObject* ciobj = get_unloaded_method_type_constant(signature);
|
||||||
return ciConstant(T_OBJECT, ciobj);
|
return ciConstant(T_OBJECT, ciobj);
|
||||||
} else if (tag.is_method_handle()) {
|
} else if (tag.is_method_handle() || tag.is_method_handle_in_error()) {
|
||||||
// must execute Java code to link this CP entry into cache[i].f1
|
// must execute Java code to link this CP entry into cache[i].f1
|
||||||
int ref_kind = cpool->method_handle_ref_kind_at(index);
|
int ref_kind = cpool->method_handle_ref_kind_at(index);
|
||||||
int callee_index = cpool->method_handle_klass_index_at(index);
|
int callee_index = cpool->method_handle_klass_index_at(index);
|
||||||
@ -759,10 +747,10 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
|||||||
ciSymbol* signature = get_symbol(cpool->method_handle_signature_ref_at(index));
|
ciSymbol* signature = get_symbol(cpool->method_handle_signature_ref_at(index));
|
||||||
ciObject* ciobj = get_unloaded_method_handle_constant(callee, name, signature, ref_kind);
|
ciObject* ciobj = get_unloaded_method_handle_constant(callee, name, signature, ref_kind);
|
||||||
return ciConstant(T_OBJECT, ciobj);
|
return ciConstant(T_OBJECT, ciobj);
|
||||||
} else if (tag.is_dynamic_constant()) {
|
} else if (tag.is_dynamic_constant() || tag.is_dynamic_constant_in_error()) {
|
||||||
return ciConstant();
|
return ciConstant(); // not supported
|
||||||
} else {
|
} else {
|
||||||
ShouldNotReachHere();
|
assert(false, "unknown tag: %d (%s)", tag.value(), tag.internal_name());
|
||||||
return ciConstant();
|
return ciConstant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -251,6 +251,14 @@ constantTag ciBytecodeStream::get_constant_pool_tag(int index) const {
|
|||||||
return _method->get_Method()->constants()->constant_tag_at(index);
|
return _method->get_Method()->constants()->constant_tag_at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// ciBytecodeStream::get_basic_type_for_constant_at
|
||||||
|
//
|
||||||
|
BasicType ciBytecodeStream::get_basic_type_for_constant_at(int index) const {
|
||||||
|
VM_ENTRY_MARK;
|
||||||
|
return _method->get_Method()->constants()->basic_type_for_constant_at(index);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// ciBytecodeStream::get_field_index
|
// ciBytecodeStream::get_field_index
|
||||||
//
|
//
|
||||||
|
|||||||
@ -140,7 +140,7 @@ public:
|
|||||||
|
|
||||||
bool is_wide() const { return ( _pc == _was_wide ); }
|
bool is_wide() const { return ( _pc == _was_wide ); }
|
||||||
|
|
||||||
// Does this instruction contain an index which refes into the CP cache?
|
// Does this instruction contain an index which refers into the CP cache?
|
||||||
bool has_cache_index() const { return Bytecodes::uses_cp_cache(cur_bc_raw()); }
|
bool has_cache_index() const { return Bytecodes::uses_cp_cache(cur_bc_raw()); }
|
||||||
|
|
||||||
int get_index_u1() const {
|
int get_index_u1() const {
|
||||||
@ -226,8 +226,9 @@ public:
|
|||||||
// constant. Do not attempt to resolve it, since that would require
|
// constant. Do not attempt to resolve it, since that would require
|
||||||
// execution of Java code. If it is not resolved, return an unloaded
|
// execution of Java code. If it is not resolved, return an unloaded
|
||||||
// object (ciConstant.as_object()->is_loaded() == false).
|
// object (ciConstant.as_object()->is_loaded() == false).
|
||||||
ciConstant get_constant();
|
ciConstant get_constant();
|
||||||
constantTag get_constant_pool_tag(int index) const;
|
constantTag get_constant_pool_tag(int index) const;
|
||||||
|
BasicType get_basic_type_for_constant_at(int index) const;
|
||||||
|
|
||||||
// True if the klass-using bytecode points to an unresolved klass
|
// True if the klass-using bytecode points to an unresolved klass
|
||||||
bool is_unresolved_klass() const {
|
bool is_unresolved_klass() const {
|
||||||
@ -235,9 +236,17 @@ public:
|
|||||||
return tag.is_unresolved_klass();
|
return tag.is_unresolved_klass();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_unresolved_klass_in_error() const {
|
bool is_in_error() const {
|
||||||
constantTag tag = get_constant_pool_tag(get_klass_index());
|
assert(cur_bc() == Bytecodes::_ldc ||
|
||||||
return tag.is_unresolved_klass_in_error();
|
cur_bc() == Bytecodes::_ldc_w ||
|
||||||
|
cur_bc() == Bytecodes::_ldc2_w, "not supported: %s", Bytecodes::name(cur_bc()));
|
||||||
|
|
||||||
|
int index = get_constant_pool_index();
|
||||||
|
constantTag tag = get_constant_pool_tag(index);
|
||||||
|
return tag.is_unresolved_klass_in_error() ||
|
||||||
|
tag.is_method_handle_in_error() ||
|
||||||
|
tag.is_method_type_in_error() ||
|
||||||
|
tag.is_dynamic_constant_in_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this bytecode is one of get_field, get_static, put_field,
|
// If this bytecode is one of get_field, get_static, put_field,
|
||||||
|
|||||||
@ -720,6 +720,11 @@ void ciTypeFlow::StateVector::do_jsr(ciBytecodeStream* str) {
|
|||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// ciTypeFlow::StateVector::do_ldc
|
// ciTypeFlow::StateVector::do_ldc
|
||||||
void ciTypeFlow::StateVector::do_ldc(ciBytecodeStream* str) {
|
void ciTypeFlow::StateVector::do_ldc(ciBytecodeStream* str) {
|
||||||
|
if (str->is_in_error()) {
|
||||||
|
trap(str, NULL, Deoptimization::make_trap_request(Deoptimization::Reason_unhandled,
|
||||||
|
Deoptimization::Action_none));
|
||||||
|
return;
|
||||||
|
}
|
||||||
ciConstant con = str->get_constant();
|
ciConstant con = str->get_constant();
|
||||||
if (con.is_valid()) {
|
if (con.is_valid()) {
|
||||||
BasicType basic_type = con.basic_type();
|
BasicType basic_type = con.basic_type();
|
||||||
@ -735,14 +740,10 @@ void ciTypeFlow::StateVector::do_ldc(ciBytecodeStream* str) {
|
|||||||
push_translate(ciType::make(basic_type));
|
push_translate(ciType::make(basic_type));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (str->is_unresolved_klass_in_error()) {
|
// OutOfMemoryError in the CI while loading constant.
|
||||||
trap(str, NULL, Deoptimization::make_trap_request(Deoptimization::Reason_unhandled,
|
// Unresolved condy also lands here (not yet supported).
|
||||||
Deoptimization::Action_none));
|
push_null();
|
||||||
} else {
|
outer()->record_failure("ldc did not link");
|
||||||
// OutOfMemoryError in the CI while loading constant
|
|
||||||
push_null();
|
|
||||||
outer()->record_failure("ldc did not link");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2173,7 +2174,7 @@ bool ciTypeFlow::can_trap(ciBytecodeStream& str) {
|
|||||||
case Bytecodes::_ldc:
|
case Bytecodes::_ldc:
|
||||||
case Bytecodes::_ldc_w:
|
case Bytecodes::_ldc_w:
|
||||||
case Bytecodes::_ldc2_w:
|
case Bytecodes::_ldc2_w:
|
||||||
return str.is_unresolved_klass_in_error();
|
return str.is_in_error();
|
||||||
|
|
||||||
case Bytecodes::_aload_0:
|
case Bytecodes::_aload_0:
|
||||||
// These bytecodes can trap for rewriting. We need to assume that
|
// These bytecodes can trap for rewriting. We need to assume that
|
||||||
|
|||||||
@ -885,11 +885,9 @@ void ConstantPool::save_and_throw_exception(const constantPoolHandle& this_cp, i
|
|||||||
|
|
||||||
constantTag ConstantPool::constant_tag_at(int which) {
|
constantTag ConstantPool::constant_tag_at(int which) {
|
||||||
constantTag tag = tag_at(which);
|
constantTag tag = tag_at(which);
|
||||||
if (tag.is_dynamic_constant() ||
|
if (tag.is_dynamic_constant()) {
|
||||||
tag.is_dynamic_constant_in_error()) {
|
|
||||||
BasicType bt = basic_type_for_constant_at(which);
|
BasicType bt = basic_type_for_constant_at(which);
|
||||||
// dynamic constant could return an array, treat as object
|
return constantTag(constantTag::type2tag(bt));
|
||||||
return constantTag::ofBasicType(is_reference_type(bt) ? T_OBJECT : bt);
|
|
||||||
}
|
}
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
@ -976,7 +974,6 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
|
|||||||
switch (tag.value()) {
|
switch (tag.value()) {
|
||||||
|
|
||||||
case JVM_CONSTANT_UnresolvedClass:
|
case JVM_CONSTANT_UnresolvedClass:
|
||||||
case JVM_CONSTANT_UnresolvedClassInError:
|
|
||||||
case JVM_CONSTANT_Class:
|
case JVM_CONSTANT_Class:
|
||||||
{
|
{
|
||||||
assert(cache_index == _no_index_sentinel, "should not have been set");
|
assert(cache_index == _no_index_sentinel, "should not have been set");
|
||||||
@ -1044,14 +1041,6 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
|
|||||||
result_oop = string_at_impl(this_cp, index, cache_index, CHECK_NULL);
|
result_oop = string_at_impl(this_cp, index, cache_index, CHECK_NULL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JVM_CONSTANT_DynamicInError:
|
|
||||||
case JVM_CONSTANT_MethodHandleInError:
|
|
||||||
case JVM_CONSTANT_MethodTypeInError:
|
|
||||||
{
|
|
||||||
throw_resolution_error(this_cp, index, CHECK_NULL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JVM_CONSTANT_MethodHandle:
|
case JVM_CONSTANT_MethodHandle:
|
||||||
{
|
{
|
||||||
int ref_kind = this_cp->method_handle_ref_kind_at(index);
|
int ref_kind = this_cp->method_handle_ref_kind_at(index);
|
||||||
@ -1065,11 +1054,14 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
|
|||||||
callee_index, name->as_C_string(), signature->as_C_string());
|
callee_index, name->as_C_string(), signature->as_C_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
Klass* callee = klass_at_impl(this_cp, callee_index, CHECK_NULL);
|
Klass* callee = klass_at_impl(this_cp, callee_index, THREAD);
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Check constant pool method consistency
|
// Check constant pool method consistency
|
||||||
if ((callee->is_interface() && m_tag.is_method()) ||
|
if ((callee->is_interface() && m_tag.is_method()) ||
|
||||||
((!callee->is_interface() && m_tag.is_interface_method()))) {
|
(!callee->is_interface() && m_tag.is_interface_method())) {
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
stringStream ss;
|
stringStream ss;
|
||||||
ss.print("Inconsistent constant pool data in classfile for class %s. "
|
ss.print("Inconsistent constant pool data in classfile for class %s. "
|
||||||
@ -1081,17 +1073,18 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
|
|||||||
index,
|
index,
|
||||||
callee->is_interface() ? "CONSTANT_MethodRef" : "CONSTANT_InterfaceMethodRef",
|
callee->is_interface() ? "CONSTANT_MethodRef" : "CONSTANT_InterfaceMethodRef",
|
||||||
callee->is_interface() ? "CONSTANT_InterfaceMethodRef" : "CONSTANT_MethodRef");
|
callee->is_interface() ? "CONSTANT_InterfaceMethodRef" : "CONSTANT_MethodRef");
|
||||||
THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), ss.as_string());
|
Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IncompatibleClassChangeError(), "%s", ss.as_string());
|
||||||
|
save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
Klass* klass = this_cp->pool_holder();
|
Klass* klass = this_cp->pool_holder();
|
||||||
Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind,
|
Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind,
|
||||||
callee, name, signature,
|
callee, name, signature,
|
||||||
THREAD);
|
THREAD);
|
||||||
result_oop = value();
|
|
||||||
if (HAS_PENDING_EXCEPTION) {
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
|
save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
|
||||||
}
|
}
|
||||||
|
result_oop = value();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1136,10 +1129,15 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
|
|||||||
result_oop = java_lang_boxing_object::create(T_DOUBLE, &prim_value, CHECK_NULL);
|
result_oop = java_lang_boxing_object::create(T_DOUBLE, &prim_value, CHECK_NULL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case JVM_CONSTANT_UnresolvedClassInError:
|
||||||
|
case JVM_CONSTANT_DynamicInError:
|
||||||
|
case JVM_CONSTANT_MethodHandleInError:
|
||||||
|
case JVM_CONSTANT_MethodTypeInError:
|
||||||
|
throw_resolution_error(this_cp, index, CHECK_NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DEBUG_ONLY( tty->print_cr("*** %p: tag at CP[%d/%d] = %d",
|
fatal("unexpected constant tag at CP %p[%d/%d] = %d", this_cp(), index, cache_index, tag.value());
|
||||||
this_cp(), index, cache_index, tag.value()));
|
|
||||||
assert(false, "unexpected constant tag");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1865,33 +1865,35 @@ void Parse::do_one_bytecode() {
|
|||||||
case Bytecodes::_bipush: push(intcon(iter().get_constant_u1())); break;
|
case Bytecodes::_bipush: push(intcon(iter().get_constant_u1())); break;
|
||||||
case Bytecodes::_sipush: push(intcon(iter().get_constant_u2())); break;
|
case Bytecodes::_sipush: push(intcon(iter().get_constant_u2())); break;
|
||||||
case Bytecodes::_aconst_null: push(null()); break;
|
case Bytecodes::_aconst_null: push(null()); break;
|
||||||
|
|
||||||
case Bytecodes::_ldc:
|
case Bytecodes::_ldc:
|
||||||
case Bytecodes::_ldc_w:
|
case Bytecodes::_ldc_w:
|
||||||
case Bytecodes::_ldc2_w:
|
case Bytecodes::_ldc2_w: {
|
||||||
// If the constant is unresolved, run this BC once in the interpreter.
|
ciConstant constant = iter().get_constant();
|
||||||
{
|
if (constant.is_loaded()) {
|
||||||
ciConstant constant = iter().get_constant();
|
|
||||||
if (!constant.is_valid() ||
|
|
||||||
(constant.basic_type() == T_OBJECT &&
|
|
||||||
!constant.as_object()->is_loaded())) {
|
|
||||||
int index = iter().get_constant_pool_index();
|
|
||||||
constantTag tag = iter().get_constant_pool_tag(index);
|
|
||||||
uncommon_trap(Deoptimization::make_trap_request
|
|
||||||
(Deoptimization::Reason_unloaded,
|
|
||||||
Deoptimization::Action_reinterpret,
|
|
||||||
index),
|
|
||||||
NULL, tag.internal_name());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(constant.basic_type() != T_OBJECT || constant.as_object()->is_instance(),
|
assert(constant.basic_type() != T_OBJECT || constant.as_object()->is_instance(),
|
||||||
"must be java_mirror of klass");
|
"must be java_mirror of klass");
|
||||||
const Type* con_type = Type::make_from_constant(constant);
|
const Type* con_type = Type::make_from_constant(constant);
|
||||||
if (con_type != NULL) {
|
if (con_type != NULL) {
|
||||||
push_node(con_type->basic_type(), makecon(con_type));
|
push_node(con_type->basic_type(), makecon(con_type));
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
// If the constant is unresolved or in error state, run this BC in the interpreter.
|
||||||
|
if (iter().is_in_error()) {
|
||||||
|
uncommon_trap(Deoptimization::make_trap_request(Deoptimization::Reason_unhandled,
|
||||||
|
Deoptimization::Action_none),
|
||||||
|
NULL, "constant in error state", true /* must_throw */);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int index = iter().get_constant_pool_index();
|
||||||
|
uncommon_trap(Deoptimization::make_trap_request(Deoptimization::Reason_unloaded,
|
||||||
|
Deoptimization::Action_reinterpret,
|
||||||
|
index),
|
||||||
|
NULL, "unresolved constant", false /* must_throw */);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Bytecodes::_aload_0:
|
case Bytecodes::_aload_0:
|
||||||
push( local(0) );
|
push( local(0) );
|
||||||
|
|||||||
@ -86,6 +86,13 @@ class constantTag {
|
|||||||
return _tag == JVM_CONSTANT_DynamicInError;
|
return _tag == JVM_CONSTANT_DynamicInError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_in_error() const {
|
||||||
|
return is_unresolved_klass_in_error() ||
|
||||||
|
is_method_handle_in_error() ||
|
||||||
|
is_method_type_in_error() ||
|
||||||
|
is_dynamic_constant_in_error();
|
||||||
|
}
|
||||||
|
|
||||||
bool is_klass_index() const { return _tag == JVM_CONSTANT_ClassIndex; }
|
bool is_klass_index() const { return _tag == JVM_CONSTANT_ClassIndex; }
|
||||||
bool is_string_index() const { return _tag == JVM_CONSTANT_StringIndex; }
|
bool is_string_index() const { return _tag == JVM_CONSTANT_StringIndex; }
|
||||||
|
|
||||||
@ -121,18 +128,24 @@ class constantTag {
|
|||||||
_tag = tag;
|
_tag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constantTag ofBasicType(BasicType bt) {
|
static jbyte type2tag(BasicType bt) {
|
||||||
if (is_subword_type(bt)) bt = T_INT;
|
if (is_subword_type(bt)) {
|
||||||
switch (bt) {
|
bt = T_INT;
|
||||||
case T_OBJECT: return constantTag(JVM_CONSTANT_String);
|
}
|
||||||
case T_INT: return constantTag(JVM_CONSTANT_Integer);
|
if (bt == T_ARRAY) {
|
||||||
case T_LONG: return constantTag(JVM_CONSTANT_Long);
|
bt = T_OBJECT;
|
||||||
case T_FLOAT: return constantTag(JVM_CONSTANT_Float);
|
}
|
||||||
case T_DOUBLE: return constantTag(JVM_CONSTANT_Double);
|
switch (bt) {
|
||||||
default: break;
|
case T_INT: return JVM_CONSTANT_Integer;
|
||||||
|
case T_LONG: return JVM_CONSTANT_Long;
|
||||||
|
case T_FLOAT: return JVM_CONSTANT_Float;
|
||||||
|
case T_DOUBLE: return JVM_CONSTANT_Double;
|
||||||
|
case T_OBJECT: return JVM_CONSTANT_String;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false, "not supported: %s", type2name(bt));
|
||||||
|
return JVM_CONSTANT_Invalid;
|
||||||
}
|
}
|
||||||
assert(false, "bad basic type for tag");
|
|
||||||
return constantTag();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jbyte value() const { return _tag; }
|
jbyte value() const { return _tag; }
|
||||||
|
|||||||
283
test/hotspot/jtreg/compiler/runtime/TestConstantsInError.java
Normal file
283
test/hotspot/jtreg/compiler/runtime/TestConstantsInError.java
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8279822
|
||||||
|
* @requires vm.flagless
|
||||||
|
* @library /test/lib
|
||||||
|
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||||
|
*
|
||||||
|
* @run main compiler.runtime.TestConstantsInError
|
||||||
|
*/
|
||||||
|
package compiler.runtime;
|
||||||
|
|
||||||
|
import jdk.internal.org.objectweb.asm.*;
|
||||||
|
import jdk.test.lib.Platform;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandleProxies;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static jdk.internal.org.objectweb.asm.ClassWriter.*;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
interface OutputProcessor {
|
||||||
|
default void process(OutputAnalyzer output, boolean isC1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class TestConstantsInError implements OutputProcessor {
|
||||||
|
static final String TEST_PREFIX = class2desc(TestConstantsInError.class) + "$Test";
|
||||||
|
|
||||||
|
public interface Test extends Runnable {}
|
||||||
|
|
||||||
|
|
||||||
|
interface Generator {
|
||||||
|
void generate(MethodVisitor mv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String class2desc(Class<?> cls) {
|
||||||
|
return cls.getName().replace('.', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String PATH = System.getProperty("test.classes", ".") + java.io.File.separator;
|
||||||
|
|
||||||
|
static byte[] generateClassFile(String suffix, Generator g) throws IOException {
|
||||||
|
var cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||||
|
String name = TEST_PREFIX + "_" + suffix;
|
||||||
|
cw.visit(V18, ACC_PUBLIC | ACC_SUPER, name, null, "java/lang/Object", null);
|
||||||
|
|
||||||
|
{
|
||||||
|
var mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "()V", null, null);
|
||||||
|
mv.visitCode();
|
||||||
|
g.generate(mv);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(0, 0);
|
||||||
|
}
|
||||||
|
byte[] classFile = cw.toByteArray();
|
||||||
|
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(PATH + name + ".class")) {
|
||||||
|
fos.write(classFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return classFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Test generate(String suffix, Class<? extends LinkageError> expectedError, Generator g) {
|
||||||
|
try {
|
||||||
|
byte[] classFile = generateClassFile(suffix, g);
|
||||||
|
MethodHandles.Lookup testLookup = MethodHandles.lookup().defineHiddenClass(classFile, true);
|
||||||
|
MethodHandle testMH = testLookup.findStatic(testLookup.lookupClass(), "test", MethodType.methodType(void.class));
|
||||||
|
|
||||||
|
testMH = MethodHandles.filterReturnValue(testMH,
|
||||||
|
MethodHandles.insertArguments(
|
||||||
|
MethodHandles.throwException(void.class, AssertionError.class),
|
||||||
|
0, new AssertionError("no exception thrown")));
|
||||||
|
|
||||||
|
// Install empty handler for linkage exceptions.
|
||||||
|
testMH = MethodHandles.catchException(testMH, expectedError,
|
||||||
|
MethodHandles.empty(MethodType.methodType(void.class, expectedError)));
|
||||||
|
|
||||||
|
return MethodHandleProxies.asInterfaceInstance(Test.class, testMH);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run(String name, Class<? extends LinkageError> expectedError, Generator g) {
|
||||||
|
Test test = generate(name, expectedError, g);
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestConstantClass extends TestConstantsInError {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
run("C1", NoClassDefFoundError.class, mv -> mv.visitLdcInsn(Type.getType("LUnknownClass;"))); // non-existent class
|
||||||
|
run("C2", IllegalAccessError.class, mv -> mv.visitLdcInsn(Type.getType("Ljava/lang/invoke/LambdaForm;"))); // inaccessible
|
||||||
|
|
||||||
|
// class loader constraints?
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(OutputAnalyzer results, boolean isC1) {
|
||||||
|
results.shouldMatch("Test_C1/.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_C2/.*::test \\(3 bytes\\)$");
|
||||||
|
|
||||||
|
if (isC1 && Platform.isAArch64()) { // no code patching
|
||||||
|
results.shouldMatch("Test_C1/.*::test \\(3 bytes\\) made not entrant")
|
||||||
|
.shouldMatch("Test_C2/.*::test \\(3 bytes\\) made not entrant");
|
||||||
|
} else {
|
||||||
|
results.shouldNotContain("made not entrant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processC2(OutputAnalyzer results) {
|
||||||
|
results.shouldNotContain("made not entrant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestConstantMethodHandle extends TestConstantsInError {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Non-existent holder class
|
||||||
|
run("MH1", NoClassDefFoundError.class,
|
||||||
|
mv -> mv.visitLdcInsn(new Handle(H_INVOKESTATIC, "UnknownClass", "ignored", "()V", false)));
|
||||||
|
|
||||||
|
// Inaccessible holder class
|
||||||
|
run("MH2", IllegalAccessError.class,
|
||||||
|
mv -> mv.visitLdcInsn(new Handle(H_INVOKESTATIC, "java/lang/invoke/LambdaForm", "ignored", "()V", false)));
|
||||||
|
|
||||||
|
// Method vs InterfaceMethod mismatch
|
||||||
|
run("MH3", IncompatibleClassChangeError.class,
|
||||||
|
mv -> mv.visitLdcInsn(new Handle(H_INVOKESTATIC, "java/lang/Object", "ignored", "()V", true)));
|
||||||
|
|
||||||
|
// Non-existent method
|
||||||
|
run("MH4", NoSuchMethodError.class,
|
||||||
|
mv -> mv.visitLdcInsn(new Handle(H_INVOKESTATIC, "java/lang/Object", "cast", "()V", false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(OutputAnalyzer results, boolean isC1) {
|
||||||
|
results.shouldMatch("Test_MH1/.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_MH2/.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_MH3/.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_MH4/.*::test \\(3 bytes\\)$");
|
||||||
|
|
||||||
|
if (isC1 && Platform.isAArch64()) { // no code patching
|
||||||
|
results.shouldMatch("Test_MH1/.*::test \\(3 bytes\\) made not entrant")
|
||||||
|
.shouldMatch("Test_MH2/.*::test \\(3 bytes\\) made not entrant")
|
||||||
|
.shouldMatch("Test_MH3/.*::test \\(3 bytes\\) made not entrant")
|
||||||
|
.shouldMatch("Test_MH4/.*::test \\(3 bytes\\) made not entrant");
|
||||||
|
} else {
|
||||||
|
results.shouldNotContain("made not entrant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestConstantMethodType extends TestConstantsInError {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
run("MT1", NoClassDefFoundError.class,
|
||||||
|
mv -> mv.visitLdcInsn(Type.getMethodType("(LUnknownClass;)V")));
|
||||||
|
run("MT2", NoClassDefFoundError.class,
|
||||||
|
mv -> mv.visitLdcInsn(Type.getMethodType("()LUnknownClass;")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(OutputAnalyzer results, boolean isC1) {
|
||||||
|
results.shouldMatch("Test_MT1/.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_MT2/.*::test \\(3 bytes\\)$");
|
||||||
|
|
||||||
|
if (isC1 && Platform.isAArch64()) { // no code patching
|
||||||
|
results.shouldMatch("Test_MT1/.*::test \\(3 bytes\\) made not entrant")
|
||||||
|
.shouldMatch("Test_MT2/.*::test \\(3 bytes\\) made not entrant");
|
||||||
|
} else {
|
||||||
|
results.shouldNotContain("made not entrant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestConstantDynamic extends TestConstantsInError {
|
||||||
|
static int bsm1() throws Exception {
|
||||||
|
throw new AssertionError("should not be invoked");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bsm2(MethodHandles.Lookup lookup, String name, Class c) throws Exception {
|
||||||
|
throw new Exception("expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Handle BSM1 = new Handle(H_INVOKESTATIC, class2desc(TestConstantDynamic.class), "bsm1", "()I", false);
|
||||||
|
static final Handle BSM2 = new Handle(H_INVOKESTATIC, class2desc(TestConstantDynamic.class), "bsm2",
|
||||||
|
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)I",
|
||||||
|
false);
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
run("CD1", NoClassDefFoundError.class,
|
||||||
|
mv -> {
|
||||||
|
Handle bsm = new Handle(H_INVOKESTATIC, "UnknownClass", "unknown", "()LUnknownClass;", false);
|
||||||
|
mv.visitLdcInsn(new ConstantDynamic("tmp", "LUnknownClass;", bsm));
|
||||||
|
});
|
||||||
|
run("CD2", NoSuchMethodError.class,
|
||||||
|
mv -> {
|
||||||
|
Handle bsm = new Handle(H_INVOKESTATIC, class2desc(TestConstantDynamic.class), "unknown", "()I", false);
|
||||||
|
mv.visitLdcInsn(new ConstantDynamic("tmp", "LUnknownClass;", bsm));
|
||||||
|
});
|
||||||
|
run("CD3", BootstrapMethodError.class, mv -> mv.visitLdcInsn(new ConstantDynamic("tmp", "I", BSM1)));
|
||||||
|
run("CD4", BootstrapMethodError.class, mv -> mv.visitLdcInsn(new ConstantDynamic("tmp", "I", BSM2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(OutputAnalyzer results, boolean isC1) {
|
||||||
|
if (isC1) {
|
||||||
|
results.shouldMatch("Test_CD1.*::test \\(3 bytes\\) COMPILE SKIPPED: could not resolve a constant")
|
||||||
|
.shouldMatch("Test_CD2.*::test \\(3 bytes\\) COMPILE SKIPPED: could not resolve a constant")
|
||||||
|
.shouldMatch("Test_CD3.*::test \\(3 bytes\\) COMPILE SKIPPED: could not resolve a constant")
|
||||||
|
.shouldMatch("Test_CD4.*::test \\(3 bytes\\) COMPILE SKIPPED: could not resolve a constant");
|
||||||
|
} else {
|
||||||
|
results.shouldMatch("Test_CD1.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_CD2.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_CD3.*::test \\(3 bytes\\)$")
|
||||||
|
.shouldMatch("Test_CD4.*::test \\(3 bytes\\)$");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run(TestConstantsInError test) throws Exception {
|
||||||
|
List<String> commonArgs = List.of(
|
||||||
|
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED",
|
||||||
|
"-Xbatch", "-XX:CompileThreshold=100",
|
||||||
|
"-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,*::test",
|
||||||
|
"-XX:+PrintCompilation",
|
||||||
|
"-XX:CompileCommand=print,*::test",
|
||||||
|
"-Dtest.classes=" + System.getProperty("test.classes", "."),
|
||||||
|
"-XX:+IgnoreUnrecognizedVMOptions",
|
||||||
|
test.getClass().getName());
|
||||||
|
|
||||||
|
ArrayList<String> c1Args = new ArrayList<>();
|
||||||
|
c1Args.addAll(List.of("-XX:+TieredCompilation", "-XX:TieredStopAtLevel=1", "-XX:+TracePatching"));
|
||||||
|
c1Args.addAll(commonArgs);
|
||||||
|
|
||||||
|
OutputAnalyzer outputC1 = ProcessTools.executeTestJvm(c1Args)
|
||||||
|
.shouldHaveExitValue(0);
|
||||||
|
|
||||||
|
test.process(outputC1, true);
|
||||||
|
|
||||||
|
ArrayList<String> c2Args = new ArrayList<>();
|
||||||
|
c2Args.add("-XX:-TieredCompilation");
|
||||||
|
c2Args.addAll(commonArgs);
|
||||||
|
|
||||||
|
OutputAnalyzer outputC2 = ProcessTools.executeTestJvm(c2Args)
|
||||||
|
.shouldHaveExitValue(0);
|
||||||
|
|
||||||
|
test.process(outputC2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
run(new TestConstantClass());
|
||||||
|
run(new TestConstantMethodType());
|
||||||
|
run(new TestConstantMethodHandle());
|
||||||
|
run(new TestConstantDynamic());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user