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