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