Annotation Support

By Mathias Panzenböck < panzi@complang.tuwien.ac.at >

A short Introduction to Annotations in Java

Since JDK 5.0 there is the possibility to annotate certain elements. This can be used to supply some kind of documentation to the annotated elements (=metadata), to influence compiler behaviour (e.g. the all known @SuppressWarnings("unchecked")), or to do nifty tricks like some frameworks do (e.g. like Hibernate uses annotations for object relational mapping; or for really easy command line option parsing).

Annotations can be defined for classes and all that can be represented by an instance of java.lang.Class (=class, interface, @interface (annotation type), enum, package), for fields, for methods, for constructors and for parameters. Local variables can also be annotated, but the annotations cannot be accessed at runtime and are simply discarded by the compiler.

Annotations are stored as attributes in the classfile. There are several annotation attributes defined:

These attributes are loaded when the classfile is read, but will only be parsed when the corresponding annotation is accessed.

Depending on its usage an annotation might not be needed at runtime. E.g. the SuppressWarnings annotation is only needed by the compiler. To tell the compiler that an annotation should be visible at runtime, the annotation itself has to have the annotation @Retention(RetentionPolicy.RUNTIME) declared.

Runtime invisible annotations usually aren't even included in the classfile, but that depends on the compiler (or on compiler flags). If they are included in the classfile, the virtual machine should not load them, unless an implementation specific flag is set, which tells the JVM to do so. CACAO doesn't have such a flag right now and therefore runtime invisible annotations are simply discarded.

You declare annotations like this:

   1 public @interface MyAnnotation {
   2                 int              intValue();
   3                 String           stringValue()     default "foo bar";
   4                 Class<?>[]       classArray()      default {Foo.class, Class.class};
   5                 SuppressWarnings annotationValue() default @SuppressWarnings("unused");
   6 }

All methods defined in annotations have zero parameters and their return type must be one of the following:

You use annotations like this:

   1 import java.util.Map;
   2 @MyAnnotation(intValue=42)
   3 class MyClass {
   4                 @MyAnnotation(intValue=23, stringValue="hello world")
   5                 public int aField;
   6                 @Deprecated
   7                 public void aDeprecatedMethod(
   8                         @MyAnnotation(intValue=5, classArray=void.class)
   9                         int a) {
  10                 }
  11                 @SuppressWarnings({"unchecked", "unused"})
  12                 public MyClass() {
  13                         Map<String,String>[] map = new Map[16];
  14                 }
  15 }

The annotations @SuppressWarnings and @Deprecated are defined by the Java library (amongst many others).

You can then access the annotations of an annotated element by the reflection API. There are several methods to do so. Each annotated element can have an arbitrary number of annotations, but only one per annotation type.

The annotated element interface looks like this:

   1 package java.lang.reflect;
   2 import java.lang.annotation.Annotation;
   3 public interface AnnotatedElement
   4 {
   5   <T extends Annotation> T getAnnotation(Class<T> annotationClass);
   6   Annotation[] getAnnotations();
   7   Annotation[] getDeclaredAnnotations();
   8   boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
   9 }

Some classes offer more than this basic set of methods.

java.lang.Class has the method public boolean isAnnotation() which tells you if the referred class is an annotation.

java.lang.reflect.Constructor and java.lang.reflect.Method have the method public Annotation[][] getParameterAnnotations() which returns the annotations for each parameter as a two-dimensional array.

java.lang.reflect.Method has the method public Object getDefaultValue() which returns the default value of a method of an annotation.

Annotations in Java are interfaces. Therefore there has to be an implementing class for each annotation type. But the Java compiler does not generate such an implementation, this actually happens at runtime. The Java runtime has to create an implementation for an annotation type the first time an annotation of a specific annotation type is requested. It then remembers this on the fly generated class and uses it for each further instance of the same annotation type.

Overview of Annotation Support in CACAO

Even though annotations in Java exist since JDK 5.0 I used the classfile specification of JDK 6.0 (JSR202) to implement the annotations support. However, there where no changes between this versions.

