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