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 }