1 /*
2 * Copyright (c) 2001, 2025, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.internal.reflect;
27
28 import java.lang.reflect.*;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.Set;
33
34 import jdk.internal.access.JavaLangAccess;
35 import jdk.internal.access.SharedSecrets;
36 import jdk.internal.misc.VM;
37 import jdk.internal.module.ModuleBootstrap;
38 import jdk.internal.vm.annotation.ForceInline;
39 import jdk.internal.vm.annotation.IntrinsicCandidate;
40
41 /** Common utility routines used by both java.lang and
42 java.lang.reflect */
43
44 public class Reflection {
45
46 /** Used to filter out fields and methods from certain classes from public
47 view, where they are sensitive or they may contain VM-internal objects.
48 These Maps are updated very rarely. Rather than synchronize on
49 each access, we use copy-on-write */
50 private static volatile Map<Class<?>, Set<String>> fieldFilterMap;
51 private static volatile Map<Class<?>, Set<String>> methodFilterMap;
52 private static final String WILDCARD = "*";
53 public static final Set<String> ALL_MEMBERS = Set.of(WILDCARD);
54
55 static {
56 fieldFilterMap = Map.of(
57 Reflection.class, ALL_MEMBERS,
58 AccessibleObject.class, ALL_MEMBERS,
59 Class.class, Set.of("classLoader", "classData", "modifiers", "protectionDomain", "primitive"),
60 ClassLoader.class, ALL_MEMBERS,
61 Constructor.class, ALL_MEMBERS,
62 Field.class, ALL_MEMBERS,
63 Method.class, ALL_MEMBERS,
64 Module.class, ALL_MEMBERS
65 );
66 methodFilterMap = Map.of();
67 }
68
69 /** Returns the class of the caller of the method calling this method,
70 ignoring frames associated with java.lang.reflect.Method.invoke()
71 and its implementation. */
72 @CallerSensitive
73 @IntrinsicCandidate
74 public static native Class<?> getCallerClass();
75
76 /** Retrieves the access flags written to the class file. For
77 inner classes these flags may differ from those returned by
78 Class.getModifiers(), which searches the InnerClasses
79 attribute to find the source-level access flags. This is used
80 instead of Class.getModifiers() for run-time access checks due
81 to compatibility reasons; see 4471811. Only the values of the
82 low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
83 valid. */
84 public static int getClassAccessFlags(Class<?> c) {
85 return SharedSecrets.getJavaLangAccess().getClassFileAccessFlags(c);
86 }
87
88
89 /**
90 * Ensures that access to a member is granted and throws
91 * IllegalAccessException if not.
92 *
93 * @param currentClass the class performing the access
94 * @param memberClass the declaring class of the member being accessed
95 * @param targetClass the class of target object if accessing instance
96 * field or method;
97 * or the declaring class if accessing constructor;
98 * or null if accessing static field or method
99 * @param modifiers the member's access modifiers
100 * @throws IllegalAccessException if access to member is denied
101 */
102 public static void ensureMemberAccess(Class<?> currentClass,
103 Class<?> memberClass,
104 Class<?> targetClass,
105 int modifiers)
106 throws IllegalAccessException
107 {
108 if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
109 throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
110 }
111 }
112
113 @ForceInline
114 public static void ensureNativeAccess(Class<?> currentClass, Class<?> owner, String methodName, boolean jni) {
115 // if there is no caller class, act as if the call came from unnamed module of system class loader
116 Module module = currentClass != null ?
117 currentClass.getModule() :
118 ClassLoader.getSystemClassLoader().getUnnamedModule();
119 class Holder {
120 static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
121 }
122 if (module != null) {
123 // not in init phase
124 Holder.JLA.ensureNativeAccess(module, owner, methodName, currentClass, jni);
125 }
126 }
127
128 /**
129 * Verify access to a member and return {@code true} if it is granted.
130 *
131 * @param currentClass the class performing the access
132 * @param memberClass the declaring class of the member being accessed
133 * @param targetClass the class of target object if accessing instance
134 * field or method;
135 * or the declaring class if accessing constructor;
136 * or null if accessing static field or method
137 * @param modifiers the member's access modifiers
138 * @return {@code true} if access to member is granted
139 */
140 public static boolean verifyMemberAccess(Class<?> currentClass,
141 Class<?> memberClass,
142 Class<?> targetClass,
143 int modifiers)
144 {
145 Objects.requireNonNull(currentClass);
146 Objects.requireNonNull(memberClass);
147
148 if (currentClass == memberClass) {
149 // Always succeeds
150 return true;
151 }
152
153 if (!verifyModuleAccess(currentClass.getModule(), memberClass)) {
154 return false;
155 }
156
157 boolean gotIsSameClassPackage = false;
158 boolean isSameClassPackage = false;
159
160 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
161 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
162 gotIsSameClassPackage = true;
163 if (!isSameClassPackage) {
164 return false;
165 }
166 }
167
168 // At this point we know that currentClass can access memberClass.
169
170 if (Modifier.isPublic(modifiers)) {
171 return true;
172 }
173
174 // Check for nestmate access if member is private
175 if (Modifier.isPrivate(modifiers)) {
176 // Note: targetClass may be outside the nest, but that is okay
177 // as long as memberClass is in the nest.
178 if (areNestMates(currentClass, memberClass)) {
179 return true;
180 }
181 }
182
183 boolean successSoFar = false;
184
185 if (Modifier.isProtected(modifiers)) {
186 // See if currentClass is a subclass of memberClass
187 if (isSubclassOf(currentClass, memberClass)) {
188 successSoFar = true;
189 }
190 }
191
192 if (!successSoFar && !Modifier.isPrivate(modifiers)) {
193 if (!gotIsSameClassPackage) {
194 isSameClassPackage = isSameClassPackage(currentClass,
195 memberClass);
196 gotIsSameClassPackage = true;
197 }
198
199 if (isSameClassPackage) {
200 successSoFar = true;
201 }
202 }
203
204 if (!successSoFar) {
205 return false;
206 }
207
208 // Additional test for protected instance members
209 // and protected constructors: JLS 6.6.2
210 if (targetClass != null && Modifier.isProtected(modifiers) &&
211 targetClass != currentClass)
212 {
213 if (!gotIsSameClassPackage) {
214 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
215 gotIsSameClassPackage = true;
216 }
217 if (!isSameClassPackage) {
218 if (!isSubclassOf(targetClass, currentClass)) {
219 return false;
220 }
221 }
222 }
223
224 return true;
225 }
226
227 /*
228 * Verify if a member is public and memberClass is a public type
229 * in a package that is unconditionally exported and
230 * return {@code true} if it is granted.
231 *
232 * @param memberClass the declaring class of the member being accessed
233 * @param modifiers the member's access modifiers
234 * @return {@code true} if the member is public and in a publicly accessible type
235 */
236 public static boolean verifyPublicMemberAccess(Class<?> memberClass, int modifiers) {
237 Module m = memberClass.getModule();
238 return Modifier.isPublic(modifiers)
239 && m.isExported(memberClass.getPackageName())
240 && Modifier.isPublic(Reflection.getClassAccessFlags(memberClass));
241 }
242
243 /**
244 * Returns {@code true} if memberClass's module exports memberClass's
245 * package to currentModule.
246 */
247 public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
248 Module memberModule = memberClass.getModule();
249 if (currentModule == memberModule) {
250 // same module (named or unnamed) or both null if called
251 // before module system is initialized, which means we are
252 // dealing with java.base only.
253 return true;
254 } else {
255 String pkg = memberClass.getPackageName();
256 return memberModule.isExported(pkg, currentModule);
257 }
258 }
259
260 /**
261 * Returns true if two classes in the same package.
262 */
263 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
264 if (c1.getClassLoader() != c2.getClassLoader())
265 return false;
266 return Objects.equals(c1.getPackageName(), c2.getPackageName());
267 }
268
269 static boolean isSubclassOf(Class<?> queryClass,
270 Class<?> ofClass)
271 {
272 while (queryClass != null) {
273 if (queryClass == ofClass) {
274 return true;
275 }
276 queryClass = queryClass.getSuperclass();
277 }
278 return false;
279 }
280
281 // fieldNames must contain only interned Strings
282 public static synchronized void registerFieldsToFilter(Class<?> containingClass,
283 Set<String> fieldNames) {
284 fieldFilterMap =
285 registerFilter(fieldFilterMap, containingClass, fieldNames);
286 }
287
288 // methodNames must contain only interned Strings
289 public static synchronized void registerMethodsToFilter(Class<?> containingClass,
290 Set<String> methodNames) {
291 methodFilterMap =
292 registerFilter(methodFilterMap, containingClass, methodNames);
293 }
294
295 private static Map<Class<?>, Set<String>> registerFilter(Map<Class<?>, Set<String>> map,
296 Class<?> containingClass,
297 Set<String> names) {
298 if (map.get(containingClass) != null) {
299 throw new IllegalArgumentException
300 ("Filter already registered: " + containingClass);
301 }
302 map = new HashMap<>(map);
303 map.put(containingClass, Set.copyOf(names));
304 return map;
305 }
306
307 public static Field[] filterFields(Class<?> containingClass, Field[] fields) {
308 if (fieldFilterMap == null) {
309 // Bootstrapping
310 return fields;
311 }
312 return (Field[])filter(fields, fieldFilterMap.get(containingClass));
313 }
314
315 public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
316 if (methodFilterMap == null) {
317 // Bootstrapping
318 return methods;
319 }
320 return (Method[])filter(methods, methodFilterMap.get(containingClass));
321 }
322
323 private static Member[] filter(Member[] members, Set<String> filteredNames) {
324 if ((filteredNames == null) || (members.length == 0)) {
325 return members;
326 }
327 Class<?> memberType = members[0].getClass();
328 if (filteredNames.contains(WILDCARD)) {
329 return (Member[]) Array.newInstance(memberType, 0);
330 }
331 int numNewMembers = 0;
332 for (Member member : members) {
333 if (!filteredNames.contains(member.getName())) {
334 ++numNewMembers;
335 }
336 }
337 Member[] newMembers = (Member[])Array.newInstance(memberType, numNewMembers);
338 int destIdx = 0;
339 for (Member member : members) {
340 if (!filteredNames.contains(member.getName())) {
341 newMembers[destIdx++] = member;
342 }
343 }
344 return newMembers;
345 }
346
347 /**
348 * Tests if the given method is caller-sensitive and the declaring class
349 * is defined by either the bootstrap class loader or platform class loader.
350 */
351 public static boolean isCallerSensitive(Method m) {
352 final ClassLoader loader = m.getDeclaringClass().getClassLoader();
353 if (VM.isSystemDomainLoader(loader)) {
354 return m.isAnnotationPresent(CallerSensitive.class);
355 }
356 return false;
357 }
358
359 /*
360 * Tests if the given Field is a trusted final field and it cannot be
361 * modified reflectively regardless of the value of its accessible flag.
362 */
363 public static boolean isTrustedFinalField(Field field) {
364 return SharedSecrets.getJavaLangReflectAccess().isTrustedFinalField(field);
365 }
366
367 /**
368 * Returns an IllegalAccessException with an exception message based on
369 * the access that is denied.
370 */
371 public static IllegalAccessException newIllegalAccessException(Class<?> currentClass,
372 Class<?> memberClass,
373 Class<?> targetClass,
374 int modifiers)
375 {
376 if (currentClass == null)
377 return newIllegalAccessException(memberClass, modifiers);
378
379 String currentSuffix = "";
380 String memberSuffix = "";
381 Module m1 = currentClass.getModule();
382 if (m1.isNamed())
383 currentSuffix = " (in " + m1 + ")";
384 Module m2 = memberClass.getModule();
385 if (m2.isNamed())
386 memberSuffix = " (in " + m2 + ")";
387
388 String memberPackageName = memberClass.getPackageName();
389
390 String msg = currentClass + currentSuffix + " cannot access ";
391 if (m2.isExported(memberPackageName, m1)) {
392 // module access okay so include the modifiers in the message
393 msg += "a member of " + memberClass + memberSuffix + msgSuffix(modifiers);
394 } else {
395 // module access failed
396 msg += memberClass + memberSuffix+ " because "
397 + m2 + " does not export " + memberPackageName;
398 if (m2.isNamed()) msg += " to " + m1;
399 }
400
401 return new IllegalAccessException(msg);
402 }
403
404 /**
405 * Returns an IllegalAccessException with an exception message where
406 * there is no caller frame.
407 */
408 private static IllegalAccessException newIllegalAccessException(Class<?> memberClass,
409 int modifiers)
410 {
411 String memberSuffix = "";
412 Module m2 = memberClass.getModule();
413 if (m2.isNamed())
414 memberSuffix = " (in " + m2 + ")";
415
416 String memberPackageName = memberClass.getPackageName();
417
418 String msg = "JNI attached native thread (null caller frame) cannot access ";
419 if (m2.isExported(memberPackageName)) {
420 // module access okay so include the modifiers in the message
421 msg += "a member of " + memberClass + memberSuffix + msgSuffix(modifiers);
422 } else {
423 // module access failed
424 msg += memberClass + memberSuffix+ " because "
425 + m2 + " does not export " + memberPackageName;
426 }
427
428 return new IllegalAccessException(msg);
429 }
430
431 private static String msgSuffix(int modifiers) {
432 boolean packageAccess =
433 ((Modifier.PRIVATE |
434 Modifier.PROTECTED |
435 Modifier.PUBLIC) & modifiers) == 0;
436 return packageAccess ?
437 " with package access" :
438 " with modifiers \"" + Modifier.toString(modifiers) + "\"";
439 }
440
441 /**
442 * Returns true if {@code currentClass} and {@code memberClass}
443 * are nestmates - that is, if they have the same nesthost as
444 * determined by the VM.
445 */
446 public static native boolean areNestMates(Class<?> currentClass,
447 Class<?> memberClass);
448 }