< prev index next >

src/hotspot/share/classfile/classFileParser.cpp

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com

@@ -1090,11 +1090,11 @@
     : _location(location), _annotations_present(0)
   {
     assert((int)_annotation_LIMIT <= (int)sizeof(_annotations_present) * BitsPerByte, "");
   }
   // If this annotation name has an ID, report it (or _none).
-  ID annotation_index(const ClassLoaderData* loader_data, const Symbol* name);
+  ID annotation_index(const ClassLoaderData* loader_data, const Symbol* name, const bool can_access_vm_annotations);
   // Set the annotation name:
   void set_annotation(ID id) {
     assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob");
     _annotations_present |= nth_bit((int)id);
   }

@@ -1223,10 +1223,11 @@
 // Sift through annotations, looking for those significant to the VM:
 static void parse_annotations(const ConstantPool* const cp,
                               const u1* buffer, int limit,
                               AnnotationCollector* coll,
                               ClassLoaderData* loader_data,
+                              const bool can_access_vm_annotations,
                               TRAPS) {
 
   assert(cp != NULL, "invariant");
   assert(buffer != NULL, "invariant");
   assert(coll != NULL, "invariant");

@@ -1268,11 +1269,11 @@
       member = check_symbol_at(cp, member_index);
       if (member == NULL)  break;  // invalid member name
     }
 
     // Here is where parsing particular annotations will take place.
