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