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