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 }