Because OpenJDK does a lot concerning annotation support in it's J2SE library I started with annotations support for CACAO + OpenJDK. I thought this would be easier and I would be able to gather some experience for working on the annotation support for CACAO + GNU Classpath. OpenJDK does the annotation parsing in Java code.

What had to be done:

The code for the annotation loading is placed in the files src/vmcore/annotation.h and src/vmcore/annotation.c.

This code is called by the loader functions:

See: src/vmcore/class.c, src/vmcore/method.c, src/vmcore/field.c

All annotations and also the annotation default values are stored in the classinfo struct as unparsed byte arrays:

 147 #if defined(ENABLE_ANNOTATIONS)
 148         java_object_t *annotations;
 149 
 150         java_object_t *method_annotations;
 151         java_object_t *method_parameterannotations;
 152         java_object_t *method_annotationdefaults;
 153         java_object_t *field_annotations;
 154 #endif

See: src/vmcore/class.h

I decided to use Java objects (java_bytearray_t and java_objectarray_t) rather than implementing my own array structs, because the annotation parser, that is written in Java, needs Java bytearrays anyway. I would have had to copy the whole unparsed annotation when they are parsed, which would had reduced speed and increased memory usage.

For this to be possible twisti had to adapt CACAOs bootstrap process, so that the primitive table already exists when classes will be loaded (because I use builtin_newarray_byte, bultin_anewarray and primitive_arrayclass_get_by_type during loading of the annotation attributes).

Futher I added functions to access the unparsed annotations as Java byte arrays:

See: src/vmcore/class.h, src/vmcore/field.h, src/vmcore/method.h

And used them in src/native/vm/openjdk/jvm.c to implement these functions:

Then I had to add an implementation for OpenJDKs ConstantPool class by filling in these functions (not all of them are used in the annotations support):

In the annotation attributes in the classfile are no values stored, but indices of members of the constant pool of the annotated class. The values at these indices represent the values of the annotations' field. Therefore the ConstantPool class is needed by the annotation parser to access those constants.

This was roughly all that what was needed for annotation support for CACAO + OpenJDK. For GNU Classpath much more had to be done. First there is no annotations parser in GNU Classpath, but because of the GPL I just imported the one from OpenJDK. So far so good, but GNU Classpath did not do anything about annotations except in java.lang.Class. I had to implement the "high level" annotations interface:

See: src/native/vm/gnuclasspath/java_lang_VMClass.c, src/classes/gnuclasspath/java/lang/reflect/Constructor.java, src/native/vm/gnuclasspath/java_lang_reflect_Constructor.c, src/classes/gnuclasspath/java/lang/reflect/Field.java, src/native/vm/gnuclasspath/java_lang_reflect_Field.c, src/classes/gnuclasspath/java/lang/reflect/Method.java, src/native/vm/gnuclasspath/java_lang_reflect_Method.c

I implementined caching of the parsed annotations almost identical to OpenJDK, except for java.lang.Class, because that class already had the annotation interface implemented (without caching). So I would have had to import this class from GNU Classpath, change it and keep it up to date. Twisti said that's not worth the effort and that OpenJDK is the future.

Because I used the annotation parser from OpenJDK I had to implement the sun.reflect.ConstantPool class for GNU Classpath, too.

See: src/classes/gnuclasspath/sun/reflect/ConstantPool.java, src/native/vm/gnuclasspath/sun_reflect_ConstantPool.cpp

The ugly bit there is the redundant implementation of this class. It is still under discussion what we do in such a case. We agreed that until a better solution is found, we will just keep the redundant implementations.

Imported Files

Following files where imported from GNU Classpath or OpenJDK and have to be kept up to date. Files marked with * where changed or extended a bit to be usable with CACAO. All this imported files are used with GNU Classpath only.

Importet from GNU Classpath:

Imported from OpenJDK:

TODO: Keep these imported files up to date with OpenJDK/GNU Classpath.

Files I touched and what I did to them

List of touched files:

THIRDPARTY

view file