-    AnnotationCollector::ID id = coll->annotation_index(loader_data, aname);
+    AnnotationCollector::ID id = coll->annotation_index(loader_data, aname, can_access_vm_annotations);
     if (AnnotationCollector::_unknown == id)  continue;
     coll->set_annotation(id);
 
     if (AnnotationCollector::_jdk_internal_vm_annotation_Contended == id) {
       // @Contended can optionally specify the contention group.

@@ -1394,10 +1395,11 @@
         parse_annotations(cp,
                           runtime_visible_annotations,
                           runtime_visible_annotations_length,
                           parsed_annotations,
                           _loader_data,
+                          _can_access_vm_annotations,
                           CHECK);
         cfs->skip_u1_fast(runtime_visible_annotations_length);
       } else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) {
         if (runtime_invisible_annotations_exists) {
           classfile_parse_error(

@@ -2057,16 +2059,17 @@
       name->as_C_string(), _class_name->as_C_string(), sig->as_C_string());
 }
 
 AnnotationCollector::ID
 AnnotationCollector::annotation_index(const ClassLoaderData* loader_data,
-                                      const Symbol* name) {
+                                      const Symbol* name,
+                                      const bool can_access_vm_annotations) {
   const vmSymbols::SID sid = vmSymbols::find_sid(name);
   // Privileged code can use all annotations.  Other code silently drops some.
-  const bool privileged = loader_data->is_the_null_class_loader_data() ||
+  const bool privileged = loader_data->is_boot_class_loader_data() ||
                           loader_data->is_platform_class_loader_data() ||
-                          loader_data->is_unsafe_anonymous();
+                          can_access_vm_annotations;
   switch (sid) {
     case vmSymbols::VM_SYMBOL_ENUM_NAME(reflect_CallerSensitive_signature): {
       if (_location != _in_method)  break;  // only allow for methods
       if (!privileged)              break;  // only allow in privileged code
       return _method_CallerSensitive;

@@ -2669,10 +2672,11 @@
         parse_annotations(cp,
                           runtime_visible_annotations,
                           runtime_visible_annotations_length,
                           &parsed_annotations,
                           _loader_data,
+                          _can_access_vm_annotations,
                           CHECK_NULL);
         cfs->skip_u1_fast(runtime_visible_annotations_length);
       } else if (method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) {
         if (runtime_invisible_annotations_exists) {
           classfile_parse_error(

@@ -2863,10 +2867,14 @@
   }
 
   if (parsed_annotations.has_any_annotations())
     parsed_annotations.apply_to(methodHandle(THREAD, m));
 
+  if (is_hidden()) { // Mark methods in hidden classes as 'hidden'.
+    m->set_hidden(true);
+  }
+
   // Copy annotations
   copy_method_annotations(m->constMethod(),
                           runtime_visible_annotations,
                           runtime_visible_annotations_length,
                           runtime_invisible_annotations,

@@ -3594,10 +3602,11 @@
         parse_annotations(cp,
                           runtime_visible_annotations,
                           runtime_visible_annotations_length,
                           parsed_annotations,
                           _loader_data,
+                          _can_access_vm_annotations,
                           CHECK);
         cfs->skip_u1_fast(runtime_visible_annotations_length);
       } else if (tag == vmSymbols::tag_runtime_invisible_annotations()) {
         if (runtime_invisible_annotations_exists) {
           classfile_parse_error(

@@ -5590,19 +5599,25 @@
     } // CheckIntrinsics
 #endif // ASSERT
   }
 }
 
-InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook, TRAPS) {
+InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook,
+                                                      const ClassInstanceInfo& cl_inst_info,
+                                                      TRAPS) {
   if (_klass != NULL) {
     return _klass;
   }
 
   InstanceKlass* const ik =
     InstanceKlass::allocate_instance_klass(*this, CHECK_NULL);
 
-  fill_instance_klass(ik, changed_by_loadhook, CHECK_NULL);
+  if (is_hidden()) {
+    mangle_hidden_class_name(ik);
+  }
+
+  fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL);
 
   assert(_klass == ik, "invariant");
 
 
   if (ik->should_store_fingerprint()) {

@@ -5624,11 +5639,14 @@
   }
 
   return ik;
 }
 
-void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loadhook, TRAPS) {
+void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
+                                          bool changed_by_loadhook,
+                                          const ClassInstanceInfo& cl_inst_info,
+                                          TRAPS) {
   assert(ik != NULL, "invariant");
 
   // Set name and CLD before adding to CLD
   ik->set_class_loader_data(_loader_data);
   ik->set_name(_class_name);

@@ -5660,10 +5678,15 @@
 
   // this transfers ownership of a lot of arrays from
   // the parser onto the InstanceKlass*
   apply_parsed_class_metadata(ik, _java_fields_count, CHECK);
 
+  // can only set dynamic nest-host after static nest information is set
+  if (cl_inst_info.dynamic_nest_host() != NULL) {
+    ik->set_nest_host(cl_inst_info.dynamic_nest_host(), THREAD);
+  }
+
   // note that is not safe to use the fields in the parser from this point on
   assert(NULL == _cp, "invariant");
   assert(NULL == _fields, "invariant");
   assert(NULL == _methods, "invariant");
   assert(NULL == _inner_classes, "invariant");

@@ -5684,15 +5707,15 @@
   // has to be changed accordingly.
   ik->set_initial_method_idnum(ik->methods()->length());
 
   ik->set_this_class_index(_this_class_index);
 
-  if (is_unsafe_anonymous()) {
+  if (_is_hidden || is_unsafe_anonymous()) {
     // _this_class_index is a CONSTANT_Class entry that refers to this
-    // anonymous class itself. If this class needs to refer to its own methods or
-    // fields, it would use a CONSTANT_MethodRef, etc, which would reference
-    // _this_class_index. However, because this class is anonymous (it's
+    // hidden or anonymous class itself. If this class needs to refer to its own
+    // methods or fields, it would use a CONSTANT_MethodRef, etc, which would reference
+    // _this_class_index. However, because this class is hidden or anonymous (it's
     // not stored in SystemDictionary), _this_class_index cannot be resolved
     // with ConstantPool::klass_at_impl, which does a SystemDictionary lookup.
     // Therefore, we must eagerly resolve _this_class_index now.
     ik->constants()->klass_at_put(_this_class_index, ik);
   }

@@ -5704,10 +5727,13 @@
 
   if (_unsafe_anonymous_host != NULL) {
     assert (ik->is_unsafe_anonymous(), "should be the same");
     ik->set_unsafe_anonymous_host(_unsafe_anonymous_host);
   }
+  if (_is_hidden) {
+    ik->set_is_hidden();
+  }
 
   // Set PackageEntry for this_klass
   oop cl = ik->class_loader();
   Handle clh = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(cl));
   ClassLoaderData* cld = ClassLoaderData::class_loader_data_or_null(clh());

@@ -5783,10 +5809,11 @@
   // The create_mirror() call will also call compute_modifiers()
   java_lang_Class::create_mirror(ik,
                                  Handle(THREAD, _loader_data->class_loader()),
                                  module_handle,
                                  _protection_domain,
+                                 cl_inst_info.class_data(),
                                  CHECK);
 
   assert(_all_mirandas != NULL, "invariant");
 
   // Generate any default methods - default methods are public interface methods

@@ -5867,11 +5894,10 @@
   // Now the ClassFileParser owns this name and will decrement in
   // the destructor.
   _class_name->increment_refcount();
 }
 
-
 // For an unsafe anonymous class that is in the unnamed package, move it to its host class's
 // package by prepending its host class's package name to its class name and setting
 // its _class_name field.
 void ClassFileParser::prepend_host_package_name(const InstanceKlass* unsafe_anonymous_host, TRAPS) {
   ResourceMark rm(THREAD);

@@ -5920,12 +5946,12 @@
     }
   }
 }
 
 static bool relax_format_check_for(ClassLoaderData* loader_data) {
-  bool trusted = (loader_data->is_the_null_class_loader_data() ||
-                  SystemDictionary::is_platform_class_loader(loader_data->class_loader()));
+  bool trusted = loader_data->is_boot_class_loader_data() ||
+                 loader_data->is_platform_class_loader_data();
   bool need_verify =
     // verifyAll
     (BytecodeVerificationLocal && BytecodeVerificationRemote) ||
     // verifyRemote
     (!BytecodeVerificationLocal && BytecodeVerificationRemote && !trusted);

@@ -5933,21 +5959,20 @@
 }
 
 ClassFileParser::ClassFileParser(ClassFileStream* stream,
                                  Symbol* name,
                                  ClassLoaderData* loader_data,
-                                 Handle protection_domain,
-                                 const InstanceKlass* unsafe_anonymous_host,
-                                 GrowableArray<Handle>* cp_patches,
+                                 const ClassLoadInfo* cl_info,
                                  Publicity pub_level,
                                  TRAPS) :
   _stream(stream),
