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 jdk.internal.reflect; 27 28 29 import java.lang.reflect.*; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 import java.util.HashMap; 33 import java.util.Map; 34 import java.util.Objects; 35 import jdk.internal.HotSpotIntrinsicCandidate; 36 import jdk.internal.misc.VM; 37 38 /** Common utility routines used by both java.lang and 39 java.lang.reflect */ 40 41 public class Reflection { 42 43 /** Used to filter out fields and methods from certain classes from public 44 view, where they are sensitive or they may contain VM-internal objects. 45 These Maps are updated very rarely. Rather than synchronize on 46 each access, we use copy-on-write */ 47 private static volatile Map<Class<?>,String[]> fieldFilterMap; 48 private static volatile Map<Class<?>,String[]> methodFilterMap; 49 50 static { 51 Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>(); 52 map.put(Reflection.class, 53 new String[] {"fieldFilterMap", "methodFilterMap"}); 54 map.put(System.class, new String[] {"security"}); 55 map.put(Class.class, new String[] {"classLoader"}); 56 fieldFilterMap = map; 57 58 methodFilterMap = new HashMap<>(); 59 } 60 61 /** Returns the class of the caller of the method calling this method, 62 ignoring frames associated with java.lang.reflect.Method.invoke() 63 and its implementation. */ 64 @CallerSensitive 65 @HotSpotIntrinsicCandidate 66 public static native Class<?> getCallerClass(); 67 68 /** 69 * @deprecated This method will be removed. 70 * This method is a private JDK API and retained temporarily to 71 * simplify the implementation of sun.misc.Reflection.getCallerClass. 72 */ 73 @Deprecated(forRemoval=true) 74 public static native Class<?> getCallerClass(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 @HotSpotIntrinsicCandidate 85 public static native int getClassAccessFlags(Class<?> c); 86 87 88 public static void ensureMemberAccess(Class<?> currentClass, 89 Class<?> memberClass, 90 Object target, 91 int modifiers) 92 throws IllegalAccessException 93 { 94 if (currentClass == null || memberClass == null) { 95 throw new InternalError(); 96 } 97 98 if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) { 99 throwIllegalAccessException(currentClass, memberClass, target, modifiers); 100 } 101 } 102 103 public static boolean verifyMemberAccess(Class<?> currentClass, 104 // Declaring class of field 105 // or method 106 Class<?> memberClass, 107 // May be NULL in case of statics 108 Object target, 109 int modifiers) 110 { 111 // Verify that currentClass can access a field, method, or 112 // constructor of memberClass, where that member's access bits are 113 // "modifiers". 114 115 boolean gotIsSameClassPackage = false; 116 boolean isSameClassPackage = false; 117 118 if (currentClass == memberClass) { 119 // Always succeeds 120 return true; 121 } 122 123 if (!verifyModuleAccess(currentClass, memberClass)) { 124 return false; 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 /** 186 * Returns {@code true} if memberClass's's module exports memberClass's 187 * package to currentClass's module. 188 */ 189 public static boolean verifyModuleAccess(Class<?> currentClass, 190 Class<?> memberClass) { 191 return verifyModuleAccess(currentClass.getModule(), memberClass); 192 } 193 194 public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) { 195 Module memberModule = memberClass.getModule(); 196 197 // module may be null during startup (initLevel 0) 198 if (currentModule == memberModule) 199 return true; // same module (named or unnamed) 200 201 // memberClass may be primitive or array class 202 Class<?> c = memberClass; 203 while (c.isArray()) { 204 c = c.getComponentType(); 205 } 206 if (c.isPrimitive()) 207 return true; 208 209 // check that memberModule exports the package to currentModule 210 return memberModule.isExported(c.getPackageName(), currentModule); 211 } 212 213 /** 214 * Returns true if two classes in the same package. 215 */ 216 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { 217 if (c1.getClassLoader() != c2.getClassLoader()) 218 return false; 219 while (c1.isArray()) 220 c1 = c1.getComponentType(); 221 while (c2.isArray()) 222 c2 = c2.getComponentType(); 223 return Objects.equals(c1.getPackageName(), c2.getPackageName()); 224 } 225 226 static boolean isSubclassOf(Class<?> queryClass, 227 Class<?> ofClass) 228 { 229 while (queryClass != null) { 230 if (queryClass == ofClass) { 231 return true; 232 } 233 queryClass = queryClass.getSuperclass(); 234 } 235 return false; 236 } 237 238 // fieldNames must contain only interned Strings 239 public static synchronized void registerFieldsToFilter(Class<?> containingClass, 240 String ... fieldNames) { 241 fieldFilterMap = 242 registerFilter(fieldFilterMap, containingClass, fieldNames); 243 } 244 245 // methodNames must contain only interned Strings 246 public static synchronized void registerMethodsToFilter(Class<?> containingClass, 247 String ... methodNames) { 248 methodFilterMap = 249 registerFilter(methodFilterMap, containingClass, methodNames); 250 } 251 252 private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map, 253 Class<?> containingClass, String ... names) { 254 if (map.get(containingClass) != null) { 255 throw new IllegalArgumentException 256 ("Filter already registered: " + containingClass); 257 } 258 map = new HashMap<Class<?>,String[]>(map); 259 map.put(containingClass, names); 260 return map; 261 } 262 263 public static Field[] filterFields(Class<?> containingClass, 264 Field[] fields) { 265 if (fieldFilterMap == null) { 266 // Bootstrapping 267 return fields; 268 } 269 return (Field[])filter(fields, fieldFilterMap.get(containingClass)); 270 } 271 272 public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { 273 if (methodFilterMap == null) { 274 // Bootstrapping 275 return methods; 276 } 277 return (Method[])filter(methods, methodFilterMap.get(containingClass)); 278 } 279 280 private static Member[] filter(Member[] members, String[] filteredNames) { 281 if ((filteredNames == null) || (members.length == 0)) { 282 return members; 283 } 284 int numNewMembers = 0; 285 for (Member member : members) { 286 boolean shouldSkip = false; 287 for (String filteredName : filteredNames) { 288 if (member.getName() == filteredName) { 289 shouldSkip = true; 290 break; 291 } 292 } 293 if (!shouldSkip) { 294 ++numNewMembers; 295 } 296 } 297 Member[] newMembers = 298 (Member[])Array.newInstance(members[0].getClass(), numNewMembers); 299 int destIdx = 0; 300 for (Member member : members) { 301 boolean shouldSkip = false; 302 for (String filteredName : filteredNames) { 303 if (member.getName() == filteredName) { 304 shouldSkip = true; 305 break; 306 } 307 } 308 if (!shouldSkip) { 309 newMembers[destIdx++] = member; 310 } 311 } 312 return newMembers; 313 } 314 315 /** 316 * Tests if the given method is caller-sensitive and the declaring class 317 * is defined by either the bootstrap class loader or platform class loader. 318 */ 319 public static boolean isCallerSensitive(Method m) { 320 final ClassLoader loader = m.getDeclaringClass().getClassLoader(); 321 if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader)) { 322 return m.isAnnotationPresent(CallerSensitive.class); 323 } 324 return false; 325 } 326 327 private static boolean isExtClassLoader(ClassLoader loader) { 328 ClassLoader cl = ClassLoader.getSystemClassLoader(); 329 while (cl != null) { 330 if (cl.getParent() == null && cl == loader) { 331 return true; 332 } 333 cl = cl.getParent(); 334 } 335 return false; 336 } 337 338 339 // true to print a stack trace when IAE is thrown 340 private static volatile boolean printStackWhenAccessFails; 341 342 // true if printStackWhenAccessFails has been initialized 343 private static volatile boolean printStackWhenAccessFailsSet; 344 345 private static void printStackTraceIfNeeded(Throwable e) { 346 if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) { 347 // can't use method reference here, might be too early in startup 348 PrivilegedAction<Boolean> pa = new PrivilegedAction<Boolean>() { 349 public Boolean run() { 350 String s; 351 s = System.getProperty("sun.reflect.debugModuleAccessChecks"); 352 return (s != null && !s.equalsIgnoreCase("false")); 353 } 354 }; 355 printStackWhenAccessFails = AccessController.doPrivileged(pa); 356 printStackWhenAccessFailsSet = true; 357 } 358 if (printStackWhenAccessFails) { 359 e.printStackTrace(); 360 } 361 } 362 363 /** 364 * Throws IllegalAccessException with the an exception message based on 365 * the access that is denied. 366 */ 367 private static void throwIllegalAccessException(Class<?> currentClass, 368 Class<?> memberClass, 369 Object target, 370 int modifiers) 371 throws IllegalAccessException 372 { 373 String currentSuffix = ""; 374 String memberSuffix = ""; 375 Module m1 = currentClass.getModule(); 376 if (m1.isNamed()) 377 currentSuffix = " (in " + m1 + ")"; 378 Module m2 = memberClass.getModule(); 379 if (m2.isNamed()) 380 memberSuffix = " (in " + m2 + ")"; 381 382 Class<?> c = memberClass; 383 while (c.isArray()) { 384 c = c.getComponentType(); 385 } 386 String memberPackageName = c.getPackageName(); 387 388 String msg = currentClass + currentSuffix + " cannot access "; 389 if (m2.isExported(memberPackageName, m1)) { 390 391 // module access okay so include the modifiers in the message 392 msg += "a member of " + memberClass + memberSuffix + 393 " with modifiers \"" + Modifier.toString(modifiers) + "\""; 394 395 } else { 396 // module access failed 397 msg += memberClass + memberSuffix+ " because " 398 + m2 + " does not export " + memberPackageName; 399 if (m2.isNamed()) msg += " to " + m1; 400 } 401 402 throwIllegalAccessException(msg); 403 } 404 405 /** 406 * Throws IllegalAccessException with the given exception message. 407 */ 408 public static void throwIllegalAccessException(String msg) 409 throws IllegalAccessException 410 { 411 IllegalAccessException e = new IllegalAccessException(msg); 412 printStackTraceIfNeeded(e); 413 throw e; 414 } 415 416 /** 417 * Throws InaccessibleObjectException with the given exception message. 418 */ 419 public static void throwInaccessibleObjectException(String msg) { 420 InaccessibleObjectException e = new InaccessibleObjectException(msg); 421 printStackTraceIfNeeded(e); 422 throw e; 423 } 424 425 }