|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.internal.reflect; |
|
|
|
|
|
import java.lang.reflect.*; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import java.util.Objects; |
|
import jdk.internal.HotSpotIntrinsicCandidate; |
|
import jdk.internal.loader.ClassLoaders; |
|
import jdk.internal.misc.VM; |
|
|
|
/** 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 |
|
@HotSpotIntrinsicCandidate |
|
public static native Class<?> getCallerClass(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
valid. */ |
|
@HotSpotIntrinsicCandidate |
|
public static native int getClassAccessFlags(Class<?> c); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static void ensureMemberAccess(Class<?> currentClass, |
|
Class<?> memberClass, |
|
Class<?> targetClass, |
|
int modifiers) |
|
throws IllegalAccessException |
|
{ |
|
if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) { |
|
throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static boolean verifyMemberAccess(Class<?> currentClass, |
|
Class<?> memberClass, |
|
Class<?> targetClass, |
|
int modifiers) |
|
{ |
|
if (currentClass == memberClass) { |
|
|
|
return true; |
|
} |
|
|
|
if (!verifyModuleAccess(currentClass.getModule(), memberClass)) { |
|
return false; |
|
} |
|
|
|
boolean gotIsSameClassPackage = false; |
|
boolean isSameClassPackage = false; |
|
|
|
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; |
|
} |
|
|
|
|
|
if (Modifier.isPrivate(modifiers)) { |
|
// Note: targetClass may be outside the nest, but that is okay |
|
|
|
if (areNestMates(currentClass, memberClass)) { |
|
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; |
|
} |
|
|
|
// Additional test for protected instance members |
|
|
|
if (targetClass != null && Modifier.isProtected(modifiers) && |
|
targetClass != currentClass) |
|
{ |
|
if (!gotIsSameClassPackage) { |
|
isSameClassPackage = isSameClassPackage(currentClass, memberClass); |
|
gotIsSameClassPackage = true; |
|
} |
|
if (!isSameClassPackage) { |
|
if (!isSubclassOf(targetClass, currentClass)) { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) { |
|
Module memberModule = memberClass.getModule(); |
|
if (currentModule == memberModule) { |
|
// same module (named or unnamed) or both null if called |
|
// before module system is initialized, which means we are |
|
|
|
return true; |
|
} else { |
|
String pkg = memberClass.getPackageName(); |
|
return memberModule.isExported(pkg, currentModule); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { |
|
if (c1.getClassLoader() != c2.getClassLoader()) |
|
return false; |
|
return Objects.equals(c1.getPackageName(), c2.getPackageName()); |
|
} |
|
|
|
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 (VM.isSystemDomainLoader(loader)) { |
|
return m.isAnnotationPresent(CallerSensitive.class); |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static IllegalAccessException newIllegalAccessException(Class<?> currentClass, |
|
Class<?> memberClass, |
|
Class<?> targetClass, |
|
int modifiers) |
|
throws IllegalAccessException |
|
{ |
|
String currentSuffix = ""; |
|
String memberSuffix = ""; |
|
Module m1 = currentClass.getModule(); |
|
if (m1.isNamed()) |
|
currentSuffix = " (in " + m1 + ")"; |
|
Module m2 = memberClass.getModule(); |
|
if (m2.isNamed()) |
|
memberSuffix = " (in " + m2 + ")"; |
|
|
|
String memberPackageName = memberClass.getPackageName(); |
|
|
|
String msg = currentClass + currentSuffix + " cannot access "; |
|
if (m2.isExported(memberPackageName, m1)) { |
|
|
|
|
|
msg += "a member of " + memberClass + memberSuffix + |
|
" with modifiers \"" + Modifier.toString(modifiers) + "\""; |
|
|
|
} else { |
|
|
|
msg += memberClass + memberSuffix+ " because " |
|
+ m2 + " does not export " + memberPackageName; |
|
if (m2.isNamed()) msg += " to " + m1; |
|
} |
|
|
|
return new IllegalAccessException(msg); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static native boolean areNestMates(Class<?> currentClass, |
|
Class<?> memberClass); |
|
} |