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