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         Class<?> memberType = members[0].getClass();
 288         if (filteredNames.contains(WILDCARD)) {
 289             return (Member[]) Array.newInstance(memberType, 0);
 290         }
 291         int numNewMembers = 0;
 292         for (Member member : members) {
 293             if (!filteredNames.contains(member.getName())) {
 294                 ++numNewMembers;
 295             }
 296         }
 297         Member[] newMembers = (Member[])Array.newInstance(memberType, numNewMembers);
 298         int destIdx = 0;
 299         for (Member member : members) {
 300             if (!filteredNames.contains(member.getName())) {
 301                 newMembers[destIdx++] = member;
 302             }
 303         }
 304         return newMembers;
 305     }
 306 
 307     /**
 308      * Tests if the given method is caller-sensitive and the declaring class
 309      * is defined by either the bootstrap class loader or platform class loader.
 310      */
 311     public static boolean isCallerSensitive(Method m) {
 312         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 313         if (VM.isSystemDomainLoader(loader)) {
 314             return m.isAnnotationPresent(CallerSensitive.class);
 315         }
 316         return false;
 317     }
 318 
 319     /**
 320      * Returns an IllegalAccessException with an exception message based on
 321      * the access that is denied.
 322      */
 323     public static IllegalAccessException newIllegalAccessException(Class<?> currentClass,
 324                                                                    Class<?> memberClass,
 325                                                                    Class<?> targetClass,
 326                                                                    int modifiers)
 327         throws IllegalAccessException
 328     {
 329         String currentSuffix = "";
 330         String memberSuffix = "";
 331         Module m1 = currentClass.getModule();
 332         if (m1.isNamed())
 333             currentSuffix = " (in " + m1 + ")";
 334         Module m2 = memberClass.getModule();
 335         if (m2.isNamed())
 336             memberSuffix = " (in " + m2 + ")";
 337 
 338         String memberPackageName = memberClass.getPackageName();
 339 
 340         String msg = currentClass + currentSuffix + " cannot access ";
 341         if (m2.isExported(memberPackageName, m1)) {
 342 
 343             // module access okay so include the modifiers in the message
 344             msg += "a member of " + memberClass + memberSuffix +
 345                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 346 
 347         } else {
 348             // module access failed
 349             msg += memberClass + memberSuffix+ " because "
 350                    + m2 + " does not export " + memberPackageName;
 351             if (m2.isNamed()) msg += " to " + m1;
 352         }
 353 
 354         return new IllegalAccessException(msg);
 355     }
 356 
 357     /**
 358      * Returns true if {@code currentClass} and {@code memberClass}
 359      * are nestmates - that is, if they have the same nesthost as
 360      * determined by the VM.
 361      */
 362     public static native boolean areNestMates(Class<?> currentClass,
 363                                               Class<?> memberClass);
 364 }