Added copyright notice for this imported OpenJDK files:

configure.ac

view file

Annotations Support will be built if the configure option --enable-annotations is supplied. As of the next release after the summer of 2007 this option will be enabled by default.

Twisti later moved this option to the file m4/annotations.m4.

If possible, all annotations support code is #ifdef-ed with the ENABLE_ANNOTATIONS macro.

src/cacaoh/dummy.c

view file

Simple/dummy implementations of following functions where added:

src/classes/Makefile.am

view file

I added src/classes/gnuclasspath/java/lang/reflect/Constructor.java to VM_JAVA_FILES in this file.

Following files are only added to VM_JAVA_FILES if ENABLE_ANNOTATIONS is defined:

src/classes/gnuclasspath/java/lang/reflect/Constructor.java

view file

This file was imported from GNU Classpath because I needed to add some methods.

The additions to this class are inspired by OpenJDK (the private interface looks and works the same but is implemented differently).

Following fields where added:

  92   private byte[] annotations = null;

This field holds the unparsed annotations.

  97   private byte[] parameterAnnotations = null;

This field holds the unparsed parameter annotations.

 103   private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations = null;

This field holds the parsed annotations.

 111   private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY =
 112     new Annotation[0];

Used in getDeclaredAnnotations(). Look there for a description.

Following methods where added/implemented:

 443   private synchronized native Map<Class<? extends Annotation>, Annotation> declaredAnnotations();

When called the first time, this method will parse the declared annotations, which are stored unparsed in the field annotations and then stores the result in the field declaredAnnotations as a HashMap. Each successive call will just return this field.

 425   public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
 426     if (annotationClass == null)
 427       throw new NullPointerException();
 428     return (T)declaredAnnotations().get(annotationClass);
 429   }

This method gets the annotation of the constructor of the type specified by annotationClass, or null, if there is no such annotation present.

 435   public Annotation[] getDeclaredAnnotations() {
 436     return declaredAnnotations().values().toArray(EMPTY_ANNOTATIONS_ARRAY);
 437   }

This method gets all the declared annotations in an array. Because the generic type java.util.Collection<Annotation> cannot create an array of type Annotation[] (because of type erasure) the toArray() method must get a zero length template array passed, with which an annotation array can be created.

 461   public native Annotation[][] getParameterAnnotations();

This method parses and returns all the parameter annotations in a two-dimensional Annotation array.

TODO: Maybe also cache parsed parameter annotations? However, the API specification does not specify an annotation lookup method like getAnnotation(Class<T> annotationClass) for parameter annotations, and so no Map has to be stored for this. The question is: Is caching the parsed parameter annotation worth it? (OpenJDk does not do it.)

src/classes/gnuclasspath/java/lang/reflect/Field.java

view file

Following fields where added:

   1   private byte[] annotations = null;
   2   private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations = null;
   3   private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY =
   4     new Annotation[0];

See: Constructor.java

Following methods where added/implemented:

   1   public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
   2   public Annotation[] getDeclaredAnnotations()
   3   private synchronized native Map<Class<? extends Annotation>, Annotation> declaredAnnotations();

See: Constructor.java

src/classes/gnuclasspath/java/lang/reflect/Method.java

view file

Following fields where added:

   1   private byte[] annotations          = null;
   2   private byte[] parameterAnnotations = null;
   3   private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations = null;
   4   private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY =
   5     new Annotation[0];

See: Constructor.java

   1   private byte[] annotationDefault    = null;

This field stores the unparsed annotation default value, if this method belongs to an annotation interface. Otherwise, or if there is no default value, it points to null.

Following methods where added/implemented:

   1   public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
   2   public Annotation[] getDeclaredAnnotations()
   3   private synchronized native Map<Class<? extends Annotation>, Annotation> declaredAnnotations();
   4   public native Annotation[][] getParameterAnnotations();

See: Constructor.java

   1   public native Object getDefaultValue();

This method returns the parsed annotation default value of this method, if this method belongs to an annotation interface. Otherwise, or if there is no default value, it returns null.

