|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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.misc.SharedSecrets; |
|
import jdk.internal.misc.JavaLangAccess; |
|
import jdk.internal.reflect.ConstantPool; |
|
import static sun.reflect.annotation.TypeAnnotation.*; |
|
|
|
|
|
|
|
|
|
*/ |
|
public final class TypeAnnotationParser { |
|
private static final TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY = new TypeAnnotation[0]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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]; |
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <D extends GenericDeclaration> AnnotatedType[] parseAnnotatedBounds(Type[] bounds, |
|
D decl, |
|
int typeVarIndex) { |
|
return parseAnnotatedBounds(bounds, decl, typeVarIndex, LocationInfo.BASE_LOCATION); |
|
} |
|
|
|
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]; |
|
|
|
// Adjust bounds index |
|
// |
|
// Figure out if the type annotations for this bound starts with 0 |
|
// or 1. The spec says within a bound the 0:th type annotation will |
|
// always be on an bound of a Class type (not Interface type). So |
|
// if the programmer starts with an Interface type for the first |
|
// (and following) bound(s) the implicit Object bound is considered |
|
// the first (that is 0:th) bound and type annotations start on |
|
|
|
if (bounds.length > 0) { |
|
Type b0 = bounds[0]; |
|
if (!(b0 instanceof Class<?>)) { |
|
startIndex = 1; |
|
} else { |
|
Class<?> c = (Class<?>)b0; |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 { |
|
|
|
return EMPTY_TYPE_ANNOTATION_ARRAY; |
|
} |
|
return parseTypeAnnotations(rawBytes, javaLangAccess.getConstantPool(container), |
|
decl, container); |
|
} |
|
|
|
|
|
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); |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
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 |
|
|
|
private static final byte CLASS_TYPE_PARAMETER = 0x00; |
|
private static final byte METHOD_TYPE_PARAMETER = 0x01; |
|
|
|
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; |
|
|
|
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) |
|
return null; |
|
return new TypeAnnotation(ti, locationInfo, a, baseDecl); |
|
} catch (IllegalArgumentException | |
|
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; |
|
} |
|
case CLASS_EXTENDS: { |
|
short index = buf.getShort(); |
|
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); |
|
} |
|
case THROWS: |
|
return parseShortTarget(TypeAnnotationTarget.THROWS, buf); |
|
|
|
|
|
|
|
|
|
*/ |
|
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: |
|
|
|
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); |
|
} |
|
} |