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