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