1 /*
   2  * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.reflect.annotation;
  27 
  28 import java.io.ObjectInputStream;
  29 import java.lang.annotation.*;
  30 import java.lang.reflect.*;
  31 import java.io.Serializable;
  32 import java.util.*;
  33 import java.security.AccessController;
  34 import java.security.PrivilegedAction;
  35 
  36 /**
  37  * InvocationHandler for dynamic proxy implementation of Annotation.
  38  *
  39  * @author  Josh Bloch
  40  * @since   1.5
  41  */
  42 class AnnotationInvocationHandler implements InvocationHandler, Serializable {
  43     private static final long serialVersionUID = 6182022883658399397L;
  44     private final Class<? extends Annotation> type;
  45     private final Map<String, Object> memberValues;
  46 
  47     AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
  48         Class<?>[] superInterfaces = type.getInterfaces();
  49         if (!type.isAnnotation() ||
  50             superInterfaces.length != 1 ||
  51             superInterfaces[0] != java.lang.annotation.Annotation.class)
  52             throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
  53         this.type = type;
  54         this.memberValues = memberValues;
  55     }
  56 
  57     public Object invoke(Object proxy, Method method, Object[] args) {
  58         String member = method.getName();
  59         Class<?>[] paramTypes = method.getParameterTypes();
  60 
  61         // Handle Object and Annotation methods
  62         if (member.equals("equals") && paramTypes.length == 1 &&
  63             paramTypes[0] == Object.class)
  64             return equalsImpl(args[0]);
  65         if (paramTypes.length != 0)
  66             throw new AssertionError("Too many parameters for an annotation method");
  67 
  68         switch(member) {
  69         case "toString":
  70             return toStringImpl();
  71         case "hashCode":
  72             return hashCodeImpl();
  73         case "annotationType":
  74             return type;
  75         }
  76 
  77         // Handle annotation member accessors
  78         Object result = memberValues.get(member);
  79 
  80         if (result == null)
  81             throw new IncompleteAnnotationException(type, member);
  82 
  83         if (result instanceof ExceptionProxy)
  84             throw ((ExceptionProxy) result).generateException();
  85 
  86         if (result.getClass().isArray() && Array.getLength(result) != 0)
  87             result = cloneArray(result);
  88 
  89         return result;
  90     }
  91 
  92     /**
  93      * This method, which clones its array argument, would not be necessary
  94      * if Cloneable had a public clone method.
  95      */
  96     private Object cloneArray(Object array) {
  97         Class<?> type = array.getClass();
  98 
  99         if (type == byte[].class) {
 100             byte[] byteArray = (byte[])array;
 101             return byteArray.clone();
 102         }
 103         if (type == char[].class) {
 104             char[] charArray = (char[])array;
 105             return charArray.clone();
 106         }
 107         if (type == double[].class) {
 108             double[] doubleArray = (double[])array;
 109             return doubleArray.clone();
 110         }
 111         if (type == float[].class) {
 112             float[] floatArray = (float[])array;
 113             return floatArray.clone();
 114         }
 115         if (type == int[].class) {
 116             int[] intArray = (int[])array;
 117             return intArray.clone();
 118         }
 119         if (type == long[].class) {
 120             long[] longArray = (long[])array;
 121             return longArray.clone();
 122         }
 123         if (type == short[].class) {
 124             short[] shortArray = (short[])array;
 125             return shortArray.clone();
 126         }
 127         if (type == boolean[].class) {
 128             boolean[] booleanArray = (boolean[])array;
 129             return booleanArray.clone();
 130         }
 131 
 132         Object[] objectArray = (Object[])array;
 133         return objectArray.clone();
 134     }
 135 
 136 
 137     /**
 138      * Implementation of dynamicProxy.toString()
 139      */
 140     private String toStringImpl() {
 141         StringBuilder result = new StringBuilder(128);
 142         result.append('@');
 143         result.append(type.getName());
 144         result.append('(');
 145         boolean firstMember = true;
 146         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 147             if (firstMember)
 148                 firstMember = false;
 149             else
 150                 result.append(", ");
 151 
 152             result.append(e.getKey());
 153             result.append('=');
 154             result.append(memberValueToString(e.getValue()));
 155         }
 156         result.append(')');
 157         return result.toString();
 158     }
 159 
 160     /**
 161      * Translates a member value (in "dynamic proxy return form") into a string
 162      */
 163     private static String memberValueToString(Object value) {
 164         if (value instanceof ExceptionProxy) {
 165             return ((ExceptionProxy)value).memberToString();
 166         }
 167         Class<?> type = value.getClass();
 168         if (!type.isArray())    // primitive, string, class, enum const,
 169                                 // or annotation
 170             return value.toString();
 171 
 172         if (type == byte[].class)
 173             return Arrays.toString((byte[]) value);
 174         if (type == char[].class)
 175             return Arrays.toString((char[]) value);
 176         if (type == double[].class)
 177             return Arrays.toString((double[]) value);
 178         if (type == float[].class)
 179             return Arrays.toString((float[]) value);
 180         if (type == int[].class)
 181             return Arrays.toString((int[]) value);
 182         if (type == long[].class)
 183             return Arrays.toString((long[]) value);
 184         if (type == short[].class)
 185             return Arrays.toString((short[]) value);
 186         if (type == boolean[].class)
 187             return Arrays.toString((boolean[]) value);
 188         return Arrays.toString((Object[]) value);
 189     }
 190 
 191     /**
 192      * Implementation of dynamicProxy.equals(Object o)
 193      */
 194     private Boolean equalsImpl(Object o) {
 195         if (o == this)
 196             return true;
 197 
 198         if (!type.isInstance(o))
 199             return false;
 200         for (Method memberMethod : getMemberMethods()) {
 201             String member = memberMethod.getName();
 202             Object ourValue = memberValues.get(member);
 203             Object hisValue = null;
 204             AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 205             if (hisHandler != null) {
 206                 hisValue = hisHandler.memberValues.get(member);
 207             } else {
 208                 try {
 209                     hisValue = memberMethod.invoke(o);
 210                 } catch (InvocationTargetException e) {
 211                     return false;
 212                 } catch (IllegalAccessException e) {
 213                     throw new AssertionError(e);
 214                 }
 215             }
 216             if (!memberValueEquals(ourValue, hisValue))
 217                 return false;
 218         }
 219         return true;
 220     }
 221 
 222     /**
 223      * Returns an object's invocation handler if that object is a dynamic
 224      * proxy with a handler of type AnnotationInvocationHandler.
 225      * Returns null otherwise.
 226      */
 227     private AnnotationInvocationHandler asOneOfUs(Object o) {
 228         if (Proxy.isProxyClass(o.getClass())) {
 229             InvocationHandler handler = Proxy.getInvocationHandler(o);
 230             if (handler instanceof AnnotationInvocationHandler)
 231                 return (AnnotationInvocationHandler) handler;
 232         }
 233         return null;
 234     }
 235 
 236     /**
 237      * Returns true iff the two member values in "dynamic proxy return form"
 238      * are equal using the appropriate equality function depending on the
 239      * member type.  The two values will be of the same type unless one of
 240      * the containing annotations is ill-formed.  If one of the containing
 241      * annotations is ill-formed, this method will return false unless the
 242      * two members are identical object references.
 243      */
 244     private static boolean memberValueEquals(Object v1, Object v2) {
 245         Class<?> type = v1.getClass();
 246 
 247         // Check for primitive, string, class, enum const, annotation,
 248         // or ExceptionProxy
 249         if (!type.isArray())
 250             return v1.equals(v2);
 251 
 252         // Check for array of string, class, enum const, annotation,
 253         // or ExceptionProxy
 254         if (v1 instanceof Object[] && v2 instanceof Object[])
 255             return Arrays.equals((Object[]) v1, (Object[]) v2);
 256 
 257         // Check for ill formed annotation(s)
 258         if (v2.getClass() != type)
 259             return false;
 260 
 261         // Deal with array of primitives
 262         if (type == byte[].class)
 263             return Arrays.equals((byte[]) v1, (byte[]) v2);
 264         if (type == char[].class)
 265             return Arrays.equals((char[]) v1, (char[]) v2);
 266         if (type == double[].class)
 267             return Arrays.equals((double[]) v1, (double[]) v2);
 268         if (type == float[].class)
 269             return Arrays.equals((float[]) v1, (float[]) v2);
 270         if (type == int[].class)
 271             return Arrays.equals((int[]) v1, (int[]) v2);
 272         if (type == long[].class)
 273             return Arrays.equals((long[]) v1, (long[]) v2);
 274         if (type == short[].class)
 275             return Arrays.equals((short[]) v1, (short[]) v2);
 276         assert type == boolean[].class;
 277         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 278     }
 279 
 280     /**
 281      * Returns the member methods for our annotation type.  These are
 282      * obtained lazily and cached, as they're expensive to obtain
 283      * and we only need them if our equals method is invoked (which should
 284      * be rare).
 285      */
 286     private Method[] getMemberMethods() {
 287         Method[] value = memberMethods;
 288         if (value == null) {
 289             value = computeMemberMethods();
 290             memberMethods = value;
 291         }
 292         return value;
 293     }
 294 
 295     private Method[] computeMemberMethods() {
 296         return AccessController.doPrivileged(
 297             new PrivilegedAction<Method[]>() {
 298                 public Method[] run() {
 299                     final Method[] methods = type.getDeclaredMethods();
 300                     validateAnnotationMethods(methods);
 301                     AccessibleObject.setAccessible(methods, true);
 302                     return methods;
 303                 }});
 304     }
 305 
 306     private transient volatile Method[] memberMethods;
 307 
 308     /**
 309      * Validates that a method is structurally appropriate for an
 310      * annotation type. As of Java SE 8, annotation types cannot
 311      * contain static methods and the declared methods of an
 312      * annotation type must take zero arguments and there are
 313      * restrictions on the return type.
 314      */
 315     private void validateAnnotationMethods(Method[] memberMethods) {
 316         /*
 317          * Specification citations below are from JLS
 318          * 9.6.1. Annotation Type Elements
 319          */
 320         boolean valid = true;
 321         for(Method method : memberMethods) {
 322             /*
 323              * "By virtue of the AnnotationTypeElementDeclaration
 324              * production, a method declaration in an annotation type
 325              * declaration cannot have formal parameters, type
 326              * parameters, or a throws clause.
 327              *
 328              * "By virtue of the AnnotationTypeElementModifier
 329              * production, a method declaration in an annotation type
 330              * declaration cannot be default or static."
 331              */
 332             if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) ||
 333                 method.isDefault() ||
 334                 method.getParameterCount() != 0 ||
 335                 method.getExceptionTypes().length != 0) {
 336                 valid = false;
 337                 break;
 338             }
 339 
 340             /*
 341              * "It is a compile-time error if the return type of a
 342              * method declared in an annotation type is not one of the
 343              * following: a primitive type, String, Class, any
 344              * parameterized invocation of Class, an enum type
 345              * (section 8.9), an annotation type, or an array type
 346              * (chapter 10) whose element type is one of the preceding
 347              * types."
 348              */
 349             Class<?> returnType = method.getReturnType();
 350             if (returnType.isArray()) {
 351                 returnType = returnType.getComponentType();
 352                 if (returnType.isArray()) { // Only single dimensional arrays
 353                     valid = false;
 354                     break;
 355                 }
 356             }
 357 
 358             if (!((returnType.isPrimitive() && returnType != void.class) ||
 359                   returnType == java.lang.String.class ||
 360                   returnType == java.lang.Class.class ||
 361                   returnType.isEnum() ||
 362                   returnType.isAnnotation())) {
 363                 valid = false;
 364                 break;
 365             }
 366 
 367             /*
 368              * "It is a compile-time error if any method declared in an
 369              * annotation type has a signature that is
 370              * override-equivalent to that of any public or protected
 371              * method declared in class Object or in the interface
 372              * java.lang.annotation.Annotation."
 373              *
 374              * The methods in Object or Annotation meeting the other
 375              * criteria (no arguments, contrained return type, etc.)
 376              * above are:
 377              *
 378              * String toString()
 379              * int hashCode()
 380              * Class<? extends Annotation> annotationType()
 381              */
 382             String methodName = method.getName();
 383             if ((methodName.equals("toString") && returnType == java.lang.String.class) ||
 384                 (methodName.equals("hashCode") && returnType == int.class) ||
 385                 (methodName.equals("annotationType") && returnType == java.lang.Class.class)) {
 386                 valid = false;
 387                 break;
 388             }
 389         }
 390         if (valid)
 391             return;
 392         else
 393             throw new AnnotationFormatError("Malformed method on an annotation type");
 394     }
 395 
 396     /**
 397      * Implementation of dynamicProxy.hashCode()
 398      */
 399     private int hashCodeImpl() {
 400         int result = 0;
 401         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 402             result += (127 * e.getKey().hashCode()) ^
 403                 memberValueHashCode(e.getValue());
 404         }
 405         return result;
 406     }
 407 
 408     /**
 409      * Computes hashCode of a member value (in "dynamic proxy return form")
 410      */
 411     private static int memberValueHashCode(Object value) {
 412         Class<?> type = value.getClass();
 413         if (!type.isArray())    // primitive, string, class, enum const,
 414                                 // or annotation
 415             return value.hashCode();
 416 
 417         if (type == byte[].class)
 418             return Arrays.hashCode((byte[]) value);
 419         if (type == char[].class)
 420             return Arrays.hashCode((char[]) value);
 421         if (type == double[].class)
 422             return Arrays.hashCode((double[]) value);
 423         if (type == float[].class)
 424             return Arrays.hashCode((float[]) value);
 425         if (type == int[].class)
 426             return Arrays.hashCode((int[]) value);
 427         if (type == long[].class)
 428             return Arrays.hashCode((long[]) value);
 429         if (type == short[].class)
 430             return Arrays.hashCode((short[]) value);
 431         if (type == boolean[].class)
 432             return Arrays.hashCode((boolean[]) value);
 433         return Arrays.hashCode((Object[]) value);
 434     }
 435 
 436     private void readObject(java.io.ObjectInputStream s)
 437         throws java.io.IOException, ClassNotFoundException {
 438         ObjectInputStream.GetField fields = s.readFields();
 439 
 440         @SuppressWarnings("unchecked")
 441         Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
 442         @SuppressWarnings("unchecked")
 443         Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null);
 444 
 445         // Check to make sure that types have not evolved incompatibly
 446 
 447         AnnotationType annotationType = null;
 448         try {
 449             annotationType = AnnotationType.getInstance(t);
 450         } catch(IllegalArgumentException e) {
 451             // Class is no longer an annotation type; time to punch out
 452             throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
 453         }
 454 
 455         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 456         // consistent with runtime Map type
 457         Map<String, Object> mv = new LinkedHashMap<>();
 458 
 459         // If there are annotation members without values, that
 460         // situation is handled by the invoke method.
 461         for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) {
 462             String name = memberValue.getKey();
 463             Object value = null;
 464             Class<?> memberType = memberTypes.get(name);
 465             if (memberType != null) {  // i.e. member still exists
 466                 value = memberValue.getValue();
 467                 if (!(memberType.isInstance(value) ||
 468                       value instanceof ExceptionProxy)) {
 469                     value = new AnnotationTypeMismatchExceptionProxy(
 470                             value.getClass() + "[" + value + "]").setMember(
 471                                 annotationType.members().get(name));
 472                 }
 473             }
 474             mv.put(name, value);
 475         }
 476 
 477         UnsafeAccessor.setType(this, t);
 478         UnsafeAccessor.setMemberValues(this, mv);
 479     }
 480 
 481     private static class UnsafeAccessor {
 482         private static final jdk.internal.misc.Unsafe unsafe;
 483         private static final long typeOffset;
 484         private static final long memberValuesOffset;
 485         static {
 486             try {
 487                 unsafe = jdk.internal.misc.Unsafe.getUnsafe();
 488                 typeOffset = unsafe.objectFieldOffset
 489                         (AnnotationInvocationHandler.class.getDeclaredField("type"));
 490                 memberValuesOffset = unsafe.objectFieldOffset
 491                         (AnnotationInvocationHandler.class.getDeclaredField("memberValues"));
 492             } catch (Exception ex) {
 493                 throw new ExceptionInInitializerError(ex);
 494             }
 495         }
 496         static void setType(AnnotationInvocationHandler o,
 497                             Class<? extends Annotation> type) {
 498             unsafe.putObject(o, typeOffset, type);
 499         }
 500 
 501         static void setMemberValues(AnnotationInvocationHandler o,
 502                                     Map<String, Object> memberValues) {
 503             unsafe.putObject(o, memberValuesOffset, memberValues);
 504         }
 505     }
 506 }