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