1 /*
   2  * Copyright (c) 2001, 2013, 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.misc.VM;
  35 import sun.security.action.GetPropertyAction;
  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 (currentClass == null || memberClass == null) {
 107             throw new InternalError();
 108         }
 109 
 110         if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
 111             throwIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
 112         }
 113     }
 114 
 115     private static boolean verifyMemberAccess(Class<?> currentClass,
 116                                               Class<?> memberClass,
 117                                               Class<?> targetClass,
 118                                               int modifiers)
 119     {
 120         // Verify that currentClass can access a field, method, or
 121         // constructor of memberClass, where that member's access bits are
 122         // "modifiers".
 123 
 124         boolean gotIsSameClassPackage = false;
 125         boolean isSameClassPackage = false;
 126 
 127         if (currentClass == memberClass) {
 128             // Always succeeds
 129             return true;
 130         }
 131 
 132         if (!verifyModuleAccess(currentClass, memberClass)) {
 133             return false;
 134         }
 135 
 136         if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
 137             isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 138             gotIsSameClassPackage = true;
 139             if (!isSameClassPackage) {
 140                 return false;
 141             }
 142         }
 143 
 144         // At this point we know that currentClass can access memberClass.
 145 
 146         if (Modifier.isPublic(modifiers)) {
 147             return true;
 148         }
 149 
 150         boolean successSoFar = false;
 151 
 152         if (Modifier.isProtected(modifiers)) {
 153             // See if currentClass is a subclass of memberClass
 154             if (isSubclassOf(currentClass, memberClass)) {
 155                 successSoFar = true;
 156             }
 157         }
 158 
 159         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
 160             if (!gotIsSameClassPackage) {
 161                 isSameClassPackage = isSameClassPackage(currentClass,
 162                                                         memberClass);
 163                 gotIsSameClassPackage = true;
 164             }
 165 
 166             if (isSameClassPackage) {
 167                 successSoFar = true;
 168             }
 169         }
 170 
 171         if (!successSoFar) {
 172             return false;
 173         }
 174 
 175         // Additional test for protected instance members
 176         // and protected constructors: JLS 6.6.2
 177         if (targetClass != null && Modifier.isProtected(modifiers) &&
 178             targetClass != currentClass)
 179         {
 180             if (!gotIsSameClassPackage) {
 181                 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 182                 gotIsSameClassPackage = true;
 183             }
 184             if (!isSameClassPackage) {
 185                 if (!isSubclassOf(targetClass, currentClass)) {
 186                     return false;
 187                 }
 188             }
 189         }
 190 
 191         return true;
 192     }
 193 
 194     /**
 195      * Returns {@code true} if memberClass's's module exports memberClass's
 196      * package to currentClass's module.
 197      */
 198     public static boolean verifyModuleAccess(Class<?> currentClass,
 199                                              Class<?> memberClass) {
 200         return verifyModuleAccess(currentClass.getModule(), memberClass);
 201     }
 202 
 203     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 204         Module memberModule = memberClass.getModule();
 205 
 206         // module may be null during startup (initLevel 0)
 207         if (currentModule == memberModule)
 208            return true;  // same module (named or unnamed)
 209 
 210         // memberClass may be primitive or array class
 211         Class<?> c = memberClass;
 212         while (c.isArray()) {
 213             c = c.getComponentType();
 214         }
 215         if (c.isPrimitive())
 216             return true;
 217 
 218         // check that memberModule exports the package to currentModule
 219         return memberModule.isExported(c.getPackageName(), currentModule);
 220     }
 221 
 222     /**
 223      * Returns true if two classes in the same package.
 224      */
 225     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 226         if (c1.getClassLoader() != c2.getClassLoader())
 227             return false;
 228         while (c1.isArray())
 229             c1 = c1.getComponentType();
 230         while (c2.isArray())
 231             c2 = c2.getComponentType();
 232         return Objects.equals(c1.getPackageName(), c2.getPackageName());
 233     }
 234 
 235     static boolean isSubclassOf(Class<?> queryClass,
 236                                 Class<?> ofClass)
 237     {
 238         while (queryClass != null) {
 239             if (queryClass == ofClass) {
 240                 return true;
 241             }
 242             queryClass = queryClass.getSuperclass();
 243         }
 244         return false;
 245     }
 246 
 247     // fieldNames must contain only interned Strings
 248     public static synchronized void registerFieldsToFilter(Class<?> containingClass,
 249                                               String ... fieldNames) {
 250         fieldFilterMap =
 251             registerFilter(fieldFilterMap, containingClass, fieldNames);
 252     }
 253 
 254     // methodNames must contain only interned Strings
 255     public static synchronized void registerMethodsToFilter(Class<?> containingClass,
 256                                               String ... methodNames) {
 257         methodFilterMap =
 258             registerFilter(methodFilterMap, containingClass, methodNames);
 259     }
 260 
 261     private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
 262             Class<?> containingClass, String ... names) {
 263         if (map.get(containingClass) != null) {
 264             throw new IllegalArgumentException
 265                             ("Filter already registered: " + containingClass);
 266         }
 267         map = new HashMap<Class<?>,String[]>(map);
 268         map.put(containingClass, names);
 269         return map;
 270     }
 271 
 272     public static Field[] filterFields(Class<?> containingClass,
 273                                        Field[] fields) {
 274         if (fieldFilterMap == null) {
 275             // Bootstrapping
 276             return fields;
 277         }
 278         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
 279     }
 280 
 281     public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
 282         if (methodFilterMap == null) {
 283             // Bootstrapping
 284             return methods;
 285         }
 286         return (Method[])filter(methods, methodFilterMap.get(containingClass));
 287     }
 288 
 289     private static Member[] filter(Member[] members, String[] filteredNames) {
 290         if ((filteredNames == null) || (members.length == 0)) {
 291             return members;
 292         }
 293         int numNewMembers = 0;
 294         for (Member member : members) {
 295             boolean shouldSkip = false;
 296             for (String filteredName : filteredNames) {
 297                 if (member.getName() == filteredName) {
 298                     shouldSkip = true;
 299                     break;
 300                 }
 301             }
 302             if (!shouldSkip) {
 303                 ++numNewMembers;
 304             }
 305         }
 306         Member[] newMembers =
 307             (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
 308         int destIdx = 0;
 309         for (Member member : members) {
 310             boolean shouldSkip = false;
 311             for (String filteredName : filteredNames) {
 312                 if (member.getName() == filteredName) {
 313                     shouldSkip = true;
 314                     break;
 315                 }
 316             }
 317             if (!shouldSkip) {
 318                 newMembers[destIdx++] = member;
 319             }
 320         }
 321         return newMembers;
 322     }
 323 
 324     /**
 325      * Tests if the given method is caller-sensitive and the declaring class
 326      * is defined by either the bootstrap class loader or platform class loader.
 327      */
 328     public static boolean isCallerSensitive(Method m) {
 329         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 330         if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader))  {
 331             return m.isAnnotationPresent(CallerSensitive.class);
 332         }
 333         return false;
 334     }
 335 
 336     private static boolean isExtClassLoader(ClassLoader loader) {
 337         ClassLoader cl = ClassLoader.getSystemClassLoader();
 338         while (cl != null) {
 339             if (cl.getParent() == null && cl == loader) {
 340                 return true;
 341             }
 342             cl = cl.getParent();
 343         }
 344         return false;
 345     }
 346 
 347 
 348     // true to print a stack trace when IAE is thrown
 349     private static volatile boolean printStackWhenAccessFails;
 350 
 351     // true if printStackWhenAccessFails has been initialized
 352     private static volatile boolean printStackWhenAccessFailsSet;
 353 
 354     private static void printStackTraceIfNeeded(Throwable e) {
 355         if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) {
 356             String s = GetPropertyAction.privilegedGetProperty(
 357                     "sun.reflect.debugModuleAccessChecks");
 358             printStackWhenAccessFails =
 359                     (s != null && !s.equalsIgnoreCase("false"));
 360             printStackWhenAccessFailsSet = true;
 361         }
 362         if (printStackWhenAccessFails) {
 363             e.printStackTrace();
 364         }
 365     }
 366 
 367     /**
 368      * Throws IllegalAccessException with the an exception message based on
 369      * the access that is denied.
 370      */
 371     private static void throwIllegalAccessException(Class<?> currentClass,
 372                                                     Class<?> memberClass,
 373                                                     Object target,
 374                                                     int modifiers)
 375         throws IllegalAccessException
 376     {
 377         String currentSuffix = "";
 378         String memberSuffix = "";
 379         Module m1 = currentClass.getModule();
 380         if (m1.isNamed())
 381             currentSuffix = " (in " + m1 + ")";
 382         Module m2 = memberClass.getModule();
 383         if (m2.isNamed())
 384             memberSuffix = " (in " + m2 + ")";
 385 
 386         Class<?> c = memberClass;
 387         while (c.isArray()) {
 388             c = c.getComponentType();
 389         }
 390         String memberPackageName = c.getPackageName();
 391 
 392         String msg = currentClass + currentSuffix + " cannot access ";
 393         if (m2.isExported(memberPackageName, m1)) {
 394 
 395             // module access okay so include the modifiers in the message
 396             msg += "a member of " + memberClass + memberSuffix +
 397                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 398 
 399         } else {
 400             // module access failed
 401             msg += memberClass + memberSuffix+ " because "
 402                    + m2 + " does not export " + memberPackageName;
 403             if (m2.isNamed()) msg += " to " + m1;
 404         }
 405 
 406         throwIllegalAccessException(msg);
 407     }
 408 
 409     /**
 410      * Throws IllegalAccessException with the given exception message.
 411      */
 412     public static void throwIllegalAccessException(String msg)
 413         throws IllegalAccessException
 414     {
 415         IllegalAccessException e = new IllegalAccessException(msg);
 416         printStackTraceIfNeeded(e);
 417         throw e;
 418     }
 419 
 420     /**
 421      * Throws InaccessibleObjectException with the given exception message.
 422      */
 423     public static void throwInaccessibleObjectException(String msg) {
 424         InaccessibleObjectException e = new InaccessibleObjectException(msg);
 425         printStackTraceIfNeeded(e);
 426         throw e;
 427     }
 428 
 429 }