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