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