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 
  26 package sun.reflect.misc;
  27 
  28 import java.security.AllPermission;
  29 import java.security.AccessController;
  30 import java.security.PermissionCollection;
  31 import java.security.SecureClassLoader;
  32 import java.security.PrivilegedExceptionAction;
  33 import java.security.CodeSource;
  34 import java.io.InputStream;
  35 import java.io.BufferedInputStream;
  36 import java.io.IOException;
  37 import java.net.URL;
  38 import java.net.URLConnection;
  39 import java.lang.reflect.Method;
  40 import java.lang.reflect.InvocationTargetException;
  41 import java.lang.reflect.AccessibleObject;
  42 import java.lang.reflect.Modifier;
  43 import java.util.Arrays;
  44 import java.util.HashMap;
  45 import java.util.Map;
  46 import sun.misc.IOUtils;
  47 
  48 
  49 class 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  */
  79 public 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         URL res = getResource(path);
 333         if (res != null) {
 334             try {
 335                 return defineClass(name, res);
 336             } catch (IOException e) {
 337                 throw new ClassNotFoundException(name, e);
 338             }
 339         } else {
 340             throw new ClassNotFoundException(name);
 341         }
 342     }
 343 
 344 
 345     /*
 346      * Define the proxy classes
 347      */
 348     private Class<?> defineClass(String name, URL url) throws IOException {
 349         byte[] b = getBytes(url);
 350         CodeSource cs = new CodeSource(null, (java.security.cert.Certificate[])null);
 351         if (!name.equals(TRAMPOLINE)) {
 352             throw new IOException("MethodUtil: bad name " + name);
 353         }
 354         return defineClass(name, b, 0, b.length, cs);
 355     }
 356 
 357 
 358     /*
 359      * Returns the contents of the specified URL as an array of bytes.
 360      */
 361     private static byte[] getBytes(URL url) throws IOException {
 362         URLConnection uc = url.openConnection();
 363         if (uc instanceof java.net.HttpURLConnection) {
 364             java.net.HttpURLConnection huc = (java.net.HttpURLConnection) uc;
 365             int code = huc.getResponseCode();
 366             if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) {
 367                 throw new IOException("open HTTP connection failed.");
 368             }
 369         }
 370         int len = uc.getContentLength();
 371         InputStream in = new BufferedInputStream(uc.getInputStream());
 372 
 373         byte[] b;
 374         try {
 375             b = IOUtils.readFully(in, len, true);
 376         } finally {
 377             in.close();
 378         }
 379         return b;
 380     }
 381 
 382 
 383     protected PermissionCollection getPermissions(CodeSource codesource)
 384     {
 385         PermissionCollection perms = super.getPermissions(codesource);
 386         perms.add(new AllPermission());
 387         return perms;
 388     }
 389 
 390     private static Class<?> getTrampolineClass() {
 391         try {
 392             return Class.forName(TRAMPOLINE, true, new MethodUtil());
 393         } catch (ClassNotFoundException e) {
 394         }
 395         return null;
 396     }
 397 
 398 }