/* * Copyright (c) 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.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.HashMap; import java.util.LinkedHashMap; import java.util.Map; import sun.misc.JavaLangAccess; import sun.reflect.ConstantPool; import static sun.reflect.annotation.TypeAnnotation.*; public class TypeAnnotationParser { // Position codes private static final byte CLASS_TYPE_PARAMETER_CODE = 0x00; private static final byte METHOD_TYPE_PARAMETER_CODE = 0x02; private static final byte CLASS_EXTENDS_CODE = 0x10; private static final byte CLASS_TYPE_PARAMETER_BOUND_CODE = 0x12; private static final byte METHOD_TYPE_PARAMETER_BOUND_CODE = 0x14; private static final byte FIELD_CODE = 0x16; private static final byte METHOD_RETURN_CODE = 0x18; private static final byte METHOD_RECEIVER_CODE = 0x1A; private static final byte METHOD_PARAMETER_CODE = 0x1C; private static final byte THROWS_CODE = 0x1E; private static final byte LOCAL_VARIABLE_CODE = (byte)0x80; private static final byte RESOURCE_VARIABLE_CODE = (byte)0x82; private static final byte EXCEPTION_PARAMETER_CODE = (byte)0x84; private static final byte CAST_CODE = (byte)0x86; private static final byte INSTANCEOF_CODE = (byte)0x88; private static final byte NEW_CODE = (byte)0x8A; private static final byte CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT_CODE = (byte)0x8C; private static final byte METHOD_INVOCATION_TYPE_ARGUMENT_CODE = (byte)0x8E; private static final byte LAMBDA_FORMAL_PARAMETER_CODE = (byte)0x90; private static final byte METHOD_REFERENCE_TYPE_ARGUMENT_CODE = (byte)0x92; /** * Parse type annotations encoded as an array of bytes. */ public 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 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); } /** * Parse all type annotations on the declaration supplied. */ public static TypeAnnotation[] parseAllTypeAnnotations(AnnotatedElement decl) { Class container; byte[] rawBytes; JavaLangAccess javaLangAccess = sun.misc.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); } private static TypeAnnotation parseTypeAnnotation(ByteBuffer buf, ConstantPool cp, AnnotatedElement baseDecl, Class container) { 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; TypeAnnotation ta = new TypeAnnotation(); ta.setAnnotation(a); ta.setTargetInfo(ti); ta.setBaseDeclaration(baseDecl); ta.setLocationInfo(locationInfo); return ta; } private static TypeAnnotationTargetInfo parseTargetInfo(ByteBuffer buf) { byte posCode = buf.get(); switch(posCode) { case CAST_CODE: case INSTANCEOF_CODE: case NEW_CODE: { short offset = buf.getShort(); // CONSTRUCTORS HERE } break; case LOCAL_VARIABLE_CODE: case RESOURCE_VARIABLE_CODE: short length = buf.getShort(); // probably create an array for (int i = 0; i < length; ++i) { short offset = buf.getShort(); short varLength = buf.getShort(); short index = buf.getShort(); // probably add entries to an array } // CONSTRUCTORS HERE break; case EXCEPTION_PARAMETER_CODE: { byte index = buf.get(); // CONSTRUCTORS HERE } break; case METHOD_RECEIVER_CODE: // no additional payload return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RECEIVER_TYPE); case CLASS_TYPE_PARAMETER_CODE: case METHOD_TYPE_PARAMETER_CODE: { byte index = buf.get(); TypeAnnotationTargetInfo res; if (posCode == CLASS_TYPE_PARAMETER_CODE) res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_TYPE_PARAMETER); else res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_TYPE_PARAMETER); res.setCount(index); return res; } case CLASS_TYPE_PARAMETER_BOUND_CODE: case METHOD_TYPE_PARAMETER_BOUND_CODE: { byte parameter_index = buf.get(); byte bound_index = buf.get(); TypeAnnotationTargetInfo res; if (posCode == CLASS_TYPE_PARAMETER_BOUND_CODE) res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_PARAMETER_BOUND); else res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_PARAMETER_BOUND); res.setCount(parameter_index); res.setSecondIndex(bound_index); return res; } case CLASS_EXTENDS_CODE: { short index = buf.getShort(); // CONSTRUCTORS HERE if (index == -1) { return new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_EXTENDS); } else if (index >= 0) { TypeAnnotationTargetInfo res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_IMPLEMENTS); res.setCount(index); return res; } } break; case THROWS_CODE: { short index = buf.getShort(); TypeAnnotationTargetInfo res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.THROWS); res.setCount(index); return res; } case METHOD_PARAMETER_CODE: { byte index = buf.get(); // CONSTRUCTORS HERE } break; case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT_CODE: case METHOD_INVOCATION_TYPE_ARGUMENT_CODE: case METHOD_REFERENCE_TYPE_ARGUMENT_CODE: { short offset = buf.getShort(); byte index = buf.get(); } break; case METHOD_RETURN_CODE: // no additional payload return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RETURN_TYPE); //break; /*unreachable*/ case FIELD_CODE: // no additional payload return new TypeAnnotationTargetInfo(TypeAnnotationTarget.FIELD_TYPE); //break; /*unreachable*/ case LAMBDA_FORMAL_PARAMETER_CODE: { byte index = buf.get(); // CONSTRUCTORS HERE } break; default: // probably throw some exception break; } return null; } // Helper @SuppressWarnings({"rawtypes", "unchecked"}) public static AnnotatedType[] buildAnnotatedTypes0(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); ArrayList[] l = new ArrayList[size]; // array of ArrayList 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) { l[pos] = new ArrayList(); } l[pos].add(t); } } for (int i = 0; i < size; i++) { ArrayList list = l[i]; if (list != null) { TypeAnnotation[] typeAnnotations = list.toArray(new TypeAnnotation[0]); result[i] = AnnotatedTypeFactory.buildAnnotatedType(types[i], LocationInfo.BASE_LOCATION, typeAnnotations, typeAnnotations, decl); } } return result; } public static AnnotatedType buildAnnotatedType0(byte[] rawAnnotations, ConstantPool cp, AnnotatedElement decl, Class container, Type type, TypeAnnotationTarget filter) { TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations, cp, decl, container); List l = new ArrayList<>(); for (TypeAnnotation t : tas) { TypeAnnotationTargetInfo ti = t.getTargetInfo(); if (ti.getTarget() == filter) l.add(t); } TypeAnnotation[] typeAnnotations = l.toArray(new TypeAnnotation[0]); return AnnotatedTypeFactory.buildAnnotatedType(type, LocationInfo.BASE_LOCATION, typeAnnotations, typeAnnotations, decl); } // Class public static AnnotatedType buildAnnotatedSuperclass(byte[] rawAnnotations, ConstantPool cp, Class decl) { Type supertype = decl.getGenericSuperclass(); if (supertype == null) return AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE; return buildAnnotatedType0(rawAnnotations, cp, decl, decl, supertype, TypeAnnotationTarget.CLASS_EXTENDS); } public static AnnotatedType[] buildAnnotatedInterfaces(byte[] rawAnnotations, ConstantPool cp, Class decl) { return buildAnnotatedTypes0(rawAnnotations, cp, decl, decl, decl.getGenericInterfaces(), TypeAnnotationTarget.CLASS_IMPLEMENTS); } // TypeVariable public static Annotation[] parseTypeParameterAnnotations(D genericsDecl, int typeParameterIndex) { AnnotatedElement decl; TypeAnnotationTarget predicate; if (genericsDecl instanceof Class) { decl = (Class)genericsDecl; predicate = TypeAnnotationTarget.CLASS_TYPE_PARAMETER; } else { decl = (Executable)genericsDecl; predicate = TypeAnnotationTarget.METHOD_TYPE_PARAMETER; } List typeVarAnnos = TypeAnnotation.filter(parseAllTypeAnnotations(decl), predicate); List res = new ArrayList<>(); for (TypeAnnotation t : typeVarAnnos) if (t.getTargetInfo().getCount() == typeParameterIndex) res.add(t.getAnnotation()); return res.toArray(new Annotation[0]); } public static AnnotatedType[] parseAnnotatedBounds(Type[] bounds, D decl, int typeVarIndex) { return parseAnnotatedBounds(bounds, decl, typeVarIndex, LocationInfo.BASE_LOCATION); } static AnnotatedType[] parseAnnotatedBounds(Type[] bounds, D decl, int typeVarIndex, LocationInfo loc) { List candidates = fetchBounds(decl); if (bounds != null) { int startIndex = 0; AnnotatedType[] res = new AnnotatedType[bounds.length]; Arrays.fill(res, AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE); // Adjust bounds index if (bounds.length > 0) { Type b0 = bounds[0]; if (!(b0 instanceof Class)) { startIndex = 1; } else { Class c = (Class)b0; if (c.isInterface()) { // enum? anno? startIndex = 1; } } } for (int i = 0; i < bounds.length; i++) { List l = new ArrayList<>(); for (TypeAnnotation t : candidates) { TypeAnnotationTargetInfo tInfo = t.getTargetInfo(); if (tInfo.getSecondIndex() == i + startIndex && tInfo.getCount() == typeVarIndex) { l.add(t); } res[i] = AnnotatedTypeFactory.buildAnnotatedType(bounds[i], loc, l.toArray(new TypeAnnotation[0]), candidates.toArray(new TypeAnnotation[0]), (AnnotatedElement)decl); } } return res; } return new AnnotatedType[0]; } private static List fetchBounds(D decl) { AnnotatedElement boundsDecl; TypeAnnotationTarget target; if (decl instanceof Class) { target = TypeAnnotationTarget.CLASS_PARAMETER_BOUND; boundsDecl = (Class)decl; } else { target = TypeAnnotationTarget.METHOD_PARAMETER_BOUND; boundsDecl = (Executable)decl; } return TypeAnnotation.filter(TypeAnnotationParser.parseAllTypeAnnotations(boundsDecl), target); } private static final TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY = new TypeAnnotation[0]; static Map, Annotation> mapAnnotations(TypeAnnotation[] typeAnnos) { Map, Annotation> result = new LinkedHashMap, Annotation>(); for (TypeAnnotation t : typeAnnos) { Annotation a = t.getAnnotation(); Class klass = a.annotationType(); AnnotationType type = AnnotationType.getInstance(klass); if (type.retention() == RetentionPolicy.RUNTIME) if (result.put(klass, a) != null) throw new AnnotationFormatError("Duplicate annotation for class: "+klass+": " + a); } return result; } }