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.*;
  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(proxy, 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()) {
 167             // primitive value, string, class, enum const, or annotation
 168             if (type == Class.class)
 169                 return toSourceString((Class<?>) value);
 170             else if (type == String.class)
 171                 return  toSourceString((String) value);
 172             if (type == Character.class)
 173                 return toSourceString((char) value);
 174             else if (type == double.class)
 175                 return  toSourceString((double) value);
 176             else if (type == float.class)
 177                 return  toSourceString((float) value);
 178             else if (type == long.class)
 179                 return  toSourceString((long) value);
 180             else
 181                 return value.toString();
 182         } else {
 183             Stream<String> stringStream;
 184             if (type == byte[].class)
 185                 stringStream = convert((byte[]) value);
 186             else if (type == char[].class)
 187                 stringStream = convert((char[]) value);
 188             else if (type == double[].class)
 189                 stringStream = DoubleStream.of((double[]) value)
 190                     .mapToObj(AnnotationInvocationHandler::toSourceString);
 191             else if (type == float[].class)
 192                 stringStream = convert((float[]) value);
 193             else if (type == int[].class)
 194                 stringStream = IntStream.of((int[]) value).mapToObj(String::valueOf);
 195             else if (type == long[].class) {
 196                 stringStream = LongStream.of((long[]) value)
 197                     .mapToObj(AnnotationInvocationHandler::toSourceString);
 198             } else if (type == short[].class)
 199                 stringStream = convert((short[]) value);
 200             else if (type == boolean[].class)
 201                 stringStream = convert((boolean[]) value);
 202             else if (type == Class[].class)
 203                 stringStream =
 204                     Arrays.stream((Class<?>[]) value).
 205                     map(AnnotationInvocationHandler::toSourceString);
 206             else if (type == String[].class)
 207                 stringStream =
 208                     Arrays.stream((String[])value).
 209                     map(AnnotationInvocationHandler::toSourceString);
 210             else
 211                 stringStream = Arrays.stream((Object[])value).map(Objects::toString);
 212 
 213             return stringStreamToString(stringStream);
 214         }
 215     }
 216 
 217     /**
 218      * Translates a Class value to a form suitable for use in the
 219      * string representation of an annotation.
 220      */
 221     private static String toSourceString(Class<?> clazz) {
 222         Class<?> finalComponent = clazz;
 223         StringBuilder arrayBackets = new StringBuilder();
 224 
 225         while(finalComponent.isArray()) {
 226             finalComponent = finalComponent.getComponentType();
 227             arrayBackets.append("[]");
 228         }
 229 
 230         return finalComponent.getName() + arrayBackets.toString() + ".class" ;
 231     }
 232 
 233     private static String toSourceString(float f) {
 234         if (Float.isFinite(f))
 235             return Float.toString(f) + "f" ;
 236         else {
 237             if (Float.isInfinite(f)) {
 238                 return (f < 0.0f) ? "-1.0f/0.0f": "1.0f/0.0f";
 239             } else
 240                 return "0.0f/0.0f";
 241         }
 242     }
 243 
 244     private static String toSourceString(double d) {
 245         if (Double.isFinite(d))
 246             return Double.toString(d);
 247         else {
 248             if (Double.isInfinite(d)) {
 249                 return (d < 0.0f) ? "-1.0/0.0": "1.0/0.0";
 250             } else
 251                 return "0.0/0.0";
 252         }
 253     }
 254 
 255     private static String toSourceString(char c) {
 256         StringBuilder sb = new StringBuilder();
 257         sb.append("'");
 258         if (c == '\'')
 259             sb.append("\\'");
 260         else
 261             sb.append(c);
 262         sb.append("'");
 263         return sb.toString();
 264     }
 265 
 266     private static String toSourceString(long ell) {
 267         return (Math.abs(ell) <= Integer.MAX_VALUE) ?
 268             String.valueOf(ell) :
 269             (String.valueOf(ell) + "L");
 270     }
 271 
 272     /**
 273      * Return a string suitable for use in the string representation
 274      * of an annotation.
 275      */
 276     private static String toSourceString(String s) {
 277         StringBuilder sb = new StringBuilder();
 278         sb.append('"');
 279         // Escape embedded quote characters, if present, but don't do
 280         // anything more heroic.
 281         if (s.indexOf('"') != -1) {
 282             s = s.replace("\"", "\\\"");
 283         }
 284         sb.append(s);
 285         sb.append('"');
 286         return sb.toString();
 287     }
 288 
 289     private static Stream<String> convert(byte[] values) {
 290         List<String> list = new ArrayList<>(values.length);
 291         for (byte b : values)
 292             list.add(Byte.toString(b));
 293         return list.stream();
 294     }
 295 
 296     private static Stream<String> convert(char[] values) {
 297         List<String> list = new ArrayList<>(values.length);
 298         for (char c : values)
 299             list.add(toSourceString(c));
 300         return list.stream();
 301     }
 302 
 303     private static Stream<String> convert(float[] values) {
 304         List<String> list = new ArrayList<>(values.length);
 305         for (float f : values) {
 306             list.add(toSourceString(f));
 307         }
 308         return list.stream();
 309     }
 310 
 311     private static Stream<String> convert(short[] values) {
 312         List<String> list = new ArrayList<>(values.length);
 313         for (short s : values)
 314             list.add(Short.toString(s));
 315         return list.stream();
 316     }
 317 
 318     private static Stream<String> convert(boolean[] values) {
 319         List<String> list = new ArrayList<>(values.length);
 320         for (boolean b : values)
 321             list.add(Boolean.toString(b));
 322         return list.stream();
 323     }
 324 
 325     private static String stringStreamToString(Stream<String> stream) {
 326         return stream.collect(Collectors.joining(", ", "{", "}"));
 327     }
 328 
 329     /**
 330      * Implementation of dynamicProxy.equals(Object o)
 331      */
 332     private Boolean equalsImpl(Object proxy, Object o) {
 333         if (o == proxy)
 334             return true;
 335 
 336         if (!type.isInstance(o))
 337             return false;
 338         for (Method memberMethod : getMemberMethods()) {
 339             String member = memberMethod.getName();
 340             Object ourValue = memberValues.get(member);
 341             Object hisValue = null;
 342             AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 343             if (hisHandler != null) {
 344                 hisValue = hisHandler.memberValues.get(member);
 345             } else {
 346                 try {
 347                     hisValue = memberMethod.invoke(o);
 348                 } catch (InvocationTargetException e) {
 349                     return false;
 350                 } catch (IllegalAccessException e) {
 351                     throw new AssertionError(e);
 352                 }
 353             }
 354             if (!memberValueEquals(ourValue, hisValue))
 355                 return false;
 356         }
 357         return true;
 358     }
 359 
 360     /**
 361      * Returns an object's invocation handler if that object is a dynamic
 362      * proxy with a handler of type AnnotationInvocationHandler.
 363      * Returns null otherwise.
 364      */
 365     private AnnotationInvocationHandler asOneOfUs(Object o) {
 366         if (Proxy.isProxyClass(o.getClass())) {
 367             InvocationHandler handler = Proxy.getInvocationHandler(o);
 368             if (handler instanceof AnnotationInvocationHandler)
 369                 return (AnnotationInvocationHandler) handler;
 370         }
 371         return null;
 372     }
 373 
 374     /**
 375      * Returns true iff the two member values in "dynamic proxy return form"
 376      * are equal using the appropriate equality function depending on the
 377      * member type.  The two values will be of the same type unless one of
 378      * the containing annotations is ill-formed.  If one of the containing
 379      * annotations is ill-formed, this method will return false unless the
 380      * two members are identical object references.
 381      */
 382     private static boolean memberValueEquals(Object v1, Object v2) {
 383         Class<?> type = v1.getClass();
 384 
 385         // Check for primitive, string, class, enum const, annotation,
 386         // or ExceptionProxy
 387         if (!type.isArray())
 388             return v1.equals(v2);
 389 
 390         // Check for array of string, class, enum const, annotation,
 391         // or ExceptionProxy
 392         if (v1 instanceof Object[] && v2 instanceof Object[])
 393             return Arrays.equals((Object[]) v1, (Object[]) v2);
 394 
 395         // Check for ill formed annotation(s)
 396         if (v2.getClass() != type)
 397             return false;
 398 
 399         // Deal with array of primitives
 400         if (type == byte[].class)
 401             return Arrays.equals((byte[]) v1, (byte[]) v2);
 402         if (type == char[].class)
 403             return Arrays.equals((char[]) v1, (char[]) v2);
 404         if (type == double[].class)
 405             return Arrays.equals((double[]) v1, (double[]) v2);
 406         if (type == float[].class)
 407             return Arrays.equals((float[]) v1, (float[]) v2);
 408         if (type == int[].class)
 409             return Arrays.equals((int[]) v1, (int[]) v2);
 410         if (type == long[].class)
 411             return Arrays.equals((long[]) v1, (long[]) v2);
 412         if (type == short[].class)
 413             return Arrays.equals((short[]) v1, (short[]) v2);
 414         assert type == boolean[].class;
 415         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 416     }
 417 
 418     /**
 419      * Returns the member methods for our annotation type.  These are
 420      * obtained lazily and cached, as they're expensive to obtain
 421      * and we only need them if our equals method is invoked (which should
 422      * be rare).
 423      */
 424     private Method[] getMemberMethods() {
 425         Method[] value = memberMethods;
 426         if (value == null) {
 427             value = computeMemberMethods();
 428             memberMethods = value;
 429         }
 430         return value;
 431     }
 432 
 433     private Method[] computeMemberMethods() {
 434         return AccessController.doPrivileged(
 435             new PrivilegedAction<Method[]>() {
 436                 public Method[] run() {
 437                     final Method[] methods = type.getDeclaredMethods();
 438                     validateAnnotationMethods(methods);
 439                     AccessibleObject.setAccessible(methods, true);
 440                     return methods;
 441                 }});
 442     }
 443 
 444     private transient volatile Method[] memberMethods;
 445 
 446     /**
 447      * Validates that a method is structurally appropriate for an
 448      * annotation type. As of Java SE 8, annotation types cannot
 449      * contain static methods and the declared methods of an
 450      * annotation type must take zero arguments and there are
 451      * restrictions on the return type.
 452      */
 453     private void validateAnnotationMethods(Method[] memberMethods) {
 454         /*
 455          * Specification citations below are from JLS
 456          * 9.6.1. Annotation Type Elements
 457          */
 458         boolean valid = true;
 459         for(Method method : memberMethods) {
 460             /*
 461              * "By virtue of the AnnotationTypeElementDeclaration
 462              * production, a method declaration in an annotation type
 463              * declaration cannot have formal parameters, type
 464              * parameters, or a throws clause.
 465              *
 466              * "By virtue of the AnnotationTypeElementModifier
 467              * production, a method declaration in an annotation type
 468              * declaration cannot be default or static."
 469              */
 470             if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) ||
 471                 method.isDefault() ||
 472                 method.getParameterCount() != 0 ||
 473                 method.getExceptionTypes().length != 0) {
 474                 valid = false;
 475                 break;
 476             }
 477 
 478             /*
 479              * "It is a compile-time error if the return type of a
 480              * method declared in an annotation type is not one of the
 481              * following: a primitive type, String, Class, any
 482              * parameterized invocation of Class, an enum type
 483              * (section 8.9), an annotation type, or an array type
 484              * (chapter 10) whose element type is one of the preceding
 485              * types."
 486              */
 487             Class<?> returnType = method.getReturnType();
 488             if (returnType.isArray()) {
 489                 returnType = returnType.getComponentType();
 490                 if (returnType.isArray()) { // Only single dimensional arrays
 491                     valid = false;
 492                     break;
 493                 }
 494             }
 495 
 496             if (!((returnType.isPrimitive() && returnType != void.class) ||
 497                   returnType == java.lang.String.class ||
 498                   returnType == java.lang.Class.class ||
 499                   returnType.isEnum() ||
 500                   returnType.isAnnotation())) {
 501                 valid = false;
 502                 break;
 503             }
 504 
 505             /*
 506              * "It is a compile-time error if any method declared in an
 507              * annotation type has a signature that is
 508              * override-equivalent to that of any public or protected
 509              * method declared in class Object or in the interface
 510              * java.lang.annotation.Annotation."
 511              *
 512              * The methods in Object or Annotation meeting the other
 513              * criteria (no arguments, contrained return type, etc.)
 514              * above are:
 515              *
 516              * String toString()
 517              * int hashCode()
 518              * Class<? extends Annotation> annotationType()
 519              */
 520             String methodName = method.getName();
 521             if ((methodName.equals("toString") && returnType == java.lang.String.class) ||
 522                 (methodName.equals("hashCode") && returnType == int.class) ||
 523                 (methodName.equals("annotationType") && returnType == java.lang.Class.class)) {
 524                 valid = false;
 525                 break;
 526             }
 527         }
 528         if (valid)
 529             return;
 530         else
 531             throw new AnnotationFormatError("Malformed method on an annotation type");
 532     }
 533 
 534     /**
 535      * Implementation of dynamicProxy.hashCode()
 536      */
 537     private int hashCodeImpl() {
 538         int result = 0;
 539         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 540             result += (127 * e.getKey().hashCode()) ^
 541                 memberValueHashCode(e.getValue());
 542         }
 543         return result;
 544     }
 545 
 546     /**
 547      * Computes hashCode of a member value (in "dynamic proxy return form")
 548      */
 549     private static int memberValueHashCode(Object value) {
 550         Class<?> type = value.getClass();
 551         if (!type.isArray())    // primitive, string, class, enum const,
 552                                 // or annotation
 553             return value.hashCode();
 554 
 555         if (type == byte[].class)
 556             return Arrays.hashCode((byte[]) value);
 557         if (type == char[].class)
 558             return Arrays.hashCode((char[]) value);
 559         if (type == double[].class)
 560             return Arrays.hashCode((double[]) value);
 561         if (type == float[].class)
 562             return Arrays.hashCode((float[]) value);
 563         if (type == int[].class)
 564             return Arrays.hashCode((int[]) value);
 565         if (type == long[].class)
 566             return Arrays.hashCode((long[]) value);
 567         if (type == short[].class)
 568             return Arrays.hashCode((short[]) value);
 569         if (type == boolean[].class)
 570             return Arrays.hashCode((boolean[]) value);
 571         return Arrays.hashCode((Object[]) value);
 572     }
 573 
 574     private void readObject(java.io.ObjectInputStream s)
 575         throws java.io.IOException, ClassNotFoundException {
 576         ObjectInputStream.GetField fields = s.readFields();
 577 
 578         @SuppressWarnings("unchecked")
 579         Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
 580         @SuppressWarnings("unchecked")
 581         Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null);
 582 
 583         // Check to make sure that types have not evolved incompatibly
 584 
 585         AnnotationType annotationType = null;
 586         try {
 587             annotationType = AnnotationType.getInstance(t);
 588         } catch(IllegalArgumentException e) {
 589             // Class is no longer an annotation type; time to punch out
 590             throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
 591         }
 592 
 593         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 594         // consistent with runtime Map type
 595         Map<String, Object> mv = new LinkedHashMap<>();
 596 
 597         // If there are annotation members without values, that
 598         // situation is handled by the invoke method.
 599         for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) {
 600             String name = memberValue.getKey();
 601             Object value = null;
 602             Class<?> memberType = memberTypes.get(name);
 603             if (memberType != null) {  // i.e. member still exists
 604                 value = memberValue.getValue();
 605                 if (!(memberType.isInstance(value) ||
 606                       value instanceof ExceptionProxy)) {
 607                     value = new AnnotationTypeMismatchExceptionProxy(
 608                             value.getClass() + "[" + value + "]").setMember(
 609                                 annotationType.members().get(name));
 610                 }
 611             }
 612             mv.put(name, value);
 613         }
 614 
 615         UnsafeAccessor.setType(this, t);
 616         UnsafeAccessor.setMemberValues(this, mv);
 617     }
 618 
 619     private static class UnsafeAccessor {
 620         private static final jdk.internal.misc.Unsafe unsafe;
 621         private static final long typeOffset;
 622         private static final long memberValuesOffset;
 623         static {
 624             try {
 625                 unsafe = jdk.internal.misc.Unsafe.getUnsafe();
 626                 typeOffset = unsafe.objectFieldOffset
 627                         (AnnotationInvocationHandler.class.getDeclaredField("type"));
 628                 memberValuesOffset = unsafe.objectFieldOffset
 629                         (AnnotationInvocationHandler.class.getDeclaredField("memberValues"));
 630             } catch (Exception ex) {
 631                 throw new ExceptionInInitializerError(ex);
 632             }
 633         }
 634         static void setType(AnnotationInvocationHandler o,
 635                             Class<? extends Annotation> type) {
 636             unsafe.putObject(o, typeOffset, type);
 637         }
 638 
 639         static void setMemberValues(AnnotationInvocationHandler o,
 640                                     Map<String, Object> memberValues) {
 641             unsafe.putObject(o, memberValuesOffset, memberValues);
 642         }
 643     }
 644 }