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