001: /*
002: * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.reflect;
027:
028: import java.lang.reflect.*;
029: import java.util.Collections;
030: import java.util.HashMap;
031: import java.util.Map;
032:
033: /** Common utility routines used by both java.lang and
034: java.lang.reflect */
035:
036: public class Reflection {
037:
038: /** Used to filter out fields and methods from certain classes from public
039: view, where they are sensitive or they may contain VM-internal objects.
040: These Maps are updated very rarely. Rather than synchronize on
041: each access, we use copy-on-write */
042: private static volatile Map<Class, String[]> fieldFilterMap;
043: private static volatile Map<Class, String[]> methodFilterMap;
044:
045: static {
046: Map<Class, String[]> map = new HashMap<Class, String[]>();
047: map.put(Reflection.class, new String[] { "fieldFilterMap",
048: "methodFilterMap" });
049: map.put(System.class, new String[] { "security" });
050: fieldFilterMap = map;
051:
052: methodFilterMap = new HashMap<Class, String[]>();
053: }
054:
055: /** Returns the class of the method <code>realFramesToSkip</code>
056: frames up the stack (zero-based), ignoring frames associated
057: with java.lang.reflect.Method.invoke() and its implementation.
058: The first frame is that associated with this method, so
059: <code>getCallerClass(0)</code> returns the Class object for
060: sun.reflect.Reflection. Frames associated with
061: java.lang.reflect.Method.invoke() and its implementation are
062: completely ignored and do not count toward the number of "real"
063: frames skipped. */
064: public static native Class getCallerClass(int realFramesToSkip);
065:
066: /** Retrieves the access flags written to the class file. For
067: inner classes these flags may differ from those returned by
068: Class.getModifiers(), which searches the InnerClasses
069: attribute to find the source-level access flags. This is used
070: instead of Class.getModifiers() for run-time access checks due
071: to compatibility reasons; see 4471811. Only the values of the
072: low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
073: valid. */
074: private static native int getClassAccessFlags(Class c);
075:
076: /** A quick "fast-path" check to try to avoid getCallerClass()
077: calls. */
078: public static boolean quickCheckMemberAccess(Class memberClass,
079: int modifiers) {
080: return Modifier.isPublic(getClassAccessFlags(memberClass)
081: & modifiers);
082: }
083:
084: public static void ensureMemberAccess(Class currentClass,
085: Class memberClass, Object target, int modifiers)
086: throws IllegalAccessException {
087: if (currentClass == null || memberClass == null) {
088: throw new InternalError();
089: }
090:
091: if (!verifyMemberAccess(currentClass, memberClass, target,
092: modifiers)) {
093: throw new IllegalAccessException("Class "
094: + currentClass.getName()
095: + " can not access a member of class "
096: + memberClass.getName() + " with modifiers \""
097: + Modifier.toString(modifiers) + "\"");
098: }
099: }
100:
101: public static boolean verifyMemberAccess(Class currentClass,
102: // Declaring class of field
103: // or method
104: Class memberClass,
105: // May be NULL in case of statics
106: Object target, int modifiers) {
107: // Verify that currentClass can access a field, method, or
108: // constructor of memberClass, where that member's access bits are
109: // "modifiers".
110:
111: boolean gotIsSameClassPackage = false;
112: boolean isSameClassPackage = false;
113:
114: if (currentClass == memberClass) {
115: // Always succeeds
116: return true;
117: }
118:
119: if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
120: isSameClassPackage = isSameClassPackage(currentClass,
121: memberClass);
122: gotIsSameClassPackage = true;
123: if (!isSameClassPackage) {
124: return false;
125: }
126: }
127:
128: // At this point we know that currentClass can access memberClass.
129:
130: if (Modifier.isPublic(modifiers)) {
131: return true;
132: }
133:
134: boolean successSoFar = false;
135:
136: if (Modifier.isProtected(modifiers)) {
137: // See if currentClass is a subclass of memberClass
138: if (isSubclassOf(currentClass, memberClass)) {
139: successSoFar = true;
140: }
141: }
142:
143: if (!successSoFar && !Modifier.isPrivate(modifiers)) {
144: if (!gotIsSameClassPackage) {
145: isSameClassPackage = isSameClassPackage(currentClass,
146: memberClass);
147: gotIsSameClassPackage = true;
148: }
149:
150: if (isSameClassPackage) {
151: successSoFar = true;
152: }
153: }
154:
155: if (!successSoFar) {
156: return false;
157: }
158:
159: if (Modifier.isProtected(modifiers)) {
160: // Additional test for protected members: JLS 6.6.2
161: Class targetClass = (target == null ? memberClass : target
162: .getClass());
163: if (targetClass != currentClass) {
164: if (!gotIsSameClassPackage) {
165: isSameClassPackage = isSameClassPackage(
166: currentClass, memberClass);
167: gotIsSameClassPackage = true;
168: }
169: if (!isSameClassPackage) {
170: if (!isSubclassOf(targetClass, currentClass)) {
171: return false;
172: }
173: }
174: }
175: }
176:
177: return true;
178: }
179:
180: private static boolean isSameClassPackage(Class c1, Class c2) {
181: return isSameClassPackage(c1.getClassLoader(), c1.getName(), c2
182: .getClassLoader(), c2.getName());
183: }
184:
185: /** Returns true if two classes are in the same package; classloader
186: and classname information is enough to determine a class's package */
187: private static boolean isSameClassPackage(ClassLoader loader1,
188: String name1, ClassLoader loader2, String name2) {
189: if (loader1 != loader2) {
190: return false;
191: } else {
192: int lastDot1 = name1.lastIndexOf('.');
193: int lastDot2 = name2.lastIndexOf('.');
194: if ((lastDot1 == -1) || (lastDot2 == -1)) {
195: // One of the two doesn't have a package. Only return true
196: // if the other one also doesn't have a package.
197: return (lastDot1 == lastDot2);
198: } else {
199: int idx1 = 0;
200: int idx2 = 0;
201:
202: // Skip over '['s
203: if (name1.charAt(idx1) == '[') {
204: do {
205: idx1++;
206: } while (name1.charAt(idx1) == '[');
207: if (name1.charAt(idx1) != 'L') {
208: // Something is terribly wrong. Shouldn't be here.
209: throw new InternalError("Illegal class name "
210: + name1);
211: }
212: }
213: if (name2.charAt(idx2) == '[') {
214: do {
215: idx2++;
216: } while (name2.charAt(idx2) == '[');
217: if (name2.charAt(idx2) != 'L') {
218: // Something is terribly wrong. Shouldn't be here.
219: throw new InternalError("Illegal class name "
220: + name2);
221: }
222: }
223:
224: // Check that package part is identical
225: int length1 = lastDot1 - idx1;
226: int length2 = lastDot2 - idx2;
227:
228: if (length1 != length2) {
229: return false;
230: }
231: return name1.regionMatches(false, idx1, name2, idx2,
232: length1);
233: }
234: }
235: }
236:
237: static boolean isSubclassOf(Class queryClass, Class ofClass) {
238: while (queryClass != null) {
239: if (queryClass == ofClass) {
240: return true;
241: }
242: queryClass = queryClass.getSuperclass();
243: }
244: return false;
245: }
246:
247: // fieldNames must contain only interned Strings
248: public static synchronized void registerFieldsToFilter(
249: Class containingClass, String... fieldNames) {
250: fieldFilterMap = registerFilter(fieldFilterMap,
251: containingClass, fieldNames);
252: }
253:
254: // methodNames must contain only interned Strings
255: public static synchronized void registerMethodsToFilter(
256: Class containingClass, String... methodNames) {
257: methodFilterMap = registerFilter(methodFilterMap,
258: containingClass, methodNames);
259: }
260:
261: private static Map<Class, String[]> registerFilter(
262: Map<Class, String[]> map, Class containingClass,
263: String... names) {
264: if (map.get(containingClass) != null) {
265: throw new IllegalArgumentException(
266: "Filter already registered: " + containingClass);
267: }
268: map = new HashMap<Class, String[]>(map);
269: map.put(containingClass, names);
270: return map;
271: }
272:
273: public static Field[] filterFields(Class containingClass,
274: Field[] fields) {
275: if (fieldFilterMap == null) {
276: // Bootstrapping
277: return fields;
278: }
279: return (Field[]) filter(fields, fieldFilterMap
280: .get(containingClass));
281: }
282:
283: public static Method[] filterMethods(Class containingClass,
284: Method[] methods) {
285: if (methodFilterMap == null) {
286: // Bootstrapping
287: return methods;
288: }
289: return (Method[]) filter(methods, methodFilterMap
290: .get(containingClass));
291: }
292:
293: private static Member[] filter(Member[] members,
294: 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 = (Member[]) Array.newInstance(members[0]
312: .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: }
|