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.Collectors; 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(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()) { // primitive, string, class, enum const, 167 // or annotation 168 if (type == Class.class) 169 return classValueToString((Class<?>) value); 170 else 171 return value.toString(); 172 } 173 174 if (type == byte[].class) 175 return Arrays.toString((byte[]) value); 176 if (type == char[].class) 177 return Arrays.toString((char[]) value); 178 if (type == double[].class) 179 return Arrays.toString((double[]) value); 180 if (type == float[].class) 181 return Arrays.toString((float[]) value); 182 if (type == int[].class) 183 return Arrays.toString((int[]) value); 184 if (type == long[].class) 185 return Arrays.toString((long[]) value); 186 if (type == short[].class) 187 return Arrays.toString((short[]) value); 188 if (type == boolean[].class) 189 return Arrays.toString((boolean[]) value); 190 if (type == Class[].class) 191 return classArrayValueToString((Class<?>[])value); 192 return Arrays.toString((Object[]) value); 193 } 194 195 /** 196 * Translates a Class value to a form suitable for use in the 197 * string representation of an annotation. 198 */ 199 private static String classValueToString(Class<?> clazz) { 200 return clazz.getName() + ".class" ; 201 } 202 203 private static String classArrayValueToString(Class<?>[] classes) { 204 return Arrays.stream(classes) 205 .map(AnnotationInvocationHandler::classValueToString) 206 .collect(Collectors.joining(", ", "{", "}")); 207 } 208 209 /** 210 * Implementation of dynamicProxy.equals(Object o) 211 */ 212 private Boolean equalsImpl(Object o) { 213 if (o == this) 214 return true; 215 216 if (!type.isInstance(o)) 217 return false; 218 for (Method memberMethod : getMemberMethods()) { 219 String member = memberMethod.getName(); 220 Object ourValue = memberValues.get(member); 221 Object hisValue = null; 222 AnnotationInvocationHandler hisHandler = asOneOfUs(o); 223 if (hisHandler != null) { 224 hisValue = hisHandler.memberValues.get(member); 225 } else { 226 try { 227 hisValue = memberMethod.invoke(o); 228 } catch (InvocationTargetException e) { 229 return false; 230 } catch (IllegalAccessException e) { 231 throw new AssertionError(e); 232 } 233 } 234 if (!memberValueEquals(ourValue, hisValue)) 235 return false; 236 } 237 return true; 238 } 239 240 /** 241 * Returns an object's invocation handler if that object is a dynamic 242 * proxy with a handler of type AnnotationInvocationHandler. 243 * Returns null otherwise. 244 */ 245 private AnnotationInvocationHandler asOneOfUs(Object o) { 246 if (Proxy.isProxyClass(o.getClass())) { 247 InvocationHandler handler = Proxy.getInvocationHandler(o); 248 if (handler instanceof AnnotationInvocationHandler) 249 return (AnnotationInvocationHandler) handler; 250 } 251 return null; 252 } 253 254 /** 255 * Returns true iff the two member values in "dynamic proxy return form" 256 * are equal using the appropriate equality function depending on the 257 * member type. The two values will be of the same type unless one of 258 * the containing annotations is ill-formed. If one of the containing 259 * annotations is ill-formed, this method will return false unless the 260 * two members are identical object references. 261 */ 262 private static boolean memberValueEquals(Object v1, Object v2) { 263 Class<?> type = v1.getClass(); 264 265 // Check for primitive, string, class, enum const, annotation, 266 // or ExceptionProxy 267 if (!type.isArray()) 268 return v1.equals(v2); 269 270 // Check for array of string, class, enum const, annotation, 271 // or ExceptionProxy 272 if (v1 instanceof Object[] && v2 instanceof Object[]) 273 return Arrays.equals((Object[]) v1, (Object[]) v2); 274 275 // Check for ill formed annotation(s) 276 if (v2.getClass() != type) 277 return false; 278 279 // Deal with array of primitives 280 if (type == byte[].class) 281 return Arrays.equals((byte[]) v1, (byte[]) v2); 282 if (type == char[].class) 283 return Arrays.equals((char[]) v1, (char[]) v2); 284 if (type == double[].class) 285 return Arrays.equals((double[]) v1, (double[]) v2); 286 if (type == float[].class) 287 return Arrays.equals((float[]) v1, (float[]) v2); 288 if (type == int[].class) 289 return Arrays.equals((int[]) v1, (int[]) v2); 290 if (type == long[].class) 291 return Arrays.equals((long[]) v1, (long[]) v2); 292 if (type == short[].class) 293 return Arrays.equals((short[]) v1, (short[]) v2); 294 assert type == boolean[].class; 295 return Arrays.equals((boolean[]) v1, (boolean[]) v2); 296 } 297 298 /** 299 * Returns the member methods for our annotation type. These are 300 * obtained lazily and cached, as they're expensive to obtain 301 * and we only need them if our equals method is invoked (which should 302 * be rare). 303 */ 304 private Method[] getMemberMethods() { 305 Method[] value = memberMethods; 306 if (value == null) { 307 value = computeMemberMethods(); 308 memberMethods = value; 309 } 310 return value; 311 } 312 313 private Method[] computeMemberMethods() { 314 return AccessController.doPrivileged( 315 new PrivilegedAction<Method[]>() { 316 public Method[] run() { 317 final Method[] methods = type.getDeclaredMethods(); 318 validateAnnotationMethods(methods); 319 AccessibleObject.setAccessible(methods, true); 320 return methods; 321 }}); 322 } 323 324 private transient volatile Method[] memberMethods; 325 326 /** 327 * Validates that a method is structurally appropriate for an 328 * annotation type. As of Java SE 8, annotation types cannot 329 * contain static methods and the declared methods of an 330 * annotation type must take zero arguments and there are 331 * restrictions on the return type. 332 */ 333 private void validateAnnotationMethods(Method[] memberMethods) { 334 /* 335 * Specification citations below are from JLS 336 * 9.6.1. Annotation Type Elements 337 */ 338 boolean valid = true; 339 for(Method method : memberMethods) { 340 /* 341 * "By virtue of the AnnotationTypeElementDeclaration 342 * production, a method declaration in an annotation type 343 * declaration cannot have formal parameters, type 344 * parameters, or a throws clause. 345 * 346 * "By virtue of the AnnotationTypeElementModifier 347 * production, a method declaration in an annotation type 348 * declaration cannot be default or static." 349 */ 350 if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) || 351 method.isDefault() || 352 method.getParameterCount() != 0 || 353 method.getExceptionTypes().length != 0) { 354 valid = false; 355 break; 356 } 357 358 /* 359 * "It is a compile-time error if the return type of a 360 * method declared in an annotation type is not one of the 361 * following: a primitive type, String, Class, any 362 * parameterized invocation of Class, an enum type 363 * (section 8.9), an annotation type, or an array type 364 * (chapter 10) whose element type is one of the preceding 365 * types." 366 */ 367 Class<?> returnType = method.getReturnType(); 368 if (returnType.isArray()) { 369 returnType = returnType.getComponentType(); 370 if (returnType.isArray()) { // Only single dimensional arrays 371 valid = false; 372 break; 373 } 374 } 375 376 if (!((returnType.isPrimitive() && returnType != void.class) || 377 returnType == java.lang.String.class || 378 returnType == java.lang.Class.class || 379 returnType.isEnum() || 380 returnType.isAnnotation())) { 381 valid = false; 382 break; 383 } 384 385 /* 386 * "It is a compile-time error if any method declared in an 387 * annotation type has a signature that is 388 * override-equivalent to that of any public or protected 389 * method declared in class Object or in the interface 390 * java.lang.annotation.Annotation." 391 * 392 * The methods in Object or Annotation meeting the other 393 * criteria (no arguments, contrained return type, etc.) 394 * above are: 395 * 396 * String toString() 397 * int hashCode() 398 * Class<? extends Annotation> annotationType() 399 */ 400 String methodName = method.getName(); 401 if ((methodName.equals("toString") && returnType == java.lang.String.class) || 402 (methodName.equals("hashCode") && returnType == int.class) || 403 (methodName.equals("annotationType") && returnType == java.lang.Class.class)) { 404 valid = false; 405 break; 406 } 407 } 408 if (valid) 409 return; 410 else 411 throw new AnnotationFormatError("Malformed method on an annotation type"); 412 } 413 414 /** 415 * Implementation of dynamicProxy.hashCode() 416 */ 417 private int hashCodeImpl() { 418 int result = 0; 419 for (Map.Entry<String, Object> e : memberValues.entrySet()) { 420 result += (127 * e.getKey().hashCode()) ^ 421 memberValueHashCode(e.getValue()); 422 } 423 return result; 424 } 425 426 /** 427 * Computes hashCode of a member value (in "dynamic proxy return form") 428 */ 429 private static int memberValueHashCode(Object value) { 430 Class<?> type = value.getClass(); 431 if (!type.isArray()) // primitive, string, class, enum const, 432 // or annotation 433 return value.hashCode(); 434 435 if (type == byte[].class) 436 return Arrays.hashCode((byte[]) value); 437 if (type == char[].class) 438 return Arrays.hashCode((char[]) value); 439 if (type == double[].class) 440 return Arrays.hashCode((double[]) value); 441 if (type == float[].class) 442 return Arrays.hashCode((float[]) value); 443 if (type == int[].class) 444 return Arrays.hashCode((int[]) value); 445 if (type == long[].class) 446 return Arrays.hashCode((long[]) value); 447 if (type == short[].class) 448 return Arrays.hashCode((short[]) value); 449 if (type == boolean[].class) 450 return Arrays.hashCode((boolean[]) value); 451 return Arrays.hashCode((Object[]) value); 452 } 453 454 private void readObject(java.io.ObjectInputStream s) 455 throws java.io.IOException, ClassNotFoundException { 456 ObjectInputStream.GetField fields = s.readFields(); 457 458 @SuppressWarnings("unchecked") 459 Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null); 460 @SuppressWarnings("unchecked") 461 Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null); 462 463 // Check to make sure that types have not evolved incompatibly 464 465 AnnotationType annotationType = null; 466 try { 467 annotationType = AnnotationType.getInstance(t); 468 } catch(IllegalArgumentException e) { 469 // Class is no longer an annotation type; time to punch out 470 throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream"); 471 } 472 473 Map<String, Class<?>> memberTypes = annotationType.memberTypes(); 474 // consistent with runtime Map type 475 Map<String, Object> mv = new LinkedHashMap<>(); 476 477 // If there are annotation members without values, that 478 // situation is handled by the invoke method. 479 for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) { 480 String name = memberValue.getKey(); 481 Object value = null; 482 Class<?> memberType = memberTypes.get(name); 483 if (memberType != null) { // i.e. member still exists 484 value = memberValue.getValue(); 485 if (!(memberType.isInstance(value) || 486 value instanceof ExceptionProxy)) { 487 value = new AnnotationTypeMismatchExceptionProxy( 488 value.getClass() + "[" + value + "]").setMember( 489 annotationType.members().get(name)); 490 } 491 } 492 mv.put(name, value); 493 } 494 495 UnsafeAccessor.setType(this, t); 496 UnsafeAccessor.setMemberValues(this, mv); 497 } 498 499 private static class UnsafeAccessor { 500 private static final jdk.internal.misc.Unsafe unsafe; 501 private static final long typeOffset; 502 private static final long memberValuesOffset; 503 static { 504 try { 505 unsafe = jdk.internal.misc.Unsafe.getUnsafe(); 506 typeOffset = unsafe.objectFieldOffset 507 (AnnotationInvocationHandler.class.getDeclaredField("type")); 508 memberValuesOffset = unsafe.objectFieldOffset 509 (AnnotationInvocationHandler.class.getDeclaredField("memberValues")); 510 } catch (Exception ex) { 511 throw new ExceptionInInitializerError(ex); 512 } 513 } 514 static void setType(AnnotationInvocationHandler o, 515 Class<? extends Annotation> type) { 516 unsafe.putObject(o, typeOffset, type); 517 } 518 519 static void setMemberValues(AnnotationInvocationHandler o, 520 Map<String, Object> memberValues) { 521 unsafe.putObject(o, memberValuesOffset, memberValues); 522 } 523 } 524 }