Path: blob/master/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
41159 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.reflect.annotation;2627import java.io.ObjectInputStream;28import java.lang.annotation.*;29import java.lang.reflect.*;30import java.io.Serializable;31import java.util.*;32import java.util.stream.*;33import java.security.AccessController;34import java.security.PrivilegedAction;3536/**37* InvocationHandler for dynamic proxy implementation of Annotation.38*39* @author Josh Bloch40* @since 1.541*/42class AnnotationInvocationHandler implements InvocationHandler, Serializable {43@java.io.Serial44private static final long serialVersionUID = 6182022883658399397L;45private final Class<? extends Annotation> type;46@SuppressWarnings("serial") // Not statically typed as Serializable47private final Map<String, Object> memberValues;4849AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {50Class<?>[] superInterfaces = type.getInterfaces();51if (!type.isAnnotation() ||52superInterfaces.length != 1 ||53superInterfaces[0] != java.lang.annotation.Annotation.class)54throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type: " +55type.getName());56this.type = type;57this.memberValues = memberValues;58}5960public Object invoke(Object proxy, Method method, Object[] args) {61String member = method.getName();62int parameterCount = method.getParameterCount();6364// Handle Object and Annotation methods65if (parameterCount == 1 && member == "equals" &&66method.getParameterTypes()[0] == Object.class) {67return equalsImpl(proxy, args[0]);68}69if (parameterCount != 0) {70throw new AssertionError("Too many parameters for an annotation method");71}7273if (member == "toString") {74return toStringImpl();75} else if (member == "hashCode") {76return hashCodeImpl();77} else if (member == "annotationType") {78return type;79}8081// Handle annotation member accessors82Object result = memberValues.get(member);8384if (result == null)85throw new IncompleteAnnotationException(type, member);8687if (result instanceof ExceptionProxy)88throw ((ExceptionProxy) result).generateException();8990if (result.getClass().isArray() && Array.getLength(result) != 0)91result = cloneArray(result);9293return result;94}9596/**97* This method, which clones its array argument, would not be necessary98* if Cloneable had a public clone method.99*/100private Object cloneArray(Object array) {101Class<?> type = array.getClass();102103if (type == byte[].class) {104byte[] byteArray = (byte[])array;105return byteArray.clone();106}107if (type == char[].class) {108char[] charArray = (char[])array;109return charArray.clone();110}111if (type == double[].class) {112double[] doubleArray = (double[])array;113return doubleArray.clone();114}115if (type == float[].class) {116float[] floatArray = (float[])array;117return floatArray.clone();118}119if (type == int[].class) {120int[] intArray = (int[])array;121return intArray.clone();122}123if (type == long[].class) {124long[] longArray = (long[])array;125return longArray.clone();126}127if (type == short[].class) {128short[] shortArray = (short[])array;129return shortArray.clone();130}131if (type == boolean[].class) {132boolean[] booleanArray = (boolean[])array;133return booleanArray.clone();134}135136Object[] objectArray = (Object[])array;137return objectArray.clone();138}139140141/**142* Implementation of dynamicProxy.toString()143*/144private String toStringImpl() {145StringBuilder result = new StringBuilder(128);146result.append('@');147result.append(type.getName());148result.append('(');149boolean firstMember = true;150Set<Map.Entry<String, Object>> entries = memberValues.entrySet();151boolean loneValue = entries.size() == 1;152for (Map.Entry<String, Object> e : entries) {153if (firstMember)154firstMember = false;155else156result.append(", ");157158String key = e.getKey();159if (!loneValue || !"value".equals(key)) {160result.append(key);161result.append('=');162}163loneValue = false;164result.append(memberValueToString(e.getValue()));165}166result.append(')');167return result.toString();168}169170/**171* Translates a member value (in "dynamic proxy return form") into a string.172*/173private static String memberValueToString(Object value) {174Class<?> type = value.getClass();175if (!type.isArray()) {176// primitive value, string, class, enum const, or annotation177if (type == Class.class)178return toSourceString((Class<?>) value);179else if (type == String.class)180return toSourceString((String) value);181if (type == Character.class)182return toSourceString((char) value);183else if (type == Double.class)184return toSourceString((double) value);185else if (type == Float.class)186return toSourceString((float) value);187else if (type == Long.class)188return toSourceString((long) value);189else if (type == Byte.class)190return toSourceString((byte) value);191else192return value.toString();193} else {194Stream<String> stringStream;195if (type == byte[].class)196stringStream = convert((byte[]) value);197else if (type == char[].class)198stringStream = convert((char[]) value);199else if (type == double[].class)200stringStream = DoubleStream.of((double[]) value)201.mapToObj(AnnotationInvocationHandler::toSourceString);202else if (type == float[].class)203stringStream = convert((float[]) value);204else if (type == int[].class)205stringStream = IntStream.of((int[]) value).mapToObj(String::valueOf);206else if (type == long[].class) {207stringStream = LongStream.of((long[]) value)208.mapToObj(AnnotationInvocationHandler::toSourceString);209} else if (type == short[].class)210stringStream = convert((short[]) value);211else if (type == boolean[].class)212stringStream = convert((boolean[]) value);213else if (type == Class[].class)214stringStream =215Arrays.stream((Class<?>[]) value).216map(AnnotationInvocationHandler::toSourceString);217else if (type == String[].class)218stringStream =219Arrays.stream((String[])value).220map(AnnotationInvocationHandler::toSourceString);221else222stringStream = Arrays.stream((Object[])value).map(Objects::toString);223224return stringStreamToString(stringStream);225}226}227228/**229* Translates a Class value to a form suitable for use in the230* string representation of an annotation.231*/232private static String toSourceString(Class<?> clazz) {233Class<?> finalComponent = clazz;234StringBuilder arrayBrackets = new StringBuilder();235236while(finalComponent.isArray()) {237finalComponent = finalComponent.getComponentType();238arrayBrackets.append("[]");239}240241return finalComponent.getName() + arrayBrackets.toString() + ".class";242}243244private static String toSourceString(float f) {245if (Float.isFinite(f))246return Float.toString(f) + "f" ;247else {248if (Float.isInfinite(f)) {249return (f < 0.0f) ? "-1.0f/0.0f": "1.0f/0.0f";250} else251return "0.0f/0.0f";252}253}254255private static String toSourceString(double d) {256if (Double.isFinite(d))257return Double.toString(d);258else {259if (Double.isInfinite(d)) {260return (d < 0.0f) ? "-1.0/0.0": "1.0/0.0";261} else262return "0.0/0.0";263}264}265266private static String toSourceString(char c) {267StringBuilder sb = new StringBuilder(4);268sb.append('\'');269sb.append(quote(c));270return sb.append('\'') .toString();271}272273/**274* Escapes a character if it has an escape sequence or is275* non-printable ASCII. Leaves non-ASCII characters alone.276*/277private static String quote(char ch) {278switch (ch) {279case '\b': return "\\b";280case '\f': return "\\f";281case '\n': return "\\n";282case '\r': return "\\r";283case '\t': return "\\t";284case '\'': return "\\'";285case '\"': return "\\\"";286case '\\': return "\\\\";287default:288return (isPrintableAscii(ch))289? String.valueOf(ch)290: String.format("\\u%04x", (int) ch);291}292}293294/**295* Is a character printable ASCII?296*/297private static boolean isPrintableAscii(char ch) {298return ch >= ' ' && ch <= '~';299}300301private static String toSourceString(byte b) {302return String.format("(byte)0x%02x", b);303}304305private static String toSourceString(long ell) {306return String.valueOf(ell) + "L";307}308309/**310* Return a string suitable for use in the string representation311* of an annotation.312*/313private static String toSourceString(String s) {314StringBuilder sb = new StringBuilder();315sb.append('"');316for (int i = 0; i < s.length(); i++) {317sb.append(quote(s.charAt(i)));318}319sb.append('"');320return sb.toString();321}322323private static Stream<String> convert(byte[] values) {324List<String> list = new ArrayList<>(values.length);325for (byte b : values)326list.add(toSourceString(b));327return list.stream();328}329330private static Stream<String> convert(char[] values) {331List<String> list = new ArrayList<>(values.length);332for (char c : values)333list.add(toSourceString(c));334return list.stream();335}336337private static Stream<String> convert(float[] values) {338List<String> list = new ArrayList<>(values.length);339for (float f : values) {340list.add(toSourceString(f));341}342return list.stream();343}344345private static Stream<String> convert(short[] values) {346List<String> list = new ArrayList<>(values.length);347for (short s : values)348list.add(Short.toString(s));349return list.stream();350}351352private static Stream<String> convert(boolean[] values) {353List<String> list = new ArrayList<>(values.length);354for (boolean b : values)355list.add(Boolean.toString(b));356return list.stream();357}358359private static String stringStreamToString(Stream<String> stream) {360return stream.collect(Collectors.joining(", ", "{", "}"));361}362363/**364* Implementation of dynamicProxy.equals(Object o)365*/366private Boolean equalsImpl(Object proxy, Object o) {367if (o == proxy)368return true;369370if (!type.isInstance(o))371return false;372for (Method memberMethod : getMemberMethods()) {373if (memberMethod.isSynthetic())374continue;375String member = memberMethod.getName();376Object ourValue = memberValues.get(member);377Object hisValue = null;378AnnotationInvocationHandler hisHandler = asOneOfUs(o);379if (hisHandler != null) {380hisValue = hisHandler.memberValues.get(member);381} else {382try {383hisValue = memberMethod.invoke(o);384} catch (InvocationTargetException e) {385return false;386} catch (IllegalAccessException e) {387throw new AssertionError(e);388}389}390if (!memberValueEquals(ourValue, hisValue))391return false;392}393return true;394}395396/**397* Returns an object's invocation handler if that object is a dynamic398* proxy with a handler of type AnnotationInvocationHandler.399* Returns null otherwise.400*/401private AnnotationInvocationHandler asOneOfUs(Object o) {402if (Proxy.isProxyClass(o.getClass())) {403InvocationHandler handler = Proxy.getInvocationHandler(o);404if (handler instanceof AnnotationInvocationHandler)405return (AnnotationInvocationHandler) handler;406}407return null;408}409410/**411* Returns true iff the two member values in "dynamic proxy return form"412* are equal using the appropriate equality function depending on the413* member type. The two values will be of the same type unless one of414* the containing annotations is ill-formed. If one of the containing415* annotations is ill-formed, this method will return false unless the416* two members are identical object references.417*/418private static boolean memberValueEquals(Object v1, Object v2) {419Class<?> type = v1.getClass();420421// Check for primitive, string, class, enum const, annotation,422// or ExceptionProxy423if (!type.isArray())424return v1.equals(v2);425426// Check for array of string, class, enum const, annotation,427// or ExceptionProxy428if (v1 instanceof Object[] && v2 instanceof Object[])429return Arrays.equals((Object[]) v1, (Object[]) v2);430431// Check for ill formed annotation(s)432if (v2.getClass() != type)433return false;434435// Deal with array of primitives436if (type == byte[].class)437return Arrays.equals((byte[]) v1, (byte[]) v2);438if (type == char[].class)439return Arrays.equals((char[]) v1, (char[]) v2);440if (type == double[].class)441return Arrays.equals((double[]) v1, (double[]) v2);442if (type == float[].class)443return Arrays.equals((float[]) v1, (float[]) v2);444if (type == int[].class)445return Arrays.equals((int[]) v1, (int[]) v2);446if (type == long[].class)447return Arrays.equals((long[]) v1, (long[]) v2);448if (type == short[].class)449return Arrays.equals((short[]) v1, (short[]) v2);450assert type == boolean[].class;451return Arrays.equals((boolean[]) v1, (boolean[]) v2);452}453454/**455* Returns the member methods for our annotation type. These are456* obtained lazily and cached, as they're expensive to obtain457* and we only need them if our equals method is invoked (which should458* be rare).459*/460private Method[] getMemberMethods() {461Method[] value = memberMethods;462if (value == null) {463value = computeMemberMethods();464memberMethods = value;465}466return value;467}468469@SuppressWarnings("removal")470private Method[] computeMemberMethods() {471return AccessController.doPrivileged(472new PrivilegedAction<Method[]>() {473public Method[] run() {474final Method[] methods = type.getDeclaredMethods();475validateAnnotationMethods(methods);476AccessibleObject.setAccessible(methods, true);477return methods;478}});479}480481private transient volatile Method[] memberMethods;482483/**484* Validates that a method is structurally appropriate for an485* annotation type. As of Java SE 8, annotation types cannot486* contain static methods and the declared methods of an487* annotation type must take zero arguments and there are488* restrictions on the return type.489*/490private void validateAnnotationMethods(Method[] memberMethods) {491/*492* Specification citations below are from JLS493* 9.6.1. Annotation Type Elements494*/495boolean valid = true;496Method currentMethod = null;497for(Method method : memberMethods) {498currentMethod = method;499int modifiers = method.getModifiers();500// Skip over methods that may be a static initializer or501// similar construct. A static initializer may be used for502// purposes such as initializing a lambda stored in an503// interface field.504if (method.isSynthetic() &&505(modifiers & (Modifier.STATIC | Modifier.PRIVATE)) != 0 &&506method.getParameterCount() == 0) {507continue;508}509510/*511* "By virtue of the AnnotationTypeElementDeclaration512* production, a method declaration in an annotation type513* declaration cannot have formal parameters, type514* parameters, or a throws clause.515*516* "By virtue of the AnnotationTypeElementModifier517* production, a method declaration in an annotation type518* declaration cannot be default or static."519*/520if (modifiers != (Modifier.PUBLIC | Modifier.ABSTRACT) ||521method.isDefault() ||522method.getParameterCount() != 0 ||523method.getExceptionTypes().length != 0) {524valid = false;525break;526}527528/*529* "It is a compile-time error if the return type of a530* method declared in an annotation type is not one of the531* following: a primitive type, String, Class, any532* parameterized invocation of Class, an enum type533* (section 8.9), an annotation type, or an array type534* (chapter 10) whose element type is one of the preceding535* types."536*/537Class<?> returnType = method.getReturnType();538if (returnType.isArray()) {539returnType = returnType.getComponentType();540if (returnType.isArray()) { // Only single dimensional arrays541valid = false;542break;543}544}545546if (!((returnType.isPrimitive() && returnType != void.class) ||547returnType == java.lang.String.class ||548returnType == java.lang.Class.class ||549returnType.isEnum() ||550returnType.isAnnotation())) {551valid = false;552break;553}554555/*556* "It is a compile-time error if any method declared in an557* annotation type has a signature that is558* override-equivalent to that of any public or protected559* method declared in class Object or in the interface560* java.lang.annotation.Annotation."561*562* The methods in Object or Annotation meeting the other563* criteria (no arguments, contrained return type, etc.)564* above are:565*566* String toString()567* int hashCode()568* Class<? extends Annotation> annotationType()569*/570String methodName = method.getName();571if ((methodName.equals("toString") && returnType == java.lang.String.class) ||572(methodName.equals("hashCode") && returnType == int.class) ||573(methodName.equals("annotationType") && returnType == java.lang.Class.class)) {574valid = false;575break;576}577}578if (valid)579return;580else581throw new AnnotationFormatError("Malformed method on an annotation type: " +582currentMethod.toString());583}584585/**586* Implementation of dynamicProxy.hashCode()587*/588private int hashCodeImpl() {589int result = 0;590for (Map.Entry<String, Object> e : memberValues.entrySet()) {591result += (127 * e.getKey().hashCode()) ^592memberValueHashCode(e.getValue());593}594return result;595}596597/**598* Computes hashCode of a member value (in "dynamic proxy return form")599*/600private static int memberValueHashCode(Object value) {601Class<?> type = value.getClass();602if (!type.isArray()) // primitive, string, class, enum const,603// or annotation604return value.hashCode();605606if (type == byte[].class)607return Arrays.hashCode((byte[]) value);608if (type == char[].class)609return Arrays.hashCode((char[]) value);610if (type == double[].class)611return Arrays.hashCode((double[]) value);612if (type == float[].class)613return Arrays.hashCode((float[]) value);614if (type == int[].class)615return Arrays.hashCode((int[]) value);616if (type == long[].class)617return Arrays.hashCode((long[]) value);618if (type == short[].class)619return Arrays.hashCode((short[]) value);620if (type == boolean[].class)621return Arrays.hashCode((boolean[]) value);622return Arrays.hashCode((Object[]) value);623}624625@java.io.Serial626private void readObject(java.io.ObjectInputStream s)627throws java.io.IOException, ClassNotFoundException {628ObjectInputStream.GetField fields = s.readFields();629630@SuppressWarnings("unchecked")631Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);632@SuppressWarnings("unchecked")633Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null);634635// Check to make sure that types have not evolved incompatibly636637AnnotationType annotationType = null;638try {639annotationType = AnnotationType.getInstance(t);640} catch(IllegalArgumentException e) {641// Class is no longer an annotation type; time to punch out642throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");643}644645Map<String, Class<?>> memberTypes = annotationType.memberTypes();646// consistent with runtime Map type647Map<String, Object> mv = new LinkedHashMap<>();648649// If there are annotation members without values, that650// situation is handled by the invoke method.651for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) {652String name = memberValue.getKey();653Object value = null;654Class<?> memberType = memberTypes.get(name);655if (memberType != null) { // i.e. member still exists656value = memberValue.getValue();657if (!(memberType.isInstance(value) ||658value instanceof ExceptionProxy)) {659value = new AnnotationTypeMismatchExceptionProxy(660value.getClass() + "[" + value + "]").setMember(661annotationType.members().get(name));662}663}664mv.put(name, value);665}666667UnsafeAccessor.setType(this, t);668UnsafeAccessor.setMemberValues(this, mv);669}670671private static class UnsafeAccessor {672private static final jdk.internal.misc.Unsafe unsafe673= jdk.internal.misc.Unsafe.getUnsafe();674private static final long typeOffset = unsafe.objectFieldOffset675(AnnotationInvocationHandler.class, "type");676private static final long memberValuesOffset = unsafe.objectFieldOffset677(AnnotationInvocationHandler.class, "memberValues");678679static void setType(AnnotationInvocationHandler o,680Class<? extends Annotation> type) {681unsafe.putReference(o, typeOffset, type);682}683684static void setMemberValues(AnnotationInvocationHandler o,685Map<String, Object> memberValues) {686unsafe.putReference(o, memberValuesOffset, memberValues);687}688}689}690691692