1 /* 2 * Copyright (c) 2001, 2013, 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; 27 28 import java.lang.reflect.*; 29 import java.util.HashMap; 30 import java.util.Map; 31 32 /** Common utility routines used by both java.lang and 33 java.lang.reflect */ 34 35 public class Reflection { 36 37 /** Used to filter out fields and methods from certain classes from public 38 view, where they are sensitive or they may contain VM-internal objects. 39 These Maps are updated very rarely. Rather than synchronize on 40 each access, we use copy-on-write */ 41 private static volatile Map<Class<?>,String[]> fieldFilterMap; 42 private static volatile Map<Class<?>,String[]> methodFilterMap; 43 44 static { 45 Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>(); 46 map.put(Reflection.class, 47 new String[] {"fieldFilterMap", "methodFilterMap"}); 48 map.put(System.class, new String[] {"security"}); 49 map.put(Class.class, new String[] {"classLoader"}); 50 fieldFilterMap = map; 51 52 methodFilterMap = new HashMap<>(); 53 } 54 55 /** Returns the class of the caller of the method calling this method, 56 ignoring frames associated with java.lang.reflect.Method.invoke() 57 and its implementation. */ 58 @CallerSensitive 59 public static native Class<?> getCallerClass(); 60 61 /** 62 * @deprecated This method will be removed in JDK 9. 63 * This method is a private JDK API and retained temporarily for 64 * existing code to run until a replacement API is defined. 65 */ 66 @Deprecated 67 public static native Class<?> getCallerClass(int depth); 68 69 /** Retrieves the access flags written to the class file. For 70 inner classes these flags may differ from those returned by 71 Class.getModifiers(), which searches the InnerClasses 72 attribute to find the source-level access flags. This is used 73 instead of Class.getModifiers() for run-time access checks due 74 to compatibility reasons; see 4471811. Only the values of the 75 low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be 76 valid. */ 77 public static native int getClassAccessFlags(Class<?> c); 78 79 /** A quick "fast-path" check to try to avoid getCallerClass() 80 calls. */ 81 public static boolean quickCheckMemberAccess(Class<?> memberClass, 82 int modifiers) 83 { 84 return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers); 85 } 86 87 public static void ensureMemberAccess(Class<?> currentClass, 88 Class<?> memberClass, 89 Object target, 90 int modifiers) 91 throws IllegalAccessException 92 { 93 if (currentClass == null || memberClass == null) { 94 throw new InternalError(); 95 } 96 97 if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) { 98 throw new IllegalAccessException("Class " + currentClass.getName() + 99 " can not access a member of class " + 100 memberClass.getName() + 101 " with modifiers \"" + 102 Modifier.toString(modifiers) + 103 "\""); 104 } 105 } 106 107 public static boolean verifyMemberAccess(Class<?> currentClass, 108 // Declaring class of field 109 // or method 110 Class<?> memberClass, 111 // May be NULL in case of statics 112 Object target, 113 int modifiers) 114 { 115 // Verify that currentClass can access a field, method, or 116 // constructor of memberClass, where that member's access bits are 117 // "modifiers". 118 119 boolean gotIsSameClassPackage = false; 120 boolean isSameClassPackage = false; 121 122 if (currentClass == memberClass) { 123 // Always succeeds 124 return true; 125 } 126 127 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { 128 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 129 gotIsSameClassPackage = true; 130 if (!isSameClassPackage) { 131 return false; 132 } 133 } 134 135 // At this point we know that currentClass can access memberClass. 136 137 if (Modifier.isPublic(modifiers)) { 138 return true; 139 } 140 141 boolean successSoFar = false; 142 143 if (Modifier.isProtected(modifiers)) { 144 // See if currentClass is a subclass of memberClass 145 if (isSubclassOf(currentClass, memberClass)) { 146 successSoFar = true; 147 } 148 } 149 150 if (!successSoFar && !Modifier.isPrivate(modifiers)) { 151 if (!gotIsSameClassPackage) { 152 isSameClassPackage = isSameClassPackage(currentClass, 153 memberClass); 154 gotIsSameClassPackage = true; 155 } 156 157 if (isSameClassPackage) { 158 successSoFar = true; 159 } 160 } 161 162 if (!successSoFar) { 163 return false; 164 } 165 166 if (Modifier.isProtected(modifiers)) { 167 // Additional test for protected members: JLS 6.6.2 168 Class<?> targetClass = (target == null ? memberClass : target.getClass()); 169 if (targetClass != currentClass) { 170 if (!gotIsSameClassPackage) { 171 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 172 gotIsSameClassPackage = true; 173 } 174 if (!isSameClassPackage) { 175 if (!isSubclassOf(targetClass, currentClass)) { 176 return false; 177 } 178 } 179 } 180 } 181 182 return true; 183 } 184 185 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { 186 return isSameClassPackage(c1.getClassLoader(), c1.getName(), 187 c2.getClassLoader(), c2.getName()); 188 } 189 190 /** Returns true if two classes are in the same package; classloader 191 and classname information is enough to determine a class's package */ 192 private static boolean isSameClassPackage(ClassLoader loader1, String name1, 193 ClassLoader loader2, String name2) 194 { 195 if (loader1 != loader2) { 196 return false; 197 } else { 198 int lastDot1 = name1.lastIndexOf('.'); 199 int lastDot2 = name2.lastIndexOf('.'); 200 if ((lastDot1 == -1) || (lastDot2 == -1)) { 201 // One of the two doesn't have a package. Only return true 202 // if the other one also doesn't have a package. 203 return (lastDot1 == lastDot2); 204 } else { 205 int idx1 = 0; 206 int idx2 = 0; 207 208 // Skip over '['s 209 if (name1.charAt(idx1) == '[') { 210 do { 211 idx1++; 212 } while (name1.charAt(idx1) == '['); 213 if (name1.charAt(idx1) != 'L') { 214 // Something is terribly wrong. Shouldn't be here. 215 throw new InternalError("Illegal class name " + name1); 216 } 217 } 218 if (name2.charAt(idx2) == '[') { 219 do { 220 idx2++; 221 } while (name2.charAt(idx2) == '['); 222 if (name2.charAt(idx2) != 'L') { 223 // Something is terribly wrong. Shouldn't be here. 224 throw new InternalError("Illegal class name " + name2); 225 } 226 } 227 228 // Check that package part is identical 229 int length1 = lastDot1 - idx1; 230 int length2 = lastDot2 - idx2; 231 232 if (length1 != length2) { 233 return false; 234 } 235 return name1.regionMatches(false, idx1, name2, idx2, length1); 236 } 237 } 238 } 239 240 static boolean isSubclassOf(Class<?> queryClass, 241 Class<?> ofClass) 242 { 243 while (queryClass != null) { 244 if (queryClass == ofClass) { 245 return true; 246 } 247 queryClass = queryClass.getSuperclass(); 248 } 249 return false; 250 } 251 252 // fieldNames must contain only interned Strings 253 public static synchronized void registerFieldsToFilter(Class<?> containingClass, 254 String ... fieldNames) { 255 fieldFilterMap = 256 registerFilter(fieldFilterMap, containingClass, fieldNames); 257 } 258 259 // methodNames must contain only interned Strings 260 public static synchronized void registerMethodsToFilter(Class<?> containingClass, 261 String ... methodNames) { 262 methodFilterMap = 263 registerFilter(methodFilterMap, containingClass, methodNames); 264 } 265 266 private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map, 267 Class<?> containingClass, String ... names) { 268 if (map.get(containingClass) != null) { 269 throw new IllegalArgumentException 270 ("Filter already registered: " + containingClass); 271 } 272 map = new HashMap<Class<?>,String[]>(map); 273 map.put(containingClass, names); 274 return map; 275 } 276 277 public static Field[] filterFields(Class<?> containingClass, 278 Field[] fields) { 279 if (fieldFilterMap == null) { 280 // Bootstrapping 281 return fields; 282 } 283 return (Field[])filter(fields, fieldFilterMap.get(containingClass)); 284 } 285 286 public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { 287 if (methodFilterMap == null) { 288 // Bootstrapping 289 return methods; 290 } 291 return (Method[])filter(methods, methodFilterMap.get(containingClass)); 292 } 293 294 private static Member[] filter(Member[] members, String[] filteredNames) { 295 if ((filteredNames == null) || (members.length == 0)) { 296 return members; 297 } 298 int numNewMembers = 0; 299 for (Member member : members) { 300 boolean shouldSkip = false; 301 for (String filteredName : filteredNames) { 302 if (member.getName() == filteredName) { 303 shouldSkip = true; 304 break; 305 } 306 } 307 if (!shouldSkip) { 308 ++numNewMembers; 309 } 310 } 311 Member[] newMembers = 312 (Member[])Array.newInstance(members[0].getClass(), numNewMembers); 313 int destIdx = 0; 314 for (Member member : members) { 315 boolean shouldSkip = false; 316 for (String filteredName : filteredNames) { 317 if (member.getName() == filteredName) { 318 shouldSkip = true; 319 break; 320 } 321 } 322 if (!shouldSkip) { 323 newMembers[destIdx++] = member; 324 } 325 } 326 return newMembers; 327 } 328 329 /** 330 * Tests if the given method is caller-sensitive and the declaring class 331 * is defined by either the bootstrap class loader or extension class loader. 332 */ 333 public static boolean isCallerSensitive(Method m) { 334 final ClassLoader loader = m.getDeclaringClass().getClassLoader(); 335 if (sun.misc.VM.isSystemDomainLoader(loader) || isExtClassLoader(loader)) { 336 return m.isAnnotationPresent(CallerSensitive.class); 337 } 338 return false; 339 } 340 341 private static boolean isExtClassLoader(ClassLoader loader) { 342 ClassLoader cl = ClassLoader.getSystemClassLoader(); 343 while (cl != null) { 344 if (cl.getParent() == null && cl == loader) { 345 return true; 346 } 347 cl = cl.getParent(); 348 } 349 return false; 350 } 351 }