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 
  29 import java.lang.reflect.*;
  30 import java.util.HashMap;
  31 import java.util.Map;
  32 import java.util.Objects;
  33 import jdk.internal.HotSpotIntrinsicCandidate;
  34 import jdk.internal.loader.ClassLoaders;
  35 import jdk.internal.misc.VM;
  36 
  37 /** Common utility routines used by both java.lang and
  38     java.lang.reflect */
  39 
  40 public class Reflection {
  41 
  42     /** Used to filter out fields and methods from certain classes from public
  43         view, where they are sensitive or they may contain VM-internal objects.
  44         These Maps are updated very rarely. Rather than synchronize on
  45         each access, we use copy-on-write */
  46     private static volatile Map<Class<?>,String[]> fieldFilterMap;
  47     private static volatile Map<Class<?>,String[]> methodFilterMap;
  48 
  49     static {
  50         Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>();
  51         map.put(Reflection.class,
  52             new String[] {"fieldFilterMap", "methodFilterMap"});
  53         map.put(System.class, new String[] {"security"});
  54         map.put(Class.class, new String[] {"classLoader"});
  55         fieldFilterMap = map;
  56 
  57         methodFilterMap = new HashMap<>();
  58     }
  59 
  60     /** Returns the class of the caller of the method calling this method,
  61         ignoring frames associated with java.lang.reflect.Method.invoke()
  62         and its implementation. */
  63     @CallerSensitive
  64     @HotSpotIntrinsicCandidate
  65     public static native Class<?> getCallerClass();
  66 
  67     /** Retrieves the access flags written to the class file. For
  68         inner classes these flags may differ from those returned by
  69         Class.getModifiers(), which searches the InnerClasses
  70         attribute to find the source-level access flags. This is used
  71         instead of Class.getModifiers() for run-time access checks due
  72         to compatibility reasons; see 4471811. Only the values of the
  73         low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
  74         valid. */
  75     @HotSpotIntrinsicCandidate
  76     public static native int getClassAccessFlags(Class<?> c);
  77 
  78 
  79     /**
  80      * Ensures that access to a member is granted and throws
  81      * IllegalAccessException if not.
  82      *
  83      * @param currentClass the class performing the access
  84      * @param memberClass the declaring class of the member being accessed
  85      * @param targetClass the class of target object if accessing instance
  86      *                    field or method;
  87      *                    or the declaring class if accessing constructor;
  88      *                    or null if accessing static field or method
  89      * @param modifiers the member's access modifiers
  90      * @throws IllegalAccessException if access to member is denied
  91      */
  92     public static void ensureMemberAccess(Class<?> currentClass,
  93                                           Class<?> memberClass,
  94                                           Class<?> targetClass,
  95                                           int modifiers)
  96         throws IllegalAccessException
  97     {
  98         if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
  99             throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
 100         }
 101     }
 102 
 103     /**
 104      * Verify access to a member and return {@code true} if it is granted.
 105      *
 106      * @param currentClass the class performing the access
 107      * @param memberClass the declaring class of the member being accessed
 108      * @param targetClass the class of target object if accessing instance
 109      *                    field or method;
 110      *                    or the declaring class if accessing constructor;
 111      *                    or null if accessing static field or method
 112      * @param modifiers the member's access modifiers
 113      * @return {@code true} if access to member is granted
 114      */
 115     public static boolean verifyMemberAccess(Class<?> currentClass,
 116                                              Class<?> memberClass,
 117                                              Class<?> targetClass,
 118                                              int modifiers)
 119     {
 120         if (currentClass == memberClass) {
 121             // Always succeeds
 122             return true;
 123         }
 124 
 125         if (!verifyModuleAccess(currentClass.getModule(), memberClass)) {
 126             return false;
 127         }
 128 
 129         boolean gotIsSameClassPackage = false;
 130         boolean isSameClassPackage = false;
 131 
 132         if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
 133             isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 134             gotIsSameClassPackage = true;
 135             if (!isSameClassPackage) {
 136                 return false;
 137             }
 138         }
 139 
 140         // At this point we know that currentClass can access memberClass.
 141 
 142         if (Modifier.isPublic(modifiers)) {
 143             return true;
 144         }
 145 
 146         // Check for nestmate access if member is private
 147         if (Modifier.isPrivate(modifiers)) {
 148             // Note: targetClass may be outside the nest, but that is okay
 149             //       as long as memberClass is in the nest.
 150             if (areNestMates(currentClass, memberClass)) {
 151                 return true;
 152             }
 153         }
 154 
 155         boolean successSoFar = false;
 156 
 157         if (Modifier.isProtected(modifiers)) {
 158             // See if currentClass is a subclass of memberClass
 159             if (isSubclassOf(currentClass, memberClass)) {
 160                 successSoFar = true;
 161             }
 162         }
 163 
 164         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
 165             if (!gotIsSameClassPackage) {
 166                 isSameClassPackage = isSameClassPackage(currentClass,
 167                                                         memberClass);
 168                 gotIsSameClassPackage = true;
 169             }
 170 
 171             if (isSameClassPackage) {
 172                 successSoFar = true;
 173             }
 174         }
 175 
 176         if (!successSoFar) {
 177             return false;
 178         }
 179 
 180         // Additional test for protected instance members
 181         // and protected constructors: JLS 6.6.2
 182         if (targetClass != null && Modifier.isProtected(modifiers) &&
 183             targetClass != currentClass)
 184         {
 185             if (!gotIsSameClassPackage) {
 186                 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 187                 gotIsSameClassPackage = true;
 188             }
 189             if (!isSameClassPackage) {
 190                 if (!isSubclassOf(targetClass, currentClass)) {
 191                     return false;
 192                 }
 193             }
 194         }
 195 
 196         return true;
 197     }
 198 
 199     /**
 200      * Returns {@code true} if memberClass's module exports memberClass's
 201      * package to currentModule.
 202      */
 203     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 204         Module memberModule = memberClass.getModule();
 205         if (currentModule == memberModule) {
 206             // same module (named or unnamed) or both null if called
 207             // before module system is initialized, which means we are
 208             // dealing with java.base only.
 209             return true;
 210         } else {
 211             String pkg = memberClass.getPackageName();
 212             return memberModule.isExported(pkg, currentModule);
 213         }
 214     }
 215 
 216     /**
 217      * Returns true if two classes in the same package.
 218      */
 219     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 220         if (c1.getClassLoader() != c2.getClassLoader())
 221             return false;
 222         return Objects.equals(c1.getPackageName(), c2.getPackageName());
 223     }
 224 
 225     static boolean isSubclassOf(Class<?> queryClass,
 226                                 Class<?> ofClass)
 227     {
 228         while (queryClass != null) {
 229             if (queryClass == ofClass) {
 230                 return true;
 231             }
 232             queryClass = queryClass.getSuperclass();
 233         }
 234         return false;
 235     }
 236 
 237     // fieldNames must contain only interned Strings
 238     public static synchronized void registerFieldsToFilter(Class<?> containingClass,
 239                                               String ... fieldNames) {
 240         fieldFilterMap =
 241             registerFilter(fieldFilterMap, containingClass, fieldNames);
 242     }
 243 
 244     // methodNames must contain only interned Strings
 245     public static synchronized void registerMethodsToFilter(Class<?> containingClass,
 246                                               String ... methodNames) {
 247         methodFilterMap =
 248             registerFilter(methodFilterMap, containingClass, methodNames);
 249     }
 250 
 251     private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
 252             Class<?> containingClass, String ... names) {
 253         if (map.get(containingClass) != null) {
 254             throw new IllegalArgumentException
 255                             ("Filter already registered: " + containingClass);
 256         }
 257         map = new HashMap<Class<?>,String[]>(map);
 258         map.put(containingClass, names);
 259         return map;
 260     }
 261 
 262     public static Field[] filterFields(Class<?> containingClass,
 263                                        Field[] fields) {
 264         if (fieldFilterMap == null) {
 265             // Bootstrapping
 266             return fields;
 267         }
 268         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
 269     }
 270 
 271     public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
 272         if (methodFilterMap == null) {
 273             // Bootstrapping
 274             return methods;
 275         }
 276         return (Method[])filter(methods, methodFilterMap.get(containingClass));
 277     }
 278 
 279     private static Member[] filter(Member[] members, String[] filteredNames) {
 280         if ((filteredNames == null) || (members.length == 0)) {
 281             return members;
 282         }
 283         int numNewMembers = 0;
 284         for (Member member : members) {
 285             boolean shouldSkip = false;
 286             for (String filteredName : filteredNames) {
 287                 if (member.getName() == filteredName) {
 288                     shouldSkip = true;
 289                     break;
 290                 }
 291             }
 292             if (!shouldSkip) {
 293                 ++numNewMembers;
 294             }
 295         }
 296         Member[] newMembers =
 297             (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
 298         int destIdx = 0;
 299         for (Member member : members) {
 300             boolean shouldSkip = false;
 301             for (String filteredName : filteredNames) {
 302                 if (member.getName() == filteredName) {
 303                     shouldSkip = true;
 304                     break;
 305                 }
 306             }
 307             if (!shouldSkip) {
 308                 newMembers[destIdx++] = member;
 309             }
 310         }
 311         return newMembers;
 312     }
 313 
 314     /**
 315      * Tests if the given method is caller-sensitive and the declaring class
 316      * is defined by either the bootstrap class loader or platform class loader.
 317      */
 318     public static boolean isCallerSensitive(Method m) {
 319         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 320         if (VM.isSystemDomainLoader(loader)) {
 321             return m.isAnnotationPresent(CallerSensitive.class);
 322         }
 323         return false;
 324     }
 325 
 326     /**
 327      * Returns an IllegalAccessException with an exception message based on
 328      * the access that is denied.
 329      */
 330     public static IllegalAccessException newIllegalAccessException(Class<?> currentClass,
 331                                                                    Class<?> memberClass,
 332                                                                    Class<?> targetClass,
 333                                                                    int modifiers)
 334         throws IllegalAccessException
 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 true if {@code currentClass} and {@code memberClass}
 366      * are nestmates - that is, if they have the same nesthost as
 367      * determined by the VM.
 368      */
 369     public static native boolean areNestMates(Class<?> currentClass,
 370                                               Class<?> memberClass);
 371 }