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 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 if (areNestMates(currentClass, memberClass)) { 152 return true; 153 } 154 } 155 156 boolean successSoFar = false; 157 158 if (Modifier.isProtected(modifiers)) { 159 // See if currentClass is a subclass of memberClass 160 if (isSubclassOf(currentClass, memberClass)) { 161 successSoFar = true; 162 } 163 } 164 165 if (!successSoFar && !Modifier.isPrivate(modifiers)) { 166 if (!gotIsSameClassPackage) { 167 isSameClassPackage = isSameClassPackage(currentClass, 168 memberClass); 169 gotIsSameClassPackage = true; 170 } 171 172 if (isSameClassPackage) { 173 successSoFar = true; 174 } 175 } 176 177 if (!successSoFar) { 178 return false; 179 } 180 181 // Additional test for protected instance members 182 // and protected constructors: JLS 6.6.2 183 if (targetClass != null && Modifier.isProtected(modifiers) && 184 targetClass != currentClass) 185 { 186 if (!gotIsSameClassPackage) { 187 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 188 gotIsSameClassPackage = true; 189 } 190 if (!isSameClassPackage) { 191 if (!isSubclassOf(targetClass, currentClass)) { 192 return false; 193 } 194 } 195 } 196 197 return true; 198 } 199 200 /** 201 * Returns {@code true} if memberClass's module exports memberClass's 202 * package to currentModule. 203 */ 204 public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) { 205 Module memberModule = memberClass.getModule(); 206 if (currentModule == memberModule) { 207 // same module (named or unnamed) or both null if called 208 // before module system is initialized, which means we are 209 // dealing with java.base only. 210 return true; 211 } else { 212 String pkg = memberClass.getPackageName(); 213 return memberModule.isExported(pkg, currentModule); 214 } 215 } 216 217 /** 218 * Returns true if two classes in the same package. 219 */ 220 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { 221 if (c1.getClassLoader() != c2.getClassLoader()) 222 return false; 223 return Objects.equals(c1.getPackageName(), c2.getPackageName()); 224 } 225 226 static boolean isSubclassOf(Class<?> queryClass, 227 Class<?> ofClass) 228 { 229 while (queryClass != null) { 230 if (queryClass == ofClass) { 231 return true; 232 } 233 queryClass = queryClass.getSuperclass(); 234 } 235 return false; 236 } 237 238 // fieldNames must contain only interned Strings 239 public static synchronized void registerFieldsToFilter(Class<?> containingClass, 240 String ... fieldNames) { 241 fieldFilterMap = 242 registerFilter(fieldFilterMap, containingClass, fieldNames); 243 } 244 245 // methodNames must contain only interned Strings 246 public static synchronized void registerMethodsToFilter(Class<?> containingClass, 247 String ... methodNames) { 248 methodFilterMap = 249 registerFilter(methodFilterMap, containingClass, methodNames); 250 } 251 252 private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map, 253 Class<?> containingClass, String ... names) { 254 if (map.get(containingClass) != null) { 255 throw new IllegalArgumentException 256 ("Filter already registered: " + containingClass); 257 } 258 map = new HashMap<Class<?>,String[]>(map); 259 map.put(containingClass, names); 260 return map; 261 } 262 263 public static Field[] filterFields(Class<?> containingClass, 264 Field[] fields) { 265 if (fieldFilterMap == null) { 266 // Bootstrapping 267 return fields; 268 } 269 return (Field[])filter(fields, fieldFilterMap.get(containingClass)); 270 } 271 272 public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { 273 if (methodFilterMap == null) { 274 // Bootstrapping 275 return methods; 276 } 277 return (Method[])filter(methods, methodFilterMap.get(containingClass)); 278 } 279 280 private static Member[] filter(Member[] members, String[] filteredNames) { 281 if ((filteredNames == null) || (members.length == 0)) { 282 return members; 283 } 284 int numNewMembers = 0; 285 for (Member member : members) { 286 boolean shouldSkip = false; 287 for (String filteredName : filteredNames) { 288 if (member.getName() == filteredName) { 289 shouldSkip = true; 290 break; 291 } 292 } 293 if (!shouldSkip) { 294 ++numNewMembers; 295 } 296 } 297 Member[] newMembers = 298 (Member[])Array.newInstance(members[0].getClass(), numNewMembers); 299 int destIdx = 0; 300 for (Member member : members) { 301 boolean shouldSkip = false; 302 for (String filteredName : filteredNames) { 303 if (member.getName() == filteredName) { 304 shouldSkip = true; 305 break; 306 } 307 } 308 if (!shouldSkip) { 309 newMembers[destIdx++] = member; 310 } 311 } 312 return newMembers; 313 } 314 315 /** 316 * Tests if the given method is caller-sensitive and the declaring class 317 * is defined by either the bootstrap class loader or platform class loader. 318 */ 319 public static boolean isCallerSensitive(Method m) { 320 final ClassLoader loader = m.getDeclaringClass().getClassLoader(); 321 if (VM.isSystemDomainLoader(loader)) { 322 return m.isAnnotationPresent(CallerSensitive.class); 323 } 324 return false; 325 } 326 327 /** 328 * Returns an IllegalAccessException with an exception message based on 329 * the access that is denied. 330 */ 331 public static IllegalAccessException newIllegalAccessException(Class<?> currentClass, 332 Class<?> memberClass, 333 Class<?> targetClass, 334 int modifiers) 335 throws IllegalAccessException 336 { 337 String currentSuffix = ""; 338 String memberSuffix = ""; 339 Module m1 = currentClass.getModule(); 340 if (m1.isNamed()) 341 currentSuffix = " (in " + m1 + ")"; 342 Module m2 = memberClass.getModule(); 343 if (m2.isNamed()) 344 memberSuffix = " (in " + m2 + ")"; 345 346 String memberPackageName = memberClass.getPackageName(); 347 348 String msg = currentClass + currentSuffix + " cannot access "; 349 if (m2.isExported(memberPackageName, m1)) { 350 351 // module access okay so include the modifiers in the message 352 msg += "a member of " + memberClass + memberSuffix + 353 " with modifiers \"" + Modifier.toString(modifiers) + "\""; 354 355 } else { 356 // module access failed 357 msg += memberClass + memberSuffix+ " because " 358 + m2 + " does not export " + memberPackageName; 359 if (m2.isNamed()) msg += " to " + m1; 360 } 361 362 return new IllegalAccessException(msg); 363 } 364 365 /** 366 * Returns true if {@code currentClass} and {@code memberClass} 367 * are nestmates - that is, if they have the same nesthost as 368 * determined by the VM. 369 */ 370 public static native boolean areNestMates(Class<?> currentClass, 371 Class<?> memberClass); 372 }