1 /*
   2  * Copyright (c) 2001, 2017, 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 
  36 /** Common utility routines used by both java.lang and
  37     java.lang.reflect */
  38 
  39 public class Reflection {
  40 
  41     /** Used to filter out fields and methods from certain classes from public
  42         view, where they are sensitive or they may contain VM-internal objects.
  43         These Maps are updated very rarely. Rather than synchronize on
  44         each access, we use copy-on-write */
  45     private static volatile Map<Class<?>,String[]> fieldFilterMap;
  46     private static volatile Map<Class<?>,String[]> methodFilterMap;
  47 
  48     static {
  49         Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>();
  50         map.put(Reflection.class,
  51             new String[] {"fieldFilterMap", "methodFilterMap"});
  52         map.put(System.class, new String[] {"security"});
  53         map.put(Class.class, new String[] {"classLoader"});
  54         fieldFilterMap = map;
  55 
  56         methodFilterMap = new HashMap<>();
  57     }
  58 
  59     /** Returns the class of the caller of the method calling this method,
  60         ignoring frames associated with java.lang.reflect.Method.invoke()
  61         and its implementation. */
  62     @CallerSensitive
  63     @HotSpotIntrinsicCandidate
  64     public static native Class<?> getCallerClass();
  65 
  66     /**
  67      * @deprecated This method will be removed.
  68      * This method is a private JDK API and retained temporarily to
  69      * simplify the implementation of sun.misc.Reflection.getCallerClass.
  70      */
  71     @Deprecated(forRemoval=true)
  72     public static native Class<?> getCallerClass(int depth);
  73 
  74     /** Retrieves the access flags written to the class file. For
  75         inner classes these flags may differ from those returned by
  76         Class.getModifiers(), which searches the InnerClasses
  77         attribute to find the source-level access flags. This is used
  78         instead of Class.getModifiers() for run-time access checks due
  79         to compatibility reasons; see 4471811. Only the values of the
  80         low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
  81         valid. */
  82     @HotSpotIntrinsicCandidate
  83     public static native int getClassAccessFlags(Class<?> c);
  84 
  85 
  86     /**
  87      * Ensures that access to a member is granted and throws
  88      * IllegalAccessException if not.
  89      *
  90      * @param currentClass the class performing the access
  91      * @param memberClass the declaring class of the member being accessed
  92      * @param targetClass the class of target object if accessing instance
  93      *                    field or method;
  94      *                    or the declaring class if accessing constructor;
  95      *                    or null if accessing static field or method
  96      * @param modifiers the member's access modifiers
  97      * @throws IllegalAccessException if access to member is denied
  98      */
  99     public static void ensureMemberAccess(Class<?> currentClass,
 100                                           Class<?> memberClass,
 101                                           Class<?> targetClass,
 102                                           int modifiers)
 103         throws IllegalAccessException
 104     {
 105         if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
 106             throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
 107         }
 108     }
 109 
 110     /**
 111      * Verify access to a member and return {@code true} if it is granted.
 112      *
 113      * @param currentClass the class performing the access
 114      * @param memberClass the declaring class of the member being accessed
 115      * @param targetClass the class of target object if accessing instance
 116      *                    field or method;
 117      *                    or the declaring class if accessing constructor;
 118      *                    or null if accessing static field or method
 119      * @param modifiers the member's access modifiers
 120      * @return {@code true} if access to member is granted
 121      */
 122     public static boolean verifyMemberAccess(Class<?> currentClass,
 123                                              Class<?> memberClass,
 124                                              Class<?> targetClass,
 125                                              int modifiers)
 126     {
 127         if (currentClass == memberClass) {
 128             // Always succeeds
 129             return true;
 130         }
 131 
 132         if (!verifyModuleAccess(currentClass.getModule(), memberClass)) {
 133             return false;
 134         }
 135 
 136         boolean gotIsSameClassPackage = false;
 137         boolean isSameClassPackage = false;
 138 
 139         if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
 140             isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 141             gotIsSameClassPackage = true;
 142             if (!isSameClassPackage) {
 143                 return false;
 144             }
 145         }
 146 
 147         // At this point we know that currentClass can access memberClass.
 148 
 149         if (Modifier.isPublic(modifiers)) {
 150             return true;
 151         }
 152 
 153         boolean successSoFar = false;
 154 
 155         if (Modifier.isProtected(modifiers)) {
 156             // See if currentClass is a subclass of memberClass
 157             if (isSubclassOf(currentClass, memberClass)) {
 158                 successSoFar = true;
 159             }
 160         }
 161 
 162         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
 163             if (!gotIsSameClassPackage) {
 164                 isSameClassPackage = isSameClassPackage(currentClass,
 165                                                         memberClass);
 166                 gotIsSameClassPackage = true;
 167             }
 168 
 169             if (isSameClassPackage) {
 170                 successSoFar = true;
 171             }
 172         }
 173 
 174         if (!successSoFar) {
 175             return false;
 176         }
 177 
 178         // Additional test for protected instance members
 179         // and protected constructors: JLS 6.6.2
 180         if (targetClass != null && Modifier.isProtected(modifiers) &&
 181             targetClass != currentClass)
 182         {
 183             if (!gotIsSameClassPackage) {
 184                 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 185                 gotIsSameClassPackage = true;
 186             }
 187             if (!isSameClassPackage) {
 188                 if (!isSubclassOf(targetClass, currentClass)) {
 189                     return false;
 190                 }
 191             }
 192         }
 193 
 194         return true;
 195     }
 196 
 197     /**
 198      * Returns {@code true} if memberClass's module exports memberClass's
 199      * package to currentModule.
 200      */
 201     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 202         Module memberModule = memberClass.getModule();
 203         if (currentModule == memberModule) {
 204             // same module (named or unnamed) or both null if called
 205             // before module system is initialized, which means we are
 206             // dealing with java.base only.
 207             return true;
 208         } else {
 209             String pkg = memberClass.getPackageName();
 210             return memberModule.isExported(pkg, currentModule);
 211         }
 212     }
 213 
 214     /**
 215      * Returns true if two classes in the same package.
 216      */
 217     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 218         if (c1.getClassLoader() != c2.getClassLoader())
 219             return false;
 220         return Objects.equals(c1.getPackageName(), c2.getPackageName());
 221     }
 222 
 223     static boolean isSubclassOf(Class<?> queryClass,
 224                                 Class<?> ofClass)
 225     {
 226         while (queryClass != null) {
 227             if (queryClass == ofClass) {
 228                 return true;
 229             }
 230             queryClass = queryClass.getSuperclass();
 231         }
 232         return false;
 233     }
 234 
 235     // fieldNames must contain only interned Strings
 236     public static synchronized void registerFieldsToFilter(Class<?> containingClass,
 237                                               String ... fieldNames) {
 238         fieldFilterMap =
 239             registerFilter(fieldFilterMap, containingClass, fieldNames);
 240     }
 241 
 242     // methodNames must contain only interned Strings
 243     public static synchronized void registerMethodsToFilter(Class<?> containingClass,
 244                                               String ... methodNames) {
 245         methodFilterMap =
 246             registerFilter(methodFilterMap, containingClass, methodNames);
 247     }
 248 
 249     private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
 250             Class<?> containingClass, String ... names) {
 251         if (map.get(containingClass) != null) {
 252             throw new IllegalArgumentException
 253                             ("Filter already registered: " + containingClass);
 254         }
 255         map = new HashMap<Class<?>,String[]>(map);
 256         map.put(containingClass, names);
 257         return map;
 258     }
 259 
 260     public static Field[] filterFields(Class<?> containingClass,
 261                                        Field[] fields) {
 262         if (fieldFilterMap == null) {
 263             // Bootstrapping
 264             return fields;
 265         }
 266         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
 267     }
 268 
 269     public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
 270         if (methodFilterMap == null) {
 271             // Bootstrapping
 272             return methods;
 273         }
 274         return (Method[])filter(methods, methodFilterMap.get(containingClass));
 275     }
 276 
 277     private static Member[] filter(Member[] members, String[] filteredNames) {
 278         if ((filteredNames == null) || (members.length == 0)) {
 279             return members;
 280         }
 281         int numNewMembers = 0;
 282         for (Member member : members) {
 283             boolean shouldSkip = false;
 284             for (String filteredName : filteredNames) {
 285                 if (member.getName() == filteredName) {
 286                     shouldSkip = true;
 287                     break;
 288                 }
 289             }
 290             if (!shouldSkip) {
 291                 ++numNewMembers;
 292             }
 293         }
 294         Member[] newMembers =
 295             (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
 296         int destIdx = 0;
 297         for (Member member : members) {
 298             boolean shouldSkip = false;
 299             for (String filteredName : filteredNames) {
 300                 if (member.getName() == filteredName) {
 301                     shouldSkip = true;
 302                     break;
 303                 }
 304             }
 305             if (!shouldSkip) {
 306                 newMembers[destIdx++] = member;
 307             }
 308         }
 309         return newMembers;
 310     }
 311 
 312     /**
 313      * Tests if the given method is caller-sensitive and the declaring class
 314      * is defined by either the bootstrap class loader or platform class loader.
 315      */
 316     public static boolean isCallerSensitive(Method m) {
 317         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 318         if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader))  {
 319             return m.isAnnotationPresent(CallerSensitive.class);
 320         }
 321         return false;
 322     }
 323 
 324     private static boolean isExtClassLoader(ClassLoader loader) {
 325         ClassLoader cl = ClassLoader.getSystemClassLoader();
 326         while (cl != null) {
 327             if (cl.getParent() == null && cl == loader) {
 328                 return true;
 329             }
 330             cl = cl.getParent();
 331         }
 332         return false;
 333     }
 334 
 335     /**
 336      * Returns an IllegalAccessException with an exception message based on
 337      * the access that is denied.
 338      */
 339     public static IllegalAccessException newIllegalAccessException(
 340         Class<?> currentClass,
 341         Class<?> memberClass,
 342         Class<?> targetClass,
 343         int modifiers)
 344         throws IllegalAccessException
 345     {
 346         String currentSuffix = "";
 347         String memberSuffix = "";
 348         Module m1 = currentClass.getModule();
 349         if (m1.isNamed())
 350             currentSuffix = " (in " + m1 + ")";
 351         Module m2 = memberClass.getModule();
 352         if (m2.isNamed())
 353             memberSuffix = " (in " + m2 + ")";
 354 
 355         String memberPackageName = memberClass.getPackageName();
 356 
 357         String msg = currentClass + currentSuffix + " cannot access ";
 358         if (m2.isExported(memberPackageName, m1)) {
 359 
 360             // module access okay so include the modifiers in the message
 361             msg += "a member of " + memberClass + memberSuffix +
 362                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 363 
 364         } else {
 365             // module access failed
 366             msg += memberClass + memberSuffix+ " because "
 367                    + m2 + " does not export " + memberPackageName;
 368             if (m2.isNamed()) msg += " to " + m1;
 369         }
 370 
 371         return new IllegalAccessException(msg);
 372     }
 373 }