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