/*
 * Copyright (c) 2013, 2018, 2020, 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.lang.reflect.*;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.reflect.ConstantPool;
import static sun.reflect.annotation.TypeAnnotation.*;

TypeAnnotationParser implements the logic needed to parse TypeAnnotations from an array of bytes.
/** * TypeAnnotationParser implements the logic needed to parse * TypeAnnotations from an array of bytes. */
public final class TypeAnnotationParser { private static final TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY = new TypeAnnotation[0];
Build an AnnotatedType from the parameters supplied. This method and buildAnnotatedTypes are probably the entry points you are looking for.
Params:
  • rawAnnotations – the byte[] encoding of all type annotations on this declaration
  • cp – the ConstantPool needed to parse the embedded Annotation
  • decl – the declaration this type annotation is on
  • container – the Class this type annotation is on (may be the same as decl)
  • type – the type the AnnotatedType corresponds to
  • filter – the type annotation targets included in this AnnotatedType
/** * Build an AnnotatedType from the parameters supplied. * * This method and {@code buildAnnotatedTypes} are probably * the entry points you are looking for. * * @param rawAnnotations the byte[] encoding of all type annotations on this declaration * @param cp the ConstantPool needed to parse the embedded Annotation * @param decl the declaration this type annotation is on * @param container the Class this type annotation is on (may be the same as decl) * @param type the type the AnnotatedType corresponds to * @param filter the type annotation targets included in this AnnotatedType */
public static AnnotatedType buildAnnotatedType(byte[] rawAnnotations, ConstantPool cp, AnnotatedElement decl, Class<?> container, Type type, TypeAnnotationTarget filter) { TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations, cp, decl, container); List<TypeAnnotation> l = new ArrayList<>(tas.length); for (TypeAnnotation t : tas) { TypeAnnotationTargetInfo ti = t.getTargetInfo(); if (ti.getTarget() == filter) l.add(t); } TypeAnnotation[] typeAnnotations = l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY); return AnnotatedTypeFactory.buildAnnotatedType(type, AnnotatedTypeFactory.nestingForType(type, LocationInfo.BASE_LOCATION), typeAnnotations, typeAnnotations, decl); }
Build an array of AnnotatedTypes from the parameters supplied. This method and buildAnnotatedType are probably the entry points you are looking for.
Params:
  • rawAnnotations – the byte[] encoding of all type annotations on this declaration
  • cp – the ConstantPool needed to parse the embedded Annotation
  • decl – the declaration this type annotation is on
  • container – the Class this type annotation is on (may be the same as decl)
  • types – the Types the AnnotatedTypes corresponds to
  • filter – the type annotation targets that included in this AnnotatedType
