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 public static void ensureMemberAccess(Class<?> currentClass, 88 Class<?> memberClass, 89 Object target, 90 int modifiers) 91 throws IllegalAccessException 92 { 93 if (currentClass == null || memberClass == null) { 94 throw new InternalError(); 95 } 96 97 if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) { 98 throwIllegalAccessException(currentClass, memberClass, target, modifiers); 99 } 100 } 101 102 public static boolean verifyMemberAccess(Class<?> currentClass, 103 // Declaring class of field 104 // or method 105 Class<?> memberClass, 106 // May be NULL in case of statics 107 Object target, 108 int modifiers) 109 { 110 // Verify that currentClass can access a field, method, or 111 // constructor of memberClass, where that member's access bits are 112 // "modifiers". 113 114 boolean gotIsSameClassPackage = false; 115 boolean isSameClassPackage = false; 116 117 if (currentClass == memberClass) { 118 // Always succeeds 119 return true; 120 } 121 122 if (!verifyModuleAccess(currentClass, memberClass)) { 123 return false; 124 } 125 126 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { 127 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 128 gotIsSameClassPackage = true; 129 if (!isSameClassPackage) { 130 return false; 131 } 132 } 133 134 // At this point we know that currentClass can access memberClass. 135 136 if (Modifier.isPublic(modifiers)) { 137 return true; 138 } 139 140 boolean successSoFar = false; 141 142 if (Modifier.isProtected(modifiers)) { 143 // See if currentClass is a subclass of memberClass 144 if (isSubclassOf(currentClass, memberClass)) { 145 successSoFar = true; 146 } 147 } 148 149 if (!successSoFar && !Modifier.isPrivate(modifiers)) { 150 if (!gotIsSameClassPackage) { 151 isSameClassPackage = isSameClassPackage(currentClass, 152 memberClass); 153 gotIsSameClassPackage = true; 154 } 155 156 if (isSameClassPackage) { 157 successSoFar = true; 158 } 159 } 160 161 if (!successSoFar) { 162 return false; 163 } 164 165 if (Modifier.isProtected(modifiers)) { 166 // Additional test for protected members: JLS 6.6.2 167 Class<?> targetClass = (target == null ? memberClass : target.getClass()); 168 if (targetClass != currentClass) { 169 if (!gotIsSameClassPackage) { 170 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 171 gotIsSameClassPackage = true; 172 } 173 if (!isSameClassPackage) { 174 if (!isSubclassOf(targetClass, currentClass)) { 175 return false; 176 } 177 } 178 } 179 } 180 181 return true; 182 } 183 184 /** 185 * Returns {@code true} if memberClass's's module exports memberClass's 186 * package to currentClass's module. 187 */ 188 public static boolean verifyModuleAccess(Class<?> currentClass, 189 Class<?> memberClass) { 190 return verifyModuleAccess(currentClass.getModule(), memberClass); 191 } 192 193 public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) { 194 Module memberModule = memberClass.getModule(); 195 196 // module may be null during startup (initLevel 0) 197 if (currentModule == memberModule) 198 return true; // same module (named or unnamed) 199 200 // memberClass may be primitive or array class 201 Class<?> c = memberClass; 202 while (c.isArray()) { 203 c = c.getComponentType(); 204 } 205 if (c.isPrimitive()) 206 return true; 207 208 // check that memberModule exports the package to currentModule 209 return memberModule.isExported(c.getPackageName(), currentModule); 210 } 211 212 /** 213 * Returns true if two classes in the same package. 214 */ 215 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { 216 if (c1.getClassLoader() != c2.getClassLoader()) 217 return false; 218 while (c1.isArray()) 219 c1 = c1.getComponentType(); 220 while (c2.isArray()) 221 c2 = c2.getComponentType(); 222 return Objects.equals(c1.getPackageName(), c2.getPackageName()); 223 } 224 225 static boolean isSubclassOf(Class<?> queryClass, 226 Class<?> ofClass) 227 { 228 while (queryClass != null) { 229 if (queryClass == ofClass) { 230 return true; 231 } 232 queryClass = queryClass.getSuperclass(); 233 } 234 return false; 235 } 236 237 // fieldNames must contain only interned Strings 238 public static synchronized void registerFieldsToFilter(Class<?> containingClass, 239 String ... fieldNames) { 240 fieldFilterMap = 241 registerFilter(fieldFilterMap, containingClass, fieldNames); 242 } 243 244 // methodNames must contain only interned Strings 245 public static synchronized void registerMethodsToFilter(Class<?> containingClass, 246 String ... methodNames) { 247 methodFilterMap = 248 registerFilter(methodFilterMap, containingClass, methodNames); 249 } 250 251 private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map, 252 Class<?> containingClass, String ... names) { 253 if (map.get(containingClass) != null) { 254 throw new IllegalArgumentException 255 ("Filter already registered: " + containingClass); 256 } 257 map = new HashMap<Class<?>,String[]>(map); 258 map.put(containingClass, names); 259 return map; 260 } 261 262 public static Field[] filterFields(Class<?> containingClass, 263 Field[] fields) { 264 if (fieldFilterMap == null) { 265 // Bootstrapping 266 return fields; 267 } 268 return (Field[])filter(fields, fieldFilterMap.get(containingClass)); 269 } 270 271 public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { 272 if (methodFilterMap == null) { 273 // Bootstrapping 274 return methods; 275 } 276 return (Method[])filter(methods, methodFilterMap.get(containingClass)); 277 } 278 279 private static Member[] filter(Member[] members, String[] filteredNames) { 280 if ((filteredNames == null) || (members.length == 0)) { 281 return members; 282 } 283 int numNewMembers = 0; 284 for (Member member : members) { 285 boolean shouldSkip = false; 286 for (String filteredName : filteredNames) { 287 if (member.getName() == filteredName) { 288 shouldSkip = true; 289 break; 290 } 291 } 292 if (!shouldSkip) { 293 ++numNewMembers; 294 } 295 } 296 Member[] newMembers = 297 (Member[])Array.newInstance(members[0].getClass(), numNewMembers); 298 int destIdx = 0; 299 for (Member member : members) { 300 boolean shouldSkip = false; 301 for (String filteredName : filteredNames) { 302 if (member.getName() == filteredName) { 303 shouldSkip = true; 304 break; 305 } 306 } 307 if (!shouldSkip) { 308 newMembers[destIdx++] = member; 309 } 310 } 311 return newMembers; 312 } 313 314 /** 315 * Tests if the given method is caller-sensitive and the declaring class 316 * is defined by either the bootstrap class loader or platform class loader. 317 */ 318 public static boolean isCallerSensitive(Method m) { 319 final ClassLoader loader = m.getDeclaringClass().getClassLoader(); 320 if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader)) { 321 return m.isAnnotationPresent(CallerSensitive.class); 322 } 323 return false; 324 } 325 326 private static boolean isExtClassLoader(ClassLoader loader) { 327 ClassLoader cl = ClassLoader.getSystemClassLoader(); 328 while (cl != null) { 329 if (cl.getParent() == null && cl == loader) { 330 return true; 331 } 332 cl = cl.getParent(); 333 } 334 return false; 335 } 336 337 338 // true to print a stack trace when IAE is thrown 339 private static volatile boolean printStackWhenAccessFails; 340 341 // true if printStackWhenAccessFails has been initialized 342 private static volatile boolean printStackWhenAccessFailsSet; 343 344 private static void printStackTraceIfNeeded(Throwable e) { 345 if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) { 346 String s = GetPropertyAction.privilegedGetProperty( 347 "sun.reflect.debugModuleAccessChecks"); 348 printStackWhenAccessFails = 349 (s != null && !s.equalsIgnoreCase("false")); 350 printStackWhenAccessFailsSet = true; 351 } 352 if (printStackWhenAccessFails) { 353 e.printStackTrace(); 354 } 355 } 356 357 /** 358 * Throws IllegalAccessException with the an exception message based on 359 * the access that is denied. 360 */ 361 private static void throwIllegalAccessException(Class<?> currentClass, 362 Class<?> memberClass, 363 Object target, 364 int modifiers) 365 throws IllegalAccessException 366 { 367 String currentSuffix = ""; 368 String memberSuffix = ""; 369 Module m1 = currentClass.getModule(); 370 if (m1.isNamed()) 371 currentSuffix = " (in " + m1 + ")"; 372 Module m2 = memberClass.getModule(); 373 if (m2.isNamed()) 374 memberSuffix = " (in " + m2 + ")"; 375 376 Class<?> c = memberClass; 377 while (c.isArray()) { 378 c = c.getComponentType(); 379 } 380 String memberPackageName = c.getPackageName(); 381 382 String msg = currentClass + currentSuffix + " cannot access "; 383 if (m2.isExported(memberPackageName, m1)) { 384 385 // module access okay so include the modifiers in the message 386 msg += "a member of " + memberClass + memberSuffix + 387 " with modifiers \"" + Modifier.toString(modifiers) + "\""; 388 389 } else { 390 // module access failed 391 msg += memberClass + memberSuffix+ " because " 392 + m2 + " does not export " + memberPackageName; 393 if (m2.isNamed()) msg += " to " + m1; 394 } 395 396 throwIllegalAccessException(msg); 397 } 398 399 /** 400 * Throws IllegalAccessException with the given exception message. 401 */ 402 public static void throwIllegalAccessException(String msg) 403 throws IllegalAccessException 404 { 405 IllegalAccessException e = new IllegalAccessException(msg); 406 printStackTraceIfNeeded(e); 407 throw e; 408 } 409 410 /** 411 * Throws InaccessibleObjectException with the given exception message. 412 */ 413 public static void throwInaccessibleObjectException(String msg) { 414 InaccessibleObjectException e = new InaccessibleObjectException(msg); 415 printStackTraceIfNeeded(e); 416 throw e; 417 } 418 419 }