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