1/*
2 * Copyright (c) 2001, 2017, 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
26package jdk.internal.reflect;
27
28
29import java.lang.reflect.*;
30import java.util.HashMap;
31import java.util.Map;
32import java.util.Objects;
33import jdk.internal.HotSpotIntrinsicCandidate;
34import jdk.internal.loader.ClassLoaders;
35import jdk.internal.misc.VM;
36
37/** Common utility routines used by both java.lang and
38    java.lang.reflect */
39
40public class Reflection {
41
42    /** Used to filter out fields and methods from certain classes from public
43        view, where they are sensitive or they may contain VM-internal objects.
44        These Maps are updated very rarely. Rather than synchronize on
45        each access, we use copy-on-write */
46    private static volatile Map<Class<?>,String[]> fieldFilterMap;
47    private static volatile Map<Class<?>,String[]> methodFilterMap;
48
49    static {
50        Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>();
51        map.put(Reflection.class,
52            new String[] {"fieldFilterMap", "methodFilterMap"});
53        map.put(System.class, new String[] {"security"});
54        map.put(Class.class, new String[] {"classLoader"});
55        fieldFilterMap = map;
56
57        methodFilterMap = new HashMap<>();
58    }
59
60    /** Returns the class of the caller of the method calling this method,
61        ignoring frames associated with java.lang.reflect.Method.invoke()
62        and its implementation. */
63    @CallerSensitive
64    @HotSpotIntrinsicCandidate
65    public static native Class<?> getCallerClass();
66
67    /**
68     * @deprecated This method will be removed.
69     * This method is a private JDK API and retained temporarily to
70     * simplify the implementation of sun.misc.Reflection.getCallerClass.
71     */
72    @Deprecated(forRemoval=true)
73    public static native Class<?> getCallerClass(int depth);
74
75    /** Retrieves the access flags written to the class file. For
76        inner classes these flags may differ from those returned by
77        Class.getModifiers(), which searches the InnerClasses
78        attribute to find the source-level access flags. This is used
79        instead of Class.getModifiers() for run-time access checks due
80        to compatibility reasons; see 4471811. Only the values of the
81        low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
82        valid. */
83    @HotSpotIntrinsicCandidate
84    public static native int getClassAccessFlags(Class<?> c);
85
86
87    /**
88     * Ensures that access to a member is granted and throws
89     * IllegalAccessException if not.
90     *
91     * @param currentClass the class performing the access
92     * @param memberClass the declaring class of the member being accessed
93     * @param targetClass the class of target object if accessing instance
94     *                    field or method;
95     *                    or the declaring class if accessing constructor;
96     *                    or null if accessing static field or method
97     * @param modifiers the member's access modifiers
98     * @throws IllegalAccessException if access to member is denied
99     */
100    public static void ensureMemberAccess(Class<?> currentClass,
101                                          Class<?> memberClass,
102                                          Class<?> targetClass,
103                                          int modifiers)
104        throws IllegalAccessException
105    {
106        if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
107            throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
108        }
109    }
110
111    /**
112     * Verify access to a member and return {@code true} if it is granted.
113     *
114     * @param currentClass the class performing the access
115     * @param memberClass the declaring class of the member being accessed
116     * @param targetClass the class of target object if accessing instance
117     *                    field or method;
118     *                    or the declaring class if accessing constructor;
119     *                    or null if accessing static field or method
120     * @param modifiers the member's access modifiers
121     * @return {@code true} if access to member is granted
122     */
123    public static boolean verifyMemberAccess(Class<?> currentClass,
124                                             Class<?> memberClass,
125                                             Class<?> targetClass,
126                                             int modifiers)
127    {
128        if (currentClass == memberClass) {
129            // Always succeeds
130            return true;
131        }
132
133        if (!verifyModuleAccess(currentClass.getModule(), memberClass)) {
134            return false;
135        }
136
137        boolean gotIsSameClassPackage = false;
138        boolean isSameClassPackage = false;
139
140        if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
141            isSameClassPackage = isSameClassPackage(currentClass, memberClass);
142            gotIsSameClassPackage = true;
143            if (!isSameClassPackage) {
144                return false;
145            }
146        }
147
148        // At this point we know that currentClass can access memberClass.
149
150        if (Modifier.isPublic(modifiers)) {
151            return true;
152        }
153
154        boolean successSoFar = false;
155
156        if (Modifier.isProtected(modifiers)) {
157            // See if currentClass is a subclass of memberClass
158            if (isSubclassOf(currentClass, memberClass)) {
159                successSoFar = true;
160            }
161        }
162
163        if (!successSoFar && !Modifier.isPrivate(modifiers)) {
164            if (!gotIsSameClassPackage) {
165                isSameClassPackage = isSameClassPackage(currentClass,
166                                                        memberClass);
167                gotIsSameClassPackage = true;
168            }
169
170            if (isSameClassPackage) {
171                successSoFar = true;
172            }
173        }
174
175        if (!successSoFar) {
176            return false;
177        }
178
179        // Additional test for protected instance members
180        // and protected constructors: JLS 6.6.2
181        if (targetClass != null && Modifier.isProtected(modifiers) &&
182            targetClass != currentClass)
183        {
184            if (!gotIsSameClassPackage) {
185                isSameClassPackage = isSameClassPackage(currentClass, memberClass);
186                gotIsSameClassPackage = true;
187            }
188            if (!isSameClassPackage) {
189                if (!isSubclassOf(targetClass, currentClass)) {
190                    return false;
191                }
192            }
193        }
194
195        return true;
196    }
197
198    /**
199     * Returns {@code true} if memberClass's module exports memberClass's
200     * package to currentModule.
201     */
202    public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
203        Module memberModule = memberClass.getModule();
204        if (currentModule == memberModule) {
205            // same module (named or unnamed) or both null if called
206            // before module system is initialized, which means we are
207            // dealing with java.base only.
208            return true;
209        } else {
210            String pkg = memberClass.getPackageName();
211            return memberModule.isExported(pkg, currentModule);
212        }
213    }
214
215    /**
216     * Returns true if two classes in the same package.
217     */
218    private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
219        if (c1.getClassLoader() != c2.getClassLoader())
220            return false;
221        return Objects.equals(c1.getPackageName(), c2.getPackageName());
222    }
223
224    static boolean isSubclassOf(Class<?> queryClass,
225                                Class<?> ofClass)
226    {
227        while (queryClass != null) {
228            if (queryClass == ofClass) {
229                return true;
230            }
231            queryClass = queryClass.getSuperclass();
232        }
233        return false;
234    }
235
236    // fieldNames must contain only interned Strings
237    public static synchronized void registerFieldsToFilter(Class<?> containingClass,
238                                              String ... fieldNames) {
239        fieldFilterMap =
240            registerFilter(fieldFilterMap, containingClass, fieldNames);
241    }
242
243    // methodNames must contain only interned Strings
244    public static synchronized void registerMethodsToFilter(Class<?> containingClass,
245                                              String ... methodNames) {
246        methodFilterMap =
247            registerFilter(methodFilterMap, containingClass, methodNames);
248    }
249
250    private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
251            Class<?> containingClass, String ... names) {
252        if (map.get(containingClass) != null) {
253            throw new IllegalArgumentException
254                            ("Filter already registered: " + containingClass);
255        }
256        map = new HashMap<Class<?>,String[]>(map);
257        map.put(containingClass, names);
258        return map;
259    }
260
261    public static Field[] filterFields(Class<?> containingClass,
262                                       Field[] fields) {
263        if (fieldFilterMap == null) {
264            // Bootstrapping
265            return fields;
266        }
267        return (Field[])filter(fields, fieldFilterMap.get(containingClass));
268    }
269
270    public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
271        if (methodFilterMap == null) {
272            // Bootstrapping
273            return methods;
274        }
275        return (Method[])filter(methods, methodFilterMap.get(containingClass));
276    }
277
278    private static Member[] filter(Member[] members, String[] filteredNames) {
279        if ((filteredNames == null) || (members.length == 0)) {
280            return members;
281        }
282        int numNewMembers = 0;
283        for (Member member : members) {
284            boolean shouldSkip = false;
285            for (String filteredName : filteredNames) {
286                if (member.getName() == filteredName) {
287                    shouldSkip = true;
288                    break;
289                }
290            }
291            if (!shouldSkip) {
292                ++numNewMembers;
293            }
294        }
295        Member[] newMembers =
296            (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
297        int destIdx = 0;
298        for (Member member : members) {
299            boolean shouldSkip = false;
300            for (String filteredName : filteredNames) {
301                if (member.getName() == filteredName) {
302                    shouldSkip = true;
303                    break;
304                }
305            }
306            if (!shouldSkip) {
307                newMembers[destIdx++] = member;
308            }
309        }
310        return newMembers;
311    }
312
313    /**
314     * Tests if the given method is caller-sensitive and the declaring class
315     * is defined by either the bootstrap class loader or platform class loader.
316     */
317    public static boolean isCallerSensitive(Method m) {
318        final ClassLoader loader = m.getDeclaringClass().getClassLoader();
319        if (VM.isSystemDomainLoader(loader)) {
320            return m.isAnnotationPresent(CallerSensitive.class);
321        }
322        return false;
323    }
324
325    /**
326     * Returns an IllegalAccessException with an exception message based on
327     * the access that is denied.
328     */
329    public static IllegalAccessException newIllegalAccessException(Class<?> currentClass,
330                                                                   Class<?> memberClass,
331                                                                   Class<?> targetClass,
332                                                                   int modifiers)
333        throws IllegalAccessException
334    {
335        String currentSuffix = "";
336        String memberSuffix = "";
337        Module m1 = currentClass.getModule();
338        if (m1.isNamed())
339            currentSuffix = " (in " + m1 + ")";
340        Module m2 = memberClass.getModule();
341        if (m2.isNamed())
342            memberSuffix = " (in " + m2 + ")";
343
344        String memberPackageName = memberClass.getPackageName();
345
346        String msg = currentClass + currentSuffix + " cannot access ";
347        if (m2.isExported(memberPackageName, m1)) {
348
349            // module access okay so include the modifiers in the message
350            msg += "a member of " + memberClass + memberSuffix +
351                    " with modifiers \"" + Modifier.toString(modifiers) + "\"";
352
353        } else {
354            // module access failed
355            msg += memberClass + memberSuffix+ " because "
356                   + m2 + " does not export " + memberPackageName;
357            if (m2.isNamed()) msg += " to " + m1;
358        }
359
360        return new IllegalAccessException(msg);
361    }
362}
363