|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.internal.reflect; |
|
|
|
import java.lang.reflect.*; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import java.util.Objects; |
|
import java.util.Set; |
|
import jdk.internal.access.SharedSecrets; |
|
import jdk.internal.misc.VM; |
|
import jdk.internal.vm.annotation.IntrinsicCandidate; |
|
|
|
/** 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<?>, Set<String>> fieldFilterMap; |
|
private static volatile Map<Class<?>, Set<String>> methodFilterMap; |
|
private static final String WILDCARD = "*"; |
|
public static final Set<String> ALL_MEMBERS = Set.of(WILDCARD); |
|
|
|
static { |
|
fieldFilterMap = Map.of( |
|
Reflection.class, ALL_MEMBERS, |
|
AccessibleObject.class, ALL_MEMBERS, |
|
Class.class, Set.of("classLoader", "classData"), |
|
ClassLoader.class, ALL_MEMBERS, |
|
Constructor.class, ALL_MEMBERS, |
|
Field.class, ALL_MEMBERS, |
|
Method.class, ALL_MEMBERS, |
|
Module.class, ALL_MEMBERS, |
|
System.class, Set.of("security") |
|
); |
|
methodFilterMap = Map.of(); |
|
} |
|
|
|
|
|
|
|
and its implementation. */ |
|
@CallerSensitive |
|
@IntrinsicCandidate |
|
public static native Class<?> getCallerClass(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
valid. */ |
|
@IntrinsicCandidate |
|
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 void ensureNativeAccess(Class<?> currentClass) { |
|
Module module = currentClass.getModule(); |
|
if (!SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module)) { |
|
throw new IllegalCallerException("Illegal native access from: " + module); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static boolean verifyMemberAccess(Class<?> currentClass, |
|
Class<?> memberClass, |
|
Class<?> targetClass, |
|
int modifiers) |
|
{ |
|
Objects.requireNonNull(currentClass); |
|
Objects.requireNonNull(memberClass); |
|
|
|
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 verifyPublicMemberAccess(Class<?> memberClass, int modifiers) { |
|
Module m = memberClass.getModule(); |
|
return Modifier.isPublic(modifiers) |
|
&& m.isExported(memberClass.getPackageName()) |
|
&& Modifier.isPublic(Reflection.getClassAccessFlags(memberClass)); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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, |
|
Set<String> fieldNames) { |
|
fieldFilterMap = |
|
registerFilter(fieldFilterMap, containingClass, fieldNames); |
|
} |
|
|
|
|
|
public static synchronized void registerMethodsToFilter(Class<?> containingClass, |
|
Set<String> methodNames) { |
|
methodFilterMap = |
|
registerFilter(methodFilterMap, containingClass, methodNames); |
|
} |
|
|
|
private static Map<Class<?>, Set<String>> registerFilter(Map<Class<?>, Set<String>> map, |
|
Class<?> containingClass, |
|
Set<String> names) { |
|
if (map.get(containingClass) != null) { |
|
throw new IllegalArgumentException |
|
("Filter already registered: " + containingClass); |
|
} |
|
map = new HashMap<>(map); |
|
map.put(containingClass, Set.copyOf(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, Set<String> filteredNames) { |
|
if ((filteredNames == null) || (members.length == 0)) { |
|
return members; |
|
} |
|
Class<?> memberType = members[0].getClass(); |
|
if (filteredNames.contains(WILDCARD)) { |
|
return (Member[]) Array.newInstance(memberType, 0); |
|
} |
|
int numNewMembers = 0; |
|
for (Member member : members) { |
|
if (!filteredNames.contains(member.getName())) { |
|
++numNewMembers; |
|
} |
|
} |
|
Member[] newMembers = (Member[])Array.newInstance(memberType, numNewMembers); |
|
int destIdx = 0; |
|
for (Member member : members) { |
|
if (!filteredNames.contains(member.getName())) { |
|
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 boolean isTrustedFinalField(Field field) { |
|
return SharedSecrets.getJavaLangReflectAccess().isTrustedFinalField(field); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static IllegalAccessException newIllegalAccessException(Class<?> currentClass, |
|
Class<?> memberClass, |
|
Class<?> targetClass, |
|
int modifiers) |
|
{ |
|
if (currentClass == null) |
|
return newIllegalAccessException(memberClass, modifiers); |
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static IllegalAccessException newIllegalAccessException(Class<?> memberClass, |
|
int modifiers) |
|
{ |
|
String memberSuffix = ""; |
|
Module m2 = memberClass.getModule(); |
|
if (m2.isNamed()) |
|
memberSuffix = " (in " + m2 + ")"; |
|
|
|
String memberPackageName = memberClass.getPackageName(); |
|
|
|
String msg = "JNI attached native thread (null caller frame) cannot access "; |
|
if (m2.isExported(memberPackageName)) { |
|
|
|
|
|
msg += "a member of " + memberClass + memberSuffix + |
|
" with modifiers \"" + Modifier.toString(modifiers) + "\""; |
|
|
|
} else { |
|
|
|
msg += memberClass + memberSuffix+ " because " |
|
+ m2 + " does not export " + memberPackageName; |
|
} |
|
|
|
return new IllegalAccessException(msg); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static native boolean areNestMates(Class<?> currentClass, |
|
Class<?> memberClass); |
|
} |