< prev index next >

src/hotspot/share/oops/instanceKlass.cpp

Print this page
rev 50339 : 8203824: Chain exception from initialization in later NoClassDefFoundErrors.

@@ -511,20 +511,24 @@
 
 bool InstanceKlass::link_class_impl(bool throw_verifyerror, TRAPS) {
   if (DumpSharedSpaces && is_in_error_state()) {
     // This is for CDS dumping phase only -- we use the in_error_state to indicate that
     // the class has failed verification. Throwing the NoClassDefFoundError here is just
-    // a convenient way to stop repeat attempts to verify the same (bad) class.
+    // a convenient way to stop repeated attempts to verify the same (bad) class.
     //
     // Note that the NoClassDefFoundError is not part of the JLS, and should not be thrown
     // if we are executing Java code. This is not a problem for CDS dumping phase since
     // it doesn't execute any Java code.
     ResourceMark rm(THREAD);
-    Exceptions::fthrow(THREAD_AND_LOCATION,
+    stringStream ss;
+    ss.print("%s: cannot link class because prior initialization attempt failed",
+             class_loader_and_module_name());
+    // Chain the exception that originally caused clinit to fail.
+    Exceptions::_throw_msg_cause(THREAD_AND_LOCATION,
                        vmSymbols::java_lang_NoClassDefFoundError(),
-                       "Class %s, or one of its supertypes, failed class initialization",
-                       external_name());
+                                 ss.as_string(),
+                                 Handle(THREAD, java_lang_Class::exceptionThrownDuringClinit(java_mirror())));
     return false;
   }
   // return if already verified
   if (is_linked()) {
     return true;

@@ -689,10 +693,19 @@
       ik->initialize(CHECK);
     }
   }
 }
 
+// Store exception h_exception thrown during clinit to the corresponding field of the
+// class that could not be initialized by calling the appropriate java method.
+static void set_exception_thrown_during_clinit(Handle h_mirror, Handle h_exception, TRAPS) {
+  JavaValue result(T_VOID);
+  JavaCalls::call_virtual(&result, h_mirror, h_mirror->klass(),
+                          vmSymbols::setExceptionThrownDuringClinit_name(),
+                          vmSymbols::throwable_void_signature(), h_exception, CHECK);
+}
+
 void InstanceKlass::initialize_impl(TRAPS) {
   HandleMark hm(THREAD);
 
   // Make sure klass is linked (verified) before initialization
   // A class could already be verified, since it has been reflected upon.

@@ -733,21 +746,19 @@
 
     // Step 5
     if (is_in_error_state()) {
       DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);
       ResourceMark rm(THREAD);
-      const char* desc = "Could not initialize class ";
-      const char* className = external_name();
-      size_t msglen = strlen(desc) + strlen(className) + 1;
-      char* message = NEW_RESOURCE_ARRAY(char, msglen);
-      if (NULL == message) {
-        // Out of memory: can't create detailed error message
-          THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
-      } else {
-        jio_snprintf(message, msglen, "%s%s", desc, className);
-          THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
-      }
+      stringStream ss;
+      ss.print("%s: cannot initialize class because prior initialization attempt failed",
+               class_loader_and_module_name());
+      // Chain the exception that originally caused clinit to fail.
+      Exceptions::_throw_msg_cause(THREAD_AND_LOCATION,
+                                   vmSymbols::java_lang_NoClassDefFoundError(),
+                                   ss.as_string(),
+                                   Handle(THREAD, java_lang_Class::exceptionThrownDuringClinit(java_mirror())));
+      return;
     }
 
     // Step 6
     set_init_state(being_initialized);
     set_init_thread(self);

@@ -773,20 +784,22 @@
     if (HAS_PENDING_EXCEPTION) {
       Handle e(THREAD, PENDING_EXCEPTION);
       CLEAR_PENDING_EXCEPTION;
       {
         EXCEPTION_MARK;
+        // Store the exception that originally caused clinit to fail so it can be
+        // chained in later NoClassDefFoundErrors.
+        set_exception_thrown_during_clinit(Handle(THREAD, java_mirror()), e, THREAD);
         // Locks object, set state, and notify all waiting threads
         set_initialization_state_and_notify(initialization_error, THREAD);
         CLEAR_PENDING_EXCEPTION;
       }
       DTRACE_CLASSINIT_PROBE_WAIT(super__failed, -1, wait);
       THROW_OOP(e());
     }
   }
 
-
   // Look for aot compiled methods for this klass, including class initializer.
   AOTLoader::load_for_klass(this, THREAD);
 
   // Step 8
   {

@@ -818,30 +831,47 @@
     // JVMTI has already reported the pending exception
     // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
     JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
     {
       EXCEPTION_MARK;
+      // Wrap anything but errors into ExceptionInInitializerError.
+      if (!e->is_a(SystemDictionary::Error_klass())) {
+        JavaCallArguments args(e);
+        Handle h_loader(THREAD, NULL);
+        Handle h_prot(THREAD, NULL);
+        Handle h_cause(THREAD, NULL);
+        e = Exceptions::new_exception(THREAD, vmSymbols::java_lang_ExceptionInInitializerError(),
+                                      vmSymbols::throwable_void_signature(), &args, h_cause, h_loader, h_prot);
+        // If we failed to wrap, something like an OOM must have
+        // occurred - just pass that on. The previous code does the
+        // wrapping after the CLEAR_PENDING_EXCEPTION below (with
+        // identical effect for the user), but we want to store the
+        // wrapped exception as exception_thrown_during_clinit, so we
+        // must wrap it here. In fact, whatever is thrown during
+        // wrapping (Error or RuntimeException) is passed on to the
+        // caller.
+        if (HAS_PENDING_EXCEPTION) {
+          e = Handle(THREAD, PENDING_EXCEPTION);
+          CLEAR_PENDING_EXCEPTION; // Ignore any exception thrown, e is thrown below.
+        }
+      }
+
+      // Store the exception that originally caused clinit to fail so it can be
+      // chained in later NoClassDefFoundErrors.
+      set_exception_thrown_during_clinit(Handle(THREAD, java_mirror()), e, THREAD);
       set_initialization_state_and_notify(initialization_error, THREAD);
       CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
       // JVMTI has already reported the pending exception
       // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
       JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
     }
     DTRACE_CLASSINIT_PROBE_WAIT(error, -1, wait);
-    if (e->is_a(SystemDictionary::Error_klass())) {
       THROW_OOP(e());
-    } else {
-      JavaCallArguments args(e);
-      THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
-                vmSymbols::throwable_void_signature(),
-                &args);
-    }
   }
   DTRACE_CLASSINIT_PROBE_WAIT(end, -1, wait);
 }
 
-
 void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS) {
   Handle h_init_lock(THREAD, init_lock());
   if (h_init_lock() != NULL) {
     ObjectLocker ol(h_init_lock, THREAD);
     set_init_state(state);
< prev index next >