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 }