1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package java.lang; 18 19 import java.lang.reflect.AccessibleObject; 20 import java.lang.reflect.Field; 21 import java.lang.reflect.InvocationTargetException; 22 import java.lang.reflect.Method; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collections; 26 import java.util.Comparator; 27 import java.util.EnumSet; 28 import java.util.HashSet; 29 import java.util.List; 30 import org.apache.harmony.kernel.vm.LangAccess; 31 import org.apache.harmony.kernel.vm.ReflectionAccess; 32 33 /** 34 * Cache of per-class data, meant to help the performance of reflection 35 * methods. 36 * 37 * <p><b>Note:</b> None of the methods perform access checks. It is up 38 * to the (package internal) clients of this code to perform such 39 * checks as necessary.</p> 40 * 41 * <p><b>Also Note:</b> None of the returned array values are 42 * protected in any way. It is up to the (again, package internal) 43 * clients of this code to protect the arrays if they should ever 44 * escape the package.</p> 45 */ 46 /*package*/ class ClassCache<T> { 47 // TODO: Add caching for constructors and fields. 48 49 /** non-null; comparator used for enumerated values */ 50 private static final EnumComparator ENUM_COMPARATOR = 51 new EnumComparator(); 52 53 /** non-null; reflection access bridge */ 54 /*package*/ static final ReflectionAccess REFLECT = getReflectionAccess(); 55 56 /** non-null; class that this instance represents */ 57 private final Class<T> clazz; 58 59 /** null-ok; list of all public methods, both direct and inherited */ 60 private volatile Method[] methods; 61 62 /** null-ok; list of all declared methods */ 63 private volatile Method[] declaredMethods; 64 65 /** null-ok; list of all public declared methods */ 66 private volatile Method[] declaredPublicMethods; 67 68 /** null-ok; list of all declared fields */ 69 private volatile Field[] declaredFields; 70 71 /** null-ok; list of all public declared fields */ 72 private volatile Field[] declaredPublicFields; 73 74 /** null-ok; list of all fields, both direct and inherited */ 75 private volatile Field[] allFields; 76 77 /** null-ok; list of all public fields, both direct and inherited */ 78 private volatile Field[] allPublicFields; 79 80 /** 81 * null-ok; array of enumerated values in their original order, if this 82 * instance's class is an enumeration 83 */ 84 private volatile T[] enumValuesInOrder; 85 86 /** 87 * null-ok; array of enumerated values sorted by name, if this 88 * instance's class is an enumeration 89 */ 90 private volatile T[] enumValuesByName; 91 92 static { 93 /* 94 * Provide access to this package from java.util as part of 95 * bootstrap. TODO: See if this can be removed in favor of the 96 * simpler mechanism below. (That is, see if EnumSet will be 97 * happy calling LangAccess.getInstance().) 98 */ 99 Field field; 100 101 try { 102 field = EnumSet.class.getDeclaredField("LANG_BOOTSTRAP"); REFLECT.setAccessibleNoCheck(field, true)103 REFLECT.setAccessibleNoCheck(field, true); 104 } catch (NoSuchFieldException ex) { 105 // This shouldn't happen because the field is in fact defined. 106 throw new AssertionError(ex); 107 } 108 109 try { field.set(null, LangAccessImpl.THE_ONE)110 field.set(null, LangAccessImpl.THE_ONE); 111 } catch (IllegalAccessException ex) { 112 // This shouldn't happen because we made the field accessible. 113 throw new AssertionError(ex); 114 } 115 116 // Also set up the bootstrap-classpath-wide access mechanism. 117 LangAccess.setInstance(LangAccessImpl.THE_ONE); 118 } 119 120 /** 121 * Constructs an instance. 122 * 123 * @param clazz non-null; class that this instance represents 124 */ ClassCache(Class<T> clazz)125 /*package*/ ClassCache(Class<T> clazz) { 126 if (clazz == null) { 127 throw new NullPointerException("clazz == null"); 128 } 129 130 this.clazz = clazz; 131 } 132 133 /** 134 * Gets the list of all declared methods. 135 * 136 * @return non-null; the list of all declared methods 137 */ getDeclaredMethods()138 public Method[] getDeclaredMethods() { 139 if (declaredMethods == null) { 140 declaredMethods = Class.getDeclaredMethods(clazz, false); 141 } 142 143 return declaredMethods; 144 } 145 146 /** 147 * Gets the list of all declared public methods. 148 * 149 * @return non-null; the list of all declared public methods 150 */ getDeclaredPublicMethods()151 private Method[] getDeclaredPublicMethods() { 152 if (declaredPublicMethods == null) { 153 declaredPublicMethods = Class.getDeclaredMethods(clazz, true); 154 } 155 156 return declaredPublicMethods; 157 } 158 159 /** 160 * Returns public methods defined by {@code clazz}, its superclasses and all 161 * implemented interfaces, not including overridden methods. This method 162 * performs no security checks. 163 */ getMethods()164 public Method[] getMethods() { 165 Method[] cachedResult = methods; 166 if (cachedResult == null) { 167 methods = findMethods(); 168 } 169 170 return methods; 171 } 172 findMethods()173 private Method[] findMethods() { 174 List<Method> allMethods = new ArrayList<Method>(); 175 getMethodsRecursive(clazz, allMethods); 176 177 /* 178 * Remove methods defined by multiple types, preferring to keep methods 179 * declared by derived types. 180 * 181 * Classes may define multiple methods with the same name and parameter 182 * types due to covariant return types. In this case both are returned, 183 * with the non-synthetic method first because it is preferred by 184 * getMethod(String,Class[]). 185 */ 186 Collections.sort(allMethods, Method.ORDER_BY_SIGNATURE); 187 List<Method> natural = new ArrayList<Method>(allMethods.size()); 188 List<Method> synthetic = new ArrayList<Method>(allMethods.size()); 189 Method previous = null; 190 for (Method method : allMethods) { 191 if (previous != null 192 && Method.ORDER_BY_SIGNATURE.compare(method, previous) == 0 193 && method.getDeclaringClass() != previous.getDeclaringClass()) { 194 continue; 195 } 196 if (method.isSynthetic()) { 197 synthetic.add(method); 198 } else { 199 natural.add(method); 200 } 201 previous = method; 202 } 203 List<Method> result = new ArrayList<Method>(allMethods.size()); 204 result.addAll(natural); 205 result.addAll(synthetic); 206 return result.toArray(new Method[result.size()]); 207 } 208 209 /** 210 * Populates {@code sink} with public methods defined by {@code clazz}, its 211 * superclasses, and all implemented interfaces, including overridden methods. 212 * This method performs no security checks. 213 */ getMethodsRecursive(Class<?> clazz, List<Method> result)214 private static void getMethodsRecursive(Class<?> clazz, List<Method> result) { 215 for (Class<?> c = clazz; c != null; c = c.getSuperclass()) { 216 for (Method method : c.getClassCache().getDeclaredPublicMethods()) { 217 result.add(method); 218 } 219 } 220 221 for (Class<?> ifc : clazz.getInterfaces()) { 222 getMethodsRecursive(ifc, result); 223 } 224 } 225 226 /** 227 * Finds and returns a method with a given name and signature. Use 228 * this with one of the method lists returned by instances of this class. 229 * 230 * @param list non-null; the list of methods to search through 231 * @param parameterTypes non-null; the formal parameter list 232 * @return non-null; the matching method 233 * @throws NoSuchMethodException thrown if the method does not exist 234 */ findMethodByName(Method[] list, String name, Class<?>[] parameterTypes)235 public static Method findMethodByName(Method[] list, String name, 236 Class<?>[] parameterTypes) throws NoSuchMethodException { 237 if (name == null) { 238 throw new NullPointerException("Method name must not be null."); 239 } 240 for (Method method : list) { 241 if (method.getName().equals(name) 242 && compareClassLists(method.getParameterTypes(), parameterTypes)) { 243 return method; 244 } 245 } 246 247 throw new NoSuchMethodException(name); 248 } 249 250 /** 251 * Compares two class lists for equality. Empty and 252 * <code>null</code> lists are considered equal. This is useful 253 * for matching methods and constructors. 254 * 255 * <p>TODO: Take into account assignment compatibility?</p> 256 * 257 * @param a null-ok; the first list of types 258 * @param b null-ok; the second list of types 259 * @return true if and only if the lists are equal 260 */ compareClassLists(Class<?>[] a, Class<?>[] b)261 public static boolean compareClassLists(Class<?>[] a, Class<?>[] b) { 262 if (a == null) { 263 return (b == null) || (b.length == 0); 264 } 265 266 int length = a.length; 267 268 if (b == null) { 269 return (length == 0); 270 } 271 272 if (length != b.length) { 273 return false; 274 } 275 276 for (int i = length - 1; i >= 0; i--) { 277 if (a[i] != b[i]) { 278 return false; 279 } 280 } 281 282 return true; 283 } 284 285 /** 286 * Makes a deep copy of the given array of methods. This is useful 287 * when handing out arrays from the public API. 288 * 289 * <p><b>Note:</b> In such cases, it is insufficient to just make 290 * a shallow copy of the array, since method objects aren't 291 * immutable due to the existence of {@link 292 * AccessibleObject#setAccessible}.</p> 293 * 294 * @param orig non-null; array to copy 295 * @return non-null; a deep copy of the given array 296 */ deepCopy(Method[] orig)297 public static Method[] deepCopy(Method[] orig) { 298 int length = orig.length; 299 Method[] result = new Method[length]; 300 301 for (int i = length - 1; i >= 0; i--) { 302 result[i] = REFLECT.clone(orig[i]); 303 } 304 305 return result; 306 } 307 308 /** 309 * Gets the list of all declared fields. 310 * 311 * @return non-null; the list of all declared fields 312 */ getDeclaredFields()313 public Field[] getDeclaredFields() { 314 if (declaredFields == null) { 315 declaredFields = Class.getDeclaredFields(clazz, false); 316 } 317 318 return declaredFields; 319 } 320 321 /** 322 * Gets the list of all declared public fields. 323 * 324 * @return non-null; the list of all declared public fields 325 */ getDeclaredPublicFields()326 public Field[] getDeclaredPublicFields() { 327 if (declaredPublicFields == null) { 328 declaredPublicFields = Class.getDeclaredFields(clazz, true); 329 } 330 331 return declaredPublicFields; 332 } 333 334 /** 335 * Gets either the list of declared fields or the list of declared 336 * public fields. 337 * 338 * @param publicOnly whether to only return public fields 339 */ getDeclaredFields(boolean publicOnly)340 public Field[] getDeclaredFields(boolean publicOnly) { 341 return publicOnly ? getDeclaredPublicFields() : getDeclaredFields(); 342 } 343 344 /** 345 * Gets the list of all fields, both directly 346 * declared and inherited. 347 * 348 * @return non-null; the list of all fields 349 */ getAllFields()350 public Field[] getAllFields() { 351 if (allFields == null) { 352 allFields = getFullListOfFields(false); 353 } 354 355 return allFields; 356 } 357 358 /** 359 * Gets the list of all public fields, both directly 360 * declared and inherited. 361 * 362 * @return non-null; the list of all public fields 363 */ getAllPublicFields()364 public Field[] getAllPublicFields() { 365 if (allPublicFields == null) { 366 allPublicFields = getFullListOfFields(true); 367 } 368 369 return allPublicFields; 370 } 371 372 /* 373 * Returns the list of fields without performing any security checks 374 * first. This includes the fields inherited from superclasses. If no 375 * fields exist at all, an empty array is returned. 376 * 377 * @param publicOnly reflects whether we want only public fields 378 * or all of them 379 * @return the list of fields 380 */ getFullListOfFields(boolean publicOnly)381 private Field[] getFullListOfFields(boolean publicOnly) { 382 ArrayList<Field> fields = new ArrayList<Field>(); 383 HashSet<String> seen = new HashSet<String>(); 384 385 findAllfields(clazz, fields, seen, publicOnly); 386 387 return fields.toArray(new Field[fields.size()]); 388 } 389 390 /** 391 * Collects the list of fields without performing any security checks 392 * first. This includes the fields inherited from superclasses and from 393 * all implemented interfaces. The latter may also implement multiple 394 * interfaces, so we (potentially) recursively walk through a whole tree of 395 * classes. If no fields exist at all, an empty array is returned. 396 * 397 * @param clazz non-null; class to inspect 398 * @param fields non-null; the target list to add the results to 399 * @param seen non-null; a set of signatures we've already seen 400 * @param publicOnly reflects whether we want only public fields 401 * or all of them 402 */ findAllfields(Class<?> clazz, ArrayList<Field> fields, HashSet<String> seen, boolean publicOnly)403 private static void findAllfields(Class<?> clazz, 404 ArrayList<Field> fields, HashSet<String> seen, 405 boolean publicOnly) { 406 407 // Traverse class and superclasses, get rid of dupes by signature 408 while (clazz != null) { 409 for (Field field : clazz.getClassCache().getDeclaredFields(publicOnly)) { 410 String signature = field.toString(); 411 if (!seen.contains(signature)) { 412 fields.add(field); 413 seen.add(signature); 414 } 415 } 416 417 // Traverse all interfaces, and do the same recursively. 418 Class<?>[] interfaces = clazz.getInterfaces(); 419 for (Class<?> intf : interfaces) { 420 findAllfields(intf, fields, seen, publicOnly); 421 } 422 423 clazz = clazz.getSuperclass(); 424 } 425 } 426 427 /** 428 * Finds and returns a field with a given name and signature. Use 429 * this with one of the field lists returned by instances of this class. 430 * 431 * @param list non-null; the list of fields to search through 432 * @return non-null; the matching field 433 * @throws NoSuchFieldException thrown if the field does not exist 434 */ findFieldByName(Field[] list, String name)435 public static Field findFieldByName(Field[] list, String name) 436 throws NoSuchFieldException { 437 if (name == null) { 438 throw new NullPointerException("Field name must not be null."); 439 } 440 for (Field field : list) { 441 if (field.getName().equals(name)) { 442 return field; 443 } 444 } 445 446 throw new NoSuchFieldException(name); 447 } 448 449 /** 450 * Makes a deep copy of the given array of fields. This is useful 451 * when handing out arrays from the public API. 452 * 453 * <p><b>Note:</b> In such cases, it is insufficient to just make 454 * a shallow copy of the array, since field objects aren't 455 * immutable due to the existence of {@link 456 * AccessibleObject#setAccessible}.</p> 457 * 458 * @param orig non-null; array to copy 459 * @return non-null; a deep copy of the given array 460 */ deepCopy(Field[] orig)461 public static Field[] deepCopy(Field[] orig) { 462 int length = orig.length; 463 Field[] result = new Field[length]; 464 465 for (int i = length - 1; i >= 0; i--) { 466 result[i] = REFLECT.clone(orig[i]); 467 } 468 469 return result; 470 } 471 472 /** 473 * Gets the enumerated value with a given name. 474 * 475 * @param name non-null; name of the value 476 * @return null-ok; the named enumerated value or <code>null</code> 477 * if this instance's class doesn't have such a value (including 478 * if this instance isn't in fact an enumeration) 479 */ 480 @SuppressWarnings("unchecked") getEnumValue(String name)481 public T getEnumValue(String name) { 482 Enum[] values = (Enum[]) getEnumValuesByName(); 483 484 if (values == null) { 485 return null; 486 } 487 488 // Binary search. 489 490 int min = 0; 491 int max = values.length - 1; 492 493 while (min <= max) { 494 /* 495 * The guessIdx calculation is equivalent to ((min + max) 496 * / 2) but won't go wonky when min and max are close to 497 * Integer.MAX_VALUE. 498 */ 499 int guessIdx = min + ((max - min) >> 1); 500 Enum guess = values[guessIdx]; 501 int cmp = name.compareTo(guess.name()); 502 503 if (cmp < 0) { 504 max = guessIdx - 1; 505 } else if (cmp > 0) { 506 min = guessIdx + 1; 507 } else { 508 return (T) guess; 509 } 510 } 511 512 return null; 513 } 514 515 /** 516 * Gets the array of enumerated values, sorted by name. 517 * 518 * @return null-ok; the value array, or <code>null</code> if this 519 * instance's class isn't in fact an enumeration 520 */ getEnumValuesByName()521 public T[] getEnumValuesByName() { 522 if (enumValuesByName == null) { 523 T[] values = getEnumValuesInOrder(); 524 525 if (values != null) { 526 values = (T[]) values.clone(); 527 Arrays.sort((Enum<?>[]) values, ENUM_COMPARATOR); 528 529 /* 530 * Note: It's only safe (concurrency-wise) to set the 531 * instance variable after the array is properly sorted. 532 */ 533 enumValuesByName = values; 534 } 535 } 536 537 return enumValuesByName; 538 } 539 540 /** 541 * Gets the array of enumerated values, in their original declared 542 * order. 543 * 544 * @return null-ok; the value array, or <code>null</code> if this 545 * instance's class isn't in fact an enumeration 546 */ getEnumValuesInOrder()547 public T[] getEnumValuesInOrder() { 548 if ((enumValuesInOrder == null) && clazz.isEnum()) { 549 enumValuesInOrder = callEnumValues(); 550 } 551 552 return enumValuesInOrder; 553 } 554 555 /** 556 * Calls the static method <code>values()</code> on this 557 * instance's class, which is presumed to be a properly-formed 558 * enumeration class, using proper privilege hygiene. 559 * 560 * @return non-null; the array of values as reported by 561 * <code>value()</code> 562 */ 563 @SuppressWarnings("unchecked") callEnumValues()564 private T[] callEnumValues() { 565 Method method; 566 567 try { 568 Method[] methods = getDeclaredPublicMethods(); 569 method = findMethodByName(methods, "values", (Class[]) null); 570 method = REFLECT.accessibleClone(method); 571 } catch (NoSuchMethodException ex) { 572 // This shouldn't happen if the class is a well-formed enum. 573 throw new UnsupportedOperationException(ex); 574 } 575 576 try { 577 return (T[]) method.invoke((Object[]) null); 578 } catch (IllegalAccessException ex) { 579 // This shouldn't happen because the method is "accessible." 580 throw new Error(ex); 581 } catch (InvocationTargetException ex) { 582 Throwable te = ex.getTargetException(); 583 if (te instanceof RuntimeException) { 584 throw (RuntimeException) te; 585 } else if (te instanceof Error) { 586 throw (Error) te; 587 } else { 588 throw new Error(te); 589 } 590 } 591 } 592 593 /** 594 * Gets the reflection access object. This uses reflection to do 595 * so. My head is spinning. 596 * 597 * @return non-null; the reflection access object 598 */ getReflectionAccess()599 private static ReflectionAccess getReflectionAccess() { 600 /* 601 * Note: We can't do AccessibleObject.class.getCache() to 602 * get the cache, since that would cause a circularity in 603 * initialization. So instead, we do a direct call into the 604 * native side. 605 */ 606 Method[] methods = 607 Class.getDeclaredMethods(AccessibleObject.class, false); 608 609 try { 610 Method method = findMethodByName(methods, "getReflectionAccess", 611 (Class[]) null); 612 Class.setAccessibleNoCheck(method, true); 613 return (ReflectionAccess) method.invoke((Object[]) null); 614 } catch (NoSuchMethodException ex) { 615 /* 616 * This shouldn't happen because the method 617 * AccessibleObject.getReflectionAccess() really is defined 618 * in this module. 619 */ 620 throw new Error(ex); 621 } catch (IllegalAccessException ex) { 622 // This shouldn't happen because the method is "accessible." 623 throw new Error(ex); 624 } catch (InvocationTargetException ex) { 625 throw new Error(ex); 626 } 627 } 628 629 /** 630 * Comparator class for enumerated values. It compares strictly 631 * by name. 632 */ 633 private static class EnumComparator implements Comparator<Enum<?>> { compare(Enum<?> e1, Enum<?> e2)634 public int compare(Enum<?> e1, Enum<?> e2) { 635 return e1.name().compareTo(e2.name()); 636 } 637 } 638 } 639