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