blob: 1afcdcf2822067a9b2c6611af975389e587ad94f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.reflect;
27
28import java.lang.reflect.*;
29import java.util.Collections;
30import java.util.HashMap;
31import java.util.Map;
32
33/** Common utility routines used by both java.lang and
34 java.lang.reflect */
35
36public 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}