src/classes/gnuclasspath/sun/reflect/ConstantPool.java

view file

I imported this class from OpenJDk, where it is used to access the constant pool entries of a class. This is needed when parsing annotations (and at the moment only then).

I guess because it could be a security risk to grant normal Java code access to this class, certain measures where taken to prevent that. First of all, there is no obvious way to get a ConstantPool object in normal Java code. Further the field constantPoolOop of the class ConstantPool is hidden from the reflection API by this code:

  61   static {
  62       Reflection.registerFieldsToFilter(ConstantPool.class, new String[] { "constantPoolOop" });
  63   }

However, this is OpenJDK specific and I imported this class for usage with GNU Classpath. I just out commented this static block, because I could not find a similar mechanism in GNU Classpath. I'm not sure if this is even necessary because there is no way to get a ConstantPool object in normal java code.

However, the field constantPoolOop is just the class object for which the constant pool was requested. I figured this is the easiest way, because in CACAO the access to the constant pool entries takes place by the function voidptr class_getconstant(classinfo *class, u4 pos, u4 ctype).

src/classes/gnuclasspath/sun/reflect/annotation/AnnotationParser.java

view file

Like all classes in src/classes/gnuclasspath/sun/reflect/annotation this class was imported from OpenJDK.

  59     public static Annotation[] parseAnnotationsIntoArray(
  60                 byte[] rawAnnotations,
  61                 ConstantPool constPool,
  62                 Class container)

This method is only used by java.lang.Class.

  79     public static Annotation[][] parseParameterAnnotations(
  80                     byte[] parameterAnnotations,
  81                     ConstantPool constPool,
  82                     Class container,
  83                     int numParameters)

This method is a wrapper around public static Annotation[][] parseParameterAnnotations(byte[] rawAnnotations, ConstantPool constPool, Class container) which basically adds a check if parameterAnnotations == null (= no parameter annotations at all) and a check for the parameter count and throws appropriate exceptions.

 111     public static Object parseAnnotationDefault(Method method,
 112                                                 byte[] annotationDefault,
 113                                                 ConstantPool constPool)

This is basically a copy from OpenJDKs java.lang.reflect.Method.getAnnotationDefault() method because I wanted to change as less as possible in the Classpath code and there this method is declared as native. However, I would have to write a method to get a ConstantPool object in java code from which I rather stay away. My policy is: You can't get a ConstantPool, but the VM can give one to "you" (to the right method).

 465     private static Class<?> parseSig(String sig, Class container) {
 466         if (sig.equals("V")) {
 467             return void.class;
 468         }
 469         else {
 470             return toClass(new FieldSignatureParser(container, sig).getFieldType());
 471         }
 472     }

I had to rewrite this method. It was much more complex and used a lot of classes from OpenJDK I would have had to import. But GNU Classpath has it's own signature parser (gnu.java.lang.reflect.FieldSignatureParser) which I rather used instead. The sig.equals("V") comparison was already in OpenJDKs version. It seems that neither GNU Classpath nor OpenJDK has a return type signature parser, but looking at the specs one can see that a field type signature parser plus this check for void does the same.

src/classes/gnuclasspath/sun/reflect/annotation/AnnotationType.java

view file

Imported from OpenJDK. Needed by AnnotationParser.java.

This class used OpenJDks sun.misc.SharedSecrets feature to keep an annotation Class to AnnotationType mapping at a shared but secrete place. In particular the methods void sun.misc.SharedSecrets.getJavaLangAccess().setAnnotationType(Class annotationClass, AnnotationType annotationType) and AnnotationType sun.misc.SharedSecrets.getJavaLangAccess().getAnnotationType(Class annotationClass) where used.

GNU Classpath does not have such a feature and therefore I simply added a Map to maintain the mapping:

  50     private static Map<Class, AnnotationType> annotationTypes =
  51         new HashMap<Class, AnnotationType>();

This map is accessed in the methods public static synchronized AnnotationType getInstance(Class annotationClass) and private AnnotationType(final Class<?> annotationClass).

