/* * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.reflect.annotation; import java.lang.annotation.*; import java.util.*; import java.nio.ByteBuffer; import java.nio.BufferUnderflowException; import java.lang.reflect.*; import java.security.AccessController; import java.security.PrivilegedAction; import jdk.internal.reflect.ConstantPool; import sun.reflect.generics.parser.SignatureParser; import sun.reflect.generics.tree.TypeSignature; import sun.reflect.generics.factory.GenericsFactory; import sun.reflect.generics.factory.CoreReflectionFactory; import sun.reflect.generics.visitor.Reifier; import sun.reflect.generics.scope.ClassScope; /** * Parser for Java programming language annotations. Translates * annotation byte streams emitted by compiler into annotation objects. * * @author Josh Bloch * @since 1.5 */ public class AnnotationParser { /** * Parses the annotations described by the specified byte array. * resolving constant references in the specified constant pool. * The array must contain an array of annotations as described * in the RuntimeVisibleAnnotations_attribute: * * u2 num_annotations; * annotation annotations[num_annotations]; * * @throws AnnotationFormatError if an annotation is found to be * malformed. */ public static Map, Annotation> parseAnnotations( byte[] rawAnnotations, ConstantPool constPool, Class container) { if (rawAnnotations == null) return Collections.emptyMap(); try { return parseAnnotations2(rawAnnotations, constPool, container, null); } catch(BufferUnderflowException e) { throw new AnnotationFormatError("Unexpected end of annotations."); } catch(IllegalArgumentException e) { // Type mismatch in constant pool throw new AnnotationFormatError(e); } } /** * Like {@link #parseAnnotations(byte[], sun.reflect.ConstantPool, Class)} * with an additional parameter {@code selectAnnotationClasses} which selects the * annotation types to parse (other than selected are quickly skipped).

* This method is only used to parse select meta annotations in the construction * phase of {@link AnnotationType} instances to prevent infinite recursion. * * @param selectAnnotationClasses an array of annotation types to select when parsing */ @SafeVarargs @SuppressWarnings("varargs") // selectAnnotationClasses is used safely static Map, Annotation> parseSelectAnnotations( byte[] rawAnnotations, ConstantPool constPool, Class container, Class ... selectAnnotationClasses) { if (rawAnnotations == null) return Collections.emptyMap(); try { return parseAnnotations2(rawAnnotations, constPool, container, selectAnnotationClasses); } catch(BufferUnderflowException e) { throw new AnnotationFormatError("Unexpected end of annotations."); } catch(IllegalArgumentException e) { // Type mismatch in constant pool throw new AnnotationFormatError(e); } } private static Map, Annotation> parseAnnotations2( byte[] rawAnnotations, ConstantPool constPool, Class container, Class[] selectAnnotationClasses) { Map, Annotation> result = new LinkedHashMap, Annotation>(); ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); int numAnnotations = buf.getShort() & 0xFFFF; for (int i = 0; i < numAnnotations; i++) { Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses); if (a != null) { Class klass = a.annotationType(); if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME && result.put(klass, a) != null) { throw new AnnotationFormatError( "Duplicate annotation for class: "+klass+": " + a); } } } return result; } /** * Parses the parameter annotations described by the specified byte array. * resolving constant references in the specified constant pool. * The array must contain an array of annotations as described * in the RuntimeVisibleParameterAnnotations_attribute: * * u1 num_parameters; * { * u2 num_annotations; * annotation annotations[num_annotations]; * } parameter_annotations[num_parameters]; * * Unlike parseAnnotations, rawAnnotations must not be null! * A null value must be handled by the caller. This is so because * we cannot determine the number of parameters if rawAnnotations * is null. Also, the caller should check that the number * of parameters indicated by the return value of this method * matches the actual number of method parameters. A mismatch * indicates that an AnnotationFormatError should be thrown. * * @throws AnnotationFormatError if an annotation is found to be * malformed. */ public static Annotation[][] parseParameterAnnotations( byte[] rawAnnotations, ConstantPool constPool, Class container) { try { return parseParameterAnnotations2(rawAnnotations, constPool, container); } catch(BufferUnderflowException e) { throw new AnnotationFormatError( "Unexpected end of parameter annotations."); } catch(IllegalArgumentException e) { // Type mismatch in constant pool throw new AnnotationFormatError(e); } } private static Annotation[][] parseParameterAnnotations2( byte[] rawAnnotations, ConstantPool constPool, Class container) { ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); int numParameters = buf.get() & 0xFF; Annotation[][] result = new Annotation[numParameters][]; for (int i = 0; i < numParameters; i++) { int numAnnotations = buf.getShort() & 0xFFFF; List annotations = new ArrayList(numAnnotations); for (int j = 0; j < numAnnotations; j++) { Annotation a = parseAnnotation(buf, constPool, container, false); if (a != null) { AnnotationType type = AnnotationType.getInstance( a.annotationType()); if (type.retention() == RetentionPolicy.RUNTIME) annotations.add(a); } } result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY); } return result; } private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0]; /** * Parses the annotation at the current position in the specified * byte buffer, resolving constant references in the specified constant * pool. The cursor of the byte buffer must point to an "annotation * structure" as described in the RuntimeVisibleAnnotations_attribute: * * annotation { * u2 type_index; * u2 num_member_value_pairs; * { u2 member_name_index; * member_value value; * } member_value_pairs[num_member_value_pairs]; * } * } * * Returns the annotation, or null if the annotation's type cannot * be found by the VM, or is not a valid annotation type. * * @param exceptionOnMissingAnnotationClass if true, throw * TypeNotPresentException if a referenced annotation type is not * available at runtime */ static Annotation parseAnnotation(ByteBuffer buf, ConstantPool constPool, Class container, boolean exceptionOnMissingAnnotationClass) { return parseAnnotation2(buf, constPool, container, exceptionOnMissingAnnotationClass, null); } @SuppressWarnings("unchecked") private static Annotation parseAnnotation2(ByteBuffer buf, ConstantPool constPool, Class container, boolean exceptionOnMissingAnnotationClass, Class[] selectAnnotationClasses) { int typeIndex = buf.getShort() & 0xFFFF; Class annotationClass = null; String sig = "[unknown]"; try { try { sig = constPool.getUTF8At(typeIndex); annotationClass = (Class)parseSig(sig, container); } catch (IllegalArgumentException ex) { // support obsolete early jsr175 format class files annotationClass = (Class)constPool.getClassAt(typeIndex); } } catch (NoClassDefFoundError e) { if (exceptionOnMissingAnnotationClass) // note: at this point sig is "[unknown]" or VM-style // name instead of a binary name throw new TypeNotPresentException(sig, e); skipAnnotation(buf, false); return null; } catch (TypeNotPresentException e) { if (exceptionOnMissingAnnotationClass) throw e; skipAnnotation(buf, false); return null; } if (selectAnnotationClasses != null && !contains(selectAnnotationClasses, annotationClass)) { skipAnnotation(buf, false); return null; } AnnotationType type = AnnotationType.getInstance(annotationClass); Map> memberTypes = type.memberTypes(); Map memberValues = new LinkedHashMap(type.memberDefaults()); int numMembers = buf.getShort() & 0xFFFF; for (int i = 0; i < numMembers; i++) { int memberNameIndex = buf.getShort() & 0xFFFF; String memberName = constPool.getUTF8At(memberNameIndex); Class memberType = memberTypes.get(memberName); if (memberType == null) { // Member is no longer present in annotation type; ignore it skipMemberValue(buf); } else { Object value = parseMemberValue(memberType, buf, constPool, container); if (value instanceof AnnotationTypeMismatchExceptionProxy) ((AnnotationTypeMismatchExceptionProxy) value). setMember(type.members().get(memberName)); memberValues.put(memberName, value); } } return annotationForMap(annotationClass, memberValues); } /** * Returns an annotation of the given type backed by the given * member {@literal ->} value map. */ public static Annotation annotationForMap(final Class type, final Map memberValues) { return AccessController.doPrivileged(new PrivilegedAction() { public Annotation run() { return (Annotation) Proxy.newProxyInstance( type.getClassLoader(), new Class[] { type }, new AnnotationInvocationHandler(type, memberValues)); }}); } /** * Parses the annotation member value at the current position in the * specified byte buffer, resolving constant references in the specified * constant pool. The cursor of the byte buffer must point to a * "member_value structure" as described in the * RuntimeVisibleAnnotations_attribute: * * member_value { * u1 tag; * union { * u2 const_value_index; * { * u2 type_name_index; * u2 const_name_index; * } enum_const_value; * u2 class_info_index; * annotation annotation_value; * { * u2 num_values; * member_value values[num_values]; * } array_value; * } value; * } * * The member must be of the indicated type. If it is not, this * method returns an AnnotationTypeMismatchExceptionProxy. */ @SuppressWarnings("unchecked") public static Object parseMemberValue(Class memberType, ByteBuffer buf, ConstantPool constPool, Class container) { Object result = null; int tag = buf.get(); switch(tag) { case 'e': return parseEnumValue((Class>)memberType, buf, constPool, container); case 'c': result = parseClassValue(buf, constPool, container); break; case '@': result = parseAnnotation(buf, constPool, container, true); break; case '[': return parseArray(memberType, buf, constPool, container); default: result = parseConst(tag, buf, constPool); } if (!(result instanceof ExceptionProxy) && !memberType.isInstance(result)) result = new AnnotationTypeMismatchExceptionProxy( result.getClass() + "[" + result + "]"); return result; } /** * Parses the primitive or String annotation member value indicated by * the specified tag byte at the current position in the specified byte * buffer, resolving constant reference in the specified constant pool. * The cursor of the byte buffer must point to an annotation member value * of the type indicated by the specified tag, as described in the * RuntimeVisibleAnnotations_attribute: * * u2 const_value_index; */ private static Object parseConst(int tag, ByteBuffer buf, ConstantPool constPool) { int constIndex = buf.getShort() & 0xFFFF; switch(tag) { case 'B': return Byte.valueOf((byte) constPool.getIntAt(constIndex)); case 'C': return Character.valueOf((char) constPool.getIntAt(constIndex)); case 'D': return Double.valueOf(constPool.getDoubleAt(constIndex)); case 'F': return Float.valueOf(constPool.getFloatAt(constIndex)); case 'I': return Integer.valueOf(constPool.getIntAt(constIndex)); case 'J': return Long.valueOf(constPool.getLongAt(constIndex)); case 'S': return Short.valueOf((short) constPool.getIntAt(constIndex)); case 'Z': return Boolean.valueOf(constPool.getIntAt(constIndex) != 0); case 's': return constPool.getUTF8At(constIndex); default: throw new AnnotationFormatError( "Invalid member-value tag in annotation: " + tag); } } /** * Parses the Class member value at the current position in the * specified byte buffer, resolving constant references in the specified * constant pool. The cursor of the byte buffer must point to a "class * info index" as described in the RuntimeVisibleAnnotations_attribute: * * u2 class_info_index; */ private static Object parseClassValue(ByteBuffer buf, ConstantPool constPool, Class container) { int classIndex = buf.getShort() & 0xFFFF; try { try { String sig = constPool.getUTF8At(classIndex); return parseSig(sig, container); } catch (IllegalArgumentException ex) { // support obsolete early jsr175 format class files return constPool.getClassAt(classIndex); } } catch (NoClassDefFoundError e) { return new TypeNotPresentExceptionProxy("[unknown]", e); } catch (TypeNotPresentException e) { return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause()); } } private static Class parseSig(String sig, Class container) { if (sig.equals("V")) return void.class; SignatureParser parser = SignatureParser.make(); TypeSignature typeSig = parser.parseTypeSig(sig); GenericsFactory factory = CoreReflectionFactory.make(container, ClassScope.make(container)); Reifier reify = Reifier.make(factory); typeSig.accept(reify); Type result = reify.getResult(); return toClass(result); } static Class toClass(Type o) { if (o instanceof GenericArrayType) return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()), 0) .getClass(); return (Class)o; } /** * Parses the enum constant member value at the current position in the * specified byte buffer, resolving constant references in the specified * constant pool. The cursor of the byte buffer must point to a * "enum_const_value structure" as described in the * RuntimeVisibleAnnotations_attribute: * * { * u2 type_name_index; * u2 const_name_index; * } enum_const_value; */ @SuppressWarnings({"rawtypes", "unchecked"}) private static Object parseEnumValue(Class enumType, ByteBuffer buf, ConstantPool constPool, Class container) { int typeNameIndex = buf.getShort() & 0xFFFF; String typeName = constPool.getUTF8At(typeNameIndex); int constNameIndex = buf.getShort() & 0xFFFF; String constName = constPool.getUTF8At(constNameIndex); if (!typeName.endsWith(";")) { // support now-obsolete early jsr175-format class files. if (!enumType.getName().equals(typeName)) return new AnnotationTypeMismatchExceptionProxy( typeName + "." + constName); } else if (enumType != parseSig(typeName, container)) { return new AnnotationTypeMismatchExceptionProxy( typeName + "." + constName); } try { return Enum.valueOf(enumType, constName); } catch(IllegalArgumentException e) { return new EnumConstantNotPresentExceptionProxy( (Class>)enumType, constName); } } /** * Parses the array value at the current position in the specified byte * buffer, resolving constant references in the specified constant pool. * The cursor of the byte buffer must point to an array value struct * as specified in the RuntimeVisibleAnnotations_attribute: * * { * u2 num_values; * member_value values[num_values]; * } array_value; * * If the array values do not match arrayType, an * AnnotationTypeMismatchExceptionProxy will be returned. */ @SuppressWarnings("unchecked") private static Object parseArray(Class arrayType, ByteBuffer buf, ConstantPool constPool, Class container) { int length = buf.getShort() & 0xFFFF; // Number of array components Class componentType = arrayType.getComponentType(); if (componentType == byte.class) { return parseByteArray(length, buf, constPool); } else if (componentType == char.class) { return parseCharArray(length, buf, constPool); } else if (componentType == double.class) { return parseDoubleArray(length, buf, constPool); } else if (componentType == float.class) { return parseFloatArray(length, buf, constPool); } else if (componentType == int.class) { return parseIntArray(length, buf, constPool); } else if (componentType == long.class) { return parseLongArray(length, buf, constPool); } else if (componentType == short.class) { return parseShortArray(length, buf, constPool); } else if (componentType == boolean.class) { return parseBooleanArray(length, buf, constPool); } else if (componentType == String.class) { return parseStringArray(length, buf, constPool); } else if (componentType == Class.class) { return parseClassArray(length, buf, constPool, container); } else if (componentType.isEnum()) { return parseEnumArray(length, (Class>)componentType, buf, constPool, container); } else { assert componentType.isAnnotation(); return parseAnnotationArray(length, (Class )componentType, buf, constPool, container); } } private static Object parseByteArray(int length, ByteBuffer buf, ConstantPool constPool) { byte[] result = new byte[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'B') { int index = buf.getShort() & 0xFFFF; result[i] = (byte) constPool.getIntAt(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseCharArray(int length, ByteBuffer buf, ConstantPool constPool) { char[] result = new char[length]; boolean typeMismatch = false; byte tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'C') { int index = buf.getShort() & 0xFFFF; result[i] = (char) constPool.getIntAt(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseDoubleArray(int length, ByteBuffer buf, ConstantPool constPool) { double[] result = new double[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'D') { int index = buf.getShort() & 0xFFFF; result[i] = constPool.getDoubleAt(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseFloatArray(int length, ByteBuffer buf, ConstantPool constPool) { float[] result = new float[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'F') { int index = buf.getShort() & 0xFFFF; result[i] = constPool.getFloatAt(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseIntArray(int length, ByteBuffer buf, ConstantPool constPool) { int[] result = new int[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'I') { int index = buf.getShort() & 0xFFFF; result[i] = constPool.getIntAt(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseLongArray(int length, ByteBuffer buf, ConstantPool constPool) { long[] result = new long[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'J') { int index = buf.getShort() & 0xFFFF; result[i] = constPool.getLongAt(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseShortArray(int length, ByteBuffer buf, ConstantPool constPool) { short[] result = new short[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'S') { int index = buf.getShort() & 0xFFFF; result[i] = (short) constPool.getIntAt(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseBooleanArray(int length, ByteBuffer buf, ConstantPool constPool) { boolean[] result = new boolean[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'Z') { int index = buf.getShort() & 0xFFFF; result[i] = (constPool.getIntAt(index) != 0); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseStringArray(int length, ByteBuffer buf, ConstantPool constPool) { String[] result = new String[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 's') { int index = buf.getShort() & 0xFFFF; result[i] = constPool.getUTF8At(index); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseClassArray(int length, ByteBuffer buf, ConstantPool constPool, Class container) { Object[] result = new Class[length]; boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'c') { result[i] = parseClassValue(buf, constPool, container); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseEnumArray(int length, Class> enumType, ByteBuffer buf, ConstantPool constPool, Class container) { Object[] result = (Object[]) Array.newInstance(enumType, length); boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == 'e') { result[i] = parseEnumValue(enumType, buf, constPool, container); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } private static Object parseAnnotationArray(int length, Class annotationType, ByteBuffer buf, ConstantPool constPool, Class container) { Object[] result = (Object[]) Array.newInstance(annotationType, length); boolean typeMismatch = false; int tag = 0; for (int i = 0; i < length; i++) { tag = buf.get(); if (tag == '@') { result[i] = parseAnnotation(buf, constPool, container, true); } else { skipMemberValue(tag, buf); typeMismatch = true; } } return typeMismatch ? exceptionProxy(tag) : result; } /** * Returns an appropriate exception proxy for a mismatching array * annotation where the erroneous array has the specified tag. */ private static ExceptionProxy exceptionProxy(int tag) { return new AnnotationTypeMismatchExceptionProxy( "Array with component tag: " + tag); } /** * Skips the annotation at the current position in the specified * byte buffer. The cursor of the byte buffer must point to * an "annotation structure" OR two bytes into an annotation * structure (i.e., after the type index). * * @parameter complete true if the byte buffer points to the beginning * of an annotation structure (rather than two bytes in). */ private static void skipAnnotation(ByteBuffer buf, boolean complete) { if (complete) buf.getShort(); // Skip type index int numMembers = buf.getShort() & 0xFFFF; for (int i = 0; i < numMembers; i++) { buf.getShort(); // Skip memberNameIndex skipMemberValue(buf); } } /** * Skips the annotation member value at the current position in the * specified byte buffer. The cursor of the byte buffer must point to a * "member_value structure." */ private static void skipMemberValue(ByteBuffer buf) { int tag = buf.get(); skipMemberValue(tag, buf); } /** * Skips the annotation member value at the current position in the * specified byte buffer. The cursor of the byte buffer must point * immediately after the tag in a "member_value structure." */ private static void skipMemberValue(int tag, ByteBuffer buf) { switch(tag) { case 'e': // Enum value buf.getInt(); // (Two shorts, actually.) break; case '@': skipAnnotation(buf, true); break; case '[': skipArray(buf); break; default: // Class, primitive, or String buf.getShort(); } } /** * Skips the array value at the current position in the specified byte * buffer. The cursor of the byte buffer must point to an array value * struct. */ private static void skipArray(ByteBuffer buf) { int length = buf.getShort() & 0xFFFF; for (int i = 0; i < length; i++) skipMemberValue(buf); } /** * Searches for given {@code element} in given {@code array} by identity. * Returns {@code true} if found {@code false} if not. */ private static boolean contains(Object[] array, Object element) { for (Object e : array) if (e == element) return true; return false; } /* * This method converts the annotation map returned by the parseAnnotations() * method to an array. It is called by Field.getDeclaredAnnotations(), * Method.getDeclaredAnnotations(), and Constructor.getDeclaredAnnotations(). * This avoids the reflection classes to load the Annotation class until * it is needed. */ private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; public static Annotation[] toArray(Map, Annotation> annotations) { return annotations.values().toArray(EMPTY_ANNOTATION_ARRAY); } static Annotation[] getEmptyAnnotationArray() { return EMPTY_ANNOTATION_ARRAY; } }