src/share/classes/sun/reflect/Reflection.java
author ohair
Tue May 25 15:58:33 2010 -0700 (24 months ago)
changeset 2362 00cd9dc3c2b5
parent 037a05a11f281
permissions -rw-r--r--
6943119: Rebrand source copyright notices
Reviewed-by: darcy, weijun
        1 /*
        2  * Copyright (c) 2001, 2006, 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.Collections;
       30 import java.util.HashMap;
       31 import java.util.Map;
       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         fieldFilterMap = map;
       51 
       52         methodFilterMap = new HashMap<Class,String[]>();
       53     }
       54 
       55     /** Returns the class of the method <code>realFramesToSkip</code>
       56         frames up the stack (zero-based), ignoring frames associated
       57         with java.lang.reflect.Method.invoke() and its implementation.
       58         The first frame is that associated with this method, so
       59         <code>getCallerClass(0)</code> returns the Class object for
       60         sun.reflect.Reflection. Frames associated with
       61         java.lang.reflect.Method.invoke() and its implementation are
       62         completely ignored and do not count toward the number of "real"
       63         frames skipped. */
       64     public static native Class getCallerClass(int realFramesToSkip);
       65 
       66     /** Retrieves the access flags written to the class file. For
       67         inner classes these flags may differ from those returned by
       68         Class.getModifiers(), which searches the InnerClasses
       69         attribute to find the source-level access flags. This is used
       70         instead of Class.getModifiers() for run-time access checks due
       71         to compatibility reasons; see 4471811. Only the values of the
       72         low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
       73         valid. */
       74     private static native int getClassAccessFlags(Class c);
       75 
       76     /** A quick "fast-path" check to try to avoid getCallerClass()
       77         calls. */
       78     public static boolean quickCheckMemberAccess(Class memberClass,
       79                                                  int modifiers)
       80     {
       81         return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers);
       82     }
       83 
       84     public static void ensureMemberAccess(Class currentClass,
       85                                           Class memberClass,
       86                                           Object target,
       87                                           int modifiers)
       88         throws IllegalAccessException
       89     {
       90         if (currentClass == null || memberClass == null) {
       91             throw new InternalError();
       92         }
       93 
       94         if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) {
       95             throw new IllegalAccessException("Class " + currentClass.getName() +
       96                                              " can not access a member of class " +
       97                                              memberClass.getName() +
       98                                              " with modifiers \"" +
       99                                              Modifier.toString(modifiers) +
      100                                              "\"");
      101         }
      102     }
      103 
      104     public static boolean verifyMemberAccess(Class currentClass,
      105                                              // Declaring class of field
      106                                              // or method
      107                                              Class  memberClass,
      108                                              // May be NULL in case of statics
      109                                              Object target,
      110                                              int    modifiers)
      111     {
      112         // Verify that currentClass can access a field, method, or
      113         // constructor of memberClass, where that member's access bits are
      114         // "modifiers".
      115 
      116         boolean gotIsSameClassPackage = false;
      117         boolean isSameClassPackage = false;
      118 
      119         if (currentClass == memberClass) {
      120             // Always succeeds
      121             return true;
      122         }
      123 
      124         if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
      125             isSameClassPackage = isSameClassPackage(currentClass, memberClass);
      126             gotIsSameClassPackage = true;
      127             if (!isSameClassPackage) {
      128                 return false;
      129             }
      130         }
      131 
      132         // At this point we know that currentClass can access memberClass.
      133 
      134         if (Modifier.isPublic(modifiers)) {
      135             return true;
      136         }
      137 
      138         boolean successSoFar = false;
      139 
      140         if (Modifier.isProtected(modifiers)) {
      141             // See if currentClass is a subclass of memberClass
      142             if (isSubclassOf(currentClass, memberClass)) {
      143                 successSoFar = true;
      144             }
      145         }
      146 
      147         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
      148             if (!gotIsSameClassPackage) {
      149                 isSameClassPackage = isSameClassPackage(currentClass,
      150                                                         memberClass);
      151                 gotIsSameClassPackage = true;
      152             }
      153 
      154             if (isSameClassPackage) {
      155                 successSoFar = true;
      156             }
      157         }
      158 
      159         if (!successSoFar) {
      160             return false;
      161         }
      162 
      163         if (Modifier.isProtected(modifiers)) {
      164             // Additional test for protected members: JLS 6.6.2
      165             Class targetClass = (target == null ? memberClass : target.getClass());
      166             if (targetClass != currentClass) {
      167                 if (!gotIsSameClassPackage) {
      168                     isSameClassPackage = isSameClassPackage(currentClass, memberClass);
      169                     gotIsSameClassPackage = true;
      170                 }
      171                 if (!isSameClassPackage) {
      172                     if (!isSubclassOf(targetClass, currentClass)) {
      173                         return false;
      174                     }
      175                 }
      176             }
      177         }
      178 
      179         return true;
      180     }
      181 
      182     private static boolean isSameClassPackage(Class c1, Class c2) {
      183         return isSameClassPackage(c1.getClassLoader(), c1.getName(),
      184                                   c2.getClassLoader(), c2.getName());
      185     }
      186 
      187     /** Returns true if two classes are in the same package; classloader
      188         and classname information is enough to determine a class's package */
      189     private static boolean isSameClassPackage(ClassLoader loader1, String name1,
      190                                               ClassLoader loader2, String name2)
      191     {
      192         if (loader1 != loader2) {
      193             return false;
      194         } else {
      195             int lastDot1 = name1.lastIndexOf('.');
      196             int lastDot2 = name2.lastIndexOf('.');
      197             if ((lastDot1 == -1) || (lastDot2 == -1)) {
      198                 // One of the two doesn't have a package.  Only return true
      199                 // if the other one also doesn't have a package.
      200                 return (lastDot1 == lastDot2);
      201             } else {
      202                 int idx1 = 0;
      203                 int idx2 = 0;
      204 
      205                 // Skip over '['s
      206                 if (name1.charAt(idx1) == '[') {
      207                     do {
      208                         idx1++;
      209                     } while (name1.charAt(idx1) == '[');
      210                     if (name1.charAt(idx1) != 'L') {
      211                         // Something is terribly wrong.  Shouldn't be here.
      212                         throw new InternalError("Illegal class name " + name1);
      213                     }
      214                 }
      215                 if (name2.charAt(idx2) == '[') {
      216                     do {
      217                         idx2++;
      218                     } while (name2.charAt(idx2) == '[');
      219                     if (name2.charAt(idx2) != 'L') {
      220                         // Something is terribly wrong.  Shouldn't be here.
      221                         throw new InternalError("Illegal class name " + name2);
      222                     }
      223                 }
      224 
      225                 // Check that package part is identical
      226                 int length1 = lastDot1 - idx1;
      227                 int length2 = lastDot2 - idx2;
      228 
      229                 if (length1 != length2) {
      230                     return false;
      231                 }
      232                 return name1.regionMatches(false, idx1, name2, idx2, length1);
      233             }
      234         }
      235     }
      236 
      237     static boolean isSubclassOf(Class queryClass,
      238                                 Class ofClass)
      239     {
      240         while (queryClass != null) {
      241             if (queryClass == ofClass) {
      242                 return true;
      243             }
      244             queryClass = queryClass.getSuperclass();
      245         }
      246         return false;
      247     }
      248 
      249     // fieldNames must contain only interned Strings
      250     public static synchronized void registerFieldsToFilter(Class containingClass,
      251                                               String ... fieldNames) {
      252         fieldFilterMap =
      253             registerFilter(fieldFilterMap, containingClass, fieldNames);
      254     }
      255 
      256     // methodNames must contain only interned Strings
      257     public static synchronized void registerMethodsToFilter(Class containingClass,
      258                                               String ... methodNames) {
      259         methodFilterMap =
      260             registerFilter(methodFilterMap, containingClass, methodNames);
      261     }
      262 
      263     private static Map<Class,String[]> registerFilter(Map<Class,String[]> map,
      264             Class containingClass, String ... names) {
      265         if (map.get(containingClass) != null) {
      266             throw new IllegalArgumentException
      267                             ("Filter already registered: " + containingClass);
      268         }
      269         map = new HashMap<Class,String[]>(map);
      270         map.put(containingClass, names);
      271         return map;
      272     }
      273 
      274     public static Field[] filterFields(Class containingClass,
      275                                        Field[] fields) {
      276         if (fieldFilterMap == null) {
      277             // Bootstrapping
      278             return fields;
      279         }
      280         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
      281     }
      282 
      283     public static Method[] filterMethods(Class containingClass, Method[] methods) {
      284         if (methodFilterMap == null) {
      285             // Bootstrapping
      286             return methods;
      287         }
      288         return (Method[])filter(methods, methodFilterMap.get(containingClass));
      289     }
      290 
      291     private static Member[] filter(Member[] members, String[] filteredNames) {
      292         if ((filteredNames == null) || (members.length == 0)) {
      293             return members;
      294         }
      295         int numNewMembers = 0;
      296         for (Member member : members) {
      297             boolean shouldSkip = false;
      298             for (String filteredName : filteredNames) {
      299                 if (member.getName() == filteredName) {
      300                     shouldSkip = true;
      301                     break;
      302                 }
      303             }
      304             if (!shouldSkip) {
      305                 ++numNewMembers;
      306             }
      307         }
      308         Member[] newMembers =
      309             (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
      310         int destIdx = 0;
      311         for (Member member : members) {
      312             boolean shouldSkip = false;
      313             for (String filteredName : filteredNames) {
      314                 if (member.getName() == filteredName) {
      315                     shouldSkip = true;
      316                     break;
      317                 }
      318             }
      319             if (!shouldSkip) {
      320                 newMembers[destIdx++] = member;
      321             }
      322         }
      323         return newMembers;
      324     }
      325 }