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