< prev index next >

src/hotspot/share/oops/instanceKlass.cpp

Print this page
rev 59277 : [mq]: v3

@@ -151,11 +151,13 @@
     }
   }
   return false;
 }
 
-// called to verify that k is a member of this nest
+// private: called to verify that k is a static member of this nest.
+// We know that k is an instance class in the same package and hence the
+// same classloader.
 bool InstanceKlass::has_nest_member(InstanceKlass* k, TRAPS) const {
   assert(!is_hidden(), "unexpected hidden class");
   if (_nest_members == NULL || _nest_members == Universe::the_empty_short_array()) {
     if (log_is_enabled(Trace, class, nestmates)) {
       ResourceMark rm(THREAD);

@@ -174,30 +176,31 @@
   // Check for a resolved cp entry , else fall back to a name check.
   // We don't want to resolve any class other than the one being checked.
   for (int i = 0; i < _nest_members->length(); i++) {
     int cp_index = _nest_members->at(i);
     if (_constants->tag_at(cp_index).is_klass()) {
-      Klass* k2 = _constants->klass_at(cp_index, CHECK_false);
+      Klass* k2 = _constants->klass_at(cp_index, THREAD);
+      assert(!HAS_PENDING_EXCEPTION || PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass()),
+             "Exceptions should not be possible here");
       if (k2 == k) {
         log_trace(class, nestmates)("- class is listed at nest_members[%d] => cp[%d]", i, cp_index);
         return true;
       }
     }
     else {
       Symbol* name = _constants->klass_name_at(cp_index);
       if (name == k->name()) {
         log_trace(class, nestmates)("- Found it at nest_members[%d] => cp[%d]", i, cp_index);
 
-        // Names match so check actual klass - this may trigger class loading if
-        // it doesn't match (though that should be impossible). But to be safe we
-        // have to check for a compiler thread executing here.
-        if (!THREAD->can_call_java() && !_constants->tag_at(cp_index).is_klass()) {
-          log_trace(class, nestmates)("- validation required resolution in an unsuitable thread");
-          return false;
-        }
-
-        Klass* k2 = _constants->klass_at(cp_index, CHECK_false);
+        // Names match so check actual klass. This may trigger class loading if
+        // it doesn't match though that should be impossible as it means one classloader
+        // has defined two different classes with the same name! A compiler thread won't be
+        // able to perform that loading but we can't exclude the compiler threads from
+        // executing this logic. But it should actually be impossible to trigger loading here.
+        Klass* k2 = _constants->klass_at(cp_index, THREAD);
+        assert(!HAS_PENDING_EXCEPTION || PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass()),
+               "Exceptions should not be possible here");
         if (k2 == k) {
           log_trace(class, nestmates)("- class is listed as a nest member");
           return true;
         }
         else {

@@ -211,152 +214,143 @@
   }
   log_trace(class, nestmates)("- class is NOT a nest member!");
   return false;
 }
 
-InstanceKlass* InstanceKlass::runtime_nest_host(TRAPS) {
-  // TODO: nest_host returns NULL if validation fails.  Need to follow up
-  // the specification when to evaluate the runtime nest host.  Right now
-  // it's only determined when a dynamic nestmate is added.
-  InstanceKlass* nest_host_k = nest_host(NULL, CHECK_NULL);
-  if (nest_host_k == NULL) {
-    assert(_nest_host == NULL, "should fail to validate NestNost");
-    // drop the static nest information; set dynamic nest to this class
-    if (log_is_enabled(Trace, class, nestmates)) {
-      ResourceMark rm(THREAD);
-      log_trace(class, nestmates)("Fail to validate static nest host of %s: setting nest-host to self",
-                                  this->external_name());
-    }
-    _nest_host = nest_host_k = this;
-  }
-  return nest_host_k;
-}
-
 // Return nest-host class, resolving, validating and saving it if needed.
-// In cases where this is called from a thread that can not do classloading
+// In cases where this is called from a thread that cannot do classloading
 // (such as a native JIT thread) then we simply return NULL, which in turn
 // causes the access check to return false. Such code will retry the access
-// from a more suitable environment later.
-InstanceKlass* InstanceKlass::nest_host(Symbol* validationException, TRAPS) {
+// from a more suitable environment later. Otherwise the _nest_host is always
+// set once this method returns.
+// Any errors from nest-host resolution must be preserved so they can be queried
+// from higher-level access checking code, and reported as part of access checking
+// exceptions.
+// VirtualMachineErrors are propagated with a NULL return.
+// Under any conditions where the _nest_host can be set to non-NULL the resulting value
+// of it and, if applicable, _nest_host_res_error, are idempotent. But as we can be
+// executing this code concurrently we need to ensure ordering is maintained so that
+// errors messages can safely be read.
+InstanceKlass* InstanceKlass::nest_host(TRAPS) {
   InstanceKlass* nest_host_k = _nest_host;
-  if (nest_host_k == NULL) {
-    // need to resolve and save our nest-host class. This could be attempted
-    // concurrently but as the result is idempotent and we don't use the class
-    // then we do not need any synchronization beyond what is implicitly used
-    // during class loading.
+  if (nest_host_k != NULL) {
+    return nest_host_k;
+  }
+
+  const bool doLog = log_is_enabled(Trace, class, nestmates);
+
+  // need to resolve and save our nest-host class.
     if (_nest_host_index != 0) { // we have a real nest_host
       // Before trying to resolve check if we're in a suitable context
       if (!THREAD->can_call_java() && !_constants->tag_at(_nest_host_index).is_klass()) {
-        if (log_is_enabled(Trace, class, nestmates)) {
+      if (doLog) {
           ResourceMark rm(THREAD);
           log_trace(class, nestmates)("Rejected resolution of nest-host of %s in unsuitable thread",
                                       this->external_name());
         }
-        return NULL;
+      return NULL; // sentinel to say "try again from a different context"
       }
 
-      if (log_is_enabled(Trace, class, nestmates)) {
+    if (doLog) {
         ResourceMark rm(THREAD);
         log_trace(class, nestmates)("Resolving nest-host of %s using cp entry for %s",
                                     this->external_name(),
                                     _constants->klass_name_at(_nest_host_index)->as_C_string());
       }
 
       Klass* k = _constants->klass_at(_nest_host_index, THREAD);
       if (HAS_PENDING_EXCEPTION) {
-        Handle exc_h = Handle(THREAD, PENDING_EXCEPTION);
-        if (validationException == NULL && exc_h->is_a(SystemDictionary::LinkageError_klass())) {
-          // clear exception if fails to resolve the nest host class
-          CLEAR_PENDING_EXCEPTION;
+      if (PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass())) {
+        return NULL; // propagate VMEs
         }
-        // throw a new NCDFE with the original as its cause, and a clear msg
-        if (exc_h->is_a(SystemDictionary::NoClassDefFoundError_klass()) && validationException != NULL) {
-          // throw a new NCDFE with the original as its cause, and a clear msg
           ResourceMark rm(THREAD);
-          char buf[200];
+      stringStream ss;
+      char* target_host_class = _constants->klass_name_at(_nest_host_index)->as_C_string();
+      ss.print("Nest host resolution of %s with host %s failed: ",
+               this->external_name(), target_host_class);
+      java_lang_Throwable::print(PENDING_EXCEPTION, &ss);
+      _nest_host_res_error = ss.as_string(true /* on C-heap */);
+      // ensure we see _nest_host_res_error is set if _nest_host is non-NULL
+      OrderAccess::storestore();
           CLEAR_PENDING_EXCEPTION;
-          jio_snprintf(buf, sizeof(buf),
-                       "Unable to load nest-host class (%s) of %s",
-                       _constants->klass_name_at(_nest_host_index)->as_C_string(),
-                       this->external_name());
-          log_trace(class, nestmates)("%s - NoClassDefFoundError", buf);
-          THROW_MSG_CAUSE_NULL(vmSymbols::java_lang_NoClassDefFoundError(), buf, exc_h);
-        }
-        // All other exceptions pass through (OOME, StackOverflowError, LinkageErrors etc).
-        return NULL;
+      if (doLog) {
+        log_trace(class, nestmates)("%s", _nest_host_res_error);
       }
-
+    } else {
       // A valid nest-host is an instance class in the current package that lists this
-      // class as a nest member. If any of these conditions are not met we post the
-      // requested exception type (if any) and return NULL
-
+      // class as a nest member. If any of these conditions are not met the class is
+      // its own nest-host.
       const char* error = NULL;
 
       // JVMS 5.4.4 indicates package check comes first
       if (is_same_class_package(k)) {
-
         // Now check actual membership. We can't be a member if our "host" is
         // not an instance class.
         if (k->is_instance_klass()) {
           nest_host_k = InstanceKlass::cast(k);
-
-          bool is_member = nest_host_k->has_nest_member(this, CHECK_NULL);
+          bool is_member = nest_host_k->has_nest_member(this, THREAD);
+          // exception is rare, perhaps impossible
+          if (!HAS_PENDING_EXCEPTION) {
           if (is_member) {
-            // save resolved nest-host value
-            _nest_host = nest_host_k;
-
-            if (log_is_enabled(Trace, class, nestmates)) {
+              _nest_host = nest_host_k; // save resolved nest-host value
+              if (doLog) {
               ResourceMark rm(THREAD);
               log_trace(class, nestmates)("Resolved nest-host of %s to %s",
                                           this->external_name(), k->external_name());
             }
             return nest_host_k;
+            } else {
+              error = "current type is not listed as a nest member";
+            }
+          } else {
+            if (PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass())) {
+              return NULL; // propagate VMEs
           }
+            stringStream ss;
+            ss.print("exception on member check: ");
+            java_lang_Throwable::print(PENDING_EXCEPTION, &ss);
+            error = ss.as_string();
+          }
+        } else {
+          error = "host is not an instance class";
         }
-        error = "current type is not listed as a nest member";
       } else {
         error = "types are in different packages";
       }
 
-      if (log_is_enabled(Trace, class, nestmates)) {
+      // something went wrong, so record what and log it
+      {
         ResourceMark rm(THREAD);
-        log_trace(class, nestmates)
-          ("Type %s (loader: %s) is not a nest member of "
-           "resolved type %s (loader: %s): %s",
+        stringStream ss;
+        ss.print("Type %s (loader: %s) is not a nest member of type %s (loader: %s): %s",
            this->external_name(),
            this->class_loader_data()->loader_name_and_id(),
            k->external_name(),
            k->class_loader_data()->loader_name_and_id(),
            error);
-      }
+        _nest_host_res_error = ss.as_string(true /* on C-heap */);
+        // ensure we see _nest_host_res_error is set if _nest_host is non-NULL
+        OrderAccess::storestore();
 
-      if (validationException != NULL && THREAD->can_call_java()) {
-        ResourceMark rm(THREAD);
-        Exceptions::fthrow(THREAD_AND_LOCATION,
-                           validationException,
-                           "Type %s (loader: %s) is not a nest member of %s (loader: %s): %s",
-                           this->external_name(),
-                           this->class_loader_data()->loader_name_and_id(),
-                           k->external_name(),
-                           k->class_loader_data()->loader_name_and_id(),
-                           error
-                           );
+        if (doLog) {
+          log_trace(class, nestmates)("%s", _nest_host_res_error);
+        }
+      }
       }
-      return NULL;
     } else {
-      if (log_is_enabled(Trace, class, nestmates)) {
+    if (doLog) {
         ResourceMark rm(THREAD);
         log_trace(class, nestmates)("Type %s is not part of a nest: setting nest-host to self",
                                     this->external_name());
       }
-      // save resolved nest-host value
-      return (_nest_host = this);
-    }
   }
-  return nest_host_k;
-}
 
+  // Either not in an explicit nest, or else an error occurred, so
+  // the nest-host is set to `this`. Any thread that sees this assignment
+  // will also see any setting of _nest_host_res_error, if applicable.
+  return (_nest_host = this);
+}
 
 // Dynamic nest member support: set this class's nest host to the given class.
 // This occurs as part of the class definition, as soon as the instanceKlass
 // has been created and doesn't require further resolution. The code:
 //    lookup().defineHiddenClass(bytes_for_X, NESTMATE);

@@ -367,51 +361,52 @@
 // assert some of those facts.
 void InstanceKlass::set_nest_host(InstanceKlass* host, TRAPS) {
   assert(is_hidden(), "must be a hidden class");
   assert(host != NULL, "NULL nest host specified");
   assert(_nest_host == NULL, "current class has resolved nest-host");
+  assert(_nest_host_res_error == NULL, "unexpected nest host resolution error exists: %s",
+         _nest_host_res_error);
   assert((host->_nest_host == NULL && host->_nest_host_index == 0) ||
          (host->_nest_host == host), "proposed host is not a valid nest-host");
   // Can't assert this as package is not set yet:
   // assert(is_same_class_package(host), "proposed host is in wrong package");
 
   if (log_is_enabled(Trace, class, nestmates)) {
     ResourceMark rm(THREAD);
+    const char* msg = "";
     // a hidden class does not expect a statically defined nest-host
     if (_nest_host_index > 0) {
-      log_trace(class, nestmates)("Type %s is a dynamic nest member of %s: the NestHost attribute in the current class is ignored",
-                                  this->external_name(),
-                                  host->external_name());
+      msg = "(the NestHost attribute in the current class is ignored)";
     } else if (_nest_members != NULL && _nest_members != Universe::the_empty_short_array()) {
-      log_trace(class, nestmates)("Type %s is a dynamic nest member of %s: the NestMembers attribute in the current class is ignored",
-                                  this->external_name(),
-                                  host->external_name());
+      msg = "(the NestMembers attribute in the current class is ignored)";
     }
+    log_trace(class, nestmates)("Injected type %s into the nest of %s %s",
+                                this->external_name(),
+                                host->external_name(),
+                                msg);
   }
   // set dynamic nest host
   _nest_host = host;
 }
 
 // check if 'this' and k are nestmates (same nest_host), or k is our nest_host,
 // or we are k's nest_host - all of which is covered by comparing the two
-// resolved_nest_hosts
+// resolved_nest_hosts.
+// Any exceptions (i.e. VMEs) are propagated.
 bool InstanceKlass::has_nestmate_access_to(InstanceKlass* k, TRAPS) {
 
   assert(this != k, "this should be handled by higher-level code");
 
   // Per JVMS 5.4.4 we first resolve and validate the current class, then
-  // the target class k. Resolution exceptions will be passed on by upper
-  // layers. IncompatibleClassChangeErrors from membership validation failures
-  // will also be passed through.
+  // the target class k.
 
-  Symbol* icce = vmSymbols::java_lang_IncompatibleClassChangeError();
-  InstanceKlass* cur_host = nest_host(icce, CHECK_false);
+  InstanceKlass* cur_host = nest_host(CHECK_false);
   if (cur_host == NULL) {
     return false;
   }
 
-  Klass* k_nest_host = k->nest_host(icce, CHECK_false);
+  Klass* k_nest_host = k->nest_host(CHECK_false);
   if (k_nest_host == NULL) {
     return false;
   }
 
   bool access = (cur_host == k_nest_host);

@@ -495,10 +490,11 @@
 InstanceKlass::InstanceKlass(const ClassFileParser& parser, unsigned kind, KlassID id) :
   Klass(id),
   _nest_members(NULL),
   _nest_host_index(0),
   _nest_host(NULL),
+  _nest_host_res_error(NULL),
   _record_components(NULL),
   _static_field_size(parser.static_field_size()),
   _nonstatic_oop_map_size(nonstatic_oop_map_size(parser.total_oop_map_count())),
   _itable_len(parser.itable_size()),
   _init_thread(NULL),

@@ -2480,10 +2476,11 @@
   _methods_jmethod_ids = NULL;
   _jni_ids = NULL;
   _oop_map_cache = NULL;
   // clear _nest_host to ensure re-load at runtime
   _nest_host = NULL;
+  _nest_host_res_error = NULL;
   _package_entry = NULL;
   _dep_context_last_cleaned = 0;
 }
 
 void InstanceKlass::remove_java_mirror() {

@@ -2645,10 +2642,16 @@
 
   // unreference array name derived from this class name (arrays of an unloaded
   // class can't be referenced anymore).
   if (_array_name != NULL)  _array_name->decrement_refcount();
   FREE_C_HEAP_ARRAY(char, _source_debug_extension);
+
+  // deallocate memoized nest-host resolution error
+  if (_nest_host_res_error != NULL) {
+    FREE_C_HEAP_ARRAY(char, _nest_host_res_error);
+    _nest_host_res_error = NULL;
+  }
 }
 
 void InstanceKlass::set_source_debug_extension(const char* array, int length) {
   if (array == NULL) {
     _source_debug_extension = NULL;
< prev index next >