< prev index next >

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

Print this page

        

*** 30,39 **** --- 30,40 ---- 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,81 **** private final boolean inherited; /** * Returns an AnnotationType instance for the specified annotation type. * ! * @throws IllegalArgumentException if the specified class object * does not represent a valid annotation type */ public static AnnotationType getInstance( Class<? extends Annotation> annotationClass) { --- 72,82 ---- private final boolean inherited; /** * Returns an AnnotationType instance for the specified annotation type. * ! * @throws AnnotationFormatError if the specified class object * does not represent a valid annotation type */ public static AnnotationType getInstance( Class<? extends Annotation> annotationClass) {
*** 96,136 **** /** * Sole constructor. * * @param annotationClass the class object for the annotation type ! * @throws IllegalArgumentException if the specified class object for * does not represent a valid annotation type */ private AnnotationType(final Class<? extends Annotation> annotationClass) { ! if (!annotationClass.isAnnotation()) ! throw new IllegalArgumentException("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"); ! } 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); } } --- 97,136 ---- /** * Sole constructor. * * @param annotationClass the class object for the annotation type ! * @throws AnnotationFormatError if the specified class object * does not represent a valid annotation type */ private AnnotationType(final Class<? extends Annotation> annotationClass) { ! 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() { 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) { ! // 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,210 **** --- 201,238 ---- */ 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,238 **** --- 261,357 ---- " 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 >