< prev index next >

src/java.base/share/classes/sun/reflect/annotation/AnnotationType.java

Print this page

        

@@ -30,10 +30,11 @@
 import java.util.*;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.JavaLangAccess;
+import jdk.internal.reflect.ReflectionFactory;
 
 /**
  * Represents an annotation type at run time.  Used to type-check annotations
  * and apply member defaults.
  *

@@ -71,11 +72,11 @@
     private final boolean inherited;
 
     /**
      * Returns an AnnotationType instance for the specified annotation type.
      *
-     * @throws IllegalArgumentException if the specified class object
+     * @throws AnnotationFormatError if the specified class object
      *         does not represent a valid annotation type
      */
     public static AnnotationType getInstance(
         Class<? extends Annotation> annotationClass)
     {

@@ -96,41 +97,40 @@
 
     /**
      * Sole constructor.
      *
      * @param annotationClass the class object for the annotation type
-     * @throws IllegalArgumentException if the specified class object for
+     * @throws AnnotationFormatError if the specified class object
      *         does not represent a valid annotation type
      */
     private AnnotationType(final Class<? extends Annotation> annotationClass) {
-        if (!annotationClass.isAnnotation())
-            throw new IllegalArgumentException("Not an annotation type");
+        Class<?>[] superInterfaces = annotationClass.getInterfaces();
+        if (!annotationClass.isAnnotation() ||
+            superInterfaces.length != 1 ||
+            superInterfaces[0] != Annotation.class) {
+            throw new AnnotationFormatError("Not an annotation type.");
+        }
 
         Method[] methods =
             AccessController.doPrivileged(new PrivilegedAction<>() {
                 public Method[] run() {
-                    // Initialize memberTypes and defaultValues
                     return annotationClass.getDeclaredMethods();
                 }
             });
 
         memberTypes = new HashMap<>(methods.length+1, 1.0f);
         memberDefaults = new HashMap<>(0);
         members = new HashMap<>(methods.length+1, 1.0f);
 
         for (Method method : methods) {
-            if (Modifier.isPublic(method.getModifiers()) &&
-                Modifier.isAbstract(method.getModifiers()) &&
-                !method.isSynthetic()) {
-                if (method.getParameterTypes().length != 0) {
-                    throw new IllegalArgumentException(method + " has params");
-                }
+            // skip synthetic methods as would be created for lambdas for example
+            if (!method.isSynthetic()) {
+                validateAnnotationMethod(method);
                 String name = method.getName();
                 Class<?> type = method.getReturnType();
                 memberTypes.put(name, invocationHandlerReturnType(type));
                 members.put(name, method);
-
                 Object defaultValue = method.getDefaultValue();
                 if (defaultValue != null) {
                     memberDefaults.put(name, defaultValue);
                 }
             }

@@ -201,10 +201,38 @@
      */
     public Map<String, Method> members() {
         return members;
     }
 
+    private volatile Map<String, Method> accessibleMembers;
+
+    /**
+     * Returns members of this annotation type
+     * (member name {@literal ->} associated Method object mapping)
+     * which are made {@link Method#setAccessible(boolean) accessible}.
+     */
+    Map<String, Method> accessibleMembers() {
+        Map<String, Method> accMembers = accessibleMembers;
+        if (accMembers == null) {
+            accMembers = AccessController.doPrivileged(new PrivilegedAction<>() {
+                @Override
+                public Map<String, Method> run() {
+                    Map<String, Method> ams = new HashMap<>(members.size()+1, 1.0f);
+                    ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
+                    for (Method m : members.values()) {
+                        Method am = rf.leafCopyMethod(m);
+                        am.setAccessible(true);
+                        ams.put(am.getName(), am);
+                    }
+                    return ams;
+                }
+            });
+            accessibleMembers = accMembers;
+        }
+        return accMembers;
+    }
+
     /**
      * Returns the default values for this annotation type
      * (Member name {@literal ->} default value mapping).
      */
     public Map<String, Object> memberDefaults() {

@@ -233,6 +261,97 @@
                "   Member types: " + memberTypes + "\n" +
                "   Member defaults: " + memberDefaults + "\n" +
                "   Retention policy: " + retention + "\n" +
                "   Inherited: " + inherited;
     }
+
+    /**
+     * Validates that specified method is structurally appropriate for an
+     * annotation type. As of Java SE 8, annotation types cannot
+     * contain static methods and the declared methods of an
+     * annotation type must take zero arguments and there are
+     * restrictions on the return type.
+     * @throws AnnotationFormatError if specified method is
+     *         inappropriate method for an annotation type.
+     */
+    private static void validateAnnotationMethod(Method method) {
+        /*
+         * Specification citations below are from JLS
+         * 9.6.1. Annotation Type Elements
+         */
+        boolean valid = true;
+        block: {
+            /*
+             * "By virtue of the AnnotationTypeElementDeclaration
+             * production, a method declaration in an annotation type
+             * declaration cannot have formal parameters, type
+             * parameters, or a throws clause.
+             *
+             * "By virtue of the AnnotationTypeElementModifier
+             * production, a method declaration in an annotation type
+             * declaration cannot be default or static."
+             */
+            if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) ||
+                method.isDefault() ||
+                method.getParameterCount() != 0 ||
+                method.getExceptionTypes().length != 0) {
+                valid = false;
+                break block;
+            }
+
+            /*
+             * "It is a compile-time error if the return type of a
+             * method declared in an annotation type is not one of the
+             * following: a primitive type, String, Class, any
+             * parameterized invocation of Class, an enum type
+             * (section 8.9), an annotation type, or an array type
+             * (chapter 10) whose element type is one of the preceding
+             * types."
+             */
+            Class<?> returnType = method.getReturnType();
+            if (returnType.isArray()) {
+                returnType = returnType.getComponentType();
+                if (returnType.isArray()) { // Only single dimensional arrays
+                    valid = false;
+                    break block;
+                }
+            }
+
+            if (!((returnType.isPrimitive() && returnType != void.class) ||
+                  returnType == java.lang.String.class ||
+                  returnType == java.lang.Class.class ||
+                  returnType.isEnum() ||
+                  returnType.isAnnotation())) {
+                valid = false;
+                break block;
+            }
+
+            /*
+             * "It is a compile-time error if any method declared in an
+             * annotation type has a signature that is
+             * override-equivalent to that of any public or protected
+             * method declared in class Object or in the interface
+             * java.lang.annotation.Annotation."
+             *
+             * The methods in Object or Annotation meeting the other
+             * criteria (no arguments, contrained return type, etc.)
+             * above are:
+             *
+             * String toString()
+             * int hashCode()
+             * Class<? extends Annotation> annotationType()
+             */
+            String methodName = method.getName();
+            if ((methodName.equals("toString") && returnType == java.lang.String.class) ||
+                (methodName.equals("hashCode") && returnType == int.class) ||
+                (methodName.equals("annotationType") && returnType == java.lang.Class.class)) {
+                valid = false;
+                break block;
+            }
+        }
+
+        if (!valid) {
+            throw new AnnotationFormatError(
+                "Malformed method on an annotation type: " + method);
+        }
+    }
 }
< prev index next >