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