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