8262377: Parallel class resolution loses constant pool error

Co-authored-by: Vladimir Ivanov <vlivanov@openjdk.org>
Co-authored-by: Coleen Phillimore <coleenp@openjdk.org>
Co-authored-by: Ioi Lam <iklam@openjdk.org>
Reviewed-by: dholmes, iklam, hseigel, kvn
This commit is contained in:
Coleen Phillimore 2021-03-10 20:58:18 +00:00
parent b482733f94
commit 57f16f9fe5
14 changed files with 361 additions and 109 deletions

View file

@ -658,6 +658,8 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
assert (constant->is_instance(), "must be an instance, or not? ");
return ciConstant(T_OBJECT, constant);
}
} else if (tag.is_unresolved_klass_in_error()) {
return ciConstant();
} 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);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2021, 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
@ -233,6 +233,11 @@ public:
return tag.is_unresolved_klass();
}
bool is_unresolved_klass_in_error() const {
constantTag tag = get_constant_pool_tag(get_klass_index());
return tag.is_unresolved_klass_in_error();
}
// If this bytecode is one of get_field, get_static, put_field,
// or put_static, get the referenced field.
ciField* get_field(bool& will_link);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2021, 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
@ -721,13 +721,8 @@ void ciTypeFlow::StateVector::do_jsr(ciBytecodeStream* str) {
// ciTypeFlow::StateVector::do_ldc
void ciTypeFlow::StateVector::do_ldc(ciBytecodeStream* str) {
ciConstant con = str->get_constant();
if (con.is_valid()) {
BasicType basic_type = con.basic_type();
if (basic_type == T_ILLEGAL) {
// OutOfMemoryError in the CI while loading constant
push_null();
outer()->record_failure("ldc did not link");
return;
}
if (is_reference_type(basic_type)) {
ciObject* obj = con.as_object();
if (obj->is_null_object()) {
@ -739,6 +734,16 @@ void ciTypeFlow::StateVector::do_ldc(ciBytecodeStream* str) {
} else {
push_translate(ciType::make(basic_type));
}
} else {
if (str->is_unresolved_klass_in_error()) {
trap(str, NULL, Deoptimization::make_trap_request(Deoptimization::Reason_unhandled,
Deoptimization::Action_none));
} else {
// OutOfMemoryError in the CI while loading constant
push_null();
outer()->record_failure("ldc did not link");
}
}
}
// ------------------------------------------------------------------
@ -2168,6 +2173,8 @@ bool ciTypeFlow::can_trap(ciBytecodeStream& str) {
case Bytecodes::_ldc:
case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w:
return str.is_unresolved_klass_in_error();
case Bytecodes::_aload_0:
// These bytecodes can trap for rewriting. We need to assume that
// they do not throw exceptions to make the monitor analysis work.

View file

@ -1958,6 +1958,7 @@ int java_lang_Throwable::_backtrace_offset;
int java_lang_Throwable::_detailMessage_offset;
int java_lang_Throwable::_stackTrace_offset;
int java_lang_Throwable::_depth_offset;
int java_lang_Throwable::_cause_offset;
int java_lang_Throwable::_static_unassigned_stacktrace_offset;
#define THROWABLE_FIELDS_DO(macro) \
@ -1965,6 +1966,7 @@ int java_lang_Throwable::_static_unassigned_stacktrace_offset;
macro(_detailMessage_offset, k, "detailMessage", string_signature, false); \
macro(_stackTrace_offset, k, "stackTrace", java_lang_StackTraceElement_array, false); \
macro(_depth_offset, k, "depth", int_signature, false); \
macro(_cause_offset, k, "cause", throwable_signature, false); \
macro(_static_unassigned_stacktrace_offset, k, "UNASSIGNED_STACK", java_lang_StackTraceElement_array, true)
void java_lang_Throwable::compute_offsets() {
@ -2005,6 +2007,9 @@ oop java_lang_Throwable::message(oop throwable) {
return throwable->obj_field(_detailMessage_offset);
}
oop java_lang_Throwable::cause(oop throwable) {
return throwable->obj_field(_cause_offset);
}
// Return Symbol for detailed_message or NULL
Symbol* java_lang_Throwable::detail_message(oop throwable) {

View file

@ -499,6 +499,7 @@ class java_lang_Throwable: AllStatic {
static int _detailMessage_offset;
static int _stackTrace_offset;
static int _depth_offset;
static int _cause_offset;
static int _static_unassigned_stacktrace_offset;
// StackTrace (programmatic access, new since 1.4)
@ -516,6 +517,7 @@ class java_lang_Throwable: AllStatic {
static int get_detailMessage_offset() { CHECK_INIT(_detailMessage_offset); }
// Message
static oop message(oop throwable);
static oop cause(oop throwable);
static void set_message(oop throwable, oop value);
static Symbol* detail_message(oop throwable);
static void print_stack_element(outputStream *st, Method* method, int bci);

View file

@ -33,19 +33,27 @@
#include "runtime/safepoint.hpp"
#include "utilities/hashtable.inline.hpp"
// add new entry to the table
// create new error entry
void ResolutionErrorTable::add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int cp_index,
Symbol* error, Symbol* message)
Symbol* error, Symbol* message,
Symbol* cause, Symbol* cause_msg)
{
assert_locked_or_safepoint(SystemDictionary_lock);
assert(!pool.is_null() && error != NULL, "adding NULL obj");
ResolutionErrorEntry* entry = new_entry(hash, pool(), cp_index, error, message);
ResolutionErrorEntry* entry = (ResolutionErrorEntry*)Hashtable<ConstantPool*, mtClass>::new_entry(hash, pool());
entry->set_cp_index(cp_index);
entry->set_error(error);
entry->set_message(message);
entry->set_nest_host_error(NULL);
entry->set_cause(cause);
entry->set_cause_msg(cause_msg);
add_entry(index, entry);
}
// add new entry to the table
// create new nest host error entry
void ResolutionErrorTable::add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int cp_index,
const char* message)
@ -53,7 +61,14 @@ void ResolutionErrorTable::add_entry(int index, unsigned int hash,
assert_locked_or_safepoint(SystemDictionary_lock);
assert(!pool.is_null() && message != NULL, "adding NULL obj");
ResolutionErrorEntry* entry = new_entry(hash, pool(), cp_index, message);
ResolutionErrorEntry* entry = (ResolutionErrorEntry*)Hashtable<ConstantPool*, mtClass>::new_entry(hash, pool());
entry->set_cp_index(cp_index);
entry->set_nest_host_error(message);
entry->set_error(NULL);
entry->set_message(NULL);
entry->set_cause(NULL);
entry->set_cause_msg(NULL);
add_entry(index, entry);
}
@ -87,37 +102,24 @@ void ResolutionErrorEntry::set_message(Symbol* c) {
}
}
void ResolutionErrorEntry::set_cause(Symbol* c) {
_cause = c;
if (_cause != NULL) {
_cause->increment_refcount();
}
}
void ResolutionErrorEntry::set_cause_msg(Symbol* c) {
_cause_msg = c;
if (_cause_msg != NULL) {
_cause_msg->increment_refcount();
}
}
void ResolutionErrorEntry::set_nest_host_error(const char* message) {
_nest_host_error = message;
}
// create new error entry
ResolutionErrorEntry* ResolutionErrorTable::new_entry(int hash, ConstantPool* pool,
int cp_index, Symbol* error,
Symbol* message)
{
ResolutionErrorEntry* entry = (ResolutionErrorEntry*)Hashtable<ConstantPool*, mtClass>::new_entry(hash, pool);
entry->set_cp_index(cp_index);
entry->set_error(error);
entry->set_message(message);
entry->set_nest_host_error(NULL);
return entry;
}
// create new nest host error entry
ResolutionErrorEntry* ResolutionErrorTable::new_entry(int hash, ConstantPool* pool,
int cp_index, const char* message)
{
ResolutionErrorEntry* entry = (ResolutionErrorEntry*)Hashtable<ConstantPool*, mtClass>::new_entry(hash, pool);
entry->set_cp_index(cp_index);
entry->set_nest_host_error(message);
entry->set_error(NULL);
entry->set_message(NULL);
return entry;
}
void ResolutionErrorTable::free_entry(ResolutionErrorEntry *entry) {
// decrement error refcount
if (entry->error() != NULL) {
@ -126,6 +128,12 @@ void ResolutionErrorTable::free_entry(ResolutionErrorEntry *entry) {
if (entry->message() != NULL) {
entry->message()->decrement_refcount();
}
if (entry->cause() != NULL) {
entry->cause()->decrement_refcount();
}
if (entry->cause_msg() != NULL) {
entry->cause_msg()->decrement_refcount();
}
if (entry->nest_host_error() != NULL) {
FREE_C_HEAP_ARRAY(char, entry->nest_host_error());
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2021, 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
@ -41,15 +41,12 @@ const int CPCACHE_INDEX_MANGLE_VALUE = 1000000;
class ResolutionErrorTable : public Hashtable<ConstantPool*, mtClass> {
private:
void free_entry(ResolutionErrorEntry *entry);
public:
ResolutionErrorTable(int table_size);
ResolutionErrorEntry* new_entry(int hash, ConstantPool* pool, int cp_index,
Symbol* error, Symbol* message);
ResolutionErrorEntry* new_entry(int hash, ConstantPool* pool, int cp_index,
const char* message);
void free_entry(ResolutionErrorEntry *entry);
ResolutionErrorEntry* bucket(int i) {
return (ResolutionErrorEntry*)Hashtable<ConstantPool*, mtClass>::bucket(i);
}
@ -64,7 +61,8 @@ public:
}
void add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int which, Symbol* error, Symbol* message);
const constantPoolHandle& pool, int which, Symbol* error, Symbol* message,
Symbol* cause, Symbol* cause_msg);
void add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int which, const char* message);
@ -99,6 +97,8 @@ class ResolutionErrorEntry : public HashtableEntry<ConstantPool*, mtClass> {
int _cp_index;
Symbol* _error;
Symbol* _message;
Symbol* _cause;
Symbol* _cause_msg;
const char* _nest_host_error;
public:
@ -113,6 +113,12 @@ class ResolutionErrorEntry : public HashtableEntry<ConstantPool*, mtClass> {
Symbol* message() const { return _message; }
void set_message(Symbol* c);
Symbol* cause() const { return _cause; }
void set_cause(Symbol* c);
Symbol* cause_msg() const { return _cause_msg; }
void set_cause_msg(Symbol* c);
const char* nest_host_error() const { return _nest_host_error; }
void set_nest_host_error(const char* message);

View file

@ -1982,12 +1982,16 @@ bool SystemDictionary::add_loader_constraint(Symbol* class_name,
// Add entry to resolution error table to record the error when the first
// attempt to resolve a reference to a class has failed.
void SystemDictionary::add_resolution_error(const constantPoolHandle& pool, int which,
Symbol* error, Symbol* message) {
Symbol* error, Symbol* message,
Symbol* cause, Symbol* cause_msg) {
unsigned int hash = resolution_errors()->compute_hash(pool, which);
int index = resolution_errors()->hash_to_index(hash);
{
MutexLocker ml(Thread::current(), SystemDictionary_lock);
resolution_errors()->add_entry(index, hash, pool, which, error, message);
ResolutionErrorEntry* entry = resolution_errors()->find_entry(index, hash, pool, which);
if (entry == NULL) {
resolution_errors()->add_entry(index, hash, pool, which, error, message, cause, cause_msg);
}
}
}
@ -1998,7 +2002,7 @@ void SystemDictionary::delete_resolution_error(ConstantPool* pool) {
// Lookup resolution error table. Returns error if found, otherwise NULL.
Symbol* SystemDictionary::find_resolution_error(const constantPoolHandle& pool, int which,
Symbol** message) {
Symbol** message, Symbol** cause, Symbol** cause_msg) {
unsigned int hash = resolution_errors()->compute_hash(pool, which);
int index = resolution_errors()->hash_to_index(hash);
{
@ -2006,6 +2010,8 @@ Symbol* SystemDictionary::find_resolution_error(const constantPoolHandle& pool,
ResolutionErrorEntry* entry = resolution_errors()->find_entry(index, hash, pool, which);
if (entry != NULL) {
*message = entry->message();
*cause = entry->cause();
*cause_msg = entry->cause_msg();
return entry->error();
} else {
return NULL;

View file

@ -277,10 +277,10 @@ public:
// Record the error when the first attempt to resolve a reference from a constant
// pool entry to a class fails.
static void add_resolution_error(const constantPoolHandle& pool, int which, Symbol* error,
Symbol* message);
Symbol* message, Symbol* cause = NULL, Symbol* cause_msg = NULL);
static void delete_resolution_error(ConstantPool* pool);
static Symbol* find_resolution_error(const constantPoolHandle& pool, int which,
Symbol** message);
Symbol** message, Symbol** cause, Symbol** cause_msg);
// Record a nest host resolution/validation error

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2021, 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
@ -535,13 +535,14 @@
template(object_object_signature, "(Ljava/lang/Object;)Ljava/lang/Object;") \
template(string_void_signature, "(Ljava/lang/String;)V") \
template(string_int_signature, "(Ljava/lang/String;)I") \
template(throwable_signature, "Ljava/lang/Throwable;") \
template(throwable_void_signature, "(Ljava/lang/Throwable;)V") \
template(void_throwable_signature, "()Ljava/lang/Throwable;") \
template(throwable_throwable_signature, "(Ljava/lang/Throwable;)Ljava/lang/Throwable;") \
template(class_void_signature, "(Ljava/lang/Class;)V") \
template(class_int_signature, "(Ljava/lang/Class;)I") \
template(class_long_signature, "(Ljava/lang/Class;)J") \
template(class_boolean_signature, "(Ljava/lang/Class;)Z") \
template(throwable_throwable_signature, "(Ljava/lang/Throwable;)Ljava/lang/Throwable;") \
template(throwable_string_void_signature, "(Ljava/lang/Throwable;Ljava/lang/String;)V") \
template(string_array_void_signature, "([Ljava/lang/String;)V") \
template(string_array_string_array_void_signature, "([Ljava/lang/String;[Ljava/lang/String;)V") \

View file

@ -483,7 +483,7 @@ void ConstantPool::trace_class_resolution(const constantPoolHandle& this_cp, Kla
}
Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which,
bool save_resolution_error, TRAPS) {
TRAPS) {
JavaThread* javaThread = THREAD->as_Java_thread();
// A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
@ -494,10 +494,14 @@ Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which,
int name_index = kslot.name_index();
assert(this_cp->tag_at(name_index).is_symbol(), "sanity");
// The tag must be JVM_CONSTANT_Class in order to read the correct value from
// the unresolved_klasses() array.
if (this_cp->tag_at(which).is_klass()) {
Klass* klass = this_cp->resolved_klasses()->at(resolved_klass_index);
if (klass != NULL) {
return klass;
}
}
// This tag doesn't change back to unresolved class unless at a safepoint.
if (this_cp->tag_at(which).is_unresolved_klass_in_error()) {
@ -535,29 +539,38 @@ Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which,
// Failed to resolve class. We must record the errors so that subsequent attempts
// to resolve this constant pool entry fail with the same error (JVMS 5.4.3).
if (HAS_PENDING_EXCEPTION) {
if (save_resolution_error) {
save_and_throw_exception(this_cp, which, constantTag(JVM_CONSTANT_UnresolvedClass), CHECK_NULL);
// If CHECK_NULL above doesn't return the exception, that means that
// some other thread has beaten us and has resolved the class.
// To preserve old behavior, we return the resolved class.
klass = this_cp->resolved_klasses()->at(resolved_klass_index);
Klass* klass = this_cp->resolved_klasses()->at(resolved_klass_index);
assert(klass != NULL, "must be resolved if exception was cleared");
return klass;
} else {
return NULL; // return the pending exception
}
}
// logging for class+resolve.
if (log_is_enabled(Debug, class, resolve)){
trace_class_resolution(this_cp, k);
}
Klass** adr = this_cp->resolved_klasses()->adr_at(resolved_klass_index);
Atomic::release_store(adr, k);
// The interpreter assumes when the tag is stored, the klass is resolved
// and the Klass* stored in _resolved_klasses is non-NULL, so we need
// hardware store ordering here.
this_cp->release_tag_at_put(which, JVM_CONSTANT_Class);
// We also need to CAS to not overwrite an error from a racing thread.
jbyte old_tag = Atomic::cmpxchg((jbyte*)this_cp->tag_addr_at(which),
(jbyte)JVM_CONSTANT_UnresolvedClass,
(jbyte)JVM_CONSTANT_Class);
// We need to recheck exceptions from racing thread and return the same.
if (old_tag == JVM_CONSTANT_UnresolvedClassInError) {
// Remove klass.
this_cp->resolved_klasses()->at_put(resolved_klass_index, NULL);
throw_resolution_error(this_cp, which, CHECK_NULL);
}
return k;
}
@ -572,9 +585,12 @@ Klass* ConstantPool::klass_at_if_loaded(const constantPoolHandle& this_cp, int w
int name_index = kslot.name_index();
assert(this_cp->tag_at(name_index).is_symbol(), "sanity");
if (this_cp->tag_at(which).is_klass()) {
Klass* k = this_cp->resolved_klasses()->at(resolved_klass_index);
if (k != NULL) {
assert(k != NULL, "should be resolved");
return k;
} else if (this_cp->tag_at(which).is_unresolved_klass_in_error()) {
return NULL;
} else {
Thread *thread = Thread::current();
Symbol* name = this_cp->symbol_at(name_index);
@ -767,7 +783,7 @@ void ConstantPool::resolve_string_constants_impl(const constantPoolHandle& this_
}
}
Symbol* ConstantPool::exception_message(const constantPoolHandle& this_cp, int which, constantTag tag, oop pending_exception) {
static Symbol* exception_message(const constantPoolHandle& this_cp, int which, constantTag tag, oop pending_exception) {
// Dig out the detailed message to reuse if possible
Symbol* message = java_lang_Throwable::detail_message(pending_exception);
if (message != NULL) {
@ -799,24 +815,57 @@ Symbol* ConstantPool::exception_message(const constantPoolHandle& this_cp, int w
return message;
}
static void add_resolution_error(const constantPoolHandle& this_cp, int which,
constantTag tag, oop pending_exception) {
Symbol* error = pending_exception->klass()->name();
oop cause = java_lang_Throwable::cause(pending_exception);
// Also dig out the exception cause, if present.
Symbol* cause_sym = NULL;
Symbol* cause_msg = NULL;
if (cause != NULL && cause != pending_exception) {
cause_sym = cause->klass()->name();
cause_msg = java_lang_Throwable::detail_message(cause);
}
Symbol* message = exception_message(this_cp, which, tag, pending_exception);
SystemDictionary::add_resolution_error(this_cp, which, error, message, cause_sym, cause_msg);
}
void ConstantPool::throw_resolution_error(const constantPoolHandle& this_cp, int which, TRAPS) {
ResourceMark rm(THREAD);
Symbol* message = NULL;
Symbol* error = SystemDictionary::find_resolution_error(this_cp, which, &message);
Symbol* cause = NULL;
Symbol* cause_msg = NULL;
Symbol* error = SystemDictionary::find_resolution_error(this_cp, which, &message, &cause, &cause_msg);
assert(error != NULL, "checking");
const char* cause_str = cause_msg != NULL ? cause_msg->as_C_string() : NULL;
CLEAR_PENDING_EXCEPTION;
if (message != NULL) {
ResourceMark rm;
THROW_MSG(error, message->as_C_string());
char* msg = message->as_C_string();
if (cause != NULL) {
Handle h_cause = Exceptions::new_exception(THREAD, cause, cause_str);
THROW_MSG_CAUSE(error, msg, h_cause);
} else {
THROW_MSG(error, msg);
}
} else {
if (cause != NULL) {
Handle h_cause = Exceptions::new_exception(THREAD, cause, cause_str);
THROW_CAUSE(error, h_cause);
} else {
THROW(error);
}
}
}
// 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();
int error_tag = tag.error_value();
@ -827,8 +876,7 @@ void ConstantPool::save_and_throw_exception(const constantPoolHandle& this_cp, i
// and OutOfMemoryError, etc, or if the thread was hit by stop()
// Needs clarification to section 5.4.3 of the VM spec (see 6308271)
} else if (this_cp->tag_at(which).value() != error_tag) {
Symbol* message = exception_message(this_cp, which, tag, PENDING_EXCEPTION);
SystemDictionary::add_resolution_error(this_cp, which, error, message);
add_resolution_error(this_cp, which, tag, PENDING_EXCEPTION);
// CAS in the tag. If a thread beat us to registering this error that's fine.
// If another thread resolved the reference, this is a race condition. This
// thread may have had a security manager or something temporary.
@ -947,7 +995,7 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
case JVM_CONSTANT_Class:
{
assert(cache_index == _no_index_sentinel, "should not have been set");
Klass* resolved = klass_at_impl(this_cp, index, true, CHECK_NULL);
Klass* resolved = klass_at_impl(this_cp, index, CHECK_NULL);
// ldc wants the java mirror.
result_oop = resolved->java_mirror();
break;
@ -1036,7 +1084,7 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
callee_index, name->as_C_string(), signature->as_C_string());
}
Klass* callee = klass_at_impl(this_cp, callee_index, true, CHECK_NULL);
Klass* callee = klass_at_impl(this_cp, callee_index, CHECK_NULL);
// Check constant pool method consistency
if ((callee->is_interface() && m_tag.is_method()) ||
@ -2336,14 +2384,8 @@ void ConstantPool::print_entry_on(const int index, outputStream* st) {
int resolved_klass_index = kslot.resolved_klass_index();
int name_index = kslot.name_index();
assert(tag_at(name_index).is_symbol(), "sanity");
Klass* klass = resolved_klasses()->at(resolved_klass_index);
if (klass != NULL) {
klass->print_value_on(st);
} else {
symbol_at(name_index)->print_value_on(st);
}
}
break;
case JVM_CONSTANT_MethodHandle :
case JVM_CONSTANT_MethodHandleInError :

View file

@ -418,13 +418,7 @@ class ConstantPool : public Metadata {
Klass* klass_at(int which, TRAPS) {
constantPoolHandle h_this(THREAD, this);
return klass_at_impl(h_this, which, true, THREAD);
}
// Version of klass_at that doesn't save the resolution error, called during deopt
Klass* klass_at_ignore_error(int which, TRAPS) {
constantPoolHandle h_this(THREAD, this);
return klass_at_impl(h_this, which, false, THREAD);
return klass_at_impl(h_this, which, THREAD);
}
CPKlassSlot klass_slot_at(int which) const {
@ -886,8 +880,7 @@ class ConstantPool : public Metadata {
// Implementation of methods that needs an exposed 'this' pointer, in order to
// handle GC while executing the method
static Klass* klass_at_impl(const constantPoolHandle& this_cp, int which,
bool save_resolution_error, TRAPS);
static Klass* klass_at_impl(const constantPoolHandle& this_cp, int which, TRAPS);
static oop string_at_impl(const constantPoolHandle& this_cp, int which, int obj_index, TRAPS);
static void trace_class_resolution(const constantPoolHandle& this_cp, Klass* k);
@ -903,7 +896,6 @@ class ConstantPool : public Metadata {
bool must_resolve, Handle if_not_available, TRAPS);
// Exception handling
static Symbol* exception_message(const constantPoolHandle& this_cp, int which, constantTag tag, oop pending_exception);
static void save_and_throw_exception(const constantPoolHandle& this_cp, int which, constantTag tag, TRAPS);
public:

View file

@ -1820,7 +1820,7 @@ void Deoptimization::load_class_by_index(const constantPoolHandle& constant_pool
// So this whole "class index" feature should probably be removed.
if (constant_pool->tag_at(index).is_unresolved_klass()) {
Klass* tk = constant_pool->klass_at_ignore_error(index, THREAD);
Klass* tk = constant_pool->klass_at(index, THREAD);
if (HAS_PENDING_EXCEPTION) {
// Exception happened during classloading. We ignore the exception here, since it
// is going to be rethrown since the current activation is going to be deoptimized and

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2021, 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 8262046
* @summary JVMS 5.4.3 If an attempt by the Java Virtual Machine to resolve a symbolic reference fails
* because an error is thrown that is an instance of LinkageError (or a subclass), then subsequent
* attempts to resolve the reference always fail with the same error that was thrown as a result of
* the initial resolution attempt.
* @run main/othervm SaveResolutionErrorTest
*/
public class SaveResolutionErrorTest {
static byte classfile_for_Tester[];
static byte classfile_for_Loadee[];
public static void main(java.lang.String[] args) throws Exception {
ClassLoader appLoader = SaveResolutionErrorTest.class.getClassLoader();
classfile_for_Tester = appLoader.getResourceAsStream("SaveResolutionErrorTest$Tester.class").readAllBytes();
classfile_for_Loadee = appLoader.getResourceAsStream("SaveResolutionErrorTest$Loadee.class").readAllBytes();
long started = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
System.out.println("Test: " + i);
MyLoader loader = new MyLoader(appLoader);
loader.doTest();
if (System.currentTimeMillis() - started > 100000) {
break;
}
}
System.out.println("Succeed");
}
public static class Tester extends Thread {
static int errorCount = 0;
synchronized static void incrError() {
++ errorCount;
}
public volatile static boolean go = false;
public static void doit() throws Exception {
System.out.println(Tester.class.getClassLoader());
Thread t1 = new Tester();
Thread t2 = new Tester();
t1.start();
t2.start();
go = true;
t1.join();
t2.join();
System.out.println("errorCount = " + errorCount);
if (errorCount != 0 && errorCount != 2) {
throw new RuntimeException("errorCount should be 0 or 2 but is " + errorCount);
}
}
static int foobar;
static boolean debug = false;
public void run() {
while (!go) { Thread.onSpinWait(); }
try {
// The first thread who tries to resolve the CP entry for the "Loadee" class
// should (most likely) result in a resolution error. This error, if it has
// happened, must also be reported by the second thread.
//
// In some rare conditions, the resolution may succeed. In this case, both
// threads must succeed. The reasons is:
//
// The first thread who tries to resolve the CP entry gets a bad class, but
// when it tries to update the CP entry, the second thread has already succeeded in
// loading the class and has set the entry's tag to JVM_CONSTANT_Class.
foobar += Loadee.value;
} catch (Throwable t) {
Throwable cause = t.getCause();
if (cause != null) {
String s = cause.toString();
if (s.equals(t.toString())) { // wrong cause
t.printStackTrace();
throw new RuntimeException("wrong cause");
}
}
if (debug) {
System.out.println(t);
} else {
synchronized (Tester.class) {
// Enable this block to see the stack trace
System.out.println("------");
t.printStackTrace();
System.out.println("");
}
}
incrError();
}
}
}
public static class Loadee {
static int value = 1234;
}
static class MyLoader extends ClassLoader {
static int count;
static byte[] badclass = {1, 2, 3, 4, 5, 6, 7, 8};
static {
registerAsParallelCapable();
}
ClassLoader parent;
MyLoader(ClassLoader parent) {
this.parent = parent;
}
synchronized boolean hack() {
++ count;
if ((count % 2) == 1) {
return true;
} else {
return false;
}
}
public Class loadClass(String name) throws ClassNotFoundException {
if (name.equals("SaveResolutionErrorTest$Loadee")) {
if (hack()) {
return defineClass(name, badclass, 0, badclass.length);
} else {
return defineClass(name, classfile_for_Loadee, 0, classfile_for_Loadee.length);
}
}
if (name.equals("SaveResolutionErrorTest$Tester")) {
return defineClass(name, classfile_for_Tester, 0, classfile_for_Tester.length);
}
return parent.loadClass(name);
}
void doTest() throws Exception {
Class c = Class.forName("SaveResolutionErrorTest$Tester", /*init=*/true, this);
java.lang.reflect.Method m = c.getMethod("doit");
m.invoke(null);
}
}
}