|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.reflect; |
|
|
|
import java.lang.reflect.*; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
|
|
/** Common utility routines used by both java.lang and |
|
java.lang.reflect */ |
|
|
|
public class Reflection { |
|
|
|
|
|
|
|
|
|
each access, we use copy-on-write */ |
|
private static volatile Map<Class<?>,String[]> fieldFilterMap; |
|
private static volatile Map<Class<?>,String[]> methodFilterMap; |
|
|
|
static { |
|
Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>(); |
|
map.put(Reflection.class, |
|
new String[] {"fieldFilterMap", "methodFilterMap"}); |
|
map.put(System.class, new String[] {"security"}); |
|
map.put(Class.class, new String[] {"classLoader"}); |
|
fieldFilterMap = map; |
|
|
|
methodFilterMap = new HashMap<>(); |
|
} |
|
|
|
|
|
|
|
and its implementation. */ |
|
@CallerSensitive |
|
public static native Class<?> getCallerClass(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
public static native Class<?> getCallerClass(int depth); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
valid. */ |
|
public static native int getClassAccessFlags(Class<?> c); |
|
|
|
|
|
calls. */ |
|
public static boolean quickCheckMemberAccess(Class<?> memberClass, |
|
int modifiers) |
|
{ |
|
return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers); |
|
} |
|
|
|
public static void ensureMemberAccess(Class<?> currentClass, |
|
Class<?> memberClass, |
|
Object target, |
|
int modifiers) |
|
throws IllegalAccessException |
|
{ |
|
if (currentClass == null || memberClass == null) { |
|
throw new InternalError(); |
|
} |
|
|
|
if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) { |
|
throw new IllegalAccessException("Class " + currentClass.getName() + |
|
" can not access a member of class " + |
|
memberClass.getName() + |
|
" with modifiers \"" + |
|
Modifier.toString(modifiers) + |
|
"\""); |
|
} |
|
} |
|
|
|
public static boolean verifyMemberAccess(Class<?> currentClass, |
|
// Declaring class of field |
|
|
|
Class<?> memberClass, |
|
|
|
Object target, |
|
int modifiers) |
|
{ |
|
// Verify that currentClass can access a field, method, or |
|
// constructor of memberClass, where that member's access bits are |
|
// "modifiers". |
|
|
|
boolean gotIsSameClassPackage = false; |
|
boolean isSameClassPackage = false; |
|
|
|
if (currentClass == memberClass) { |
|
|
|
return true; |
|
} |
|
|
|
if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { |
|
isSameClassPackage = isSameClassPackage(currentClass, memberClass); |
|
gotIsSameClassPackage = true; |
|
if (!isSameClassPackage) { |
|
return false; |
|
} |
|
} |
|
|
|
// At this point we know that currentClass can access memberClass. |
|
|
|
if (Modifier.isPublic(modifiers)) { |
|
return true; |
|
} |
|
|
|
boolean successSoFar = false; |
|
|
|
if (Modifier.isProtected(modifiers)) { |
|
|
|
if (isSubclassOf(currentClass, memberClass)) { |
|
successSoFar = true; |
|
} |
|
} |
|
|
|
if (!successSoFar && !Modifier.isPrivate(modifiers)) { |
|
if (!gotIsSameClassPackage) { |
|
isSameClassPackage = isSameClassPackage(currentClass, |
|
memberClass); |
|
gotIsSameClassPackage = true; |
|
} |
|
|
|
if (isSameClassPackage) { |
|
successSoFar = true; |
|
} |
|
} |
|
|
|
if (!successSoFar) { |
|
return false; |
|
} |
|
|
|
if (Modifier.isProtected(modifiers)) { |
|
|
|
Class<?> targetClass = (target == null ? memberClass : target.getClass()); |
|
if (targetClass != currentClass) { |
|
if (!gotIsSameClassPackage) { |
|
isSameClassPackage = isSameClassPackage(currentClass, memberClass); |
|
gotIsSameClassPackage = true; |
|
} |
|
if (!isSameClassPackage) { |
|
if (!isSubclassOf(targetClass, currentClass)) { |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { |
|
return isSameClassPackage(c1.getClassLoader(), c1.getName(), |
|
c2.getClassLoader(), c2.getName()); |
|
} |
|
|
|
|
|
and classname information is enough to determine a class's package */ |
|
private static boolean isSameClassPackage(ClassLoader loader1, String name1, |
|
ClassLoader loader2, String name2) |
|
{ |
|
if (loader1 != loader2) { |
|
return false; |
|
} else { |
|
int lastDot1 = name1.lastIndexOf('.'); |
|
int lastDot2 = name2.lastIndexOf('.'); |
|
if ((lastDot1 == -1) || (lastDot2 == -1)) { |
|
// One of the two doesn't have a package. Only return true |
|
|
|
return (lastDot1 == lastDot2); |
|
} else { |
|
int idx1 = 0; |
|
int idx2 = 0; |
|
|
|
|
|
if (name1.charAt(idx1) == '[') { |
|
do { |
|
idx1++; |
|
} while (name1.charAt(idx1) == '['); |
|
if (name1.charAt(idx1) != 'L') { |
|
|
|
throw new InternalError("Illegal class name " + name1); |
|
} |
|
} |
|
if (name2.charAt(idx2) == '[') { |
|
do { |
|
idx2++; |
|
} while (name2.charAt(idx2) == '['); |
|
if (name2.charAt(idx2) != 'L') { |
|
|
|
throw new InternalError("Illegal class name " + name2); |
|
} |
|
} |
|
|
|
|
|
int length1 = lastDot1 - idx1; |
|
int length2 = lastDot2 - idx2; |
|
|
|
if (length1 != length2) { |
|
return false; |
|
} |
|
return name1.regionMatches(false, idx1, name2, idx2, length1); |
|
} |
|
} |
|
} |
|
|
|
static boolean isSubclassOf(Class<?> queryClass, |
|
Class<?> ofClass) |
|
{ |
|
while (queryClass != null) { |
|
if (queryClass == ofClass) { |
|
return true; |
|
} |
|
queryClass = queryClass.getSuperclass(); |
|
} |
|
return false; |
|
} |
|
|
|
|
|
public static synchronized void registerFieldsToFilter(Class<?> containingClass, |
|
String ... fieldNames) { |
|
fieldFilterMap = |
|
registerFilter(fieldFilterMap, containingClass, fieldNames); |
|
} |
|
|
|
|
|
public static synchronized void registerMethodsToFilter(Class<?> containingClass, |
|
String ... methodNames) { |
|
methodFilterMap = |
|
registerFilter(methodFilterMap, containingClass, methodNames); |
|
} |
|
|
|
private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map, |
|
Class<?> containingClass, String ... names) { |
|
if (map.get(containingClass) != null) { |
|
throw new IllegalArgumentException |
|
("Filter already registered: " + containingClass); |
|
} |
|
map = new HashMap<Class<?>,String[]>(map); |
|
map.put(containingClass, names); |
|
return map; |
|
} |
|
|
|
public static Field[] filterFields(Class<?> containingClass, |
|
Field[] fields) { |
|
if (fieldFilterMap == null) { |
|
|
|
return fields; |
|
} |
|
return (Field[])filter(fields, fieldFilterMap.get(containingClass)); |
|
} |
|
|
|
public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { |
|
if (methodFilterMap == null) { |
|
|
|
return methods; |
|
} |
|
return (Method[])filter(methods, methodFilterMap.get(containingClass)); |
|
} |
|
|
|
private static Member[] filter(Member[] members, String[] filteredNames) { |
|
if ((filteredNames == null) || (members.length == 0)) { |
|
return members; |
|
} |
|
int numNewMembers = 0; |
|
for (Member member : members) { |
|
boolean shouldSkip = false; |
|
for (String filteredName : filteredNames) { |
|
if (member.getName() == filteredName) { |
|
shouldSkip = true; |
|
break; |
|
} |
|
} |
|
if (!shouldSkip) { |
|
++numNewMembers; |
|
} |
|
} |
|
Member[] newMembers = |
|
(Member[])Array.newInstance(members[0].getClass(), numNewMembers); |
|
int destIdx = 0; |
|
for (Member member : members) { |
|
boolean shouldSkip = false; |
|
for (String filteredName : filteredNames) { |
|
if (member.getName() == filteredName) { |
|
shouldSkip = true; |
|
break; |
|
} |
|
} |
|
if (!shouldSkip) { |
|
newMembers[destIdx++] = member; |
|
} |
|
} |
|
return newMembers; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static boolean isCallerSensitive(Method m) { |
|
final ClassLoader loader = m.getDeclaringClass().getClassLoader(); |
|
if (sun.misc.VM.isSystemDomainLoader(loader) || isExtClassLoader(loader)) { |
|
return m.isAnnotationPresent(CallerSensitive.class); |
|
} |
|
return false; |
|
} |
|
|
|
private static boolean isExtClassLoader(ClassLoader loader) { |
|
ClassLoader cl = ClassLoader.getSystemClassLoader(); |
|
while (cl != null) { |
|
if (cl.getParent() == null && cl == loader) { |
|
return true; |
|
} |
|
cl = cl.getParent(); |
|
} |
|
return false; |
|
} |
|
} |