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