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