1 /*
   2  * Copyright (c) 2003, 2019, 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.*;
  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         int parameterCount = method.getParameterCount();
  61 
  62         // Handle Object and Annotation methods
  63         if (parameterCount == 1 && member == "equals" &&
  64                 method.getParameterTypes()[0] == Object.class) {
  65             return equalsImpl(proxy, args[0]);
  66         }
  67         if (parameterCount != 0) {
  68             throw new AssertionError("Too many parameters for an annotation method");
  69         }
  70 
  71         if (member == "toString") {
  72             return toStringImpl();
  73         } else if (member == "hashCode") {
  74             return hashCodeImpl();
  75         } else if (member == "annotationType") {
  76             return type;
  77         }
  78 
  79         // Handle annotation member accessors
  80         Object result = memberValues.get(member);
  81 
  82         if (result == null)
  83             throw new IncompleteAnnotationException(type, member);
  84 
  85         if (result instanceof ExceptionProxy)
  86             throw ((ExceptionProxy) result).generateException();
  87 
  88         if (result.getClass().isArray() && Array.getLength(result) != 0)
  89             result = cloneArray(result);
  90 
  91         return result;
  92     }
  93 
  94     /**
  95      * This method, which clones its array argument, would not be necessary
  96      * if Cloneable had a public clone method.
  97      */
  98     private Object cloneArray(Object array) {
  99         Class<?> type = array.getClass();
 100 
 101         if (type == byte[].class) {
 102             byte[] byteArray = (byte[])array;
 103             return byteArray.clone();
 104         }
 105         if (type == char[].class) {
 106             char[] charArray = (char[])array;
 107             return charArray.clone();
 108         }
 109         if (type == double[].class) {
 110             double[] doubleArray = (double[])array;
 111             return doubleArray.clone();
 112         }
 113         if (type == float[].class) {
 114             float[] floatArray = (float[])array;
 115             return floatArray.clone();
 116         }
 117         if (type == int[].class) {
 118             int[] intArray = (int[])array;
 119             return intArray.clone();
 120         }
 121         if (type == long[].class) {
 122             long[] longArray = (long[])array;
 123             return longArray.clone();
 124         }
 125         if (type == short[].class) {
 126             short[] shortArray = (short[])array;
 127             return shortArray.clone();
 128         }
 129         if (type == boolean[].class) {
 130             boolean[] booleanArray = (boolean[])array;
 131             return booleanArray.clone();
 132         }
 133 
 134         Object[] objectArray = (Object[])array;
 135         return objectArray.clone();
 136     }
 137 
 138 
 139     /**
 140      * Implementation of dynamicProxy.toString()
 141      */
 142     private String toStringImpl() {
 143         StringBuilder result = new StringBuilder(128);
 144         result.append('@');
 145         result.append(type.getName());
 146         result.append('(');
 147         boolean firstMember = true;
 148         Set<Map.Entry<String, Object>> entries = memberValues.entrySet();
 149         boolean loneValue = entries.size() == 1;
 150         for (Map.Entry<String, Object> e : entries) {
 151             if (firstMember)
 152                 firstMember = false;
 153             else
 154                 result.append(", ");
 155 
 156             String key = e.getKey();
 157             if (!loneValue || !"value".equals(key)) {
 158                 result.append(key);
 159                 result.append('=');
 160             }
 161             loneValue = false;
 162             result.append(memberValueToString(e.getValue()));
 163         }
 164         result.append(')');
 165         return result.toString();
 166     }
 167 
 168     /**
 169      * Translates a member value (in "dynamic proxy return form") into a string.
 170      */
 171     private static String memberValueToString(Object value) {
 172         Class<?> type = value.getClass();
 173         if (!type.isArray()) {
 174             // primitive value, string, class, enum const, or annotation
 175             if (type == Class.class)
 176                 return toSourceString((Class<?>) value);
 177             else if (type == String.class)
 178                 return  toSourceString((String) value);
 179             if (type == Character.class)
 180                 return toSourceString((char) value);
 181             else if (type == Double.class)
 182                 return  toSourceString((double) value);
 183             else if (type == Float.class)
 184                 return  toSourceString((float) value);
 185             else if (type == Long.class)
 186                 return  toSourceString((long) value);
 187             else if (type == Byte.class)
 188                 return  toSourceString((byte) value);
 189             else
 190                 return value.toString();
 191         } else {
 192             Stream<String> stringStream;
 193             if (type == byte[].class)
 194                 stringStream = convert((byte[]) value);
 195             else if (type == char[].class)
 196                 stringStream = convert((char[]) value);
 197             else if (type == double[].class)
 198                 stringStream = DoubleStream.of((double[]) value)
 199                     .mapToObj(AnnotationInvocationHandler::toSourceString);
 200             else if (type == float[].class)
 201                 stringStream = convert((float[]) value);
 202             else if (type == int[].class)
 203                 stringStream = IntStream.of((int[]) value).mapToObj(String::valueOf);
 204             else if (type == long[].class) {
 205                 stringStream = LongStream.of((long[]) value)
 206                     .mapToObj(AnnotationInvocationHandler::toSourceString);
 207             } else if (type == short[].class)
 208                 stringStream = convert((short[]) value);
 209             else if (type == boolean[].class)
 210                 stringStream = convert((boolean[]) value);
 211             else if (type == Class[].class)
 212                 stringStream =
 213                     Arrays.stream((Class<?>[]) value).
 214                     map(AnnotationInvocationHandler::toSourceString);
 215             else if (type == String[].class)
 216                 stringStream =
 217                     Arrays.stream((String[])value).
 218                     map(AnnotationInvocationHandler::toSourceString);
 219             else
 220                 stringStream = Arrays.stream((Object[])value).map(Objects::toString);
 221 
 222             return stringStreamToString(stringStream);
 223         }
 224     }
 225 
 226     /**
 227      * Translates a Class value to a form suitable for use in the
 228      * string representation of an annotation.
 229      */
 230     private static String toSourceString(Class<?> clazz) {
 231         Class<?> finalComponent = clazz;
 232         StringBuilder arrayBackets = new StringBuilder();
 233 
 234         while(finalComponent.isArray()) {
 235             finalComponent = finalComponent.getComponentType();
 236             arrayBackets.append("[]");
 237         }
 238 
 239         return finalComponent.getName() + arrayBackets.toString() + ".class";
 240     }
 241 
 242     private static String toSourceString(float f) {
 243         if (Float.isFinite(f))
 244             return Float.toString(f) + "f" ;
 245         else {
 246             if (Float.isInfinite(f)) {
 247                 return (f < 0.0f) ? "-1.0f/0.0f": "1.0f/0.0f";
 248             } else
 249                 return "0.0f/0.0f";
 250         }
 251     }
 252 
 253     private static String toSourceString(double d) {
 254         if (Double.isFinite(d))
 255             return Double.toString(d);
 256         else {
 257             if (Double.isInfinite(d)) {
 258                 return (d < 0.0f) ? "-1.0/0.0": "1.0/0.0";
 259             } else
 260                 return "0.0/0.0";
 261         }
 262     }
 263 
 264     private static String toSourceString(char c) {
 265         StringBuilder sb = new StringBuilder(4);
 266         sb.append('\'');
 267         sb.append(quote(c));
 268         return sb.append('\'') .toString();
 269     }
 270 
 271     /**
 272      * Escapes a character if it has an escape sequence or is
 273      * non-printable ASCII.  Leaves non-ASCII characters alone.
 274      */
 275     private static String quote(char ch) {
 276         switch (ch) {
 277         case '\b':  return "\\b";
 278         case '\f':  return "\\f";
 279         case '\n':  return "\\n";
 280         case '\r':  return "\\r";
 281         case '\t':  return "\\t";
 282         case '\'':  return "\\'";
 283         case '\"':  return "\\\"";
 284         case '\\':  return "\\\\";
 285         default:
 286             return (isPrintableAscii(ch))
 287                 ? String.valueOf(ch)
 288                 : String.format("\\u%04x", (int) ch);
 289         }
 290     }
 291 
 292     /**
 293      * Is a character printable ASCII?
 294      */
 295     private static boolean isPrintableAscii(char ch) {
 296         return ch >= ' ' && ch <= '~';
 297     }
 298 
 299     private static String toSourceString(byte b) {
 300         return String.format("(byte)0x%02x", b);
 301     }
 302 
 303     private static String toSourceString(long ell) {
 304         return String.valueOf(ell) + "L";
 305     }
 306 
 307     /**
 308      * Return a string suitable for use in the string representation
 309      * of an annotation.
 310      */
 311     private static String toSourceString(String s) {
 312         StringBuilder sb = new StringBuilder();
 313         sb.append('"');
 314         for (int i = 0; i < s.length(); i++) {
 315             sb.append(quote(s.charAt(i)));
 316         }
 317         sb.append('"');
 318         return sb.toString();
 319     }
 320 
 321     private static Stream<String> convert(byte[] values) {
 322         List<String> list = new ArrayList<>(values.length);
 323         for (byte b : values)
 324             list.add(toSourceString(b));
 325         return list.stream();
 326     }
 327 
 328     private static Stream<String> convert(char[] values) {
 329         List<String> list = new ArrayList<>(values.length);
 330         for (char c : values)
 331             list.add(toSourceString(c));
 332         return list.stream();
 333     }
 334 
 335     private static Stream<String> convert(float[] values) {
 336         List<String> list = new ArrayList<>(values.length);
 337         for (float f : values) {
 338             list.add(toSourceString(f));
 339         }
 340         return list.stream();
 341     }
 342 
 343     private static Stream<String> convert(short[] values) {
 344         List<String> list = new ArrayList<>(values.length);
 345         for (short s : values)
 346             list.add(Short.toString(s));
 347         return list.stream();
 348     }
 349 
 350     private static Stream<String> convert(boolean[] values) {
 351         List<String> list = new ArrayList<>(values.length);
 352         for (boolean b : values)
 353             list.add(Boolean.toString(b));
 354         return list.stream();
 355     }
 356 
 357     private static String stringStreamToString(Stream<String> stream) {
 358         return stream.collect(Collectors.joining(", ", "{", "}"));
 359     }
 360 
 361     /**
 362      * Implementation of dynamicProxy.equals(Object o)
 363      */
 364     private Boolean equalsImpl(Object proxy, Object o) {
 365         if (o == proxy)
 366             return true;
 367 
 368         if (!type.isInstance(o))
 369             return false;
 370         for (Method memberMethod : getMemberMethods()) {
 371             String member = memberMethod.getName();
 372             Object ourValue = memberValues.get(member);
 373             Object hisValue = null;
 374             AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 375             if (hisHandler != null) {
 376                 hisValue = hisHandler.memberValues.get(member);
 377             } else {
 378                 try {
 379                     hisValue = memberMethod.invoke(o);
 380                 } catch (InvocationTargetException e) {
 381                     return false;
 382                 } catch (IllegalAccessException e) {
 383                     throw new AssertionError(e);
 384                 }
 385             }
 386             if (!memberValueEquals(ourValue, hisValue))
 387                 return false;
 388         }
 389         return true;
 390     }
 391 
 392     /**
 393      * Returns an object's invocation handler if that object is a dynamic
 394      * proxy with a handler of type AnnotationInvocationHandler.
 395      * Returns null otherwise.
 396      */
 397     private AnnotationInvocationHandler asOneOfUs(Object o) {
 398         if (Proxy.isProxyClass(o.getClass())) {
 399             InvocationHandler handler = Proxy.getInvocationHandler(o);
 400             if (handler instanceof AnnotationInvocationHandler)
 401                 return (AnnotationInvocationHandler) handler;
 402         }
 403         return null;
 404     }
 405 
 406     /**
 407      * Returns true iff the two member values in "dynamic proxy return form"
 408      * are equal using the appropriate equality function depending on the
 409      * member type.  The two values will be of the same type unless one of
 410      * the containing annotations is ill-formed.  If one of the containing
 411      * annotations is ill-formed, this method will return false unless the
 412      * two members are identical object references.
 413      */
 414     private static boolean memberValueEquals(Object v1, Object v2) {
 415         Class<?> type = v1.getClass();
 416 
 417         // Check for primitive, string, class, enum const, annotation,
 418         // or ExceptionProxy
 419         if (!type.isArray())
 420             return v1.equals(v2);
 421 
 422         // Check for array of string, class, enum const, annotation,
 423         // or ExceptionProxy
 424         if (v1 instanceof Object[] && v2 instanceof Object[])
 425             return Arrays.equals((Object[]) v1, (Object[]) v2);
 426 
 427         // Check for ill formed annotation(s)
 428         if (v2.getClass() != type)
 429             return false;
 430 
 431         // Deal with array of primitives
 432         if (type == byte[].class)
 433             return Arrays.equals((byte[]) v1, (byte[]) v2);
 434         if (type == char[].class)
 435             return Arrays.equals((char[]) v1, (char[]) v2);
 436         if (type == double[].class)
 437             return Arrays.equals((double[]) v1, (double[]) v2);
 438         if (type == float[].class)
 439             return Arrays.equals((float[]) v1, (float[]) v2);
 440         if (type == int[].class)
 441             return Arrays.equals((int[]) v1, (int[]) v2);
 442         if (type == long[].class)
 443             return Arrays.equals((long[]) v1, (long[]) v2);
 444         if (type == short[].class)
 445             return Arrays.equals((short[]) v1, (short[]) v2);
 446         assert type == boolean[].class;
 447         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 448     }
 449 
 450     /**
 451      * Returns the member methods for our annotation type.  These are
 452      * obtained lazily and cached, as they're expensive to obtain
 453      * and we only need them if our equals method is invoked (which should
 454      * be rare).
 455      */
 456     private Method[] getMemberMethods() {
 457         Method[] value = memberMethods;
 458         if (value == null) {
 459             value = computeMemberMethods();
 460             memberMethods = value;
 461         }
 462         return value;
 463     }
 464 
 465     private Method[] computeMemberMethods() {
 466         return AccessController.doPrivileged(
 467             new PrivilegedAction<Method[]>() {
 468                 public Method[] run() {
 469                     final Method[] methods = type.getDeclaredMethods();
 470                     validateAnnotationMethods(methods);
 471                     AccessibleObject.setAccessible(methods, true);
 472                     return methods;
 473                 }});
 474     }
 475 
 476     private transient volatile Method[] memberMethods;
 477 
 478     /**
 479      * Validates that a method is structurally appropriate for an
 480      * annotation type. As of Java SE 8, annotation types cannot
 481      * contain static methods and the declared methods of an
 482      * annotation type must take zero arguments and there are
 483      * restrictions on the return type.
 484      */
 485     private void validateAnnotationMethods(Method[] memberMethods) {
 486         /*
 487          * Specification citations below are from JLS
 488          * 9.6.1. Annotation Type Elements
 489          */
 490         boolean valid = true;
 491         for(Method method : memberMethods) {
 492             /*
 493              * "By virtue of the AnnotationTypeElementDeclaration
 494              * production, a method declaration in an annotation type
 495              * declaration cannot have formal parameters, type
 496              * parameters, or a throws clause.
 497              *
 498              * "By virtue of the AnnotationTypeElementModifier
 499              * production, a method declaration in an annotation type
 500              * declaration cannot be default or static."
 501              */
 502             if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) ||
 503                 method.isDefault() ||
 504                 method.getParameterCount() != 0 ||
 505                 method.getExceptionTypes().length != 0) {
 506                 valid = false;
 507                 break;
 508             }
 509 
 510             /*
 511              * "It is a compile-time error if the return type of a
 512              * method declared in an annotation type is not one of the
 513              * following: a primitive type, String, Class, any
 514              * parameterized invocation of Class, an enum type
 515              * (section 8.9), an annotation type, or an array type
 516              * (chapter 10) whose element type is one of the preceding
 517              * types."
 518              */
 519             Class<?> returnType = method.getReturnType();
 520             if (returnType.isArray()) {
 521                 returnType = returnType.getComponentType();
 522                 if (returnType.isArray()) { // Only single dimensional arrays
 523                     valid = false;
 524                     break;
 525                 }
 526             }
 527 
 528             if (!((returnType.isPrimitive() && returnType != void.class) ||
 529                   returnType == java.lang.String.class ||
 530                   returnType == java.lang.Class.class ||
 531                   returnType.isEnum() ||
 532                   returnType.isAnnotation())) {
 533                 valid = false;
 534                 break;
 535             }
 536 
 537             /*
 538              * "It is a compile-time error if any method declared in an
 539              * annotation type has a signature that is
 540              * override-equivalent to that of any public or protected
 541              * method declared in class Object or in the interface
 542              * java.lang.annotation.Annotation."
 543              *
 544              * The methods in Object or Annotation meeting the other
 545              * criteria (no arguments, contrained return type, etc.)
 546              * above are:
 547              *
 548              * String toString()
 549              * int hashCode()
 550              * Class<? extends Annotation> annotationType()
 551              */
 552             String methodName = method.getName();
 553             if ((methodName.equals("toString") && returnType == java.lang.String.class) ||
 554                 (methodName.equals("hashCode") && returnType == int.class) ||
 555                 (methodName.equals("annotationType") && returnType == java.lang.Class.class)) {
 556                 valid = false;
 557                 break;
 558             }
 559         }
 560         if (valid)
 561             return;
 562         else
 563             throw new AnnotationFormatError("Malformed method on an annotation type");
 564     }
 565 
 566     /**
 567      * Implementation of dynamicProxy.hashCode()
 568      */
 569     private int hashCodeImpl() {
 570         int result = 0;
 571         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 572             result += (127 * e.getKey().hashCode()) ^
 573                 memberValueHashCode(e.getValue());
 574         }
 575         return result;
 576     }
 577 
 578     /**
 579      * Computes hashCode of a member value (in "dynamic proxy return form")
 580      */
 581     private static int memberValueHashCode(Object value) {
 582         Class<?> type = value.getClass();
 583         if (!type.isArray())    // primitive, string, class, enum const,
 584                                 // or annotation
 585             return value.hashCode();
 586 
 587         if (type == byte[].class)
 588             return Arrays.hashCode((byte[]) value);
 589         if (type == char[].class)
 590             return Arrays.hashCode((char[]) value);
 591         if (type == double[].class)
 592             return Arrays.hashCode((double[]) value);
 593         if (type == float[].class)
 594             return Arrays.hashCode((float[]) value);
 595         if (type == int[].class)
 596             return Arrays.hashCode((int[]) value);
 597         if (type == long[].class)
 598             return Arrays.hashCode((long[]) value);
 599         if (type == short[].class)
 600             return Arrays.hashCode((short[]) value);
 601         if (type == boolean[].class)
 602             return Arrays.hashCode((boolean[]) value);
 603         return Arrays.hashCode((Object[]) value);
 604     }
 605 
 606     private void readObject(java.io.ObjectInputStream s)
 607         throws java.io.IOException, ClassNotFoundException {
 608         ObjectInputStream.GetField fields = s.readFields();
 609 
 610         @SuppressWarnings("unchecked")
 611         Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
 612         @SuppressWarnings("unchecked")
 613         Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null);
 614 
 615         // Check to make sure that types have not evolved incompatibly
 616 
 617         AnnotationType annotationType = null;
 618         try {
 619             annotationType = AnnotationType.getInstance(t);
 620         } catch(IllegalArgumentException e) {
 621             // Class is no longer an annotation type; time to punch out
 622             throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
 623         }
 624 
 625         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 626         // consistent with runtime Map type
 627         Map<String, Object> mv = new LinkedHashMap<>();
 628 
 629         // If there are annotation members without values, that
 630         // situation is handled by the invoke method.
 631         for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) {
 632             String name = memberValue.getKey();
 633             Object value = null;
 634             Class<?> memberType = memberTypes.get(name);
 635             if (memberType != null) {  // i.e. member still exists
 636                 value = memberValue.getValue();
 637                 if (!(memberType.isInstance(value) ||
 638                       value instanceof ExceptionProxy)) {
 639                     value = new AnnotationTypeMismatchExceptionProxy(
 640                             value.getClass() + "[" + value + "]").setMember(
 641                                 annotationType.members().get(name));
 642                 }
 643             }
 644             mv.put(name, value);
 645         }
 646 
 647         UnsafeAccessor.setType(this, t);
 648         UnsafeAccessor.setMemberValues(this, mv);
 649     }
 650 
 651     private static class UnsafeAccessor {
 652         private static final jdk.internal.misc.Unsafe unsafe
 653                 = jdk.internal.misc.Unsafe.getUnsafe();
 654         private static final long typeOffset = unsafe.objectFieldOffset
 655                 (AnnotationInvocationHandler.class, "type");
 656         private static final long memberValuesOffset = unsafe.objectFieldOffset
 657                 (AnnotationInvocationHandler.class, "memberValues");
 658 
 659         static void setType(AnnotationInvocationHandler o,
 660                             Class<? extends Annotation> type) {
 661             unsafe.putReference(o, typeOffset, type);
 662         }
 663 
 664         static void setMemberValues(AnnotationInvocationHandler o,
 665                                     Map<String, Object> memberValues) {
 666             unsafe.putReference(o, memberValuesOffset, memberValues);
 667         }
 668     }
 669 }