jdk/src/java.base/share/classes/sun/reflect/Reflection.java
changeset 38062 430e0a96ef1f
parent 38061 5fe046aef3b9
parent 38060 954c9575f653
child 38064 0e7c67a6ad89
child 38065 025c784d9333
equal deleted inserted replaced
38061:5fe046aef3b9 38062:430e0a96ef1f
     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 sun.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 in JDK 9.
       
    70      * This method is a private JDK API and retained temporarily for
       
    71      * existing code to run until a replacement API is defined.
       
    72      */
       
    73     @Deprecated
       
    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 }