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