src/classes/gnuclasspath/sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy.java

view file

Imported from OpenJDK. Needed by AnnotationParser.java.

src/classes/gnuclasspath/sun/reflect/annotation/EnumConstantNotPresentExceptionProxy.java

view file

Imported from OpenJDK. Needed by AnnotationParser.java.

src/classes/gnuclasspath/sun/reflect/annotation/ExceptionProxy.java

view file

Imported from OpenJDK. Needed by AnnotationParser.java.

src/classes/gnuclasspath/sun/reflect/annotation/TypeNotPresentExceptionProxy.java

view file

Imported from OpenJDK. Needed by AnnotationParser.java.

src/native/include/Makefile.am

view file

Added sun_reflect_ConstantPool.h to JAVASE_HEADER_FILES.

src/native/llni.h

view file

Added two macros for accessing fields of the classinfo struct which are Java objects:

  88 /* LLNI_classinfo_field_get ***************************************************
  89 
  90    Get a field from classinfo that is a java object.
  91 ******************************************************************************/
  92 #define LLNI_classinfo_field_get(cls, field, variable) \
  93         LLNI_CRITICAL_START; \
  94         (variable) = LLNI_WRAP((cls)->field); \
  95         LLNI_CRITICAL_END
  96 /* LLNI_classinfo_field_set ***************************************************
  97 
  98    Set a field from classinfo that is a java object.
  99 ******************************************************************************/
 100 #define LLNI_classinfo_field_set(cls, field, variable) \
 101         LLNI_CRITICAL_START; \
 102         (cls)->field = LLNI_UNWRAP(variable); \
 103         LLNI_CRITICAL_END

For implementing class unloading the classinfo struct has to be placed onto the Java heap. When this will happen and handles are enabled, accessing members of classinfo will become more difficult (basically like accessing members of other Java objects). To wrap this access I added these macros. It is still not clear how exactly the access will work, but when using these macros one has only to change them instead all code that access Java object fields of classinfo.

src/native/vm/gnuclasspath/Makefile.am

view file

Added sun_reflect_ConstantPool.cpp to SUN_REFLECT_SOURCES. Added java_lang_reflect_Constructor.c to libnativevmcore_la_SOURCES.

src/native/vm/gnuclasspath/java_lang_VMClass.c

view file

If ENABLE_ANNOTATIONS is defined, the method getDeclaredAnnotations will be defined.

src/native/vm/gnuclasspath/java_lang_reflect_Constructor.c

view file

If ENABLE_ANNOTATIONS is defined, the methods declaredAnnotations and getParameterAnnotations will be defined.

Following functions where added:

This function implements java.lang.reflect.Constructor.declaredAnnotations. It uses the function struct java_util_Map* reflect_get_declaredannotatios(java_handle_bytearray_t *annotations, java_lang_Class *declaringClass, classinfo *referrer) to do so.

This function implements java.lang.reflect.Constructor.getParameterAnnotations. It uses the function java_handle_objectarray_t* reflect_get_parameterannotations(java_handle_t *parameterAnnotations, int32_t slot, java_lang_Class *declaringClass, classinfo *referrer) to do so.

src/native/vm/gnuclasspath/java_lang_reflect_Field.c

view file

If ENABLE_ANNOTATIONS is defined, the method declaredAnnotations will be defined.

Following function was added:

This function implements java.lang.reflect.Field.declaredAnnotations. It uses the function struct java_util_Map* reflect_get_declaredannotatios(java_handle_bytearray_t *annotations, java_lang_Class *declaringClass, classinfo *referrer) to do so.

src/native/vm/gnuclasspath/java_lang_reflect_Method.c

view file

If ENABLE_ANNOTATIONS is defined, the methods getDefaultValue, declaredAnnotations and getParameterAnnotations will be defined.

Following functions where added:

