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 }