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