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.
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.
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).
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.
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
28 import java.lang.reflect.*;
29 import java.util.Collections;
30 import java.util.HashMap;
33 /** Common utility routines used by both java.lang and
36 public class Reflection {
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;
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"});
52 methodFilterMap = new HashMap<Class,String[]>();
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"
64 public static native Class getCallerClass(int realFramesToSkip);
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
74 private static native int getClassAccessFlags(Class c);
76 /** A quick "fast-path" check to try to avoid getCallerClass()
78 public static boolean quickCheckMemberAccess(Class memberClass,
81 return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers);
84 public static void ensureMemberAccess(Class currentClass,
88 throws IllegalAccessException
90 if (currentClass == null || memberClass == null) {
91 throw new InternalError();
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) +
104 public static boolean verifyMemberAccess(Class currentClass,
105 // Declaring class of field
108 // May be NULL in case of statics
112 // Verify that currentClass can access a field, method, or
113 // constructor of memberClass, where that member's access bits are
116 boolean gotIsSameClassPackage = false;
117 boolean isSameClassPackage = false;
119 if (currentClass == memberClass) {
124 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
125 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
126 gotIsSameClassPackage = true;
127 if (!isSameClassPackage) {
132 // At this point we know that currentClass can access memberClass.
134 if (Modifier.isPublic(modifiers)) {
138 boolean successSoFar = false;
140 if (Modifier.isProtected(modifiers)) {
141 // See if currentClass is a subclass of memberClass
142 if (isSubclassOf(currentClass, memberClass)) {
147 if (!successSoFar && !Modifier.isPrivate(modifiers)) {
148 if (!gotIsSameClassPackage) {
149 isSameClassPackage = isSameClassPackage(currentClass,
151 gotIsSameClassPackage = true;
154 if (isSameClassPackage) {
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;
171 if (!isSameClassPackage) {
172 if (!isSubclassOf(targetClass, currentClass)) {
182 private static boolean isSameClassPackage(Class c1, Class c2) {
183 return isSameClassPackage(c1.getClassLoader(), c1.getName(),
184 c2.getClassLoader(), c2.getName());
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)
192 if (loader1 != loader2) {
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);
206 if (name1.charAt(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);
215 if (name2.charAt(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);
225 // Check that package part is identical
226 int length1 = lastDot1 - idx1;
227 int length2 = lastDot2 - idx2;
229 if (length1 != length2) {
232 return name1.regionMatches(false, idx1, name2, idx2, length1);
237 static boolean isSubclassOf(Class queryClass,
240 while (queryClass != null) {
241 if (queryClass == ofClass) {
244 queryClass = queryClass.getSuperclass();
249 // fieldNames must contain only interned Strings
250 public static synchronized void registerFieldsToFilter(Class containingClass,
251 String ... fieldNames) {
253 registerFilter(fieldFilterMap, containingClass, fieldNames);
256 // methodNames must contain only interned Strings
257 public static synchronized void registerMethodsToFilter(Class containingClass,
258 String ... methodNames) {
260 registerFilter(methodFilterMap, containingClass, methodNames);
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);
269 map = new HashMap<Class,String[]>(map);
270 map.put(containingClass, names);
274 public static Field[] filterFields(Class containingClass,
276 if (fieldFilterMap == null) {
280 return (Field[])filter(fields, fieldFilterMap.get(containingClass));
283 public static Method[] filterMethods(Class containingClass, Method[] methods) {
284 if (methodFilterMap == null) {
288 return (Method[])filter(methods, methodFilterMap.get(containingClass));
291 private static Member[] filter(Member[] members, String[] filteredNames) {
292 if ((filteredNames == null) || (members.length == 0)) {
295 int numNewMembers = 0;
296 for (Member member : members) {
297 boolean shouldSkip = false;
298 for (String filteredName : filteredNames) {
299 if (member.getName() == filteredName) {
308 Member[] newMembers =
309 (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
311 for (Member member : members) {
312 boolean shouldSkip = false;
313 for (String filteredName : filteredNames) {
314 if (member.getName() == filteredName) {
320 newMembers[destIdx++] = member;