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