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