-  _requested_name(name),
   _class_name(NULL),
   _loader_data(loader_data),
-  _unsafe_anonymous_host(unsafe_anonymous_host),
-  _cp_patches(cp_patches),
+  _unsafe_anonymous_host(cl_info->unsafe_anonymous_host()),
+  _cp_patches(cl_info->cp_patches()),
+  _is_hidden(cl_info->is_hidden()),
+  _can_access_vm_annotations(cl_info->can_access_vm_annotations()),
   _num_patched_klasses(0),
   _max_num_patched_klasses(0),
   _orig_cp_size(0),
   _first_patched_klass_resolved_index(0),
   _super_klass(),

@@ -5974,11 +5999,11 @@
   _all_mirandas(NULL),
   _vtable_size(0),
   _itable_size(0),
   _num_miranda_methods(0),
   _rt(REF_NONE),
-  _protection_domain(protection_domain),
+  _protection_domain(cl_info->protection_domain()),
   _access_flags(),
   _pub_level(pub_level),
   _bad_constant_seen(0),
   _synthetic_flag(false),
   _sde_length(false),

@@ -6177,14 +6202,19 @@
   guarantee_property(
     cp_size >= 1, "Illegal constant pool size %u in class file %s",
     cp_size, CHECK);
 
   _orig_cp_size = cp_size;
+  if (is_hidden()) { // Add a slot for hidden class name.
+    assert(_max_num_patched_klasses == 0, "Sanity check");
+    cp_size++;
+  } else {
   if (int(cp_size) + _max_num_patched_klasses > 0xffff) {
     THROW_MSG(vmSymbols::java_lang_InternalError(), "not enough space for patched classes");
   }
   cp_size += _max_num_patched_klasses;
+  }
 
   _cp = ConstantPool::allocate(_loader_data,
                                cp_size,
                                CHECK);
 

@@ -6231,40 +6261,71 @@
     _this_class_index, CHECK);
 
   Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
   assert(class_name_in_cp != NULL, "class_name can't be null");
 
-  // Update _class_name to reflect the name in the constant pool
-  update_class_name(class_name_in_cp);
-
   // Don't need to check whether this class name is legal or not.
   // It has been checked when constant pool is parsed.
   // However, make sure it is not an array type.
   if (_need_verify) {
-    guarantee_property(_class_name->char_at(0) != JVM_SIGNATURE_ARRAY,
+    guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
                        "Bad class name in class file %s",
                        CHECK);
   }
 
-  // Checks if name in class file matches requested name
-  if (_requested_name != NULL && _requested_name != _class_name) {
+#ifdef ASSERT
+  // Basic sanity checks
+  assert(!(_is_hidden && (_unsafe_anonymous_host != NULL)), "mutually exclusive variants");
+
+  if (_unsafe_anonymous_host != NULL) {
+    assert(_class_name == vmSymbols::unknown_class_name(), "A named anonymous class???");
+  }
+  if (_is_hidden) {
+    assert(_class_name != vmSymbols::unknown_class_name(), "hidden classes should have a special name");
+  }
+#endif
+
+  // Update the _class_name as needed depending on whether this is a named,
+  // un-named, hidden or unsafe-anonymous class.
+
+  if (_is_hidden) {
+    assert(_class_name != NULL, "Unexpected null _class_name");
+#ifdef ASSERT
+    if (_need_verify) {
+      verify_legal_class_name(_class_name, CHECK);
+    }
+#endif
+
+  // NOTE: !_is_hidden does not imply "findable" as it could be an old-style
+  //       "hidden" unsafe-anonymous class
+
+  // If this is an anonymous class fix up its name if it is in the unnamed
+  // package.  Otherwise, throw IAE if it is in a different package than
+  // its host class.
+  } else if (_unsafe_anonymous_host != NULL) {
+    update_class_name(class_name_in_cp);
+    fix_unsafe_anonymous_class_name(CHECK);
+
+  } else {
+    // Check if name in class file matches given name
+    if (_class_name != class_name_in_cp) {
+      if (_class_name != vmSymbols::unknown_class_name()) {
     ResourceMark rm(THREAD);
-    Exceptions::fthrow(
-      THREAD_AND_LOCATION,
+        Exceptions::fthrow(THREAD_AND_LOCATION,
       vmSymbols::java_lang_NoClassDefFoundError(),
       "%s (wrong name: %s)",
-      _class_name->as_C_string(),
-      _requested_name != NULL ? _requested_name->as_C_string() : "NoName"
+                           class_name_in_cp->as_C_string(),
+                           _class_name->as_C_string()
     );
     return;
+      } else {
+        // The class name was not known by the caller so we set it from
+        // the value in the CP.
+        update_class_name(class_name_in_cp);
+      }
+      // else nothing to do: the expected class name matches what is in the CP
   }
-
-  // if this is an anonymous class fix up its name if it's in the unnamed
-  // package.  Otherwise, throw IAE if it is in a different package than
-  // its host class.
-  if (_unsafe_anonymous_host != NULL) {
-    fix_unsafe_anonymous_class_name(CHECK);
   }
 
   // Verification prevents us from creating names with dots in them, this
   // asserts that that's the case.
   assert(is_internal_format(_class_name), "external class name format used internally");

@@ -6285,13 +6346,14 @@
     if (DumpLoadedClassList != NULL && stream->source() != NULL && classlist_file->is_open()) {
       if (!ClassLoader::has_jrt_entry()) {
         warning("DumpLoadedClassList and CDS are not supported in exploded build");
         DumpLoadedClassList = NULL;
       } else if (SystemDictionaryShared::is_sharing_possible(_loader_data) &&
+                 !_is_hidden &&
                  _unsafe_anonymous_host == NULL) {
         // Only dump the classes that can be stored into CDS archive.
-        // Unsafe anonymous classes such as generated LambdaForm classes are also not included.
+        // Hidden and unsafe anonymous classes such as generated LambdaForm classes are also not included.
         oop class_loader = _loader_data->class_loader();
         ResourceMark rm(THREAD);
         bool skip = false;
         if (class_loader == NULL || SystemDictionary::is_platform_class_loader(class_loader)) {
           // For the boot and platform class loaders, skip classes that are not found in the

@@ -6382,10 +6444,39 @@
                      CHECK);
 
   // all bytes in stream read and parsed
 }
 
+void ClassFileParser::mangle_hidden_class_name(InstanceKlass* const ik) {
+  ResourceMark rm;
+  // Construct hidden name from _class_name, "+", and &ik. Note that we can't
+  // use a '/' because that confuses finding the class's package.  Also, can't
+  // use an illegal char such as ';' because that causes serialization issues
+  // and issues with hidden classes that create their own hidden classes.
+  char addr_buf[20];
+  jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik));
+  size_t new_name_len = _class_name->utf8_length() + 2 + strlen(addr_buf);
+  char* new_name = NEW_RESOURCE_ARRAY(char, new_name_len);
+  jio_snprintf(new_name, new_name_len, "%s+%s",
+               _class_name->as_C_string(), addr_buf);
+  update_class_name(SymbolTable::new_symbol(new_name));
+
+  // Add a Utf8 entry containing the hidden name.
+  assert(_class_name != NULL, "Unexpected null _class_name");
+  int hidden_index = _orig_cp_size; // this is an extra slot we added
+  _cp->symbol_at_put(hidden_index, _class_name);
+
+  // Update this_class_index's slot in the constant pool with the new Utf8 entry.
+  // We have to update the resolved_klass_index and the name_index together
+  // so extract the existing resolved_klass_index first.
+  CPKlassSlot cp_klass_slot = _cp->klass_slot_at(_this_class_index);
+  int resolved_klass_index = cp_klass_slot.resolved_klass_index();
+  _cp->unresolved_klass_at_put(_this_class_index, hidden_index, resolved_klass_index);
+  assert(_cp->klass_slot_at(_this_class_index).name_index() == _orig_cp_size,
+         "Bad name_index");
+}
+
 void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const stream,
                                                  ConstantPool* cp,
                                                  TRAPS) {
   assert(stream != NULL, "invariant");
   assert(stream->at_eos(), "invariant");
< prev index next >