This function implements java.lang.reflect.Method.getDefaultValue. It uses the static method sun.reflect.annotation.AnnotationParser.parseAnnotationDefault(Method m, ConstantPoop cpool) to do so. Because this static method is only used here, I thought it makes sense to cache it's methodinfo here, so I don't have to resolve this method every time getDefaultValue is called. For this purpose I made the m_parseAnnotationDefault pointer static.

This function implements java.lang.reflect.Method.declaredAnnotations. It uses the function struct java_util_Map* reflect_get_declaredannotatios(java_handle_bytearray_t *annotations, java_lang_Class *declaringClass, classinfo *referrer) to do so.

This function implements java.lang.reflect.Method.getParameterAnnotations. It uses the function java_handle_objectarray_t* reflect_get_parameterannotations(java_handle_t *parameterAnnotations, int32_t slot, java_lang_Class *declaringClass, classinfo *referrer) to do so.

src/native/vm/gnuclasspath/sun_reflect_ConstantPool.cpp

view file

This file is one of two almost 100% identical implementations of the class sun.reflect.ConstantPool. The other one can be found in src/native/vm/openjdk/jvm.c. The thing is, this class is needed for OpenJDK and GNU Classpath (because I also use OpenJDKs AnnotationParser with GNU Classpath) and even though it does the same thing in both cases, it has to be implemented in different files with different method names. This is a common problem which is discussed on the mailing list. Until we find a proper solution, twisti said I just should implement it twice, no matter the redundancy.

Actually not all methods of this class are used in the annotations support (and therefore in whole OpenJDK, because annotations support still is the only thing which uses this class).

The used methods are:

Not used methods, which I implemented anyway because of their triviality are:

Methods, which I didn't implement because what they do wasn't clear to me:

Almost all the implemented functions basically are implemented by calling voidptr class_getconstant(classinfo *class, u4 pos, u4 ctype).

TODO: In the implementations for getStringAt0 and getUTF8At0 I'm not sure if I used the right string_new-function. I used java_object_t *literalstring_new(utf *u) where maybe java_handle_t *javastring_new(utf *text) would have been the correct function. Maybe this has to be changed.

I'm not sure if the implementation of getMethodAt0 is 100% right. (See comment in the source.) But this method is not used anyway.

TODO: Join the two redundant sun.reflect.ConstantPool implementations. It has to be discussed how that has to be done and where this implementation has to be placed.

src/native/vm/java_lang_Class.c

view file

Here I had to implement the getDeclaredAnnotations method, but only for GNU Classpath. OpenJDK does that in the J2SE implementation. The method is implemented in the function java_handle_objectarray_t *_Jv_java_lang_Class_getDeclaredAnnotations(java_lang_Class* klass). This function has to call the static method Annotation[] sun.reflect.annotation.AnnotationParser.parseAnnotationsIntoArray(ConstantPool cpool, Class<?> cls). The methodinfo for this method is cached like it's done in Java_java_lang_reflect_Method_getDefaultValue.

src/native/vm/java_lang_Class.h

view file

Added declaration: java_handle_objectarray_t *_Jv_java_lang_Class_getDeclaredAnnotations(java_lang_Class* klass);

src/native/vm/nativevm.c

view file

Added to GNU Classpath section:

  90 #if defined(ENABLE_ANNOTATIONS)
  91         _Jv_sun_reflect_ConstantPool_init();
  92 #endif

src/native/vm/nativevm.h

view file

Added to GNU Classpath section:

  69 #if defined(ENABLE_ANNOTATIONS)
  70 void _Jv_sun_reflect_ConstantPool_init();
  71 #endif

src/native/vm/reflect.c

view file

In the reflect_*_new-functions for the reflective types, I added the calls to the *_get_annotations functions.

Following functions were added:

This function creates a ConstantPool instance for the given declaringClass and calls the static method Map<Class, Annotation> sun.reflect.annotation.AnnotationParser.parseAnnotation(ConstantPool cpool, Class cls) in order to parse the annotations. The methodinfo for this static method is cached as a static variable.

