< prev index next >

src/hotspot/share/oops/instanceKlass.cpp

Print this page

        

@@ -143,10 +143,206 @@
     }
   }
   return false;
 }
 
+// called to verify that k is a member of this nest
+bool InstanceKlass::has_nest_member(InstanceKlass* k, TRAPS) const {
+  if (_nest_members == NULL || _nest_members == Universe::the_empty_short_array()) {
+    if (log_is_enabled(Trace, class, nestmates)) {
+      ResourceMark rm(THREAD);
+      log_trace(class, nestmates)("Checked nest membership of %s in non-nest-host class %s",
+                                  k->external_name(), this->external_name());
+    }
+    return false;
+  }
+
+  if (log_is_enabled(Trace, class, nestmates)) {
+    ResourceMark rm(THREAD);
+    log_trace(class, nestmates)("Checking nest membership of %s in %s",
+                                k->external_name(), this->external_name());
+  }
+
+  // Check names first and if they match then check actual klass. This avoids
+  // resolving anything unnecessarily.
+  for (int i = 0; i < _nest_members->length(); i++) {
+    int cp_index = _nest_members->at(i);
+    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 (but that should be impossible)
+      Klass* k2 = _constants->klass_at(cp_index, CHECK_false);
+      if (k2 == k) {
+        log_trace(class, nestmates)("- class is listed as a nest member");
+        return true;
+      }
+      else {
+        // same name but different klass!
+        log_trace(class, nestmates)(" - klass comparison failed!");
+        // can't have two names the same, so we're done
+        return false;
+      }
+    }
+  }
+  log_trace(class, nestmates)("- class is NOT a nest member!");
+  return false;
+}
+
+// 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
+// (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) {
+  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_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)) {
+          ResourceMark rm(THREAD);
+          log_trace(class, nestmates)("Rejected resolution of nest-host of %s in unsuitable thread",
+                                      this->external_name());
+        }
+        return NULL;
+      }
+
+      if (log_is_enabled(Trace, class, nestmates)) {
+        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 (exc_h->is_a(SystemDictionary::NoClassDefFoundError_klass())) {
+          // throw a new CDNFE with the original as its cause, and a clear msg
+          ResourceMark rm(THREAD);
+          char buf[200];
+          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;
+      }
+
+      // 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
+
+      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);
+
+          // FIXME: an exception from this is perhaps impossible
+          bool is_member = nest_host_k->has_nest_member(this, CHECK_NULL);
+          if (is_member) {
+            // save resolved nest-host value
+            _nest_host = nest_host_k;
+
+            if (log_is_enabled(Trace, class, nestmates)) {
+              ResourceMark rm(THREAD);
+              log_trace(class, nestmates)("Resolved nest-host of %s to %s",
+                                          this->external_name(), k->external_name());
+            }
+            return nest_host_k;
+          }
+        }
+        error = "current type is not listed as a nest member";
+      }
+      else {
+        error = "types are in different packages";
+      }
+
+      if (log_is_enabled(Trace, class, nestmates)) {
+        ResourceMark rm(THREAD);
+        log_trace(class, nestmates)("Type %s is not a nest member of resolved type %s: %s",
+                                    this->external_name(),
+                                    k->external_name(),
+                                    error);
+      }
+
+      if (validationException != NULL) {
+        ResourceMark rm(THREAD);
+        Exceptions::fthrow(THREAD_AND_LOCATION,
+                           validationException,
+                           "Type %s is not a nest member of %s: %s",
+                           this->external_name(),
+                           k->external_name(),
+                           error
+                           );
+      }
+      return NULL;
+    }
+    else {
+      if (log_is_enabled(Trace, class, nestmates)) {
+        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;
+}
+
+// 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
+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.
+
+  Symbol* icce = vmSymbols::java_lang_IncompatibleClassChangeError();
+  InstanceKlass* cur_host = nest_host(icce, THREAD);
+  if (cur_host == NULL || HAS_PENDING_EXCEPTION) {
+    return false;
+  }
+
+  Klass* k_nest_host = k->nest_host(icce, THREAD);
+  if (k_nest_host == NULL || HAS_PENDING_EXCEPTION) {
+    return false;
+  }
+
+  bool access = (cur_host == k_nest_host);
+
+  if (log_is_enabled(Trace, class, nestmates)) {
+    ResourceMark rm(THREAD);
+    log_trace(class, nestmates)("Class %s does %shave nestmate access to %s",
+                                this->external_name(),
+                                access ? "" : "NOT ",
+                                k->external_name());
+  }
+
+  return access;
+}
+
 InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& parser, TRAPS) {
   const int size = InstanceKlass::size(parser.vtable_size(),
                                        parser.itable_size(),
                                        nonstatic_oop_map_size(parser.total_oop_map_count()),
                                        parser.is_interface(),

@@ -213,11 +409,14 @@
 
 InstanceKlass::InstanceKlass(const ClassFileParser& parser, unsigned kind) :
   _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()),
-  _reference_type(parser.reference_type()) {
+  _reference_type(parser.reference_type()),
+  _nest_members(NULL),
+  _nest_host_index(0),
+  _nest_host(NULL) {
     set_vtable_length(parser.vtable_size());
     set_kind(kind);
     set_access_flags(parser.access_flags());
     set_is_anonymous(parser.is_anonymous());
     set_layout_helper(Klass::instance_layout_helper(parser.layout_size(),

@@ -357,10 +556,17 @@
       !inner_classes()->is_shared()) {
     MetadataFactory::free_array<jushort>(loader_data, inner_classes());
   }
   set_inner_classes(NULL);
 
+  if (nest_members() != NULL &&
+      nest_members() != Universe::the_empty_short_array() &&
+      !nest_members()->is_shared()) {
+    MetadataFactory::free_array<jushort>(loader_data, nest_members());
+  }
+  set_nest_members(NULL);
+
   // We should deallocate the Annotations instance if it's not in shared spaces.
   if (annotations() != NULL && !annotations()->is_shared()) {
     MetadataFactory::free_metadata(loader_data, annotations());
   }
   set_annotations(NULL);

@@ -641,11 +847,10 @@
     }
   }
   return true;
 }
 
-
 // Rewrite the byte codes of all of the methods of a class.
 // The rewriter must be called exactly once. Rewriting must happen after
 // verification but before the first method of the class is executed.
 void InstanceKlass::rewrite_class(TRAPS) {
   assert(is_loaded(), "must be loaded");

@@ -1355,26 +1560,29 @@
 
 // find_instance_method looks up the name/signature in the local methods array
 // and skips over static methods
 Method* InstanceKlass::find_instance_method(const Array<Method*>* methods,
                                             const Symbol* name,
-                                            const Symbol* signature) {
+                                            const Symbol* signature,
+                                            PrivateLookupMode private_mode) {
   Method* const meth = InstanceKlass::find_method_impl(methods,
                                                  name,
                                                  signature,
                                                  find_overpass,
                                                  skip_static,
-                                                 find_private);
+                                                 private_mode);
   assert(((meth == NULL) || !meth->is_static()),
     "find_instance_method should have skipped statics");
   return meth;
 }
 
 // find_instance_method looks up the name/signature in the local methods array
 // and skips over static methods
-Method* InstanceKlass::find_instance_method(const Symbol* name, const Symbol* signature) const {
-  return InstanceKlass::find_instance_method(methods(), name, signature);
+Method* InstanceKlass::find_instance_method(const Symbol* name,
+                                            const Symbol* signature,
+                                            PrivateLookupMode private_mode) const {
+  return InstanceKlass::find_instance_method(methods(), name, signature, private_mode);
 }
 
 // Find looks up the name/signature in the local methods array
 // and filters on the overpass, static and private flags
 // This returns the first one found

@@ -1527,22 +1735,24 @@
   }
   return -1;
 }
 
 // uncached_lookup_method searches both the local class methods array and all
