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.VM; 35 import sun.security.action.GetPropertyAction; 36 37 /** Common utility routines used by both java.lang and 38 java.lang.reflect */ 39 40 public class Reflection { 41 42 /** Used to filter out fields and methods from certain classes from public 43 view, where they are sensitive or they may contain VM-internal objects. 44 These Maps are updated very rarely. Rather than synchronize on 45 each access, we use copy-on-write */ 46 private static volatile Map<Class<?>,String[]> fieldFilterMap; 47 private static volatile Map<Class<?>,String[]> methodFilterMap; 48 49 static { 50 Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>(); 51 map.put(Reflection.class, 52 new String[] {"fieldFilterMap", "methodFilterMap"}); 53 map.put(System.class, new String[] {"security"}); 54 map.put(Class.class, new String[] {"classLoader"}); 55 fieldFilterMap = map; 56 57 methodFilterMap = new HashMap<>(); 58 } 59 60 /** Returns the class of the caller of the method calling this method, 61 ignoring frames associated with java.lang.reflect.Method.invoke() 62 and its implementation. */ 63 @CallerSensitive 64 @HotSpotIntrinsicCandidate 65 public static native Class<?> getCallerClass(); 66 67 /** 68 * @deprecated This method will be removed. 69 * This method is a private JDK API and retained temporarily to 70 * simplify the implementation of sun.misc.Reflection.getCallerClass. 71 */ 72 @Deprecated(forRemoval=true) 73 public static native Class<?> getCallerClass(int depth); 74 75 /** Retrieves the access flags written to the class file. For 76 inner classes these flags may differ from those returned by 77 Class.getModifiers(), which searches the InnerClasses 78 attribute to find the source-level access flags. This is used 79 instead of Class.getModifiers() for run-time access checks due 80 to compatibility reasons; see 4471811. Only the values of the 81 low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be 82 valid. */ 83 @HotSpotIntrinsicCandidate 84 public static native int getClassAccessFlags(Class<?> c); 85 86 87 /** 88 * Ensures that access to a member is granted and throws 89 * IllegalAccessException if not. 90 * 91 * @param currentClass the class performing the access 92 * @param memberClass the declaring class of the member being accessed 93 * @param targetClass the class of target object if accessing instance 94 * field or method; 95 * or the declaring class if accessing constructor; 96 * or null if accessing static field or method 97 * @param modifiers the member's access modifiers 98 * @throws IllegalAccessException if access to member is denied 99 */ 100 public static void ensureMemberAccess(Class<?> currentClass, 101 Class<?> memberClass, 102 Class<?> targetClass, 103 int modifiers) 104 throws IllegalAccessException 105 { 106 if (currentClass == null || memberClass == null) { 107 throw new InternalError(); 108 } 109 110 if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) { 111 throwIllegalAccessException(currentClass, memberClass, targetClass, modifiers); 112 } 113 } 114 115 private static boolean verifyMemberAccess(Class<?> currentClass, 116 Class<?> memberClass, 117 Class<?> targetClass, 118 int modifiers) 119 { 120 // Verify that currentClass can access a field, method, or 121 // constructor of memberClass, where that member's access bits are 122 // "modifiers". 123 124 boolean gotIsSameClassPackage = false; 125 boolean isSameClassPackage = false; 126 127 if (currentClass == memberClass) { 128 // Always succeeds 129 return true; 130 } 131 132 if (!verifyModuleAccess(currentClass, memberClass)) { 133 return false; 134 } 135 136 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { 137 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 138 gotIsSameClassPackage = true; 139 if (!isSameClassPackage) { 140 return false; 141 } 142 } 143 144 // At this point we know that currentClass can access memberClass. 145 146 if (Modifier.isPublic(modifiers)) { 147 return true; 148 } 149 150 boolean successSoFar = false; 151 152 if (Modifier.isProtected(modifiers)) { 153 // See if currentClass is a subclass of memberClass 154 if (isSubclassOf(currentClass, memberClass)) { 155 successSoFar = true; 156 } 157 } 158 159 if (!successSoFar && !Modifier.isPrivate(modifiers)) { 160 if (!gotIsSameClassPackage) { 161 isSameClassPackage = isSameClassPackage(currentClass, 162 memberClass); 163 gotIsSameClassPackage = true; 164 } 165 166 if (isSameClassPackage) { 167 successSoFar = true; 168 } 169 } 170 171 if (!successSoFar) { 172 return false; 173 } 174 175 // Additional test for protected instance members 176 // and protected constructors: JLS 6.6.2 177 if (targetClass != null && Modifier.isProtected(modifiers) && 178 targetClass != currentClass) 179 { 180 if (!gotIsSameClassPackage) { 181 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 182 gotIsSameClassPackage = true; 183 } 184 if (!isSameClassPackage) { 185 if (!isSubclassOf(targetClass, currentClass)) { 186 return false; 187 } 188 } 189 } 190 191 return true; 192 } 193 194 /** 195 * Returns {@code true} if memberClass's's module exports memberClass's 196 * package to currentClass's module. 197 */ 198 public static boolean verifyModuleAccess(Class<?> currentClass, 199 Class<?> memberClass) { 200 return verifyModuleAccess(currentClass.getModule(), memberClass); 201 } 202 203 public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) { 204 Module memberModule = memberClass.getModule(); 205 206 // module may be null during startup (initLevel 0) 207 if (currentModule == memberModule) 208 return true; // same module (named or unnamed) 209 210 // memberClass may be primitive or array class 211 Class<?> c = memberClass; 212 while (c.isArray()) { 213 c = c.getComponentType(); 214 } 215 if (c.isPrimitive()) 216 return true; 217 218 // check that memberModule exports the package to currentModule 219 return memberModule.isExported(c.getPackageName(), currentModule); 220 } 221 222 /** 223 * Returns true if two classes in the same package. 224 */ 225 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { 226 if (c1.getClassLoader() != c2.getClassLoader()) 227 return false; 228 while (c1.isArray()) 229 c1 = c1.getComponentType(); 230 while (c2.isArray()) 231 c2 = c2.getComponentType(); 232 return Objects.equals(c1.getPackageName(), c2.getPackageName()); 233 } 234 235 static boolean isSubclassOf(Class<?> queryClass, 236 Class<?> ofClass) 237 { 238 while (queryClass != null) { 239 if (queryClass == ofClass) { 240 return true; 241 } 242 queryClass = queryClass.getSuperclass(); 243 } 244 return false; 245 } 246 247 // fieldNames must contain only interned Strings 248 public static synchronized void registerFieldsToFilter(Class<?> containingClass, 249 String ... fieldNames) { 250 fieldFilterMap = 251 registerFilter(fieldFilterMap, containingClass, fieldNames); 252 } 253 254 // methodNames must contain only interned Strings 255 public static synchronized void registerMethodsToFilter(Class<?> containingClass, 256 String ... methodNames) { 257 methodFilterMap = 258 registerFilter(methodFilterMap, containingClass, methodNames); 259 } 260 261 private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map, 262 Class<?> containingClass, String ... names) { 263 if (map.get(containingClass) != null) { 264 throw new IllegalArgumentException 265 ("Filter already registered: " + containingClass); 266 } 267 map = new HashMap<Class<?>,String[]>(map); 268 map.put(containingClass, names); 269 return map; 270 } 271 272 public static Field[] filterFields(Class<?> containingClass, 273 Field[] fields) { 274 if (fieldFilterMap == null) { 275 // Bootstrapping 276 return fields; 277 } 278 return (Field[])filter(fields, fieldFilterMap.get(containingClass)); 279 } 280 281 public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { 282 if (methodFilterMap == null) { 283 // Bootstrapping 284 return methods; 285 } 286 return (Method[])filter(methods, methodFilterMap.get(containingClass)); 287 } 288 289 private static Member[] filter(Member[] members, String[] filteredNames) { 290 if ((filteredNames == null) || (members.length == 0)) { 291 return members; 292 } 293 int numNewMembers = 0; 294 for (Member member : members) { 295 boolean shouldSkip = false; 296 for (String filteredName : filteredNames) { 297 if (member.getName() == filteredName) { 298 shouldSkip = true; 299 break; 300 } 301 } 302 if (!shouldSkip) { 303 ++numNewMembers; 304 } 305 } 306 Member[] newMembers = 307 (Member[])Array.newInstance(members[0].getClass(), numNewMembers); 308 int destIdx = 0; 309 for (Member member : members) { 310 boolean shouldSkip = false; 311 for (String filteredName : filteredNames) { 312 if (member.getName() == filteredName) { 313 shouldSkip = true; 314 break; 315 } 316 } 317 if (!shouldSkip) { 318 newMembers[destIdx++] = member; 319 } 320 } 321 return newMembers; 322 } 323 324 /** 325 * Tests if the given method is caller-sensitive and the declaring class 326 * is defined by either the bootstrap class loader or platform class loader. 327 */ 328 public static boolean isCallerSensitive(Method m) { 329 final ClassLoader loader = m.getDeclaringClass().getClassLoader(); 330 if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader)) { 331 return m.isAnnotationPresent(CallerSensitive.class); 332 } 333 return false; 334 } 335 336 private static boolean isExtClassLoader(ClassLoader loader) { 337 ClassLoader cl = ClassLoader.getSystemClassLoader(); 338 while (cl != null) { 339 if (cl.getParent() == null && cl == loader) { 340 return true; 341 } 342 cl = cl.getParent(); 343 } 344 return false; 345 } 346 347 348 // true to print a stack trace when IAE is thrown 349 private static volatile boolean printStackWhenAccessFails; 350 351 // true if printStackWhenAccessFails has been initialized 352 private static volatile boolean printStackWhenAccessFailsSet; 353 354 private static void printStackTraceIfNeeded(Throwable e) { 355 if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) { 356 String s = GetPropertyAction.privilegedGetProperty( 357 "sun.reflect.debugModuleAccessChecks"); 358 printStackWhenAccessFails = 359 (s != null && !s.equalsIgnoreCase("false")); 360 printStackWhenAccessFailsSet = true; 361 } 362 if (printStackWhenAccessFails) { 363 e.printStackTrace(); 364 } 365 } 366 367 /** 368 * Throws IllegalAccessException with the an exception message based on 369 * the access that is denied. 370 */ 371 private static void throwIllegalAccessException(Class<?> currentClass, 372 Class<?> memberClass, 373 Object target, 374 int modifiers) 375 throws IllegalAccessException 376 { 377 String currentSuffix = ""; 378 String memberSuffix = ""; 379 Module m1 = currentClass.getModule(); 380 if (m1.isNamed()) 381 currentSuffix = " (in " + m1 + ")"; 382 Module m2 = memberClass.getModule(); 383 if (m2.isNamed()) 384 memberSuffix = " (in " + m2 + ")"; 385 386 Class<?> c = memberClass; 387 while (c.isArray()) { 388 c = c.getComponentType(); 389 } 390 String memberPackageName = c.getPackageName(); 391 392 String msg = currentClass + currentSuffix + " cannot access "; 393 if (m2.isExported(memberPackageName, m1)) { 394 395 // module access okay so include the modifiers in the message 396 msg += "a member of " + memberClass + memberSuffix + 397 " with modifiers \"" + Modifier.toString(modifiers) + "\""; 398 399 } else { 400 // module access failed 401 msg += memberClass + memberSuffix+ " because " 402 + m2 + " does not export " + memberPackageName; 403 if (m2.isNamed()) msg += " to " + m1; 404 } 405 406 throwIllegalAccessException(msg); 407 } 408 409 /** 410 * Throws IllegalAccessException with the given exception message. 411 */ 412 public static void throwIllegalAccessException(String msg) 413 throws IllegalAccessException 414 { 415 IllegalAccessException e = new IllegalAccessException(msg); 416 printStackTraceIfNeeded(e); 417 throw e; 418 } 419 420 /** 421 * Throws InaccessibleObjectException with the given exception message. 422 */ 423 public static void throwInaccessibleObjectException(String msg) { 424 InaccessibleObjectException e = new InaccessibleObjectException(msg); 425 printStackTraceIfNeeded(e); 426 throw e; 427 } 428 429 }