1 /*
   2  * Copyright (c) 2001, 2017, 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     /**
  68      * @deprecated This method will be removed.
  69      * This method is a private JDK API and retained temporarily to
  70      * simplify the implementation of sun.misc.Reflection.getCallerClass.
  71      */
  72     @Deprecated(forRemoval=true)
  73     public static native Class<?> getCallerClass(int depth);
  74 
  75     /** Retrieves the access flags written to the class file. For
  76         inner classes these flags may differ from those returned by
  77         Class.getModifiers(), which searches the InnerClasses
  78         attribute to find the source-level access flags. This is used
  79         instead of Class.getModifiers() for run-time access checks due
  80         to compatibility reasons; see 4471811. Only the values of the
  81         low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
  82         valid. */
  83     @HotSpotIntrinsicCandidate
  84     public static native int getClassAccessFlags(Class<?> c);
  85 
  86 
  87     /**
  88      * Ensures that access to a member is granted and throws
  89      * IllegalAccessException if not.
  90      *
  91      * @param currentClass the class performing the access
  92      * @param memberClass the declaring class of the member being accessed
  93      * @param targetClass the class of target object if accessing instance
  94      *                    field or method;
  95      *                    or the declaring class if accessing constructor;
  96      *                    or null if accessing static field or method
  97      * @param modifiers the member's access modifiers
  98      * @throws IllegalAccessException if access to member is denied
  99      */
 100     public static void ensureMemberAccess(Class<?> currentClass,
 101                                           Class<?> memberClass,
 102                                           Class<?> targetClass,
 103                                           int modifiers)
 104         throws IllegalAccessException
 105     {
 106         if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
 107             throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
 108         }
 109     }
 110 
 111     /**
 112      * Verify access to a member and return {@code true} if it is granted.
 113      *
 114      * @param currentClass the class performing the access
 115      * @param memberClass the declaring class of the member being accessed
 116      * @param targetClass the class of target object if accessing instance
 117      *                    field or method;
 118      *                    or the declaring class if accessing constructor;
 119      *                    or null if accessing static field or method
 120      * @param modifiers the member's access modifiers
 121      * @return {@code true} if access to member is granted
 122      */
 123     public static boolean verifyMemberAccess(Class<?> currentClass,
 124                                              Class<?> memberClass,
 125                                              Class<?> targetClass,
 126                                              int modifiers)
 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         boolean successSoFar = false;
 155 
 156         if (Modifier.isProtected(modifiers)) {
 157             // See if currentClass is a subclass of memberClass
 158             if (isSubclassOf(currentClass, memberClass)) {
 159                 successSoFar = true;
 160             }
 161         }
 162 
 163         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
 164             if (!gotIsSameClassPackage) {
 165                 isSameClassPackage = isSameClassPackage(currentClass,
 166                                                         memberClass);
 167                 gotIsSameClassPackage = true;
 168             }
 169 
 170             if (isSameClassPackage) {
 171                 successSoFar = true;
 172             }
 173         }
 174 
 175         if (!successSoFar) {
 176             return false;
 177         }
 178 
 179         // Additional test for protected instance members
 180         // and protected constructors: JLS 6.6.2
 181         if (targetClass != null && Modifier.isProtected(modifiers) &&
 182             targetClass != currentClass)
 183         {
 184             if (!gotIsSameClassPackage) {
 185                 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 186                 gotIsSameClassPackage = true;
 187             }
 188             if (!isSameClassPackage) {
 189                 if (!isSubclassOf(targetClass, currentClass)) {
 190                     return false;
 191                 }
 192             }
 193         }
 194 
 195         return true;
 196     }
 197 
 198     /**
 199      * Returns {@code true} if memberClass's module exports memberClass's
 200      * package to currentModule.
 201      */
 202     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 203         Module memberModule = memberClass.getModule();
 204         if (currentModule == memberModule) {
 205             // same module (named or unnamed) or both null if called
 206             // before module system is initialized, which means we are
 207             // dealing with java.base only.
 208             return true;
 209         } else {
 210             String pkg = memberClass.getPackageName();
 211             return memberModule.isExported(pkg, currentModule);
 212         }
 213     }
 214 
 215     /**
 216      * Returns true if two classes in the same package.
 217      */
 218     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 219         if (c1.getClassLoader() != c2.getClassLoader())
 220             return false;
 221         return Objects.equals(c1.getPackageName(), c2.getPackageName());
 222     }
 223 
 224     static boolean isSubclassOf(Class<?> queryClass,
 225                                 Class<?> ofClass)
 226     {
 227         while (queryClass != null) {
 228             if (queryClass == ofClass) {
 229                 return true;
 230             }
 231             queryClass = queryClass.getSuperclass();
 232         }
 233         return false;
 234     }
 235 
 236     // fieldNames must contain only interned Strings
 237     public static synchronized void registerFieldsToFilter(Class<?> containingClass,
 238                                               String ... fieldNames) {
 239         fieldFilterMap =
 240             registerFilter(fieldFilterMap, containingClass, fieldNames);
 241     }
 242 
 243     // methodNames must contain only interned Strings
 244     public static synchronized void registerMethodsToFilter(Class<?> containingClass,
 245                                               String ... methodNames) {
 246         methodFilterMap =
 247             registerFilter(methodFilterMap, containingClass, methodNames);
 248     }
 249 
 250     private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
 251             Class<?> containingClass, String ... names) {
 252         if (map.get(containingClass) != null) {
 253             throw new IllegalArgumentException
 254                             ("Filter already registered: " + containingClass);
 255         }
 256         map = new HashMap<Class<?>,String[]>(map);
 257         map.put(containingClass, names);
 258         return map;
 259     }
 260 
 261     public static Field[] filterFields(Class<?> containingClass,
 262                                        Field[] fields) {
 263         if (fieldFilterMap == null) {
 264             // Bootstrapping
 265             return fields;
 266         }
 267         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
 268     }
 269 
 270     public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
 271         if (methodFilterMap == null) {
 272             // Bootstrapping
 273             return methods;
 274         }
 275         return (Method[])filter(methods, methodFilterMap.get(containingClass));
 276     }
 277 
 278     private static Member[] filter(Member[] members, String[] filteredNames) {
 279         if ((filteredNames == null) || (members.length == 0)) {
 280             return members;
 281         }
 282         int numNewMembers = 0;
 283         for (Member member : members) {
 284             boolean shouldSkip = false;
 285             for (String filteredName : filteredNames) {
 286                 if (member.getName() == filteredName) {
 287                     shouldSkip = true;
 288                     break;
 289                 }
 290             }
 291             if (!shouldSkip) {
 292                 ++numNewMembers;
 293             }
 294         }
 295         Member[] newMembers =
 296             (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
 297         int destIdx = 0;
 298         for (Member member : members) {
 299             boolean shouldSkip = false;
 300             for (String filteredName : filteredNames) {
 301                 if (member.getName() == filteredName) {
 302                     shouldSkip = true;
 303                     break;
 304                 }
 305             }
 306             if (!shouldSkip) {
 307                 newMembers[destIdx++] = member;
 308             }
 309         }
 310         return newMembers;
 311     }
 312 
 313     /**
 314      * Tests if the given method is caller-sensitive and the declaring class
 315      * is defined by either the bootstrap class loader or platform class loader.
 316      */
 317     public static boolean isCallerSensitive(Method m) {
 318         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 319         if (VM.isSystemDomainLoader(loader)) {
 320             return m.isAnnotationPresent(CallerSensitive.class);
 321         }
 322         return false;
 323     }
 324 
 325     /**
 326      * Returns an IllegalAccessException with an exception message based on
 327      * the access that is denied.
 328      */
 329     public static IllegalAccessException newIllegalAccessException(Class<?> currentClass,
 330                                                                    Class<?> memberClass,
 331                                                                    Class<?> targetClass,
 332                                                                    int modifiers)
 333         throws IllegalAccessException
 334     {
 335         String currentSuffix = "";
 336         String memberSuffix = "";
 337         Module m1 = currentClass.getModule();
 338         if (m1.isNamed())
 339             currentSuffix = " (in " + m1 + ")";
 340         Module m2 = memberClass.getModule();
 341         if (m2.isNamed())
 342             memberSuffix = " (in " + m2 + ")";
 343 
 344         String memberPackageName = memberClass.getPackageName();
 345 
 346         String msg = currentClass + currentSuffix + " cannot access ";
 347         if (m2.isExported(memberPackageName, m1)) {
 348 
 349             // module access okay so include the modifiers in the message
 350             msg += "a member of " + memberClass + memberSuffix +
 351                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 352 
 353         } else {
 354             // module access failed
 355             msg += memberClass + memberSuffix+ " because "
 356                    + m2 + " does not export " + memberPackageName;
 357             if (m2.isNamed()) msg += " to " + m1;
 358         }
 359 
 360         return new IllegalAccessException(msg);
 361     }
 362 }