/** * Build an array of AnnotatedTypes from the parameters supplied. * * This method and {@code buildAnnotatedType} are probably * the entry points you are looking for. * * @param rawAnnotations the byte[] encoding of all type annotations on this declaration * @param cp the ConstantPool needed to parse the embedded Annotation * @param decl the declaration this type annotation is on * @param container the Class this type annotation is on (may be the same as decl) * @param types the Types the AnnotatedTypes corresponds to * @param filter the type annotation targets that included in this AnnotatedType */
public static AnnotatedType[] buildAnnotatedTypes(byte[] rawAnnotations, ConstantPool cp, AnnotatedElement decl, Class<?> container, Type[] types, TypeAnnotationTarget filter) { int size = types.length; AnnotatedType[] result = new AnnotatedType[size]; Arrays.fill(result, AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE); @SuppressWarnings("rawtypes") ArrayList[] l = new ArrayList[size]; // array of ArrayList<TypeAnnotation> TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations, cp, decl, container); for (TypeAnnotation t : tas) { TypeAnnotationTargetInfo ti = t.getTargetInfo(); if (ti.getTarget() == filter) { int pos = ti.getCount(); if (l[pos] == null) { ArrayList<TypeAnnotation> tmp = new ArrayList<>(tas.length); l[pos] = tmp; } @SuppressWarnings("unchecked") ArrayList<TypeAnnotation> tmp = l[pos]; tmp.add(t); } } // If a constructor has a mandated outer this, that parameter // has no annotations and the annotations to parameter mapping // should be offset by 1. boolean offset = false; if (decl instanceof Constructor) { Constructor<?> ctor = (Constructor<?>) decl; Class<?> declaringClass = ctor.getDeclaringClass(); if (!declaringClass.isEnum() && (declaringClass.isMemberClass() && (declaringClass.getModifiers() & Modifier.STATIC) == 0) ) { offset = true; } } for (int i = 0; i < size; i++) { ArrayList<TypeAnnotation> list; if (offset) { @SuppressWarnings("unchecked") ArrayList<TypeAnnotation> tmp = (i == 0) ? null : l[i - 1]; list = tmp; } else { @SuppressWarnings("unchecked") ArrayList<TypeAnnotation> tmp = l[i]; list = tmp; } TypeAnnotation[] typeAnnotations; if (list != null) { typeAnnotations = list.toArray(new TypeAnnotation[list.size()]); } else { typeAnnotations = EMPTY_TYPE_ANNOTATION_ARRAY; } result[i] = AnnotatedTypeFactory.buildAnnotatedType(types[i], AnnotatedTypeFactory.nestingForType(types[i], LocationInfo.BASE_LOCATION), typeAnnotations, typeAnnotations, decl); } return result; } // Class helpers
Build an AnnotatedType for the class decl's supertype.
Params:
  • rawAnnotations – the byte[] encoding of all type annotations on this declaration
  • cp – the ConstantPool needed to parse the embedded Annotation
  • decl – the Class which annotated supertype is being built
/** * Build an AnnotatedType for the class decl's supertype. * * @param rawAnnotations the byte[] encoding of all type annotations on this declaration * @param cp the ConstantPool needed to parse the embedded Annotation * @param decl the Class which annotated supertype is being built */
public static AnnotatedType buildAnnotatedSuperclass(byte[] rawAnnotations, ConstantPool cp, Class<?> decl) { Type supertype = decl.getGenericSuperclass(); if (supertype == null) return AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE; return buildAnnotatedType(rawAnnotations, cp, decl, decl, supertype, TypeAnnotationTarget.CLASS_EXTENDS); }
Build an array of AnnotatedTypes for the class decl's implemented interfaces.
Params:
  • rawAnnotations – the byte[] encoding of all type annotations on this declaration
  • cp – the ConstantPool needed to parse the embedded Annotation
  • decl – the Class whose annotated implemented interfaces is being built
/** * Build an array of AnnotatedTypes for the class decl's implemented * interfaces. * * @param rawAnnotations the byte[] encoding of all type annotations on this declaration * @param cp the ConstantPool needed to parse the embedded Annotation * @param decl the Class whose annotated implemented interfaces is being built */
public static AnnotatedType[] buildAnnotatedInterfaces(byte[] rawAnnotations, ConstantPool cp, Class<?> decl) { if (decl == Object.class || decl.isArray() || decl.isPrimitive() || decl == Void.TYPE) return AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE_ARRAY; return buildAnnotatedTypes(rawAnnotations, cp, decl, decl, decl.getGenericInterfaces(), TypeAnnotationTarget.CLASS_IMPLEMENTS); } // TypeVariable helpers
Parse regular annotations on a TypeVariable declared on genericDecl. Regular Annotations on TypeVariables are stored in the type annotation byte[] in the class file.
Params:
  • genericDecl – the declaration declaring the type variable
  • typeVarIndex – the 0-based index of this type variable in the declaration
/** * Parse regular annotations on a TypeVariable declared on genericDecl. * * Regular Annotations on TypeVariables are stored in the type * annotation byte[] in the class file. * * @param genericDecl the declaration declaring the type variable * @param typeVarIndex the 0-based index of this type variable in the declaration */
public static <D extends GenericDeclaration> Annotation[] parseTypeVariableAnnotations(D genericDecl, int typeVarIndex) { AnnotatedElement decl; TypeAnnotationTarget predicate; if (genericDecl instanceof Class) { decl = (Class<?>)genericDecl; predicate = TypeAnnotationTarget.CLASS_TYPE_PARAMETER; } else if (genericDecl instanceof Executable) { decl = (Executable)genericDecl; predicate = TypeAnnotationTarget.METHOD_TYPE_PARAMETER; } else { throw new AssertionError("Unknown GenericDeclaration " + genericDecl + "\nthis should not happen."); } List<TypeAnnotation> typeVarAnnos = TypeAnnotation.filter(parseAllTypeAnnotations(decl), predicate); List<Annotation> res = new ArrayList<>(typeVarAnnos.size()); for (TypeAnnotation t : typeVarAnnos) if (t.getTargetInfo().getCount() == typeVarIndex) res.add(t.getAnnotation()); return res.toArray(new Annotation[0]); }
Build an array of AnnotatedTypes for the declaration decl's bounds.
Params:
  • bounds – the bounds corresponding to the annotated bounds
  • decl – the declaration whose annotated bounds is being built
  • typeVarIndex – the index of this type variable on the decl
/** * Build an array of AnnotatedTypes for the declaration decl's bounds. * * @param bounds the bounds corresponding to the annotated bounds * @param decl the declaration whose annotated bounds is being built * @param typeVarIndex the index of this type variable on the decl */
public static <D extends GenericDeclaration> AnnotatedType[] parseAnnotatedBounds(Type[] bounds, D decl, int typeVarIndex) { return parseAnnotatedBounds(bounds, decl, typeVarIndex, LocationInfo.BASE_LOCATION); } //helper for above private static <D extends GenericDeclaration> AnnotatedType[] parseAnnotatedBounds(Type[] bounds, D decl, int typeVarIndex, LocationInfo loc) { List<TypeAnnotation> candidates = fetchBounds(decl); if (bounds != null) { int startIndex = 0; AnnotatedType[] res = new AnnotatedType[bounds.length]; // According to JVMS 4.3.4, the first bound of a parameterized type is // taken to be Object, if no explicit class bound is specified. As a // consequence, the first interface's bound is always 1. To account for // a potential mismatch between the indices of the bounds array that only // contains explicit bounds and the actual bound's index, the startIndex // is set to 1 if no explicit class type bound was set. // // This is achieved by examining the first element of the bound to be a // class or an interface, if such a bound exists. Since a bound can itself // be a parameterized type, the bound's raw type must be investigated, // if applicable. if (bounds.length > 0) { Type b0 = bounds[0]; if (b0 instanceof Class<?>) { Class<?> c = (Class<?>) b0; if (c.isInterface()) { startIndex = 1; } } else if (b0 instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType) b0; Class<?> c = (Class<?>) p.getRawType(); if (c.isInterface()) { startIndex = 1; } } } for (int i = 0; i < bounds.length; i++) { List<TypeAnnotation> l = new ArrayList<>(candidates.size()); for (TypeAnnotation t : candidates) { TypeAnnotationTargetInfo tInfo = t.getTargetInfo(); if (tInfo.getSecondaryIndex() == i + startIndex && tInfo.getCount() == typeVarIndex) { l.add(t); } } res[i] = AnnotatedTypeFactory.buildAnnotatedType(bounds[i], AnnotatedTypeFactory.nestingForType(bounds[i], loc), l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY), candidates.toArray(EMPTY_TYPE_ANNOTATION_ARRAY), (AnnotatedElement)decl); } return res; } return new AnnotatedType[0]; } private static <D extends GenericDeclaration> List<TypeAnnotation> fetchBounds(D decl) { AnnotatedElement boundsDecl; TypeAnnotationTarget target; if (decl instanceof Class) { target = TypeAnnotationTarget.CLASS_TYPE_PARAMETER_BOUND; boundsDecl = (Class)decl; } else { target = TypeAnnotationTarget.METHOD_TYPE_PARAMETER_BOUND; boundsDecl = (Executable)decl; } return TypeAnnotation.filter(TypeAnnotationParser.parseAllTypeAnnotations(boundsDecl), target); } /* * Parse all type annotations on the declaration supplied. This is needed * when you go from for example an annotated return type on a method that * is a type variable declared on the class. In this case you need to * 'jump' to the decl of the class and parse all type annotations there to * find the ones that are applicable to the type variable. */ static TypeAnnotation[] parseAllTypeAnnotations(AnnotatedElement decl) { Class<?> container; byte[] rawBytes; JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess(); if (decl instanceof Class) { container = (Class<?>)decl; rawBytes = javaLangAccess.getRawClassTypeAnnotations(container); } else if (decl instanceof Executable) { container = ((Executable)decl).getDeclaringClass(); rawBytes = javaLangAccess.getRawExecutableTypeAnnotations((Executable)decl); } else { // Should not reach here. Assert? return EMPTY_TYPE_ANNOTATION_ARRAY; } return parseTypeAnnotations(rawBytes, javaLangAccess.getConstantPool(container), decl, container); } /* Parse type annotations encoded as an array of bytes */ private static TypeAnnotation[] parseTypeAnnotations(byte[] rawAnnotations, ConstantPool cp, AnnotatedElement baseDecl, Class<?> container) { if (rawAnnotations == null) return EMPTY_TYPE_ANNOTATION_ARRAY; ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); int annotationCount = buf.getShort() & 0xFFFF; List<TypeAnnotation> typeAnnotations = new ArrayList<>(annotationCount); // Parse each TypeAnnotation for (int i = 0; i < annotationCount; i++) { TypeAnnotation ta = parseTypeAnnotation(buf, cp, baseDecl, container); if (ta != null) typeAnnotations.add(ta); } return typeAnnotations.toArray(EMPTY_TYPE_ANNOTATION_ARRAY); } // Helper static Map<Class<? extends Annotation>, Annotation> mapTypeAnnotations(TypeAnnotation[] typeAnnos) { Map<Class<? extends Annotation>, Annotation> result = new LinkedHashMap<>(); for (TypeAnnotation t : typeAnnos) { Annotation a = t.getAnnotation(); if (a != null) { Class<? extends Annotation> klass = a.annotationType(); AnnotationType type = AnnotationType.getInstance(klass); if (type.retention() == RetentionPolicy.RUNTIME && result.put(klass, a) != null) { throw new AnnotationFormatError("Duplicate annotation for class: "+klass+": " + a); } } } return result; } // Position codes // Regular type parameter annotations private static final byte CLASS_TYPE_PARAMETER = 0x00; private static final byte METHOD_TYPE_PARAMETER = 0x01; // Type Annotations outside method bodies private static final byte CLASS_EXTENDS = 0x10; private static final byte CLASS_TYPE_PARAMETER_BOUND = 0x11; private static final byte METHOD_TYPE_PARAMETER_BOUND = 0x12; private static final byte FIELD = 0x13; private static final byte METHOD_RETURN = 0x14; private static final byte METHOD_RECEIVER = 0x15; private static final byte METHOD_FORMAL_PARAMETER = 0x16; private static final byte THROWS = 0x17; // Type Annotations inside method bodies private static final byte LOCAL_VARIABLE = (byte)0x40; private static final byte RESOURCE_VARIABLE = (byte)0x41; private static final byte EXCEPTION_PARAMETER = (byte)0x42; private static final byte INSTANCEOF = (byte)0x43; private static final byte NEW = (byte)0x44; private static final byte CONSTRUCTOR_REFERENCE = (byte)0x45; private static final byte METHOD_REFERENCE = (byte)0x46; private static final byte CAST = (byte)0x47; private static final byte CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = (byte)0x48; private static final byte METHOD_INVOCATION_TYPE_ARGUMENT = (byte)0x49; private static final byte CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = (byte)0x4A; private static final byte METHOD_REFERENCE_TYPE_ARGUMENT = (byte)0x4B; private static TypeAnnotation parseTypeAnnotation(ByteBuffer buf, ConstantPool cp, AnnotatedElement baseDecl, Class<?> container) { try { TypeAnnotationTargetInfo ti = parseTargetInfo(buf); LocationInfo locationInfo = LocationInfo.parseLocationInfo(buf); Annotation a = AnnotationParser.parseAnnotation(buf, cp, container, false); if (ti == null) // Inside a method for example return null; return new TypeAnnotation(ti, locationInfo, a, baseDecl); } catch (IllegalArgumentException | // Bad type in const pool at specified index BufferUnderflowException e) { throw new AnnotationFormatError(e); } } private static TypeAnnotationTargetInfo parseTargetInfo(ByteBuffer buf) { int posCode = buf.get() & 0xFF; switch(posCode) { case CLASS_TYPE_PARAMETER: case METHOD_TYPE_PARAMETER: { int index = buf.get() & 0xFF; TypeAnnotationTargetInfo res; if (posCode == CLASS_TYPE_PARAMETER) res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_TYPE_PARAMETER, index); else res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_TYPE_PARAMETER, index); return res; } // unreachable break; case CLASS_EXTENDS: { short index = buf.getShort(); //needs to be signed if (index == -1) { return new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_EXTENDS); } else if (index >= 0) { TypeAnnotationTargetInfo res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_IMPLEMENTS, index); return res; }} break; case CLASS_TYPE_PARAMETER_BOUND: return parse2ByteTarget(TypeAnnotationTarget.CLASS_TYPE_PARAMETER_BOUND, buf); case METHOD_TYPE_PARAMETER_BOUND: return parse2ByteTarget(TypeAnnotationTarget.METHOD_TYPE_PARAMETER_BOUND, buf); case FIELD: return new TypeAnnotationTargetInfo(TypeAnnotationTarget.FIELD); case METHOD_RETURN: return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RETURN); case METHOD_RECEIVER: return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RECEIVER); case METHOD_FORMAL_PARAMETER: { int index = buf.get() & 0xFF; return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_FORMAL_PARAMETER, index); } //unreachable break; case THROWS: return parseShortTarget(TypeAnnotationTarget.THROWS, buf); /* * The ones below are inside method bodies, we don't care about them for core reflection * other than adjusting for them in the byte stream. */ case LOCAL_VARIABLE: case RESOURCE_VARIABLE: short length = buf.getShort(); for (int i = 0; i < length; ++i) { short offset = buf.getShort(); short varLength = buf.getShort(); short index = buf.getShort(); } return null; case EXCEPTION_PARAMETER: { byte index = buf.get(); } return null; case INSTANCEOF: case NEW: case CONSTRUCTOR_REFERENCE: case METHOD_REFERENCE: { short offset = buf.getShort(); } return null; case CAST: case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: case METHOD_INVOCATION_TYPE_ARGUMENT: case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: case METHOD_REFERENCE_TYPE_ARGUMENT: { short offset = buf.getShort(); byte index = buf.get(); } return null; default: // will throw error below break; } throw new AnnotationFormatError("Could not parse bytes for type annotations"); } private static TypeAnnotationTargetInfo parseShortTarget(TypeAnnotationTarget target, ByteBuffer buf) { int index = buf.getShort() & 0xFFFF; return new TypeAnnotationTargetInfo(target, index); } private static TypeAnnotationTargetInfo parse2ByteTarget(TypeAnnotationTarget target, ByteBuffer buf) { int count = buf.get() & 0xFF; int secondaryIndex = buf.get() & 0xFF; return new TypeAnnotationTargetInfo(target, count, secondaryIndex); } }