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.util.HashMap; 31 import java.util.Map; 32 import java.util.Objects; 33 import jdk.internal.HotSpotIntrinsicCandidate; 34 import jdk.internal.misc.SharedSecrets; 35 import jdk.internal.misc.VM; 36 import sun.security.action.GetPropertyAction; 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 /** 89 * Ensures that access to a member is granted and throws 90 * IllegalAccessException if not. 91 * 92 * @param currentClass the class performing the access 93 * @param memberClass the declaring class of the member being accessed 94 * @param targetClass the class of target object if accessing instance 95 * field or method; 96 * or the declaring class if accessing constructor; 97 * or null if accessing static field or method 98 * @param modifiers the member's access modifiers 99 * @throws IllegalAccessException if access to member is denied 100 */ 101 public static void ensureMemberAccess(Class<?> currentClass, 102 Class<?> memberClass, 103 Class<?> targetClass, 104 int modifiers) 105 throws IllegalAccessException 106 { 107 if (currentClass == null || memberClass == null) { 108 throw new InternalError(); 109 } 110 111 if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) { 112 throwIllegalAccessException(currentClass, memberClass, targetClass, modifiers); 113 } 114 } 115 116 /** 117 * Verify access to a member, returning {@code false} if no access 118 */ 119 public static boolean verifyMemberAccess(Class<?> currentClass, 120 Class<?> memberClass, 121 Class<?> targetClass, 122 int modifiers) 123 { 124 // Verify that currentClass can access a field, method, or 125 // constructor of memberClass, where that member's access bits are 126 // "modifiers". 127 128 boolean gotIsSameClassPackage = false; 129 boolean isSameClassPackage = false; 130 131 if (currentClass == memberClass) { 132 // Always succeeds 133 return true; 134 } 135 136 if (!verifyModuleAccess(currentClass, memberClass)) { 137 return false; 138 } 139 140 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { 141 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 142 gotIsSameClassPackage = true; 143 if (!isSameClassPackage) { 144 return false; 145 } 146 } 147 148 // At this point we know that currentClass can access memberClass. 149 150 if (Modifier.isPublic(modifiers)) { 151 return true; 152 } 153 154 boolean successSoFar = false; 155 156 if (Modifier.isProtected(modifiers)) { 157 // See if currentClass is a subclass of memberClass 158 if (isSubclassOf(currentClass, memberClass)) { 159 successSoFar = true; 160 } 161 } 162 163 if (!successSoFar && !Modifier.isPrivate(modifiers)) { 164 if (!gotIsSameClassPackage) { 165 isSameClassPackage = isSameClassPackage(currentClass, 166 memberClass); 167 gotIsSameClassPackage = true; 168 } 169 170 if (isSameClassPackage) { 171 successSoFar = true; 172 } 173 } 174 175 if (!successSoFar) { 176 return false; 177 } 178 179 // Additional test for protected instance members 180 // and protected constructors: JLS 6.6.2 181 if (targetClass != null && Modifier.isProtected(modifiers) && 182 targetClass != currentClass) 183 { 184 if (!gotIsSameClassPackage) { 185 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 186 gotIsSameClassPackage = true; 187 } 188 if (!isSameClassPackage) { 189 if (!isSubclassOf(targetClass, currentClass)) { 190 return false; 191 } 192 } 193 } 194 195 return true; 196 } 197 198 /** 199 * Returns {@code true} if memberClass's's module exports memberClass's 200 * package to currentClass's module. 201 */ 202 public static boolean verifyModuleAccess(Class<?> currentClass, 203 Class<?> memberClass) { 204 return verifyModuleAccess(currentClass.getModule(), memberClass); 205 } 206 207 public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) { 208 Module memberModule = memberClass.getModule(); 209 210 // module may be null during startup (initLevel 0) 211 if (currentModule == memberModule) 212 return true; // same module (named or unnamed) 213 214 // memberClass may be primitive or array class 215 Class<?> c = memberClass; 216 while (c.isArray()) { 217 c = c.getComponentType(); 218 } 219 if (c.isPrimitive()) 220 return true; 221 222 String pkg = c.getPackageName(); 223 boolean allowed = memberModule.isExported(pkg, currentModule); 224 if (allowed && memberModule.isNamed() && printStackTraceWhenAccessSucceeds()) { 225 if (!SharedSecrets.getJavaLangReflectModuleAccess() 226 .isStaticallyExported(memberModule, pkg, currentModule)) { 227 String msg = currentModule + " allowed access to member of " + memberClass; 228 new Exception(msg).printStackTrace(System.err); 229 } 230 } 231 return allowed; 232 } 233 234 /** 235 * Returns true if two classes in the same package. 236 */ 237 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { 238 if (c1.getClassLoader() != c2.getClassLoader()) 239 return false; 240 while (c1.isArray()) 241 c1 = c1.getComponentType(); 242 while (c2.isArray()) 243 c2 = c2.getComponentType(); 244 return Objects.equals(c1.getPackageName(), c2.getPackageName()); 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 platform class loader. 339 */ 340 public static boolean isCallerSensitive(Method m) { 341 final ClassLoader loader = m.getDeclaringClass().getClassLoader(); 342 if (VM.isSystemDomainLoader(loader) || 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 359 360 // true to print a stack trace when access fails 361 private static volatile boolean printStackWhenAccessFails; 362 363 // true to print a stack trace when access succeeds 364 private static volatile boolean printStackWhenAccessSucceeds; 365 366 // true if printStack* values are initialized 367 private static volatile boolean printStackPropertiesSet; 368 369 private static void ensurePrintStackPropertiesSet() { 370 if (!printStackPropertiesSet && VM.initLevel() >= 1) { 371 String s = GetPropertyAction.privilegedGetProperty( 372 "sun.reflect.debugModuleAccessChecks"); 373 if (s != null) { 374 printStackWhenAccessFails = !s.equalsIgnoreCase("false"); 375 printStackWhenAccessSucceeds = s.equalsIgnoreCase("access"); 376 } 377 printStackPropertiesSet = true; 378 } 379 } 380 381 public static void enableStackTraces() { 382 printStackWhenAccessFails = true; 383 printStackWhenAccessSucceeds = true; 384 printStackPropertiesSet = true; 385 } 386 387 public static boolean printStackTraceWhenAccessFails() { 388 ensurePrintStackPropertiesSet(); 389 return printStackWhenAccessFails; 390 } 391 392 public static boolean printStackTraceWhenAccessSucceeds() { 393 ensurePrintStackPropertiesSet(); 394 return printStackWhenAccessSucceeds; 395 } 396 397 /** 398 * Throws IllegalAccessException with the an exception message based on 399 * the access that is denied. 400 */ 401 private static void throwIllegalAccessException(Class<?> currentClass, 402 Class<?> memberClass, 403 Object target, 404 int modifiers) 405 throws IllegalAccessException 406 { 407 String currentSuffix = ""; 408 String memberSuffix = ""; 409 Module m1 = currentClass.getModule(); 410 if (m1.isNamed()) 411 currentSuffix = " (in " + m1 + ")"; 412 Module m2 = memberClass.getModule(); 413 if (m2.isNamed()) 414 memberSuffix = " (in " + m2 + ")"; 415 416 Class<?> c = memberClass; 417 while (c.isArray()) { 418 c = c.getComponentType(); 419 } 420 String memberPackageName = c.getPackageName(); 421 422 String msg = currentClass + currentSuffix + " cannot access "; 423 if (m2.isExported(memberPackageName, m1)) { 424 425 // module access okay so include the modifiers in the message 426 msg += "a member of " + memberClass + memberSuffix + 427 " with modifiers \"" + Modifier.toString(modifiers) + "\""; 428 429 } else { 430 // module access failed 431 msg += memberClass + memberSuffix+ " because " 432 + m2 + " does not export " + memberPackageName; 433 if (m2.isNamed()) msg += " to " + m1; 434 } 435 436 throwIllegalAccessException(msg); 437 } 438 439 /** 440 * Throws IllegalAccessException with the given exception message. 441 */ 442 public static void throwIllegalAccessException(String msg) 443 throws IllegalAccessException 444 { 445 IllegalAccessException e = new IllegalAccessException(msg); 446 ensurePrintStackPropertiesSet(); 447 if (printStackWhenAccessFails) { 448 e.printStackTrace(System.err); 449 } 450 throw e; 451 } 452 }