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