-// superclasses methods arrays, skipping any overpass methods in superclasses.
+// superclasses methods arrays, skipping any overpass methods in superclasses,
+// and possibly skipping private methods.
 Method* InstanceKlass::uncached_lookup_method(const Symbol* name,
                                               const Symbol* signature,
-                                              OverpassLookupMode overpass_mode) const {
+                                              OverpassLookupMode overpass_mode,
+                                              PrivateLookupMode private_mode) const {
   OverpassLookupMode overpass_local_mode = overpass_mode;
   const Klass* klass = this;
   while (klass != NULL) {
     Method* const method = InstanceKlass::cast(klass)->find_method_impl(name,
                                                                         signature,
                                                                         overpass_local_mode,
                                                                         find_static,
-                                                                        find_private);
+                                                                        private_mode);
     if (method != NULL) {
       return method;
     }
     klass = klass->super();
     overpass_local_mode = skip_overpass;   // Always ignore overpass methods in superclasses

@@ -2040,10 +2250,12 @@
           it->push(ime[index].method_addr());
         }
       }
     }
   }
+
+  it->push(&_nest_members);
 }
 
 void InstanceKlass::remove_unshareable_info() {
   Klass::remove_unshareable_info();
 

@@ -2087,10 +2299,12 @@
 
  _init_thread = NULL;
  _methods_jmethod_ids = NULL;
  _jni_ids = NULL;
  _oop_map_cache = NULL;
+  // clear _nest_host to ensure re-load at runtime
+  _nest_host = NULL;
 }
 
 void InstanceKlass::remove_java_mirror() {
   Klass::remove_java_mirror();
 

@@ -2938,10 +3152,11 @@
     st->print(BULLET"generic signature: ");
     generic_signature()->print_value_on(st);
     st->cr();
   }
   st->print(BULLET"inner classes:     "); inner_classes()->print_value_on(st);     st->cr();
+  st->print(BULLET"nest members:     "); nest_members()->print_value_on(st);     st->cr();
   st->print(BULLET"java mirror:       "); java_mirror()->print_value_on(st);       st->cr();
   st->print(BULLET"vtable length      %d  (start addr: " INTPTR_FORMAT ")", vtable_length(), p2i(start_of_vtable())); st->cr();
   if (vtable_length() > 0 && (Verbose || WizardMode))  print_vtable(start_of_vtable(), vtable_length(), st);
   st->print(BULLET"itable length      %d (start addr: " INTPTR_FORMAT ")", itable_length(), p2i(start_of_itable())); st->cr();
   if (itable_length() > 0 && (Verbose || WizardMode))  print_vtable(start_of_itable(), itable_length(), st);

@@ -3180,10 +3395,11 @@
   n += (sz->_method_ordering_bytes       = sz->count_array(method_ordering()));
   n += (sz->_local_interfaces_bytes      = sz->count_array(local_interfaces()));
   n += (sz->_transitive_interfaces_bytes = sz->count_array(transitive_interfaces()));
   n += (sz->_fields_bytes                = sz->count_array(fields()));
   n += (sz->_inner_classes_bytes         = sz->count_array(inner_classes()));
+  n += (sz->_nest_members_bytes          = sz->count_array(nest_members()));
   sz->_ro_bytes += n;
 
   const ConstantPool* cp = constants();
   if (cp) {
     cp->collect_statistics(sz);
< prev index next >