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