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.security.AccessController;
  31 import java.security.PrivilegedAction;
  32 import java.util.HashMap;
  33 import java.util.Map;
  34 import java.util.Objects;
  35 import jdk.internal.HotSpotIntrinsicCandidate;
  36 import jdk.internal.misc.VM;
  37 
  38 /** Common utility routines used by both java.lang and
  39     java.lang.reflect */
  40 
  41 public class Reflection {
  42 
  43     /** Used to filter out fields and methods from certain classes from public
  44         view, where they are sensitive or they may contain VM-internal objects.
  45         These Maps are updated very rarely. Rather than synchronize on
  46         each access, we use copy-on-write */
  47     private static volatile Map<Class<?>,String[]> fieldFilterMap;
  48     private static volatile Map<Class<?>,String[]> methodFilterMap;
  49 
  50     static {
  51         Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>();
  52         map.put(Reflection.class,
  53             new String[] {"fieldFilterMap", "methodFilterMap"});
  54         map.put(System.class, new String[] {"security"});
  55         map.put(Class.class, new String[] {"classLoader"});
  56         fieldFilterMap = map;
  57 
  58         methodFilterMap = new HashMap<>();
  59     }
  60 
  61     /** Returns the class of the caller of the method calling this method,
  62         ignoring frames associated with java.lang.reflect.Method.invoke()
  63         and its implementation. */
  64     @CallerSensitive
  65     @HotSpotIntrinsicCandidate
  66     public static native Class<?> getCallerClass();
  67 
  68     /**
  69      * @deprecated This method will be removed.
  70      * This method is a private JDK API and retained temporarily to
  71      * simplify the implementation of sun.misc.Reflection.getCallerClass.
  72      */
  73     @Deprecated(forRemoval=true)
  74     public static native Class<?> getCallerClass(int depth);
  75 
  76     /** Retrieves the access flags written to the class file. For
  77         inner classes these flags may differ from those returned by
  78         Class.getModifiers(), which searches the InnerClasses
  79         attribute to find the source-level access flags. This is used
  80         instead of Class.getModifiers() for run-time access checks due
  81         to compatibility reasons; see 4471811. Only the values of the
  82         low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
  83         valid. */
  84     @HotSpotIntrinsicCandidate
  85     public static native int getClassAccessFlags(Class<?> c);
  86 
  87 
  88     public static void ensureMemberAccess(Class<?> currentClass,
  89                                           Class<?> memberClass,
  90                                           Object target,
  91                                           int modifiers)
  92         throws IllegalAccessException
  93     {
  94         if (currentClass == null || memberClass == null) {
  95             throw new InternalError();
  96         }
  97 
  98         if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) {
  99             throwIllegalAccessException(currentClass, memberClass, target, modifiers);
 100         }
 101     }
 102 
 103     public static boolean verifyMemberAccess(Class<?> currentClass,
 104                                              // Declaring class of field
 105                                              // or method
 106                                              Class<?> memberClass,
 107                                              // May be NULL in case of statics
 108                                              Object   target,
 109                                              int      modifiers)
 110     {
 111         // Verify that currentClass can access a field, method, or
 112         // constructor of memberClass, where that member's access bits are
 113         // "modifiers".
 114 
 115         boolean gotIsSameClassPackage = false;
 116         boolean isSameClassPackage = false;
 117 
 118         if (currentClass == memberClass) {
 119             // Always succeeds
 120             return true;
 121         }
 122 
 123         if (!verifyModuleAccess(currentClass, memberClass)) {
 124             return false;
 125         }
 126 
 127         if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
 128             isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 129             gotIsSameClassPackage = true;
 130             if (!isSameClassPackage) {
 131                 return false;
 132             }
 133         }
 134 
 135         // At this point we know that currentClass can access memberClass.
 136 
 137         if (Modifier.isPublic(modifiers)) {
 138             return true;
 139         }
 140 
 141         boolean successSoFar = false;
 142 
 143         if (Modifier.isProtected(modifiers)) {
 144             // See if currentClass is a subclass of memberClass
 145             if (isSubclassOf(currentClass, memberClass)) {
 146                 successSoFar = true;
 147             }
 148         }
 149 
 150         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
 151             if (!gotIsSameClassPackage) {
 152                 isSameClassPackage = isSameClassPackage(currentClass,
 153                                                         memberClass);
 154                 gotIsSameClassPackage = true;
 155             }
 156 
 157             if (isSameClassPackage) {
 158                 successSoFar = true;
 159             }
 160         }
 161 
 162         if (!successSoFar) {
 163             return false;
 164         }
 165 
 166         if (Modifier.isProtected(modifiers)) {
 167             // Additional test for protected members: JLS 6.6.2
 168             Class<?> targetClass = (target == null ? memberClass : target.getClass());
 169             if (targetClass != currentClass) {
 170                 if (!gotIsSameClassPackage) {
 171                     isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 172                     gotIsSameClassPackage = true;
 173                 }
 174                 if (!isSameClassPackage) {
 175                     if (!isSubclassOf(targetClass, currentClass)) {
 176                         return false;
 177                     }
 178                 }
 179             }
 180         }
 181 
 182         return true;
 183     }
 184 
 185     /**
 186      * Returns {@code true} if memberClass's's module exports memberClass's
 187      * package to currentClass's module.
 188      */
 189     public static boolean verifyModuleAccess(Class<?> currentClass,
 190                                              Class<?> memberClass) {
 191         return verifyModuleAccess(currentClass.getModule(), memberClass);
 192     }
 193 
 194     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 195         Module memberModule = memberClass.getModule();
 196 
 197         // module may be null during startup (initLevel 0)
 198         if (currentModule == memberModule)
 199            return true;  // same module (named or unnamed)
 200 
 201         // memberClass may be primitive or array class
 202         Class<?> c = memberClass;
 203         while (c.isArray()) {
 204             c = c.getComponentType();
 205         }
 206         if (c.isPrimitive())
 207             return true;
 208 
 209         // check that memberModule exports the package to currentModule
 210         return memberModule.isExported(c.getPackageName(), currentModule);
 211     }
 212 
 213     /**
 214      * Returns true if two classes in the same package.
 215      */
 216     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 217         if (c1.getClassLoader() != c2.getClassLoader())
 218             return false;
 219         while (c1.isArray())
 220             c1 = c1.getComponentType();
 221         while (c2.isArray())
 222             c2 = c2.getComponentType();
 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) || isExtClassLoader(loader))  {
 322             return m.isAnnotationPresent(CallerSensitive.class);
 323         }
 324         return false;
 325     }
 326 
 327     private static boolean isExtClassLoader(ClassLoader loader) {
 328         ClassLoader cl = ClassLoader.getSystemClassLoader();
 329         while (cl != null) {
 330             if (cl.getParent() == null && cl == loader) {
 331                 return true;
 332             }
 333             cl = cl.getParent();
 334         }
 335         return false;
 336     }
 337 
 338 
 339     // true to print a stack trace when IAE is thrown
 340     private static volatile boolean printStackWhenAccessFails;
 341 
 342     // true if printStackWhenAccessFails has been initialized
 343     private static volatile boolean printStackWhenAccessFailsSet;
 344 
 345     private static void printStackTraceIfNeeded(Throwable e) {
 346         if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) {
 347             // can't use method reference here, might be too early in startup
 348             PrivilegedAction<Boolean> pa = new PrivilegedAction<Boolean>() {
 349                 public Boolean run() {
 350                     String s;
 351                     s = System.getProperty("sun.reflect.debugModuleAccessChecks");
 352                     return (s != null && !s.equalsIgnoreCase("false"));
 353                 }
 354             };
 355             printStackWhenAccessFails = AccessController.doPrivileged(pa);
 356             printStackWhenAccessFailsSet = true;
 357         }
 358         if (printStackWhenAccessFails) {
 359             e.printStackTrace();
 360         }
 361     }
 362 
 363     /**
 364      * Throws IllegalAccessException with the an exception message based on
 365      * the access that is denied.
 366      */
 367     private static void throwIllegalAccessException(Class<?> currentClass,
 368                                                     Class<?> memberClass,
 369                                                     Object target,
 370                                                     int modifiers)
 371         throws IllegalAccessException
 372     {
 373         String currentSuffix = "";
 374         String memberSuffix = "";
 375         Module m1 = currentClass.getModule();
 376         if (m1.isNamed())
 377             currentSuffix = " (in " + m1 + ")";
 378         Module m2 = memberClass.getModule();
 379         if (m2.isNamed())
 380             memberSuffix = " (in " + m2 + ")";
 381 
 382         Class<?> c = memberClass;
 383         while (c.isArray()) {
 384             c = c.getComponentType();
 385         }
 386         String memberPackageName = c.getPackageName();
 387 
 388         String msg = currentClass + currentSuffix + " cannot access ";
 389         if (m2.isExported(memberPackageName, m1)) {
 390 
 391             // module access okay so include the modifiers in the message
 392             msg += "a member of " + memberClass + memberSuffix +
 393                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 394 
 395         } else {
 396             // module access failed
 397             msg += memberClass + memberSuffix+ " because "
 398                    + m2 + " does not export " + memberPackageName;
 399             if (m2.isNamed()) msg += " to " + m1;
 400         }
 401 
 402         throwIllegalAccessException(msg);
 403     }
 404 
 405     /**
 406      * Throws IllegalAccessException with the given exception message.
 407      */
 408     public static void throwIllegalAccessException(String msg)
 409         throws IllegalAccessException
 410     {
 411         IllegalAccessException e = new IllegalAccessException(msg);
 412         printStackTraceIfNeeded(e);
 413         throw e;
 414     }
 415 
 416     /**
 417      * Throws InaccessibleObjectException with the given exception message.
 418      */
 419     public static void throwInaccessibleObjectException(String msg) {
 420         InaccessibleObjectException e = new InaccessibleObjectException(msg);
 421         printStackTraceIfNeeded(e);
 422         throw e;
 423     }
 424 
 425 }