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