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