1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/emul/compact/src/main/java/java/lang/invoke/MethodHandleProxies.java Sat Aug 09 11:11:13 2014 +0200
1.3 @@ -0,0 +1,321 @@
1.4 +/*
1.5 + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.lang.invoke;
1.30 +
1.31 +import java.lang.reflect.*;
1.32 +import java.security.AccessController;
1.33 +import java.security.PrivilegedAction;
1.34 +import sun.invoke.WrapperInstance;
1.35 +import java.util.ArrayList;
1.36 +import sun.reflect.CallerSensitive;
1.37 +import sun.reflect.Reflection;
1.38 +import sun.reflect.misc.ReflectUtil;
1.39 +
1.40 +/**
1.41 + * This class consists exclusively of static methods that help adapt
1.42 + * method handles to other JVM types, such as interfaces.
1.43 + */
1.44 +public class MethodHandleProxies {
1.45 +
1.46 + private MethodHandleProxies() { } // do not instantiate
1.47 +
1.48 + /**
1.49 + * Produces an instance of the given single-method interface which redirects
1.50 + * its calls to the given method handle.
1.51 + * <p>
1.52 + * A single-method interface is an interface which declares a uniquely named method.
1.53 + * When determining the uniquely named method of a single-method interface,
1.54 + * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode})
1.55 + * are disregarded. For example, {@link java.util.Comparator} is a single-method interface,
1.56 + * even though it re-declares the {@code Object.equals} method.
1.57 + * <p>
1.58 + * The interface must be public. No additional access checks are performed.
1.59 + * <p>
1.60 + * The resulting instance of the required type will respond to
1.61 + * invocation of the type's uniquely named method by calling
1.62 + * the given target on the incoming arguments,
1.63 + * and returning or throwing whatever the target
1.64 + * returns or throws. The invocation will be as if by
1.65 + * {@code target.invoke}.
1.66 + * The target's type will be checked before the
1.67 + * instance is created, as if by a call to {@code asType},
1.68 + * which may result in a {@code WrongMethodTypeException}.
1.69 + * <p>
1.70 + * The uniquely named method is allowed to be multiply declared,
1.71 + * with distinct type descriptors. (E.g., it can be overloaded,
1.72 + * or can possess bridge methods.) All such declarations are
1.73 + * connected directly to the target method handle.
1.74 + * Argument and return types are adjusted by {@code asType}
1.75 + * for each individual declaration.
1.76 + * <p>
1.77 + * The wrapper instance will implement the requested interface
1.78 + * and its super-types, but no other single-method interfaces.
1.79 + * This means that the instance will not unexpectedly
1.80 + * pass an {@code instanceof} test for any unrequested type.
1.81 + * <p style="font-size:smaller;">
1.82 + * <em>Implementation Note:</em>
1.83 + * Therefore, each instance must implement a unique single-method interface.
1.84 + * Implementations may not bundle together
1.85 + * multiple single-method interfaces onto single implementation classes
1.86 + * in the style of {@link java.awt.AWTEventMulticaster}.
1.87 + * <p>
1.88 + * The method handle may throw an <em>undeclared exception</em>,
1.89 + * which means any checked exception (or other checked throwable)
1.90 + * not declared by the requested type's single abstract method.
1.91 + * If this happens, the throwable will be wrapped in an instance of
1.92 + * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException}
1.93 + * and thrown in that wrapped form.
1.94 + * <p>
1.95 + * Like {@link java.lang.Integer#valueOf Integer.valueOf},
1.96 + * {@code asInterfaceInstance} is a factory method whose results are defined
1.97 + * by their behavior.
1.98 + * It is not guaranteed to return a new instance for every call.
1.99 + * <p>
1.100 + * Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods}
1.101 + * and other corner cases, the interface may also have several abstract methods
1.102 + * with the same name but having distinct descriptors (types of returns and parameters).
1.103 + * In this case, all the methods are bound in common to the one given target.
1.104 + * The type check and effective {@code asType} conversion is applied to each
1.105 + * method type descriptor, and all abstract methods are bound to the target in common.
1.106 + * Beyond this type check, no further checks are made to determine that the
1.107 + * abstract methods are related in any way.
1.108 + * <p>
1.109 + * Future versions of this API may accept additional types,
1.110 + * such as abstract classes with single abstract methods.
1.111 + * Future versions of this API may also equip wrapper instances
1.112 + * with one or more additional public "marker" interfaces.
1.113 + * <p>
1.114 + * If a security manager is installed, this method is caller sensitive.
1.115 + * During any invocation of the target method handle via the returned wrapper,
1.116 + * the original creator of the wrapper (the caller) will be visible
1.117 + * to context checks requested by the security manager.
1.118 + *
1.119 + * @param <T> the desired type of the wrapper, a single-method interface
1.120 + * @param intfc a class object representing {@code T}
1.121 + * @param target the method handle to invoke from the wrapper
1.122 + * @return a correctly-typed wrapper for the given target
1.123 + * @throws NullPointerException if either argument is null
1.124 + * @throws IllegalArgumentException if the {@code intfc} is not a
1.125 + * valid argument to this method
1.126 + * @throws WrongMethodTypeException if the target cannot
1.127 + * be converted to the type required by the requested interface
1.128 + */
1.129 + // Other notes to implementors:
1.130 + // <p>
1.131 + // No stable mapping is promised between the single-method interface and
1.132 + // the implementation class C. Over time, several implementation
1.133 + // classes might be used for the same type.
1.134 + // <p>
1.135 + // If the implementation is able
1.136 + // to prove that a wrapper of the required type
1.137 + // has already been created for a given
1.138 + // method handle, or for another method handle with the
1.139 + // same behavior, the implementation may return that wrapper in place of
1.140 + // a new wrapper.
1.141 + // <p>
1.142 + // This method is designed to apply to common use cases
1.143 + // where a single method handle must interoperate with
1.144 + // an interface that implements a function-like
1.145 + // API. Additional variations, such as single-abstract-method classes with
1.146 + // private constructors, or interfaces with multiple but related
1.147 + // entry points, must be covered by hand-written or automatically
1.148 + // generated adapter classes.
1.149 + //
1.150 + @CallerSensitive
1.151 + public static
1.152 + <T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) {
1.153 + if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers()))
1.154 + throw new IllegalArgumentException("not a public interface: "+intfc.getName());
1.155 + final MethodHandle mh;
1.156 + if (System.getSecurityManager() != null) {
1.157 + final Class<?> caller = Reflection.getCallerClass();
1.158 + final ClassLoader ccl = caller != null ? caller.getClassLoader() : null;
1.159 + ReflectUtil.checkProxyPackageAccess(ccl, intfc);
1.160 + mh = ccl != null ? bindCaller(target, caller) : target;
1.161 + } else {
1.162 + mh = target;
1.163 + }
1.164 + ClassLoader proxyLoader = intfc.getClassLoader();
1.165 + if (proxyLoader == null) {
1.166 + ClassLoader cl = Thread.currentThread().getContextClassLoader(); // avoid use of BCP
1.167 + proxyLoader = cl != null ? cl : ClassLoader.getSystemClassLoader();
1.168 + }
1.169 + final Method[] methods = getSingleNameMethods(intfc);
1.170 + if (methods == null)
1.171 + throw new IllegalArgumentException("not a single-method interface: "+intfc.getName());
1.172 + final MethodHandle[] vaTargets = new MethodHandle[methods.length];
1.173 + for (int i = 0; i < methods.length; i++) {
1.174 + Method sm = methods[i];
1.175 + MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes());
1.176 + MethodHandle checkTarget = mh.asType(smMT); // make throw WMT
1.177 + checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
1.178 + vaTargets[i] = checkTarget.asSpreader(Object[].class, smMT.parameterCount());
1.179 + }
1.180 + final InvocationHandler ih = new InvocationHandler() {
1.181 + private Object getArg(String name) {
1.182 + if ((Object)name == "getWrapperInstanceTarget") return target;
1.183 + if ((Object)name == "getWrapperInstanceType") return intfc;
1.184 + throw new AssertionError();
1.185 + }
1.186 + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1.187 + for (int i = 0; i < methods.length; i++) {
1.188 + if (method.equals(methods[i]))
1.189 + return vaTargets[i].invokeExact(args);
1.190 + }
1.191 + if (method.getDeclaringClass() == WrapperInstance.class)
1.192 + return getArg(method.getName());
1.193 + if (isObjectMethod(method))
1.194 + return callObjectMethod(proxy, method, args);
1.195 + throw new InternalError("bad proxy method: "+method);
1.196 + }
1.197 + };
1.198 +
1.199 + final Object proxy;
1.200 + if (System.getSecurityManager() != null) {
1.201 + // sun.invoke.WrapperInstance is a restricted interface not accessible
1.202 + // by any non-null class loader.
1.203 + final ClassLoader loader = proxyLoader;
1.204 + proxy = AccessController.doPrivileged(new PrivilegedAction<Object>() {
1.205 + public Object run() {
1.206 + return Proxy.newProxyInstance(
1.207 + loader,
1.208 + new Class<?>[]{ intfc, WrapperInstance.class },
1.209 + ih);
1.210 + }
1.211 + });
1.212 + } else {
1.213 + proxy = Proxy.newProxyInstance(proxyLoader,
1.214 + new Class<?>[]{ intfc, WrapperInstance.class },
1.215 + ih);
1.216 + }
1.217 + return intfc.cast(proxy);
1.218 + }
1.219 +
1.220 + private static MethodHandle bindCaller(MethodHandle target, Class<?> hostClass) {
1.221 + MethodHandle cbmh = MethodHandleImpl.bindCaller(target, hostClass);
1.222 + if (target.isVarargsCollector()) {
1.223 + MethodType type = cbmh.type();
1.224 + int arity = type.parameterCount();
1.225 + return cbmh.asVarargsCollector(type.parameterType(arity-1));
1.226 + }
1.227 + return cbmh;
1.228 + }
1.229 +
1.230 + /**
1.231 + * Determines if the given object was produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
1.232 + * @param x any reference
1.233 + * @return true if the reference is not null and points to an object produced by {@code asInterfaceInstance}
1.234 + */
1.235 + public static
1.236 + boolean isWrapperInstance(Object x) {
1.237 + return x instanceof WrapperInstance;
1.238 + }
1.239 +
1.240 + private static WrapperInstance asWrapperInstance(Object x) {
1.241 + try {
1.242 + if (x != null)
1.243 + return (WrapperInstance) x;
1.244 + } catch (ClassCastException ex) {
1.245 + }
1.246 + throw new IllegalArgumentException("not a wrapper instance");
1.247 + }
1.248 +
1.249 + /**
1.250 + * Produces or recovers a target method handle which is behaviorally
1.251 + * equivalent to the unique method of this wrapper instance.
1.252 + * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
1.253 + * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
1.254 + * @param x any reference
1.255 + * @return a method handle implementing the unique method
1.256 + * @throws IllegalArgumentException if the reference x is not to a wrapper instance
1.257 + */
1.258 + public static
1.259 + MethodHandle wrapperInstanceTarget(Object x) {
1.260 + return asWrapperInstance(x).getWrapperInstanceTarget();
1.261 + }
1.262 +
1.263 + /**
1.264 + * Recovers the unique single-method interface type for which this wrapper instance was created.
1.265 + * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
1.266 + * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
1.267 + * @param x any reference
1.268 + * @return the single-method interface type for which the wrapper was created
1.269 + * @throws IllegalArgumentException if the reference x is not to a wrapper instance
1.270 + */
1.271 + public static
1.272 + Class<?> wrapperInstanceType(Object x) {
1.273 + return asWrapperInstance(x).getWrapperInstanceType();
1.274 + }
1.275 +
1.276 + private static
1.277 + boolean isObjectMethod(Method m) {
1.278 + switch (m.getName()) {
1.279 + case "toString":
1.280 + return (m.getReturnType() == String.class
1.281 + && m.getParameterTypes().length == 0);
1.282 + case "hashCode":
1.283 + return (m.getReturnType() == int.class
1.284 + && m.getParameterTypes().length == 0);
1.285 + case "equals":
1.286 + return (m.getReturnType() == boolean.class
1.287 + && m.getParameterTypes().length == 1
1.288 + && m.getParameterTypes()[0] == Object.class);
1.289 + }
1.290 + return false;
1.291 + }
1.292 +
1.293 + private static
1.294 + Object callObjectMethod(Object self, Method m, Object[] args) {
1.295 + assert(isObjectMethod(m)) : m;
1.296 + switch (m.getName()) {
1.297 + case "toString":
1.298 + return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode());
1.299 + case "hashCode":
1.300 + return System.identityHashCode(self);
1.301 + case "equals":
1.302 + return (self == args[0]);
1.303 + }
1.304 + return null;
1.305 + }
1.306 +
1.307 + private static
1.308 + Method[] getSingleNameMethods(Class<?> intfc) {
1.309 + ArrayList<Method> methods = new ArrayList<Method>();
1.310 + String uniqueName = null;
1.311 + for (Method m : intfc.getMethods()) {
1.312 + if (isObjectMethod(m)) continue;
1.313 + if (!Modifier.isAbstract(m.getModifiers())) continue;
1.314 + String mname = m.getName();
1.315 + if (uniqueName == null)
1.316 + uniqueName = mname;
1.317 + else if (!uniqueName.equals(mname))
1.318 + return null; // too many abstract methods
1.319 + methods.add(m);
1.320 + }
1.321 + if (uniqueName == null) return null;
1.322 + return methods.toArray(new Method[methods.size()]);
1.323 + }
1.324 +}