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", "classData"),
  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      * Verify if a member is public and memberClass is a public type
 209      * in a package that is unconditionally exported and
 210      * return {@code true}if it is granted.
 211      *
 212      * @param memberClass the declaring class of the member being accessed
 213      * @param modifiers the member's access modifiers
 214      * @return {@code true} if the member is public and in a publicly accessible type
 215      */
 216     public static boolean verifyPublicMemberAccess(Class<?> memberClass, int modifiers) {
 217         Module m = memberClass.getModule();
 218         return Modifier.isPublic(modifiers)
 219             && m.isExported(memberClass.getPackageName())
 220             && Modifier.isPublic(Reflection.getClassAccessFlags(memberClass));
 221     }
 222 
 223     /**
 224      * Returns {@code true} if memberClass's module exports memberClass's
 225      * package to currentModule.
 226      */
 227     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 228         Module memberModule = memberClass.getModule();
 229         if (currentModule == memberModule) {
 230             // same module (named or unnamed) or both null if called
 231             // before module system is initialized, which means we are
 232             // dealing with java.base only.
 233             return true;
 234         } else {
 235             String pkg = memberClass.getPackageName();
 236             return memberModule.isExported(pkg, currentModule);
 237         }
 238     }
 239 
 240     /**
 241      * Returns true if two classes in the same package.
 242      */
 243     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 244         if (c1.getClassLoader() != c2.getClassLoader())
 245             return false;
 246         return Objects.equals(c1.getPackageName(), c2.getPackageName());
 247     }
 248 
 249     static boolean isSubclassOf(Class<?> queryClass,
 250                                 Class<?> ofClass)
 251     {
 252         while (queryClass != null) {
 253             if (queryClass == ofClass) {
 254                 return true;
 255             }
 256             queryClass = queryClass.getSuperclass();
 257         }
 258         return false;
 259     }
 260 
 261     // fieldNames must contain only interned Strings
 262     public static synchronized void registerFieldsToFilter(Class<?> containingClass,
 263                                                            Set<String> fieldNames) {
 264         fieldFilterMap =
 265             registerFilter(fieldFilterMap, containingClass, fieldNames);
 266     }
 267 
 268     // methodNames must contain only interned Strings
 269     public static synchronized void registerMethodsToFilter(Class<?> containingClass,
 270                                                             Set<String> methodNames) {
 271         methodFilterMap =
 272             registerFilter(methodFilterMap, containingClass, methodNames);
 273     }
 274 
 275     private static Map<Class<?>, Set<String>> registerFilter(Map<Class<?>, Set<String>> map,
 276                                                              Class<?> containingClass,
 277                                                              Set<String> names) {
 278         if (map.get(containingClass) != null) {
 279             throw new IllegalArgumentException
 280                             ("Filter already registered: " + containingClass);
 281         }
 282         map = new HashMap<>(map);
 283         map.put(containingClass, Set.copyOf(names));
 284         return map;
 285     }
 286 
 287     public static Field[] filterFields(Class<?> containingClass, Field[] fields) {
 288         if (fieldFilterMap == null) {
 289             // Bootstrapping
 290             return fields;
 291         }
 292         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
 293     }
 294 
 295     public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
 296         if (methodFilterMap == null) {
 297             // Bootstrapping
 298             return methods;
 299         }
 300         return (Method[])filter(methods, methodFilterMap.get(containingClass));
 301     }
 302 
 303     private static Member[] filter(Member[] members, Set<String> filteredNames) {
 304         if ((filteredNames == null) || (members.length == 0)) {
 305             return members;
 306         }
 307         Class<?> memberType = members[0].getClass();
 308         if (filteredNames.contains(WILDCARD)) {
 309             return (Member[]) Array.newInstance(memberType, 0);
 310         }
 311         int numNewMembers = 0;
 312         for (Member member : members) {
 313             if (!filteredNames.contains(member.getName())) {
 314                 ++numNewMembers;
 315             }
 316         }
 317         Member[] newMembers = (Member[])Array.newInstance(memberType, numNewMembers);
 318         int destIdx = 0;
 319         for (Member member : members) {
 320             if (!filteredNames.contains(member.getName())) {
 321                 newMembers[destIdx++] = member;
 322             }
 323         }
 324         return newMembers;
 325     }
 326 
 327     /**
 328      * Tests if the given method is caller-sensitive and the declaring class
 329      * is defined by either the bootstrap class loader or platform class loader.
 330      */
 331     public static boolean isCallerSensitive(Method m) {
 332         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 333         if (VM.isSystemDomainLoader(loader)) {
 334             return m.isAnnotationPresent(CallerSensitive.class);
 335         }
 336         return false;
 337     }
 338 
 339     /**
 340      * Returns an IllegalAccessException with an exception message based on
 341      * the access that is denied.
 342      */
 343     public static IllegalAccessException newIllegalAccessException(Class<?> currentClass,
 344                                                                    Class<?> memberClass,
 345                                                                    Class<?> targetClass,
 346                                                                    int modifiers)
 347     {
 348         if (currentClass == null)
 349             return newIllegalAccessException(memberClass, modifiers);
 350 
 351         String currentSuffix = "";
 352         String memberSuffix = "";
 353         Module m1 = currentClass.getModule();
 354         if (m1.isNamed())
 355             currentSuffix = " (in " + m1 + ")";
 356         Module m2 = memberClass.getModule();
 357         if (m2.isNamed())
 358             memberSuffix = " (in " + m2 + ")";
 359 
 360         String memberPackageName = memberClass.getPackageName();
 361 
 362         String msg = currentClass + currentSuffix + " cannot access ";
 363         if (m2.isExported(memberPackageName, m1)) {
 364 
 365             // module access okay so include the modifiers in the message
 366             msg += "a member of " + memberClass + memberSuffix +
 367                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 368 
 369         } else {
 370             // module access failed
 371             msg += memberClass + memberSuffix+ " because "
 372                    + m2 + " does not export " + memberPackageName;
 373             if (m2.isNamed()) msg += " to " + m1;
 374         }
 375 
 376         return new IllegalAccessException(msg);
 377     }
 378 
 379     /**
 380      * Returns an IllegalAccessException with an exception message where
 381      * there is no caller frame.
 382      */
 383     private static IllegalAccessException newIllegalAccessException(Class<?> memberClass,
 384                                                                     int modifiers)
 385     {
 386         String memberSuffix = "";
 387         Module m2 = memberClass.getModule();
 388         if (m2.isNamed())
 389             memberSuffix = " (in " + m2 + ")";
 390 
 391         String memberPackageName = memberClass.getPackageName();
 392 
 393         String msg = "JNI attached native thread (null caller frame) cannot access ";
 394         if (m2.isExported(memberPackageName)) {
 395 
 396             // module access okay so include the modifiers in the message
 397             msg += "a member of " + memberClass + memberSuffix +
 398                 " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 399 
 400         } else {
 401             // module access failed
 402             msg += memberClass + memberSuffix+ " because "
 403                 + m2 + " does not export " + memberPackageName;
 404         }
 405 
 406         return new IllegalAccessException(msg);
 407     }
 408 
 409     /**
 410      * Returns true if {@code currentClass} and {@code memberClass}
 411      * are nestmates - that is, if they have the same nesthost as
 412      * determined by the VM.
 413      */
 414     public static native boolean areNestMates(Class<?> currentClass,
 415                                               Class<?> memberClass);
 416 }