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