This function creates a ConstantPool instance for the given declaringClass and calls the static method Annotation[][] sun.reflect.annotation.AnnotationParser.parseAnnotation(ConstantPool cpool, Class cls, int paramcount) in order to parse the parameter annotations. The methodinfo for this static method is cached as a static variable.

src/native/vm/reflect.h

view file

Following declarations were added:

  65 #if defined(WITH_CLASSPATH_GNU) && defined(ENABLE_ANNOTATIONS)
  66 struct java_util_Map* reflect_get_declaredannotatios(
  67         java_handle_bytearray_t *annotations,
  68         java_lang_Class         *declaringClass,
  69         classinfo               *referrer);
  70 java_handle_objectarray_t* reflect_get_parameterannotations(
  71         java_handle_t   *parameterAnnotations,
  72         int32_t          slot,
  73         java_lang_Class *declaringClass,
  74         classinfo       *referrer);
  75 #endif

src/native/vm/openjdk/jvm.c

view file

Following functions where implemented:

The JVM_Get*Annotations functions just call the corresponding *_get_annotations functions.

The JVM_ConstantPool* functions are implementing the methods of the sun.reflect.ConstantPool class.

See: src/native/vm/gnuclasspath/sun_reflect_ConstantPool.cpp

The function JVM_GetClassConstantPool returns a sun.reflect.ConstantPool instance for the given java.lang.Class object.

src/vmcore/Makefile.am

view file

ANNOTATION_SOURCES is only defined if ENABLE_ANNOTATIONS is.

src/vmcore/annotation.c

view file

This file implements the annotation attribute loading.

The annotation_load_* functions are used to load the corresponding annotations and store them into the corresponding classinfo struct.

The unparsed annotations, parameter annotations and annotation default values for methods and fields are also stored in the classinfo, in order to reduce the overhead to the methodinfo and fieldinfo structs. This data is stored in arrays, where you can access the data at the members slot. These arrays are only as big as they need to be, meaning if there are e.g. 10 methods, but only the first one is annotated, the method_annotations array is only one element in size. If there aren't any method annotations at all, method_annotations is NULL.

During the classloading process I can't know which one's the highest slot with annotations, so I first allocate an array that is just big enough for the first annotated slot I parse. If during further loading higher annotated slots occur, a bigger array is allocated and the old elements are copied to it.

Maybe this could be made more efficient by first allocating an array that is as big as the method-/field-count and after loading all methods/fields, the array will be packed (the elements get copied into a smaller but big enough array).

I used java_bytearray_t to store the unparsed annotations and java_objectarray_t when I needed an array of byte-arrays.

See: src/vmcore/class.h

src/vmcore/annotation.h

view file

Following declarations where added:

  43 /* function prototypes ********************************************************/
  44 bool annotation_load_class_attribute_runtimevisibleannotations(
  45         classbuffer *cb);
  46 bool annotation_load_class_attribute_runtimeinvisibleannotations(
  47         classbuffer *cb);
  48 bool annotation_load_method_attribute_runtimevisibleannotations(
  49         classbuffer *cb, methodinfo *m);
  50 bool annotation_load_method_attribute_runtimeinvisibleannotations(
  51         classbuffer *cb, methodinfo *m);
  52 bool annotation_load_field_attribute_runtimevisibleannotations(
  53         classbuffer *cb, fieldinfo *f);
  54 bool annotation_load_field_attribute_runtimeinvisibleannotations(
  55         classbuffer *cb, fieldinfo *f);
  56 bool annotation_load_method_attribute_annotationdefault(
  57         classbuffer *cb, methodinfo *m);
  58 bool annotation_load_method_attribute_runtimevisibleparameterannotations(
  59         classbuffer *cb, methodinfo *m);
  60 bool annotation_load_method_attribute_runtimeinvisibleparameterannotations(
  61         classbuffer *cb, methodinfo *m);

These functions load the respective attributes from a classbuffer. They return true on success or false if an error has occured.

src/vmcore/class.c

view file

Declared classinfos for preloading for these two classes if ENABLE_ANNOTATIONS is defined.

class_sun_reflect_annotation_AnnotationParser is only defined if WITH_CLASSPATH_GNU is defined.

