| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package java.lang.invoke;  | 
 | 
 | 
 | 
import java.lang.reflect.*;  | 
 | 
import java.security.AccessController;  | 
 | 
import java.security.PrivilegedAction;  | 
 | 
import sun.invoke.WrapperInstance;  | 
 | 
import java.util.ArrayList;  | 
 | 
import jdk.internal.reflect.CallerSensitive;  | 
 | 
import jdk.internal.reflect.Reflection;  | 
 | 
import sun.reflect.misc.ReflectUtil;  | 
 | 
import static java.lang.invoke.MethodHandleStatics.*;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public class MethodHandleProxies { | 
 | 
 | 
 | 
    private MethodHandleProxies() { }   | 
 | 
 | 
 | 
    /**  | 
 | 
     * Produces an instance of the given single-method interface which redirects  | 
 | 
     * its calls to the given method handle.  | 
 | 
     * <p>  | 
 | 
     * A single-method interface is an interface which declares a uniquely named method.  | 
 | 
     * When determining the uniquely named method of a single-method interface,  | 
 | 
     * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode}) | 
 | 
     * are disregarded.  For example, {@link java.util.Comparator} is a single-method interface, | 
 | 
     * even though it re-declares the {@code Object.equals} method. | 
 | 
     * <p>  | 
 | 
     * The interface must be public.  No additional access checks are performed.  | 
 | 
     * <p>  | 
 | 
     * The resulting instance of the required type will respond to  | 
 | 
     * invocation of the type's uniquely named method by calling  | 
 | 
     * the given target on the incoming arguments,  | 
 | 
     * and returning or throwing whatever the target  | 
 | 
     * returns or throws.  The invocation will be as if by  | 
 | 
     * {@code target.invoke}. | 
 | 
     * The target's type will be checked before the  | 
 | 
     * instance is created, as if by a call to {@code asType}, | 
 | 
     * which may result in a {@code WrongMethodTypeException}. | 
 | 
     * <p>  | 
 | 
     * The uniquely named method is allowed to be multiply declared,  | 
 | 
     * with distinct type descriptors.  (E.g., it can be overloaded,  | 
 | 
     * or can possess bridge methods.)  All such declarations are  | 
 | 
     * connected directly to the target method handle.  | 
 | 
     * Argument and return types are adjusted by {@code asType} | 
 | 
     * for each individual declaration.  | 
 | 
     * <p>  | 
 | 
     * The wrapper instance will implement the requested interface  | 
 | 
     * and its super-types, but no other single-method interfaces.  | 
 | 
     * This means that the instance will not unexpectedly  | 
 | 
     * pass an {@code instanceof} test for any unrequested type. | 
 | 
     * <p style="font-size:smaller;">  | 
 | 
     * <em>Implementation Note:</em>  | 
 | 
     * Therefore, each instance must implement a unique single-method interface.  | 
 | 
     * Implementations may not bundle together  | 
 | 
     * multiple single-method interfaces onto single implementation classes  | 
 | 
     * in the style of {@link java.awt.AWTEventMulticaster}. | 
 | 
     * <p>  | 
 | 
     * The method handle may throw an <em>undeclared exception</em>,  | 
 | 
     * which means any checked exception (or other checked throwable)  | 
 | 
     * not declared by the requested type's single abstract method.  | 
 | 
     * If this happens, the throwable will be wrapped in an instance of  | 
 | 
     * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException} | 
 | 
     * and thrown in that wrapped form.  | 
 | 
     * <p>  | 
 | 
     * Like {@link java.lang.Integer#valueOf Integer.valueOf}, | 
 | 
     * {@code asInterfaceInstance} is a factory method whose results are defined | 
 | 
     * by their behavior.  | 
 | 
     * It is not guaranteed to return a new instance for every call.  | 
 | 
     * <p>  | 
 | 
     * Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods} | 
 | 
     * and other corner cases, the interface may also have several abstract methods  | 
 | 
     * with the same name but having distinct descriptors (types of returns and parameters).  | 
 | 
     * In this case, all the methods are bound in common to the one given target.  | 
 | 
     * The type check and effective {@code asType} conversion is applied to each | 
 | 
     * method type descriptor, and all abstract methods are bound to the target in common.  | 
 | 
     * Beyond this type check, no further checks are made to determine that the  | 
 | 
     * abstract methods are related in any way.  | 
 | 
     * <p>  | 
 | 
     * Future versions of this API may accept additional types,  | 
 | 
     * such as abstract classes with single abstract methods.  | 
 | 
     * Future versions of this API may also equip wrapper instances  | 
 | 
     * with one or more additional public "marker" interfaces.  | 
 | 
     * <p>  | 
 | 
     * If a security manager is installed, this method is caller sensitive.  | 
 | 
     * During any invocation of the target method handle via the returned wrapper,  | 
 | 
     * the original creator of the wrapper (the caller) will be visible  | 
 | 
     * to context checks requested by the security manager.  | 
 | 
     *  | 
 | 
     * @param <T> the desired type of the wrapper, a single-method interface  | 
 | 
     * @param intfc a class object representing {@code T} | 
 | 
     * @param target the method handle to invoke from the wrapper  | 
 | 
     * @return a correctly-typed wrapper for the given target  | 
 | 
     * @throws NullPointerException if either argument is null  | 
 | 
     * @throws IllegalArgumentException if the {@code intfc} is not a | 
 | 
     *         valid argument to this method  | 
 | 
     * @throws WrongMethodTypeException if the target cannot  | 
 | 
     *         be converted to the type required by the requested interface  | 
 | 
     */  | 
 | 
    // Other notes to implementors:  | 
 | 
    // <p>  | 
 | 
    // No stable mapping is promised between the single-method interface and  | 
 | 
    // the implementation class C.  Over time, several implementation  | 
 | 
    // classes might be used for the same type.  | 
 | 
    // <p>  | 
 | 
    // If the implementation is able  | 
 | 
    // to prove that a wrapper of the required type  | 
 | 
    // has already been created for a given  | 
 | 
    // method handle, or for another method handle with the  | 
 | 
    // same behavior, the implementation may return that wrapper in place of  | 
 | 
    // a new wrapper.  | 
 | 
    // <p>  | 
 | 
    // This method is designed to apply to common use cases  | 
 | 
    // where a single method handle must interoperate with  | 
 | 
    // an interface that implements a function-like  | 
 | 
    // API.  Additional variations, such as single-abstract-method classes with  | 
 | 
    // private constructors, or interfaces with multiple but related  | 
 | 
    // entry points, must be covered by hand-written or automatically  | 
 | 
    // generated adapter classes.  | 
 | 
      | 
 | 
    @CallerSensitive  | 
 | 
    public static  | 
 | 
    <T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) { | 
 | 
        if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers()))  | 
 | 
            throw newIllegalArgumentException("not a public interface", intfc.getName()); | 
 | 
        final MethodHandle mh;  | 
 | 
        if (System.getSecurityManager() != null) { | 
 | 
            final Class<?> caller = Reflection.getCallerClass();  | 
 | 
            final ClassLoader ccl = caller != null ? caller.getClassLoader() : null;  | 
 | 
            ReflectUtil.checkProxyPackageAccess(ccl, intfc);  | 
 | 
            mh = ccl != null ? bindCaller(target, caller) : target;  | 
 | 
        } else { | 
 | 
            mh = target;  | 
 | 
        }  | 
 | 
        ClassLoader proxyLoader = intfc.getClassLoader();  | 
 | 
        if (proxyLoader == null) { | 
 | 
            ClassLoader cl = Thread.currentThread().getContextClassLoader();   | 
 | 
            proxyLoader = cl != null ? cl : ClassLoader.getSystemClassLoader();  | 
 | 
        }  | 
 | 
        final Method[] methods = getSingleNameMethods(intfc);  | 
 | 
        if (methods == null)  | 
 | 
            throw newIllegalArgumentException("not a single-method interface", intfc.getName()); | 
 | 
        final MethodHandle[] vaTargets = new MethodHandle[methods.length];  | 
 | 
        for (int i = 0; i < methods.length; i++) { | 
 | 
            Method sm = methods[i];  | 
 | 
            MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes());  | 
 | 
            MethodHandle checkTarget = mh.asType(smMT);    | 
 | 
            checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));  | 
 | 
            vaTargets[i] = checkTarget.asSpreader(Object[].class, smMT.parameterCount());  | 
 | 
        }  | 
 | 
        final InvocationHandler ih = new InvocationHandler() { | 
 | 
                private Object getArg(String name) { | 
 | 
                    if ((Object)name == "getWrapperInstanceTarget")  return target;  | 
 | 
                    if ((Object)name == "getWrapperInstanceType")    return intfc;  | 
 | 
                    throw new AssertionError();  | 
 | 
                }  | 
 | 
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | 
 | 
                    for (int i = 0; i < methods.length; i++) { | 
 | 
                        if (method.equals(methods[i]))  | 
 | 
                            return vaTargets[i].invokeExact(args);  | 
 | 
                    }  | 
 | 
                    if (method.getDeclaringClass() == WrapperInstance.class)  | 
 | 
                        return getArg(method.getName());  | 
 | 
                    if (isObjectMethod(method))  | 
 | 
                        return callObjectMethod(proxy, method, args);  | 
 | 
                    throw newInternalError("bad proxy method: "+method); | 
 | 
                }  | 
 | 
            };  | 
 | 
 | 
 | 
        final Object proxy;  | 
 | 
        if (System.getSecurityManager() != null) { | 
 | 
            // sun.invoke.WrapperInstance is a restricted interface not accessible  | 
 | 
              | 
 | 
            final ClassLoader loader = proxyLoader;  | 
 | 
            proxy = AccessController.doPrivileged(new PrivilegedAction<>() { | 
 | 
                public Object run() { | 
 | 
                    return Proxy.newProxyInstance(  | 
 | 
                            loader,  | 
 | 
                            new Class<?>[]{ intfc, WrapperInstance.class }, | 
 | 
                            ih);  | 
 | 
                }  | 
 | 
            });  | 
 | 
        } else { | 
 | 
            proxy = Proxy.newProxyInstance(proxyLoader,  | 
 | 
                                           new Class<?>[]{ intfc, WrapperInstance.class }, | 
 | 
                                           ih);  | 
 | 
        }  | 
 | 
        return intfc.cast(proxy);  | 
 | 
    }  | 
 | 
 | 
 | 
    private static MethodHandle bindCaller(MethodHandle target, Class<?> hostClass) { | 
 | 
        return MethodHandleImpl.bindCaller(target, hostClass).withVarargs(target.isVarargsCollector());  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static  | 
 | 
    boolean isWrapperInstance(Object x) { | 
 | 
        return x instanceof WrapperInstance;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static WrapperInstance asWrapperInstance(Object x) { | 
 | 
        try { | 
 | 
            if (x != null)  | 
 | 
                return (WrapperInstance) x;  | 
 | 
        } catch (ClassCastException ex) { | 
 | 
        }  | 
 | 
        throw newIllegalArgumentException("not a wrapper instance"); | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static  | 
 | 
    MethodHandle wrapperInstanceTarget(Object x) { | 
 | 
        return asWrapperInstance(x).getWrapperInstanceTarget();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static  | 
 | 
    Class<?> wrapperInstanceType(Object x) { | 
 | 
        return asWrapperInstance(x).getWrapperInstanceType();  | 
 | 
    }  | 
 | 
 | 
 | 
    private static  | 
 | 
    boolean isObjectMethod(Method m) { | 
 | 
        switch (m.getName()) { | 
 | 
        case "toString":  | 
 | 
            return (m.getReturnType() == String.class  | 
 | 
                    && m.getParameterTypes().length == 0);  | 
 | 
        case "hashCode":  | 
 | 
            return (m.getReturnType() == int.class  | 
 | 
                    && m.getParameterTypes().length == 0);  | 
 | 
        case "equals":  | 
 | 
            return (m.getReturnType() == boolean.class  | 
 | 
                    && m.getParameterTypes().length == 1  | 
 | 
                    && m.getParameterTypes()[0] == Object.class);  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static  | 
 | 
    Object callObjectMethod(Object self, Method m, Object[] args) { | 
 | 
        assert(isObjectMethod(m)) : m;  | 
 | 
        switch (m.getName()) { | 
 | 
        case "toString":  | 
 | 
            return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode());  | 
 | 
        case "hashCode":  | 
 | 
            return System.identityHashCode(self);  | 
 | 
        case "equals":  | 
 | 
            return (self == args[0]);  | 
 | 
        }  | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static  | 
 | 
    Method[] getSingleNameMethods(Class<?> intfc) { | 
 | 
        ArrayList<Method> methods = new ArrayList<>();  | 
 | 
        String uniqueName = null;  | 
 | 
        for (Method m : intfc.getMethods()) { | 
 | 
            if (isObjectMethod(m))  continue;  | 
 | 
            if (!Modifier.isAbstract(m.getModifiers()))  continue;  | 
 | 
            String mname = m.getName();  | 
 | 
            if (uniqueName == null)  | 
 | 
                uniqueName = mname;  | 
 | 
            else if (!uniqueName.equals(mname))  | 
 | 
                return null;    | 
 | 
            methods.add(m);  | 
 | 
        }  | 
 | 
        if (uniqueName == null)  return null;  | 
 | 
        return methods.toArray(new Method[methods.size()]);  | 
 | 
    }  | 
 | 
}  |