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? "); assert (constant->is_instance(), "must be an instance, or not? ");
return ciConstant(T_OBJECT, constant); return ciConstant(T_OBJECT, constant);
} }
} else if (tag.is_unresolved_klass_in_error()) {
return ciConstant();
} 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 // 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);

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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -233,6 +233,11 @@ public:
return tag.is_unresolved_klass(); 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, // If this bytecode is one of get_field, get_static, put_field,
// or put_static, get the referenced field. // or put_static, get the referenced field.
ciField* get_field(bool& will_link); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -721,23 +721,28 @@ 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) {
ciConstant con = str->get_constant(); ciConstant con = str->get_constant();
BasicType basic_type = con.basic_type(); if (con.is_valid()) {
if (basic_type == T_ILLEGAL) { BasicType basic_type = con.basic_type();
// OutOfMemoryError in the CI while loading constant if (is_reference_type(basic_type)) {
push_null(); ciObject* obj = con.as_object();
outer()->record_failure("ldc did not link"); if (obj->is_null_object()) {
return; push_null();
} } else {
if (is_reference_type(basic_type)) { assert(obj->is_instance() || obj->is_array(), "must be java_mirror of klass");
ciObject* obj = con.as_object(); push_object(obj->klass());
if (obj->is_null_object()) { }
push_null();
} else { } else {
assert(obj->is_instance() || obj->is_array(), "must be java_mirror of klass"); push_translate(ciType::make(basic_type));
push_object(obj->klass());
} }
} else { } else {
push_translate(ciType::make(basic_type)); 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:
case Bytecodes::_ldc_w: case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w: case Bytecodes::_ldc2_w:
return str.is_unresolved_klass_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
// they do not throw exceptions to make the monitor analysis work. // 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::_detailMessage_offset;
int java_lang_Throwable::_stackTrace_offset; int java_lang_Throwable::_stackTrace_offset;
int java_lang_Throwable::_depth_offset; int java_lang_Throwable::_depth_offset;
int java_lang_Throwable::_cause_offset;
int java_lang_Throwable::_static_unassigned_stacktrace_offset; int java_lang_Throwable::_static_unassigned_stacktrace_offset;
#define THROWABLE_FIELDS_DO(macro) \ #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(_detailMessage_offset, k, "detailMessage", string_signature, false); \
macro(_stackTrace_offset, k, "stackTrace", java_lang_StackTraceElement_array, false); \ macro(_stackTrace_offset, k, "stackTrace", java_lang_StackTraceElement_array, false); \
macro(_depth_offset, k, "depth", int_signature, 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) macro(_static_unassigned_stacktrace_offset, k, "UNASSIGNED_STACK", java_lang_StackTraceElement_array, true)
void java_lang_Throwable::compute_offsets() { void java_lang_Throwable::compute_offsets() {
@ -2005,6 +2007,9 @@ oop java_lang_Throwable::message(oop throwable) {
return throwable->obj_field(_detailMessage_offset); 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 // Return Symbol for detailed_message or NULL
Symbol* java_lang_Throwable::detail_message(oop throwable) { 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 _detailMessage_offset;
static int _stackTrace_offset; static int _stackTrace_offset;
static int _depth_offset; static int _depth_offset;
static int _cause_offset;
static int _static_unassigned_stacktrace_offset; static int _static_unassigned_stacktrace_offset;
// StackTrace (programmatic access, new since 1.4) // 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); } static int get_detailMessage_offset() { CHECK_INIT(_detailMessage_offset); }
// Message // Message
static oop message(oop throwable); static oop message(oop throwable);
static oop cause(oop throwable);
static void set_message(oop throwable, oop value); static void set_message(oop throwable, oop value);
static Symbol* detail_message(oop throwable); static Symbol* detail_message(oop throwable);
static void print_stack_element(outputStream *st, Method* method, int bci); static void print_stack_element(outputStream *st, Method* method, int bci);

View file

@ -33,19 +33,27 @@
#include "runtime/safepoint.hpp" #include "runtime/safepoint.hpp"
#include "utilities/hashtable.inline.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, void ResolutionErrorTable::add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int cp_index, 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_locked_or_safepoint(SystemDictionary_lock);
assert(!pool.is_null() && error != NULL, "adding NULL obj"); 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_entry(index, entry);
} }
// add new entry to the table // create new nest host error entry
void ResolutionErrorTable::add_entry(int index, unsigned int hash, void ResolutionErrorTable::add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int cp_index, const constantPoolHandle& pool, int cp_index,
const char* message) const char* message)
@ -53,7 +61,14 @@ void ResolutionErrorTable::add_entry(int index, unsigned int hash,
assert_locked_or_safepoint(SystemDictionary_lock); assert_locked_or_safepoint(SystemDictionary_lock);
assert(!pool.is_null() && message != NULL, "adding NULL obj"); 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); 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) { void ResolutionErrorEntry::set_nest_host_error(const char* message) {
_nest_host_error = 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) { void ResolutionErrorTable::free_entry(ResolutionErrorEntry *entry) {
// decrement error refcount // decrement error refcount
if (entry->error() != NULL) { if (entry->error() != NULL) {
@ -126,6 +128,12 @@ void ResolutionErrorTable::free_entry(ResolutionErrorEntry *entry) {
if (entry->message() != NULL) { if (entry->message() != NULL) {
entry->message()->decrement_refcount(); 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) { if (entry->nest_host_error() != NULL) {
FREE_C_HEAP_ARRAY(char, entry->nest_host_error()); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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> { class ResolutionErrorTable : public Hashtable<ConstantPool*, mtClass> {
private:
void free_entry(ResolutionErrorEntry *entry);
public: public:
ResolutionErrorTable(int table_size); 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) { ResolutionErrorEntry* bucket(int i) {
return (ResolutionErrorEntry*)Hashtable<ConstantPool*, mtClass>::bucket(i); return (ResolutionErrorEntry*)Hashtable<ConstantPool*, mtClass>::bucket(i);
} }
@ -64,7 +61,8 @@ public:
} }
void add_entry(int index, unsigned int hash, 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, void add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int which, const char* message); const constantPoolHandle& pool, int which, const char* message);
@ -99,6 +97,8 @@ class ResolutionErrorEntry : public HashtableEntry<ConstantPool*, mtClass> {
int _cp_index; int _cp_index;
Symbol* _error; Symbol* _error;
Symbol* _message; Symbol* _message;
Symbol* _cause;
Symbol* _cause_msg;
const char* _nest_host_error; const char* _nest_host_error;
public: public:
@ -113,6 +113,12 @@ class ResolutionErrorEntry : public HashtableEntry<ConstantPool*, mtClass> {
Symbol* message() const { return _message; } Symbol* message() const { return _message; }
void set_message(Symbol* c); 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; } const char* nest_host_error() const { return _nest_host_error; }
void set_nest_host_error(const char* message); 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 // Add entry to resolution error table to record the error when the first
// attempt to resolve a reference to a class has failed. // attempt to resolve a reference to a class has failed.
void SystemDictionary::add_resolution_error(const constantPoolHandle& pool, int which, 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); unsigned int hash = resolution_errors()->compute_hash(pool, which);
int index = resolution_errors()->hash_to_index(hash); int index = resolution_errors()->hash_to_index(hash);
{ {
MutexLocker ml(Thread::current(), SystemDictionary_lock); 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. // Lookup resolution error table. Returns error if found, otherwise NULL.
Symbol* SystemDictionary::find_resolution_error(const constantPoolHandle& pool, int which, 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); unsigned int hash = resolution_errors()->compute_hash(pool, which);
int index = resolution_errors()->hash_to_index(hash); 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); ResolutionErrorEntry* entry = resolution_errors()->find_entry(index, hash, pool, which);
if (entry != NULL) { if (entry != NULL) {
*message = entry->message(); *message = entry->message();
*cause = entry->cause();
*cause_msg = entry->cause_msg();
return entry->error(); return entry->error();
} else { } else {
return NULL; return NULL;

View file

@ -277,10 +277,10 @@ public:
// Record the error when the first attempt to resolve a reference from a constant // Record the error when the first attempt to resolve a reference from a constant
// pool entry to a class fails. // pool entry to a class fails.
static void add_resolution_error(const constantPoolHandle& pool, int which, Symbol* error, 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 void delete_resolution_error(ConstantPool* pool);
static Symbol* find_resolution_error(const constantPoolHandle& pool, int which, 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 // 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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(object_object_signature, "(Ljava/lang/Object;)Ljava/lang/Object;") \
template(string_void_signature, "(Ljava/lang/String;)V") \ template(string_void_signature, "(Ljava/lang/String;)V") \
template(string_int_signature, "(Ljava/lang/String;)I") \ template(string_int_signature, "(Ljava/lang/String;)I") \
template(throwable_signature, "Ljava/lang/Throwable;") \
template(throwable_void_signature, "(Ljava/lang/Throwable;)V") \ template(throwable_void_signature, "(Ljava/lang/Throwable;)V") \
template(void_throwable_signature, "()Ljava/lang/Throwable;") \ 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_void_signature, "(Ljava/lang/Class;)V") \
template(class_int_signature, "(Ljava/lang/Class;)I") \ template(class_int_signature, "(Ljava/lang/Class;)I") \
template(class_long_signature, "(Ljava/lang/Class;)J") \ template(class_long_signature, "(Ljava/lang/Class;)J") \
template(class_boolean_signature, "(Ljava/lang/Class;)Z") \ 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(throwable_string_void_signature, "(Ljava/lang/Throwable;Ljava/lang/String;)V") \
template(string_array_void_signature, "([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") \ 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, Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which,
bool save_resolution_error, TRAPS) { TRAPS) {
JavaThread* javaThread = THREAD->as_Java_thread(); JavaThread* javaThread = THREAD->as_Java_thread();
// A resolved constantPool entry will contain a Klass*, otherwise a Symbol*. // A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
@ -494,9 +494,13 @@ Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which,
int name_index = kslot.name_index(); int name_index = kslot.name_index();
assert(this_cp->tag_at(name_index).is_symbol(), "sanity"); assert(this_cp->tag_at(name_index).is_symbol(), "sanity");
Klass* klass = this_cp->resolved_klasses()->at(resolved_klass_index); // The tag must be JVM_CONSTANT_Class in order to read the correct value from
if (klass != NULL) { // the unresolved_klasses() array.
return klass; 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. // This tag doesn't change back to unresolved class unless at a safepoint.
@ -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 // 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). // to resolve this constant pool entry fail with the same error (JVMS 5.4.3).
if (HAS_PENDING_EXCEPTION) { if (HAS_PENDING_EXCEPTION) {
if (save_resolution_error) { save_and_throw_exception(this_cp, which, constantTag(JVM_CONSTANT_UnresolvedClass), CHECK_NULL);
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
// If CHECK_NULL above doesn't return the exception, that means that // some other thread has beaten us and has resolved the class.
// some other thread has beaten us and has resolved the class. // To preserve old behavior, we return the resolved class.
// To preserve old behavior, we return the resolved class. Klass* klass = this_cp->resolved_klasses()->at(resolved_klass_index);
klass = this_cp->resolved_klasses()->at(resolved_klass_index); assert(klass != NULL, "must be resolved if exception was cleared");
assert(klass != NULL, "must be resolved if exception was cleared"); return klass;
return klass;
} else {
return NULL; // return the pending exception
}
} }
// logging for class+resolve. // logging for class+resolve.
if (log_is_enabled(Debug, class, resolve)){ if (log_is_enabled(Debug, class, resolve)){
trace_class_resolution(this_cp, k); trace_class_resolution(this_cp, k);
} }
Klass** adr = this_cp->resolved_klasses()->adr_at(resolved_klass_index); Klass** adr = this_cp->resolved_klasses()->adr_at(resolved_klass_index);
Atomic::release_store(adr, k); Atomic::release_store(adr, k);
// The interpreter assumes when the tag is stored, the klass is resolved // 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 // and the Klass* stored in _resolved_klasses is non-NULL, so we need
// hardware store ordering here. // 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; return k;
} }
@ -572,9 +585,12 @@ Klass* ConstantPool::klass_at_if_loaded(const constantPoolHandle& this_cp, int w
int name_index = kslot.name_index(); int name_index = kslot.name_index();
assert(this_cp->tag_at(name_index).is_symbol(), "sanity"); assert(this_cp->tag_at(name_index).is_symbol(), "sanity");
Klass* k = this_cp->resolved_klasses()->at(resolved_klass_index); if (this_cp->tag_at(which).is_klass()) {
if (k != NULL) { Klass* k = this_cp->resolved_klasses()->at(resolved_klass_index);
assert(k != NULL, "should be resolved");
return k; return k;
} else if (this_cp->tag_at(which).is_unresolved_klass_in_error()) {
return NULL;
} else { } else {
Thread *thread = Thread::current(); Thread *thread = Thread::current();
Symbol* name = this_cp->symbol_at(name_index); 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 // Dig out the detailed message to reuse if possible
Symbol* message = java_lang_Throwable::detail_message(pending_exception); Symbol* message = java_lang_Throwable::detail_message(pending_exception);
if (message != NULL) { if (message != NULL) {
@ -799,16 +815,50 @@ Symbol* ConstantPool::exception_message(const constantPoolHandle& this_cp, int w
return message; 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) { void ConstantPool::throw_resolution_error(const constantPoolHandle& this_cp, int which, TRAPS) {
ResourceMark rm(THREAD);
Symbol* message = NULL; 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"); assert(error != NULL, "checking");
const char* cause_str = cause_msg != NULL ? cause_msg->as_C_string() : NULL;
CLEAR_PENDING_EXCEPTION; CLEAR_PENDING_EXCEPTION;
if (message != NULL) { if (message != NULL) {
ResourceMark rm; char* msg = message->as_C_string();
THROW_MSG(error, 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 { } else {
THROW(error); if (cause != NULL) {
Handle h_cause = Exceptions::new_exception(THREAD, cause, cause_str);
THROW_CAUSE(error, h_cause);
} else {
THROW(error);
}
} }
} }
@ -816,7 +866,6 @@ void ConstantPool::throw_resolution_error(const constantPoolHandle& this_cp, int
// exception in the resolution error table, so that the same exception is thrown again. // 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, void ConstantPool::save_and_throw_exception(const constantPoolHandle& this_cp, int which,
constantTag tag, TRAPS) { constantTag tag, TRAPS) {
Symbol* error = PENDING_EXCEPTION->klass()->name();
int error_tag = tag.error_value(); 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() // and OutOfMemoryError, etc, or if the thread was hit by stop()
// Needs clarification to section 5.4.3 of the VM spec (see 6308271) // Needs clarification to section 5.4.3 of the VM spec (see 6308271)
} else if (this_cp->tag_at(which).value() != error_tag) { } else if (this_cp->tag_at(which).value() != error_tag) {
Symbol* message = exception_message(this_cp, which, tag, PENDING_EXCEPTION); add_resolution_error(this_cp, which, tag, PENDING_EXCEPTION);
SystemDictionary::add_resolution_error(this_cp, which, error, message);
// CAS in the tag. If a thread beat us to registering this error that's fine. // 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 // If another thread resolved the reference, this is a race condition. This
// thread may have had a security manager or something temporary. // 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: 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");
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. // ldc wants the java mirror.
result_oop = resolved->java_mirror(); result_oop = resolved->java_mirror();
break; 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()); 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 // Check constant pool method consistency
if ((callee->is_interface() && m_tag.is_method()) || if ((callee->is_interface() && m_tag.is_method()) ||
@ -2336,13 +2384,7 @@ void ConstantPool::print_entry_on(const int index, outputStream* st) {
int resolved_klass_index = kslot.resolved_klass_index(); int resolved_klass_index = kslot.resolved_klass_index();
int name_index = kslot.name_index(); int name_index = kslot.name_index();
assert(tag_at(name_index).is_symbol(), "sanity"); assert(tag_at(name_index).is_symbol(), "sanity");
symbol_at(name_index)->print_value_on(st);
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; break;
case JVM_CONSTANT_MethodHandle : case JVM_CONSTANT_MethodHandle :

View file

@ -418,13 +418,7 @@ class ConstantPool : public Metadata {
Klass* klass_at(int which, TRAPS) { Klass* klass_at(int which, TRAPS) {
constantPoolHandle h_this(THREAD, this); constantPoolHandle h_this(THREAD, this);
return klass_at_impl(h_this, which, true, THREAD); return klass_at_impl(h_this, which, 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);
} }
CPKlassSlot klass_slot_at(int which) const { 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 // Implementation of methods that needs an exposed 'this' pointer, in order to
// handle GC while executing the method // handle GC while executing the method
static Klass* klass_at_impl(const constantPoolHandle& this_cp, int which, static Klass* klass_at_impl(const constantPoolHandle& this_cp, int which, TRAPS);
bool save_resolution_error, TRAPS);
static oop string_at_impl(const constantPoolHandle& this_cp, int which, int obj_index, 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); 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); bool must_resolve, Handle if_not_available, TRAPS);
// Exception handling // 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); static void save_and_throw_exception(const constantPoolHandle& this_cp, int which, constantTag tag, TRAPS);
public: 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. // So this whole "class index" feature should probably be removed.
if (constant_pool->tag_at(index).is_unresolved_klass()) { 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) { if (HAS_PENDING_EXCEPTION) {
// Exception happened during classloading. We ignore the exception here, since it // 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 // 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);
}
}
}