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