8302491: NoClassDefFoundError omits the original cause of an error

Reviewed-by: coleenp, dholmes
(cherry picked from commit 5685107579)
This commit is contained in:
Ilarion Nakonechnyy
2023-03-13 17:26:25 +00:00
committed by Vitaly Provodin
parent 855076e436
commit 1a344b8ecd
3 changed files with 48 additions and 40 deletions

View File

@@ -2742,49 +2742,57 @@ void java_lang_Throwable::get_stack_trace_elements(int depth, Handle backtrace,
}
}
Handle java_lang_Throwable::get_cause_with_stack_trace(Handle throwable, TRAPS) {
// Call to JVM to fill in the stack trace and clear declaringClassObject to
// not keep classes alive in the stack trace.
// call this: public StackTraceElement[] getStackTrace()
Handle java_lang_Throwable::create_initialization_error(JavaThread* current, Handle throwable) {
// Creates an ExceptionInInitializerError to be recorded as the initialization error when class initialization
// failed due to the passed in 'throwable'. We cannot save 'throwable' directly due to issues with keeping alive
// all objects referenced via its stacktrace. So instead we save a new EIIE instance, with the same message and
// symbolic stacktrace of 'throwable'.
assert(throwable.not_null(), "shouldn't be");
// Now create the message from the original exception and thread name.
Symbol* message = java_lang_Throwable::detail_message(throwable());
ResourceMark rm(current);
stringStream st;
st.print("Exception %s%s ", throwable()->klass()->name()->as_klass_external_name(),
message == nullptr ? "" : ":");
if (message == nullptr) {
st.print("[in thread \"%s\"]", current->name());
} else {
st.print("%s [in thread \"%s\"]", message->as_C_string(), current->name());
}
Symbol* exception_name = vmSymbols::java_lang_ExceptionInInitializerError();
Handle init_error = Exceptions::new_exception(current, exception_name, st.as_string());
// If new_exception returns a different exception while creating the exception,
// abandon the attempt to save the initialization error and return null.
if (init_error->klass()->name() != exception_name) {
log_info(class, init)("Exception thrown while saving initialization exception %s",
init_error->klass()->external_name());
return Handle();
}
// Call to java to fill in the stack trace and clear declaringClassObject to
// not keep classes alive in the stack trace.
// call this: public StackTraceElement[] getStackTrace()
JavaValue result(T_ARRAY);
JavaCalls::call_virtual(&result, throwable,
vmClasses::Throwable_klass(),
vmSymbols::getStackTrace_name(),
vmSymbols::getStackTrace_signature(),
CHECK_NH);
Handle stack_trace(THREAD, result.get_oop());
assert(stack_trace->is_objArray(), "Should be an array");
// Throw ExceptionInInitializerError as the cause with this exception in
// the message and stack trace.
// Now create the message with the original exception and thread name.
Symbol* message = java_lang_Throwable::detail_message(throwable());
ResourceMark rm(THREAD);
stringStream st;
st.print("Exception %s%s ", throwable()->klass()->name()->as_klass_external_name(),
message == nullptr ? "" : ":");
if (message == nullptr) {
st.print("[in thread \"%s\"]", THREAD->name());
current);
if (!current->has_pending_exception()){
Handle stack_trace(current, result.get_oop());
assert(stack_trace->is_objArray(), "Should be an array");
java_lang_Throwable::set_stacktrace(init_error(), stack_trace());
// Clear backtrace because the stacktrace should be used instead.
set_backtrace(init_error(), nullptr);
} else {
st.print("%s [in thread \"%s\"]", message->as_C_string(), THREAD->name());
log_info(class, init)("Exception thrown while getting stack trace for initialization exception %s",
init_error->klass()->external_name());
current->clear_pending_exception();
}
Symbol* exception_name = vmSymbols::java_lang_ExceptionInInitializerError();
Handle h_cause = Exceptions::new_exception(THREAD, exception_name, st.as_string());
// If new_exception returns a different exception while creating the exception, return null.
if (h_cause->klass()->name() != exception_name) {
log_info(class, init)("Exception thrown while saving initialization exception %s",
h_cause->klass()->external_name());
return Handle();
}
java_lang_Throwable::set_stacktrace(h_cause(), stack_trace());
// Clear backtrace because the stacktrace should be used instead.
set_backtrace(h_cause(), nullptr);
return h_cause;
return init_error;
}
bool java_lang_Throwable::get_top_method_and_bci(oop throwable, Method** method, int* bci) {

View File

@@ -618,7 +618,7 @@ class java_lang_Throwable: AllStatic {
static void get_stack_trace_elements(int depth, Handle backtrace, objArrayHandle stack_trace, TRAPS);
// For recreating class initialization error exceptions.
static Handle get_cause_with_stack_trace(Handle throwable, TRAPS);
static Handle create_initialization_error(JavaThread* current, Handle throwable);
// Printing
static void print(oop throwable, outputStream* st);

View File

@@ -994,18 +994,18 @@ void InstanceKlass::add_initialization_error(JavaThread* current, Handle excepti
// If the initialization error is OOM, this might not work, but if GC kicks in
// this would be still be helpful.
JavaThread* THREAD = current;
Handle cause = java_lang_Throwable::get_cause_with_stack_trace(exception, THREAD);
if (HAS_PENDING_EXCEPTION || cause.is_null()) {
CLEAR_PENDING_EXCEPTION;
Handle init_error = java_lang_Throwable::create_initialization_error(current, exception);
ResourceMark rm(THREAD);
if (init_error.is_null()) {
log_trace(class, init)("Initialization error is null for class %s", external_name());
return;
}
MutexLocker ml(THREAD, ClassInitError_lock);
OopHandle elem = OopHandle(Universe::vm_global(), cause());
bool created = false;
OopHandle elem = OopHandle(Universe::vm_global(), init_error());
bool created;
_initialization_error_table.put_if_absent(this, elem, &created);
assert(created, "Initialization is single threaded");
ResourceMark rm(THREAD);
log_trace(class, init)("Initialization error added for class %s", external_name());
}