Added loading of annotations if ENABLE_ANNOTATIONS is defined.

This function returns the unparsed annotations as a Java byte array.

src/vmcore/class.h

view file

Following fields where added to the classinfo struct:

 147 #if defined(ENABLE_ANNOTATIONS)
 148         /* All the annotation attributes are NULL (and not a zero length array)   */
 149         /* if there is nothing.                                                   */
 150         java_object_t *annotations;   /* annotations of this class                */
 151 
 152         java_object_t *method_annotations; /* array of annotations of the methods */
 153         java_object_t *method_parameterannotations; /* array of parameter         */
 154                                       /* annotations of the methods               */
 155         java_object_t *method_annotationdefaults; /* array of annotation default  */
 156                                       /* values of the methods                    */
 157         java_object_t *field_annotations; /* array of annotations of the fields   */
 158 #endif

Following declarations where added:

 246 #if defined(ENABLE_ANNOTATIONS)
 247 extern classinfo *class_sun_reflect_ConstantPool;
 248 #if defined(WITH_CLASSPATH_GNU)
 249 extern classinfo *class_sun_reflect_annotation_AnnotationParser;
 250 #endif
 251 #endif

 385 java_handle_bytearray_t   *class_get_annotations(classinfo *c);

src/vmcore/field.c

view file

Added implementation of newly defined functions in field.h.

src/vmcore/field.h

view file

Returns the unparsed annotations as a Java byte array.

src/vmcore/linker.h

view file

Fixed formatting of a comment.

src/vmcore/loader.c

view file

Added loading of the class_sun_reflect_ConstantPool and the class_sun_reflect_annotation_AnnotationParser classinfo structs.

TODO: This is maybe a tiny bit of a memory waste. Not much but still, if no annotation support is used, this classinfos are needlessly loaded.

src/vmcore/method.c

view file

Added implementation of newly defined functions in method.h.

src/vmcore/method.h

view file

Returns the unparsed annotations as a Java byte array.

Returns the unparsed parameter annotations as a Java byte array.

Returns the unparsed annotation default value as a Java byte array.

Returns the number of parameters of the referred method. The this pointer of non-static methods is not counted.

src/vmcore/utf8.c

view file

Implemented loading of annotation attribute names into the according utf constants:

src/vmcore/utf8.h

view file

Following declarations where added:

 151 #if defined(ENABLE_ANNOTATIONS)
 152 extern utf *utf_RuntimeVisibleAnnotations;
 153 extern utf *utf_RuntimeInvisibleAnnotations;
 154 extern utf *utf_RuntimeVisibleParameterAnnotations;
 155 extern utf *utf_RuntimeInvisibleParameterAnnotations;
 156 extern utf *utf_AnnotationDefault;
 157 #endif

tests/regression/Makefile.am

view file

Added $(srcdir)/MinimalClassReflection.java and $(srcdir)/TestAnnotations.java to SOURCE_FILES.

Added MinimalClassReflection.output and TestAnnotations.output to EXTRA_DIST.

Added MinimalClassReflection and TestAnnotations to OUTPUT_JAVA_TESTS.

tests/regression/MinimalClassReflection.java

view file

Testcases for a few methods of java.lang.Class. This is already obsolete because I ported this test to the Mauve test framework and continued working on that test.

I wrote this testcase because I mentioned some odd/wrong behaviours of this methods while testing the annotations support.

Download Mauve testcase: MinimalClassReflection.zip (ZIP, 2 KB)

tests/regression/MinimalClassReflection.output

view file

Correct output for the minimal class reflection test.

tests/regression/TestAnnotations.java

view file

Testcases for the annotations support. This is already obsolete because I ported this test to the Mauve test framework and continued working on that test.

Download Mauve testcase: TestAnnotations.zip (ZIP, 9 KB)

tests/regression/TestAnnotations.output

view file

Correct output for the annotations test.

TODOs

cacaowiki: AnnotationSupport (last edited 2008-07-09 16:54:31 by panzi)