Reflection.java revision 14176:8606d027b2c2
1/*
2 * Copyright (c) 2001, 2013, 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.security.AccessController;
31import java.security.PrivilegedAction;
32import java.util.HashMap;
33import java.util.Map;
34import java.util.Objects;
35import jdk.internal.HotSpotIntrinsicCandidate;
36import jdk.internal.misc.VM;
37
38/** Common utility routines used by both java.lang and
39    java.lang.reflect */
40
41public class Reflection {
42
43    /** Used to filter out fields and methods from certain classes from public
44        view, where they are sensitive or they may contain VM-internal objects.
45        These Maps are updated very rarely. Rather than synchronize on
46        each access, we use copy-on-write */
47    private static volatile Map<Class<?>,String[]> fieldFilterMap;
48    private static volatile Map<Class<?>,String[]> methodFilterMap;
49
50    static {
51        Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>();
52        map.put(Reflection.class,
53            new String[] {"fieldFilterMap", "methodFilterMap"});
54        map.put(System.class, new String[] {"security"});
55        map.put(Class.class, new String[] {"classLoader"});
56        fieldFilterMap = map;
57
58        methodFilterMap = new HashMap<>();
59    }
60
61    /** Returns the class of the caller of the method calling this method,
62        ignoring frames associated with java.lang.reflect.Method.invoke()
63        and its implementation. */
64    @CallerSensitive
65    @HotSpotIntrinsicCandidate
66    public static native Class<?> getCallerClass();
67
68    /**
69     * @deprecated This method will be removed.
70     * This method is a private JDK API and retained temporarily to
71     * simplify the implementation of sun.misc.Reflection.getCallerClass.
72     */
73    @Deprecated(forRemoval=true)
74    public static native Class<?> getCallerClass(int depth);
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    @HotSpotIntrinsicCandidate
85    public static native int getClassAccessFlags(Class<?> c);
86
87
88    public static void ensureMemberAccess(Class<?> currentClass,
89                                          Class<?> memberClass,
90                                          Object target,
91                                          int modifiers)
92        throws IllegalAccessException
93    {
94        if (currentClass == null || memberClass == null) {
95            throw new InternalError();
96        }
97
98        if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) {
99            throwIllegalAccessException(currentClass, memberClass, target, modifiers);
100        }
101    }
102
103    public static boolean verifyMemberAccess(Class<?> currentClass,
104                                             // Declaring class of field
105                                             // or method
106                                             Class<?> memberClass,
107                                             // May be NULL in case of statics
108                                             Object   target,
109                                             int      modifiers)
110    {
111        // Verify that currentClass can access a field, method, or
112        // constructor of memberClass, where that member's access bits are
113        // "modifiers".
114
115        boolean gotIsSameClassPackage = false;
116        boolean isSameClassPackage = false;
117
118        if (currentClass == memberClass) {
119            // Always succeeds
120            return true;
121        }
122
123        if (!verifyModuleAccess(currentClass, memberClass)) {
124            return false;
125        }
126
127        if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
128            isSameClassPackage = isSameClassPackage(currentClass, memberClass);
129            gotIsSameClassPackage = true;
130            if (!isSameClassPackage) {
131                return false;
132            }
133        }
134
135        // At this point we know that currentClass can access memberClass.
136
137        if (Modifier.isPublic(modifiers)) {
138            return true;
139        }
140
141        boolean successSoFar = false;
142
143        if (Modifier.isProtected(modifiers)) {
144            // See if currentClass is a subclass of memberClass
145            if (isSubclassOf(currentClass, memberClass)) {
146                successSoFar = true;
147            }
148        }
149
150        if (!successSoFar && !Modifier.isPrivate(modifiers)) {
151            if (!gotIsSameClassPackage) {
152                isSameClassPackage = isSameClassPackage(currentClass,
153                                                        memberClass);
154                gotIsSameClassPackage = true;
155            }
156
157            if (isSameClassPackage) {
158                successSoFar = true;
159            }
160        }
161
162        if (!successSoFar) {
163            return false;
164        }
165
166        if (Modifier.isProtected(modifiers)) {
167            // Additional test for protected members: JLS 6.6.2
168            Class<?> targetClass = (target == null ? memberClass : target.getClass());
169            if (targetClass != currentClass) {
170                if (!gotIsSameClassPackage) {
171                    isSameClassPackage = isSameClassPackage(currentClass, memberClass);
172                    gotIsSameClassPackage = true;
173                }
174                if (!isSameClassPackage) {
175                    if (!isSubclassOf(targetClass, currentClass)) {
176                        return false;
177                    }
178                }
179            }
180        }
181
182        return true;
183    }
184
185    /**
186     * Returns {@code true} if memberClass's's module exports memberClass's
187     * package to currentClass's module.
188     */
189    public static boolean verifyModuleAccess(Class<?> currentClass,
190                                             Class<?> memberClass) {
191        return verifyModuleAccess(currentClass.getModule(), memberClass);
192    }
193
194    public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
195        Module memberModule = memberClass.getModule();
196
197        // module may be null during startup (initLevel 0)
198        if (currentModule == memberModule)
199           return true;  // same module (named or unnamed)
200
201        // memberClass may be primitive or array class
202        Class<?> c = memberClass;
203        while (c.isArray()) {
204            c = c.getComponentType();
205        }
206        if (c.isPrimitive())
207            return true;
208
209        // check that memberModule exports the package to currentModule
210        return memberModule.isExported(c.getPackageName(), currentModule);
211    }
212
213    /**
214     * Returns true if two classes in the same package.
215     */
216    private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
217        if (c1.getClassLoader() != c2.getClassLoader())
218            return false;
219        while (c1.isArray())
220            c1 = c1.getComponentType();
221        while (c2.isArray())
222            c2 = c2.getComponentType();
223        return Objects.equals(c1.getPackageName(), c2.getPackageName());
224    }
225
226    static boolean isSubclassOf(Class<?> queryClass,
227                                Class<?> ofClass)
228    {
229        while (queryClass != null) {
230            if (queryClass == ofClass) {
231                return true;
232            }
233            queryClass = queryClass.getSuperclass();
234        }
235        return false;
236    }
237
238    // fieldNames must contain only interned Strings
239    public static synchronized void registerFieldsToFilter(Class<?> containingClass,
240                                              String ... fieldNames) {
241        fieldFilterMap =
242            registerFilter(fieldFilterMap, containingClass, fieldNames);
243    }
244
245    // methodNames must contain only interned Strings
246    public static synchronized void registerMethodsToFilter(Class<?> containingClass,
247                                              String ... methodNames) {
248        methodFilterMap =
249            registerFilter(methodFilterMap, containingClass, methodNames);
250    }
251
252    private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
253            Class<?> containingClass, String ... names) {
254        if (map.get(containingClass) != null) {
255            throw new IllegalArgumentException
256                            ("Filter already registered: " + containingClass);
257        }
258        map = new HashMap<Class<?>,String[]>(map);
259        map.put(containingClass, names);
260        return map;
261    }
262
263    public static Field[] filterFields(Class<?> containingClass,
264                                       Field[] fields) {
265        if (fieldFilterMap == null) {
266            // Bootstrapping
267            return fields;
268        }
269        return (Field[])filter(fields, fieldFilterMap.get(containingClass));
270    }
271
272    public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
273        if (methodFilterMap == null) {
274            // Bootstrapping
275            return methods;
276        }
277        return (Method[])filter(methods, methodFilterMap.get(containingClass));
278    }
279
280    private static Member[] filter(Member[] members, String[] filteredNames) {
281        if ((filteredNames == null) || (members.length == 0)) {
282            return members;
283        }
284        int numNewMembers = 0;
285        for (Member member : members) {
286            boolean shouldSkip = false;
287            for (String filteredName : filteredNames) {
288                if (member.getName() == filteredName) {
289                    shouldSkip = true;
290                    break;
291                }
292            }
293            if (!shouldSkip) {
294                ++numNewMembers;
295            }
296        }
297        Member[] newMembers =
298            (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
299        int destIdx = 0;
300        for (Member member : members) {
301            boolean shouldSkip = false;
302            for (String filteredName : filteredNames) {
303                if (member.getName() == filteredName) {
304                    shouldSkip = true;
305                    break;
306                }
307            }
308            if (!shouldSkip) {
309                newMembers[destIdx++] = member;
310            }
311        }
312        return newMembers;
313    }
314
315    /**
316     * Tests if the given method is caller-sensitive and the declaring class
317     * is defined by either the bootstrap class loader or platform class loader.
318     */
319    public static boolean isCallerSensitive(Method m) {
320        final ClassLoader loader = m.getDeclaringClass().getClassLoader();
321        if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader))  {
322            return m.isAnnotationPresent(CallerSensitive.class);
323        }
324        return false;
325    }
326
327    private static boolean isExtClassLoader(ClassLoader loader) {
328        ClassLoader cl = ClassLoader.getSystemClassLoader();
329        while (cl != null) {
330            if (cl.getParent() == null && cl == loader) {
331                return true;
332            }
333            cl = cl.getParent();
334        }
335        return false;
336    }
337
338
339    // true to print a stack trace when IAE is thrown
340    private static volatile boolean printStackWhenAccessFails;
341
342    // true if printStackWhenAccessFails has been initialized
343    private static volatile boolean printStackWhenAccessFailsSet;
344
345    private static void printStackTraceIfNeeded(Throwable e) {
346        if (!printStackWhenAccessFailsSet && VM.initLevel() >= 1) {
347            // can't use method reference here, might be too early in startup
348            PrivilegedAction<Boolean> pa = new PrivilegedAction<Boolean>() {
349                public Boolean run() {
350                    String s;
351                    s = System.getProperty("sun.reflect.debugModuleAccessChecks");
352                    return (s != null && !s.equalsIgnoreCase("false"));
353                }
354            };
355            printStackWhenAccessFails = AccessController.doPrivileged(pa);
356            printStackWhenAccessFailsSet = true;
357        }
358        if (printStackWhenAccessFails) {
359            e.printStackTrace();
360        }
361    }
362
363    /**
364     * Throws IllegalAccessException with the an exception message based on
365     * the access that is denied.
366     */
367    private static void throwIllegalAccessException(Class<?> currentClass,
368                                                    Class<?> memberClass,
369                                                    Object target,
370                                                    int modifiers)
371        throws IllegalAccessException
372    {
373        String currentSuffix = "";
374        String memberSuffix = "";
375        Module m1 = currentClass.getModule();
376        if (m1.isNamed())
377            currentSuffix = " (in " + m1 + ")";
378        Module m2 = memberClass.getModule();
379        if (m2.isNamed())
380            memberSuffix = " (in " + m2 + ")";
381
382        Class<?> c = memberClass;
383        while (c.isArray()) {
384            c = c.getComponentType();
385        }
386        String memberPackageName = c.getPackageName();
387
388        String msg = currentClass + currentSuffix + " cannot access ";
389        if (m2.isExported(memberPackageName, m1)) {
390
391            // module access okay so include the modifiers in the message
392            msg += "a member of " + memberClass + memberSuffix +
393                    " with modifiers \"" + Modifier.toString(modifiers) + "\"";
394
395        } else {
396            // module access failed
397            msg += memberClass + memberSuffix+ " because "
398                   + m2 + " does not export " + memberPackageName;
399            if (m2.isNamed()) msg += " to " + m1;
400        }
401
402        throwIllegalAccessException(msg);
403    }
404
405    /**
406     * Throws IllegalAccessException with the given exception message.
407     */
408    public static void throwIllegalAccessException(String msg)
409        throws IllegalAccessException
410    {
411        IllegalAccessException e = new IllegalAccessException(msg);
412        printStackTraceIfNeeded(e);
413        throw e;
414    }
415
416    /**
417     * Throws InaccessibleObjectException with the given exception message.
418     */
419    public static void throwInaccessibleObjectException(String msg) {
420        InaccessibleObjectException e = new InaccessibleObjectException(msg);
421        printStackTraceIfNeeded(e);
422        throw e;
423    }
424
425}
426