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