mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
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:
parent
b482733f94
commit
57f16f9fe5
14 changed files with 361 additions and 109 deletions
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,13 +721,8 @@ 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();
|
||||||
|
if (con.is_valid()) {
|
||||||
BasicType basic_type = con.basic_type();
|
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)) {
|
if (is_reference_type(basic_type)) {
|
||||||
ciObject* obj = con.as_object();
|
ciObject* obj = con.as_object();
|
||||||
if (obj->is_null_object()) {
|
if (obj->is_null_object()) {
|
||||||
|
@ -739,6 +734,16 @@ void ciTypeFlow::StateVector::do_ldc(ciBytecodeStream* str) {
|
||||||
} else {
|
} else {
|
||||||
push_translate(ciType::make(basic_type));
|
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:
|
||||||
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.
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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") \
|
||||||
|
|
|
@ -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,10 +494,14 @@ 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");
|
||||||
|
|
||||||
|
// 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);
|
Klass* klass = this_cp->resolved_klasses()->at(resolved_klass_index);
|
||||||
if (klass != NULL) {
|
if (klass != NULL) {
|
||||||
return klass;
|
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.
|
||||||
if (this_cp->tag_at(which).is_unresolved_klass_in_error()) {
|
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
|
// 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 = 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");
|
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");
|
||||||
|
|
||||||
|
if (this_cp->tag_at(which).is_klass()) {
|
||||||
Klass* k = this_cp->resolved_klasses()->at(resolved_klass_index);
|
Klass* k = this_cp->resolved_klasses()->at(resolved_klass_index);
|
||||||
if (k != NULL) {
|
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,24 +815,57 @@ 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 {
|
||||||
|
if (cause != NULL) {
|
||||||
|
Handle h_cause = Exceptions::new_exception(THREAD, cause, cause_str);
|
||||||
|
THROW_CAUSE(error, h_cause);
|
||||||
} else {
|
} else {
|
||||||
THROW(error);
|
THROW(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If resolution for Class, Dynamic constant, MethodHandle or MethodType fails, save the
|
// 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.
|
// 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,14 +2384,8 @@ 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");
|
||||||
|
|
||||||
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);
|
symbol_at(name_index)->print_value_on(st);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case JVM_CONSTANT_MethodHandle :
|
case JVM_CONSTANT_MethodHandle :
|
||||||
case JVM_CONSTANT_MethodHandleInError :
|
case JVM_CONSTANT_MethodHandleInError :
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue