src/share/vm/classfile/classFileParser.cpp

Print this page
rev 145 : 6711908: JVM needs direct access to some annotations
Summary: Provide hook to scan annotations in the class file parser, before the instanceKlass is created.
Reviewed-by: ?

@@ -207,10 +207,17 @@
   cfs0->set_current(cfs1.current());
 }
 
 bool inline valid_cp_range(int index, int length) { return (index > 0 && index < length); }
 
+inline symbolOop check_symbol_at(constantPoolHandle cp, int index) {
+  if (valid_cp_range(index, cp->length()) && cp->tag_at(index).is_utf8())
+    return cp->symbol_at(index);
+  else
+    return NULL;
+}
+
 constantPoolHandle ClassFileParser::parse_constant_pool(TRAPS) {
   ClassFileStream* cfs = stream();
   constantPoolHandle nullHandle;
 
   cfs->guarantee_more(3, CHECK_(nullHandle)); // length, first cp tag

@@ -1194,17 +1201,131 @@
                       "Exceptions attribute has wrong length in class file %s", CHECK_NULL);
   }
   return checked_exceptions_start;
 }
 
+// Skip an annotation.  Return >=limit if there is any problem.
+int ClassFileParser::skip_annotation(u1* buffer, int limit, int index) {
+  // annotation := atype:u2 do(nmem:u2) {member:u2 value}
+  // value := switch (tag:u1) { ... } 
+  index += 2;  // skip atype
+  if ((index += 2) >= limit)  return limit;  // read nmem
+  int nmem = Bytes::get_Java_u2(buffer+index-2);
+  while (--nmem >= 0 && index < limit) {
+    index += 2; // skip member
+    index = skip_annotation_value(buffer, limit, index);
+  }
+  return index;
+}
 
+// Skip an annotation value.  Return >=limit if there is any problem.
+int ClassFileParser::skip_annotation_value(u1* buffer, int limit, int index) {
+  // value := switch (tag:u1) {
+  //   case B, C, I, S, Z, D, F, J, c: con:u2;
+  //   case e: e_class:u2 e_name:u2;
+  //   case s: s_con:u2;
+  //   case [: do(nval:u2) {value};
+  //   case @: annotation;
+  //   case s: s_con:u2;
+  // }
+  if ((index += 1) >= limit)  return limit;  // read tag
+  u1 tag = buffer[index-1];
+  switch (tag) {
+  case 'B': case 'C': case 'I': case 'S': case 'Z':
+  case 'D': case 'F': case 'J': case 'c': case 's':
+    index += 2;  // skip con or s_con
+    break;
+  case 'e':
+    index += 4;  // skip e_class, e_name
+    break;
+  case '[':
+    {
+      if ((index += 2) >= limit)  return limit;  // read nval
+      int nval = Bytes::get_Java_u2(buffer+index-2);
+      while (--nval >= 0 && index < limit) {
+        index = skip_annotation_value(buffer, limit, index);
+      }
+    }
+    break;
+  case '@':
+    index = skip_annotation(buffer, limit, index);
+    break;
+  default:
+    assert(false, "annotation tag");
+    return limit;  //  bad tag byte
+  }
+  return index;
+}
+
+// Sift through annotations, looking for those significant to the VM:
+void ClassFileParser::parse_class_annotations(u1* buffer, int limit,
+                                              constantPoolHandle cp,
+                                              symbolHandle* retention_policy,
+                                              TRAPS) {
+  // annotations := do(nann:u2) {annotation}
+  int index = 0;
+  if ((index += 2) >= limit)  return;  // read nann
+  int nann = Bytes::get_Java_u2(buffer+index-2);
+  enum {  // initial annotation layout
+    atype_off = 0,      // utf8 such as 'Ljava/lang/annotation/Retention;'
+    count_off = 2,      // u2   such as 1 (one value)
+    member_off = 4,     // utf8 such as 'value'
+    tag_off = 6,        // u1   such as 'c' (type) or 'e' (enum)
+    e_tag_val = 'e',
+      e_type_off = 7,   // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;'
+      e_con_off = 9,    // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME'
+      e_size = 11,     // end of 'e' annotation
+    c_tag_val = 'c',
+      c_con_off = 7,    // utf8 payload, such as 'I' or 'Ljava/lang/String;'
+      c_size = 9,       // end of 'c' annotation
+    min_size = 6        // smallest possible size (zero members)
+  };
+  while (--nann >= 0 && index + min_size <= limit) {
+    int index0 = index;
+    index = skip_annotation(buffer, limit, index);
+    u1* abase = buffer + index0;
+    int atype = Bytes::get_Java_u2(abase + atype_off);
+    int count = Bytes::get_Java_u2(abase + count_off);
+    symbolOop aname = check_symbol_at(cp, atype);
+    if (aname == NULL)  break;  // invalid annotation name
+    symbolOop member = NULL;
+    if (count >= 1) {
+      int member_index = Bytes::get_Java_u2(abase + member_off);
+      member = check_symbol_at(cp, member_index);
+      if (member == NULL)  break;  // invalid member name
+    }
+
+    // The parsing of @Retention is for example only.
+#define Retention_signature       classloader_signature  // just for illustration
+#define RetentionPolicy_signature thread_signature
+    if (aname == vmSymbols::Retention_signature()) {
+      symbolOop payload = NULL;
+      if (count == 1
+          && e_size == (index0 - index)  // match size
+          && e_tag_val == *(abase + tag_off)
+          && (check_symbol_at(cp, Bytes::get_Java_u2(abase + e_type_off))
+              == vmSymbols::RetentionPolicy_signature())
+          && member == vmSymbols::value_name()) {
+        payload = check_symbol_at(cp, Bytes::get_Java_u2(abase + e_con_off));
+      }
+      check_property(payload != NULL,
+                     "Invalid @Retention annotation at offset %u in class file %s",
+                     index0, CHECK);
+      (*retention_policy) = payload;  // return the payload
+    }
+  }
+#undef Retention_signature
+#undef RetentionPolicy_signature
+}
+
+
 #define MAX_ARGS_SIZE 255
 #define MAX_CODE_SIZE 65535
 #define INITIAL_MAX_LVT_NUMBER 256
 
 // Note: the parse_method below is big and clunky because all parsing of the code and exceptions
-// attribute is inlined. This is curbersome to avoid since we inline most of the parts in the
+// attribute is inlined. This is cumbersome to avoid since we inline most of the parts in the
 // methodOop to save footprint, so we only know the size of the resulting methodOop when the
 // entire method attribute is parsed.
 //
 // The promoted_flags parameter is used to pass relevant access_flags
 // from the method back up to the containing klass. These flag values

@@ -1860,27 +1981,28 @@
     return typeArrayHandle(THREAD, Universe::the_empty_int_array());
   }
 }
 
 
-void ClassFileParser::parse_classfile_sourcefile_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) {
+void ClassFileParser::parse_classfile_sourcefile_attribute(constantPoolHandle cp, symbolHandle* sourcefile_ret, TRAPS) {
   ClassFileStream* cfs = stream();
   cfs->guarantee_more(2, CHECK);  // sourcefile_index
   u2 sourcefile_index = cfs->get_u2_fast();
   check_property(
     valid_cp_range(sourcefile_index, cp->length()) &&
       cp->tag_at(sourcefile_index).is_utf8(),
     "Invalid SourceFile attribute at constant pool index %u in class file %s",
     sourcefile_index, CHECK);
-  k->set_source_file_name(cp->symbol_at(sourcefile_index));
+  (*sourcefile_ret) = cp->symbol_at(sourcefile_index);
 }
 
 
 
 void ClassFileParser::parse_classfile_source_debug_extension_attribute(constantPoolHandle cp,
-                                                                       instanceKlassHandle k,
-                                                                       int length, TRAPS) {
+                                                                       int length,
+                                                                       symbolHandle* sde_symbol_ret,
+                                                                       TRAPS) {
   ClassFileStream* cfs = stream();
   u1* sde_buffer = cfs->get_u1_buffer();
   assert(sde_buffer != NULL, "null sde buffer");
 
   // Don't bother storing it if there is no way to retrieve it

@@ -1887,11 +2009,11 @@
   if (JvmtiExport::can_get_source_debug_extension()) {
     // Optimistically assume that only 1 byte UTF format is used
     // (common case)
     symbolOop sde_symbol = oopFactory::new_symbol((char*)sde_buffer,
                                                   length, CHECK);
-    k->set_source_debug_extension(sde_symbol);
+    (*sde_symbol_ret) = symbolHandle(THREAD, sde_symbol);
   }
   // Got utf8 string, set stream position forward
   cfs->skip_u1(length, CHECK);
 }
 

@@ -1898,11 +2020,11 @@
 
 // Inner classes can be static, private or protected (classic VM does this)
 #define RECOGNIZED_INNER_CLASS_MODIFIERS (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED | JVM_ACC_STATIC)
 
 // Return number of classes in the inner classes attribute table
-u2 ClassFileParser::parse_classfile_inner_classes_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) {
+u2 ClassFileParser::parse_classfile_inner_classes_attribute(constantPoolHandle cp, typeArrayHandle* inner_classes_ret, TRAPS) {
   ClassFileStream* cfs = stream();
   cfs->guarantee_more(2, CHECK_0);  // length
   u2 length = cfs->get_u2_fast();
 
   // 4-tuples of shorts [inner_class_info_index, outer_class_info_index, inner_name_index, inner_class_access_flags]

@@ -1968,33 +2090,33 @@
       }
     }
   }
 
   // Update instanceKlass with inner class info.
-  k->set_inner_classes(inner_classes());
+  (*inner_classes_ret) = inner_classes;
   return length;
 }
 
-void ClassFileParser::parse_classfile_synthetic_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) {
-  k->set_is_synthetic();
+void ClassFileParser::parse_classfile_synthetic_attribute(constantPoolHandle cp, bool* synthetic_flag_ret, TRAPS) {
+  (*synthetic_flag_ret) = true;
 }
 
-void ClassFileParser::parse_classfile_signature_attribute(constantPoolHandle cp, instanceKlassHandle k, TRAPS) {
+void ClassFileParser::parse_classfile_signature_attribute(constantPoolHandle cp, symbolHandle* signature_ret, TRAPS) {
   ClassFileStream* cfs = stream();
   u2 signature_index = cfs->get_u2(CHECK);
   check_property(
     valid_cp_range(signature_index, cp->length()) &&
       cp->tag_at(signature_index).is_utf8(),
     "Invalid constant pool index %u in Signature attribute in class file %s",
     signature_index, CHECK);
-  k->set_generic_signature(cp->symbol_at(signature_index));
+  (*signature_ret) = cp->symbol_at(signature_index);
 }
 
-void ClassFileParser::parse_classfile_attributes(constantPoolHandle cp, instanceKlassHandle k, TRAPS) {
+void ClassFileParser::parse_classfile_attributes(constantPoolHandle cp, TRAPS) {
   ClassFileStream* cfs = stream();
   // Set inner classes attribute to default sentinel
-  k->set_inner_classes(Universe::the_empty_short_array());
+  _inner_classes = typeArrayHandle(THREAD, Universe::the_empty_short_array());
   cfs->guarantee_more(2, CHECK);  // attributes_count
   u2 attributes_count = cfs->get_u2_fast();
   bool parsed_sourcefile_attribute = false;
   bool parsed_innerclasses_attribute = false;
   bool parsed_enclosingmethod_attribute = false;

@@ -2021,22 +2143,22 @@
       if (parsed_sourcefile_attribute) {
         classfile_parse_error("Multiple SourceFile attributes in class file %s", CHECK);
       } else {
         parsed_sourcefile_attribute = true;
       }
-      parse_classfile_sourcefile_attribute(cp, k, CHECK);
+      parse_classfile_sourcefile_attribute(cp, &_sourcefile, CHECK);
     } else if (tag == vmSymbols::tag_source_debug_extension()) {
       // Check for SourceDebugExtension tag
-      parse_classfile_source_debug_extension_attribute(cp, k, (int)attribute_length, CHECK);
+      parse_classfile_source_debug_extension_attribute(cp, (int)attribute_length, &_sde_symbol, CHECK);
     } else if (tag == vmSymbols::tag_inner_classes()) {
       // Check for InnerClasses tag
       if (parsed_innerclasses_attribute) {
         classfile_parse_error("Multiple InnerClasses attributes in class file %s", CHECK);
       } else {
         parsed_innerclasses_attribute = true;
       }
-      u2 num_of_classes = parse_classfile_inner_classes_attribute(cp, k, CHECK);
+      u2 num_of_classes = parse_classfile_inner_classes_attribute(cp, &_inner_classes, CHECK);
       if (_need_verify && _major_version >= JAVA_1_5_VERSION) {
         guarantee_property(attribute_length == sizeof(num_of_classes) + 4 * sizeof(u2) * num_of_classes,
                           "Wrong InnerClasses attribute length in class file %s", CHECK);
       }
     } else if (tag == vmSymbols::tag_synthetic()) {

@@ -2045,11 +2167,11 @@
       if (attribute_length != 0) {
         classfile_parse_error(
           "Invalid Synthetic classfile attribute length %u in class file %s",
           attribute_length, CHECK);
       }
-      parse_classfile_synthetic_attribute(cp, k, CHECK);
+      parse_classfile_synthetic_attribute(cp, &_synthetic_flag, CHECK);
     } else if (tag == vmSymbols::tag_deprecated()) {
       // Check for Deprecatd tag - 4276120
       if (attribute_length != 0) {
         classfile_parse_error(
           "Invalid Deprecated classfile attribute length %u in class file %s",

@@ -2060,15 +2182,20 @@
         if (attribute_length != 2) {
           classfile_parse_error(
             "Wrong Signature attribute length %u in class file %s",
             attribute_length, CHECK);
         }
-        parse_classfile_signature_attribute(cp, k, CHECK);
+        parse_classfile_signature_attribute(cp, &_generic_signature, CHECK);
       } else if (tag == vmSymbols::tag_runtime_visible_annotations()) {
         runtime_visible_annotations_length = attribute_length;
         runtime_visible_annotations = cfs->get_u1_buffer();
         assert(runtime_visible_annotations != NULL, "null visible annotations");
+        parse_class_annotations(runtime_visible_annotations,
+                                runtime_visible_annotations_length,
+                                cp,
+                                &_retention_policy,
+                                CHECK);
         cfs->skip_u1(runtime_visible_annotations_length, CHECK);
       } else if (PreserveAllAnnotations && tag == vmSymbols::tag_runtime_invisible_annotations()) {
         runtime_invisible_annotations_length = attribute_length;
         runtime_invisible_annotations = cfs->get_u1_buffer();
         assert(runtime_invisible_annotations != NULL, "null invisible annotations");

@@ -2093,11 +2220,12 @@
         if (method_index != 0 &&
             (!cp->is_within_bounds(method_index) ||
              !cp->tag_at(method_index).is_name_and_type())) {
           classfile_parse_error("Invalid or out-of-bounds method index in EnclosingMethod attribute in class file %s", CHECK);
         }
-        k->set_enclosing_method_indices(class_index, method_index);
+        _em_class_index  = class_index;
+        _em_method_index = method_index;
       } else {
         // Unknown attribute
         cfs->skip_u1(attribute_length, CHECK);
       }
     } else {

@@ -2108,11 +2236,11 @@
   typeArrayHandle annotations = assemble_annotations(runtime_visible_annotations,
                                                      runtime_visible_annotations_length,
                                                      runtime_invisible_annotations,
                                                      runtime_invisible_annotations_length,
                                                      CHECK);
-  k->set_class_annotations(annotations());
+  _annotations = annotations;
 }
 
 
 typeArrayHandle ClassFileParser::assemble_annotations(u1* runtime_visible_annotations,
                                                       int runtime_visible_annotations_length,

@@ -2359,10 +2487,13 @@
   // Timing
   PerfTraceTime vmtimer(ClassLoader::perf_accumulated_time());
 
   _has_finalizer = _has_empty_finalizer = _has_vanilla_constructor = false;
 
+  _em_class_index = _em_method_index = 0;
+  _synthetic_flag = false;
+
   if (JvmtiExport::should_post_class_file_load_hook()) {
     unsigned char* ptr = cfs->buffer();
     unsigned char* end_ptr = cfs->buffer() + cfs->length();
 
     JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain,

@@ -2553,10 +2684,16 @@
 
     objArrayHandle methods_annotations(THREAD, methods_annotations_oop);
     objArrayHandle methods_parameter_annotations(THREAD, methods_parameter_annotations_oop);
     objArrayHandle methods_default_annotations(THREAD, methods_default_annotations_oop);
 
+    // Additional attributes
+    parse_classfile_attributes(cp, CHECK_(nullHandle));
+
+    // Make sure this is the end of class file stream
+    guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle));
+
     // We check super class after class file is parsed and format is checked
     if (super_class_index > 0) {
       symbolHandle sk (THREAD, cp->klass_name_at(super_class_index));
       if (access_flags.is_interface()) {
         // Before attempting to resolve the superclass, check for class format

@@ -3032,16 +3169,19 @@
         // super class exists and this class inherited miranda methods
         ) {
       this_klass->set_has_miranda_methods(); // then set a flag
     }
 
-    // Additional attributes
-    parse_classfile_attributes(cp, this_klass, CHECK_(nullHandle));
+    // Fill in field values from parse_classfile_attributes:
+    this_klass->set_inner_classes(_inner_classes());
+    this_klass->set_enclosing_method_indices(_em_class_index, _em_method_index);
+    this_klass->set_source_file_name(_sourcefile());
+    this_klass->set_source_debug_extension(_sde_symbol());
+    this_klass->set_generic_signature(_generic_signature());
+    if (_synthetic_flag)  this_klass->set_is_synthetic();
+    this_klass->set_class_annotations(_annotations());
 
-    // Make sure this is the end of class file stream
-    guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle));
-
     // Initialize static fields
     this_klass->do_local_static_fields(&initialize_static_field, CHECK_(nullHandle));
 
     // VerifyOops believes that once this has been set, the object is completely loaded.
     // Compute transitive closure of interfaces this class implements