MethodUtil.java revision 13901:b2a69d66dc65
1/*
2 * Copyright (c) 2005, 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 sun.reflect.misc;
27
28import java.lang.reflect.Module;
29import java.io.EOFException;
30import java.security.AllPermission;
31import java.security.AccessController;
32import java.security.PermissionCollection;
33import java.security.SecureClassLoader;
34import java.security.PrivilegedExceptionAction;
35import java.security.CodeSource;
36import java.io.InputStream;
37import java.io.BufferedInputStream;
38import java.io.IOException;
39import java.net.URL;
40import java.net.URLConnection;
41import java.lang.reflect.Method;
42import java.lang.reflect.InvocationTargetException;
43import java.lang.reflect.Modifier;
44import java.util.Arrays;
45import java.util.HashMap;
46import java.util.Map;
47
48
49class Trampoline {
50    static {
51        if (Trampoline.class.getClassLoader() == null) {
52            throw new Error(
53                "Trampoline must not be defined by the bootstrap classloader");
54        }
55    }
56
57    private static void ensureInvocableMethod(Method m)
58        throws InvocationTargetException
59    {
60        Class<?> clazz = m.getDeclaringClass();
61        if (clazz.equals(AccessController.class) ||
62            clazz.equals(Method.class) ||
63            clazz.getName().startsWith("java.lang.invoke."))
64            throw new InvocationTargetException(
65                new UnsupportedOperationException("invocation not supported"));
66    }
67
68    private static Object invoke(Method m, Object obj, Object[] params)
69        throws InvocationTargetException, IllegalAccessException
70    {
71        ensureInvocableMethod(m);
72        return m.invoke(obj, params);
73    }
74}
75
76/*
77 * Create a trampoline class.
78 */
79public final class MethodUtil extends SecureClassLoader {
80    private static final String MISC_PKG = "sun.reflect.misc.";
81    private static final String TRAMPOLINE = MISC_PKG + "Trampoline";
82    private static final Method bounce = getTrampoline();
83
84    private MethodUtil() {
85        super();
86    }
87
88    public static Method getMethod(Class<?> cls, String name, Class<?>[] args)
89        throws NoSuchMethodException {
90        ReflectUtil.checkPackageAccess(cls);
91        return cls.getMethod(name, args);
92    }
93
94    public static Method[] getMethods(Class<?> cls) {
95        ReflectUtil.checkPackageAccess(cls);
96        return cls.getMethods();
97    }
98
99    /*
100     * Discover the public methods on public classes
101     * and interfaces accessible to any caller by calling
102     * Class.getMethods() and walking towards Object until
103     * we're done.
104     */
105     public static Method[] getPublicMethods(Class<?> cls) {
106        // compatibility for update release
107        if (System.getSecurityManager() == null) {
108            return cls.getMethods();
109        }
110        Map<Signature, Method> sigs = new HashMap<Signature, Method>();
111        while (cls != null) {
112            boolean done = getInternalPublicMethods(cls, sigs);
113            if (done) {
114                break;
115            }
116            getInterfaceMethods(cls, sigs);
117            cls = cls.getSuperclass();
118        }
119        return sigs.values().toArray(new Method[sigs.size()]);
120    }
121
122    /*
123     * Process the immediate interfaces of this class or interface.
124     */
125    private static void getInterfaceMethods(Class<?> cls,
126                                            Map<Signature, Method> sigs) {
127        Class<?>[] intfs = cls.getInterfaces();
128        for (int i=0; i < intfs.length; i++) {
129            Class<?> intf = intfs[i];
130            boolean done = getInternalPublicMethods(intf, sigs);
131            if (!done) {
132                getInterfaceMethods(intf, sigs);
133            }
134        }
135    }
136
137    /*
138     *
139     * Process the methods in this class or interface
140     */
141    private static boolean getInternalPublicMethods(Class<?> cls,
142                                                    Map<Signature, Method> sigs) {
143        Method[] methods = null;
144        try {
145            /*
146             * This class or interface is non-public so we
147             * can't use any of it's methods. Go back and
148             * try again with a superclass or superinterface.
149             */
150            if (!Modifier.isPublic(cls.getModifiers())) {
151                return false;
152            }
153            if (!ReflectUtil.isPackageAccessible(cls)) {
154                return false;
155            }
156
157            methods = cls.getMethods();
158        } catch (SecurityException se) {
159            return false;
160        }
161
162        /*
163         * Check for inherited methods with non-public
164         * declaring classes. They might override and hide
165         * methods from their superclasses or
166         * superinterfaces.
167         */
168        boolean done = true;
169        for (int i=0; i < methods.length; i++) {
170            Class<?> dc = methods[i].getDeclaringClass();
171            if (!Modifier.isPublic(dc.getModifiers())) {
172                done = false;
173                break;
174            }
175        }
176
177        if (done) {
178            /*
179             * We're done. Spray all the methods into
180             * the list and then we're out of here.
181             */
182            for (int i=0; i < methods.length; i++) {
183                addMethod(sigs, methods[i]);
184            }
185        } else {
186            /*
187             * Simulate cls.getDeclaredMethods() by
188             * stripping away inherited methods.
189             */
190            for (int i=0; i < methods.length; i++) {
191                Class<?> dc = methods[i].getDeclaringClass();
192                if (cls.equals(dc)) {
193                    addMethod(sigs, methods[i]);
194                }
195            }
196        }
197        return done;
198    }
199
200    private static void addMethod(Map<Signature, Method> sigs, Method method) {
201        Signature signature = new Signature(method);
202        if (!sigs.containsKey(signature)) {
203            sigs.put(signature, method);
204        } else if (!method.getDeclaringClass().isInterface()){
205            /*
206             * Superclasses beat interfaces.
207             */
208            Method old = sigs.get(signature);
209            if (old.getDeclaringClass().isInterface()) {
210                sigs.put(signature, method);
211            }
212        }
213    }
214
215    /**
216     * A class that represents the unique elements of a method that will be a
217     * key in the method cache.
218     */
219    private static class Signature {
220        private final String methodName;
221        private final Class<?>[] argClasses;
222        private final int hashCode;
223
224        Signature(Method m) {
225            this.methodName = m.getName();
226            this.argClasses = m.getParameterTypes();
227            this.hashCode = methodName.hashCode() + Arrays.hashCode(argClasses);
228        }
229
230        @Override public int hashCode() {
231            return hashCode;
232        }
233
234        @Override public boolean equals(Object o2) {
235            if (this == o2) {
236                return true;
237            }
238            Signature that = (Signature)o2;
239            if (!(methodName.equals(that.methodName))) {
240                return false;
241            }
242            if (argClasses.length != that.argClasses.length) {
243                return false;
244            }
245            for (int i = 0; i < argClasses.length; i++) {
246                if (!(argClasses[i] == that.argClasses[i])) {
247                  return false;
248                }
249            }
250            return true;
251        }
252    }
253
254
255    /*
256     * Bounce through the trampoline.
257     */
258    public static Object invoke(Method m, Object obj, Object[] params)
259        throws InvocationTargetException, IllegalAccessException {
260        try {
261            return bounce.invoke(null, new Object[] {m, obj, params});
262        } catch (InvocationTargetException ie) {
263            Throwable t = ie.getCause();
264
265            if (t instanceof InvocationTargetException) {
266                throw (InvocationTargetException)t;
267            } else if (t instanceof IllegalAccessException) {
268                throw (IllegalAccessException)t;
269            } else if (t instanceof RuntimeException) {
270                throw (RuntimeException)t;
271            } else if (t instanceof Error) {
272                throw (Error)t;
273            } else {
274                throw new Error("Unexpected invocation error", t);
275            }
276        } catch (IllegalAccessException iae) {
277            // this can't happen
278            throw new Error("Unexpected invocation error", iae);
279        }
280    }
281
282    private static Method getTrampoline() {
283        try {
284            return AccessController.doPrivileged(
285                new PrivilegedExceptionAction<Method>() {
286                    public Method run() throws Exception {
287                        Class<?> t = getTrampolineClass();
288                        Class<?>[] types = {
289                            Method.class, Object.class, Object[].class
290                        };
291                        Method b = t.getDeclaredMethod("invoke", types);
292                        b.setAccessible(true);
293                        return b;
294                    }
295                });
296        } catch (Exception e) {
297            throw new InternalError("bouncer cannot be found", e);
298        }
299    }
300
301
302    protected synchronized Class<?> loadClass(String name, boolean resolve)
303        throws ClassNotFoundException
304    {
305        // First, check if the class has already been loaded
306        ReflectUtil.checkPackageAccess(name);
307        Class<?> c = findLoadedClass(name);
308        if (c == null) {
309            try {
310                c = findClass(name);
311            } catch (ClassNotFoundException e) {
312                // Fall through ...
313            }
314            if (c == null) {
315                c = getParent().loadClass(name);
316            }
317        }
318        if (resolve) {
319            resolveClass(c);
320        }
321        return c;
322    }
323
324
325    protected Class<?> findClass(final String name)
326        throws ClassNotFoundException
327    {
328        if (!name.startsWith(MISC_PKG)) {
329            throw new ClassNotFoundException(name);
330        }
331        String path = name.replace('.', '/').concat(".class");
332        try {
333            InputStream in = Object.class.getModule().getResourceAsStream(path);
334            if (in != null) {
335                try (in) {
336                    byte[] b = in.readAllBytes();
337                    return defineClass(name, b);
338                }
339            }
340        } catch (IOException e) {
341            throw new ClassNotFoundException(name, e);
342        }
343
344        throw new ClassNotFoundException(name);
345    }
346
347
348    /*
349     * Define the proxy classes
350     */
351    private Class<?> defineClass(String name, byte[] b) throws IOException {
352        CodeSource cs = new CodeSource(null, (java.security.cert.Certificate[])null);
353        if (!name.equals(TRAMPOLINE)) {
354            throw new IOException("MethodUtil: bad name " + name);
355        }
356        return defineClass(name, b, 0, b.length, cs);
357    }
358
359    protected PermissionCollection getPermissions(CodeSource codesource)
360    {
361        PermissionCollection perms = super.getPermissions(codesource);
362        perms.add(new AllPermission());
363        return perms;
364    }
365
366    private static Class<?> getTrampolineClass() {
367        try {
368            return Class.forName(TRAMPOLINE, true, new MethodUtil());
369        } catch (ClassNotFoundException e) {
370        }
371        return null;
372    }
373
374}
375