1/* 2 * Copyright (c) 2008, 2016, 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 26package java.lang.invoke; 27 28import java.lang.reflect.*; 29import java.security.AccessController; 30import java.security.PrivilegedAction; 31import sun.invoke.WrapperInstance; 32import java.util.ArrayList; 33import jdk.internal.reflect.CallerSensitive; 34import jdk.internal.reflect.Reflection; 35import sun.reflect.misc.ReflectUtil; 36import static java.lang.invoke.MethodHandleStatics.*; 37 38/** 39 * This class consists exclusively of static methods that help adapt 40 * method handles to other JVM types, such as interfaces. 41 * 42 * @since 1.7 43 */ 44public class MethodHandleProxies { 45 46 private MethodHandleProxies() { } // do not instantiate 47 48 /** 49 * Produces an instance of the given single-method interface which redirects 50 * its calls to the given method handle. 51 * <p> 52 * A single-method interface is an interface which declares a uniquely named method. 53 * When determining the uniquely named method of a single-method interface, 54 * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode}) 55 * are disregarded. For example, {@link java.util.Comparator} is a single-method interface, 56 * even though it re-declares the {@code Object.equals} method. 57 * <p> 58 * The interface must be public. No additional access checks are performed. 59 * <p> 60 * The resulting instance of the required type will respond to 61 * invocation of the type's uniquely named method by calling 62 * the given target on the incoming arguments, 63 * and returning or throwing whatever the target 64 * returns or throws. The invocation will be as if by 65 * {@code target.invoke}. 66 * The target's type will be checked before the 67 * instance is created, as if by a call to {@code asType}, 68 * which may result in a {@code WrongMethodTypeException}. 69 * <p> 70 * The uniquely named method is allowed to be multiply declared, 71 * with distinct type descriptors. (E.g., it can be overloaded, 72 * or can possess bridge methods.) All such declarations are 73 * connected directly to the target method handle. 74 * Argument and return types are adjusted by {@code asType} 75 * for each individual declaration. 76 * <p> 77 * The wrapper instance will implement the requested interface 78 * and its super-types, but no other single-method interfaces. 79 * This means that the instance will not unexpectedly 80 * pass an {@code instanceof} test for any unrequested type. 81 * <p style="font-size:smaller;"> 82 * <em>Implementation Note:</em> 83 * Therefore, each instance must implement a unique single-method interface. 84 * Implementations may not bundle together 85 * multiple single-method interfaces onto single implementation classes 86 * in the style of {@link java.awt.AWTEventMulticaster}. 87 * <p> 88 * The method handle may throw an <em>undeclared exception</em>, 89 * which means any checked exception (or other checked throwable) 90 * not declared by the requested type's single abstract method. 91 * If this happens, the throwable will be wrapped in an instance of 92 * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException} 93 * and thrown in that wrapped form. 94 * <p> 95 * Like {@link java.lang.Integer#valueOf Integer.valueOf}, 96 * {@code asInterfaceInstance} is a factory method whose results are defined 97 * by their behavior. 98 * It is not guaranteed to return a new instance for every call. 99 * <p> 100 * Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods} 101 * and other corner cases, the interface may also have several abstract methods 102 * with the same name but having distinct descriptors (types of returns and parameters). 103 * In this case, all the methods are bound in common to the one given target. 104 * The type check and effective {@code asType} conversion is applied to each 105 * method type descriptor, and all abstract methods are bound to the target in common. 106 * Beyond this type check, no further checks are made to determine that the 107 * abstract methods are related in any way. 108 * <p> 109 * Future versions of this API may accept additional types, 110 * such as abstract classes with single abstract methods. 111 * Future versions of this API may also equip wrapper instances 112 * with one or more additional public "marker" interfaces. 113 * <p> 114 * If a security manager is installed, this method is caller sensitive. 115 * During any invocation of the target method handle via the returned wrapper, 116 * the original creator of the wrapper (the caller) will be visible 117 * to context checks requested by the security manager. 118 * 119 * @param <T> the desired type of the wrapper, a single-method interface 120 * @param intfc a class object representing {@code T} 121 * @param target the method handle to invoke from the wrapper 122 * @return a correctly-typed wrapper for the given target 123 * @throws NullPointerException if either argument is null 124 * @throws IllegalArgumentException if the {@code intfc} is not a 125 * valid argument to this method 126 * @throws WrongMethodTypeException if the target cannot 127 * be converted to the type required by the requested interface 128 */ 129 // Other notes to implementors: 130 // <p> 131 // No stable mapping is promised between the single-method interface and 132 // the implementation class C. Over time, several implementation 133 // classes might be used for the same type. 134 // <p> 135 // If the implementation is able 136 // to prove that a wrapper of the required type 137 // has already been created for a given 138 // method handle, or for another method handle with the 139 // same behavior, the implementation may return that wrapper in place of 140 // a new wrapper. 141 // <p> 142 // This method is designed to apply to common use cases 143 // where a single method handle must interoperate with 144 // an interface that implements a function-like 145 // API. Additional variations, such as single-abstract-method classes with 146 // private constructors, or interfaces with multiple but related 147 // entry points, must be covered by hand-written or automatically 148 // generated adapter classes. 149 // 150 @CallerSensitive 151 public static 152 <T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) { 153 if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers())) 154 throw newIllegalArgumentException("not a public interface", intfc.getName()); 155 final MethodHandle mh; 156 if (System.getSecurityManager() != null) { 157 final Class<?> caller = Reflection.getCallerClass(); 158 final ClassLoader ccl = caller != null ? caller.getClassLoader() : null; 159 ReflectUtil.checkProxyPackageAccess(ccl, intfc); 160 mh = ccl != null ? bindCaller(target, caller) : target; 161 } else { 162 mh = target; 163 } 164 ClassLoader proxyLoader = intfc.getClassLoader(); 165 if (proxyLoader == null) { 166 ClassLoader cl = Thread.currentThread().getContextClassLoader(); // avoid use of BCP 167 proxyLoader = cl != null ? cl : ClassLoader.getSystemClassLoader(); 168 } 169 final Method[] methods = getSingleNameMethods(intfc); 170 if (methods == null) 171 throw newIllegalArgumentException("not a single-method interface", intfc.getName()); 172 final MethodHandle[] vaTargets = new MethodHandle[methods.length]; 173 for (int i = 0; i < methods.length; i++) { 174 Method sm = methods[i]; 175 MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes()); 176 MethodHandle checkTarget = mh.asType(smMT); // make throw WMT 177 checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class)); 178 vaTargets[i] = checkTarget.asSpreader(Object[].class, smMT.parameterCount()); 179 } 180 final InvocationHandler ih = new InvocationHandler() { 181 private Object getArg(String name) { 182 if ((Object)name == "getWrapperInstanceTarget") return target; 183 if ((Object)name == "getWrapperInstanceType") return intfc; 184 throw new AssertionError(); 185 } 186 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 187 for (int i = 0; i < methods.length; i++) { 188 if (method.equals(methods[i])) 189 return vaTargets[i].invokeExact(args); 190 } 191 if (method.getDeclaringClass() == WrapperInstance.class) 192 return getArg(method.getName()); 193 if (isObjectMethod(method)) 194 return callObjectMethod(proxy, method, args); 195 throw newInternalError("bad proxy method: "+method); 196 } 197 }; 198 199 final Object proxy; 200 if (System.getSecurityManager() != null) { 201 // sun.invoke.WrapperInstance is a restricted interface not accessible 202 // by any non-null class loader. 203 final ClassLoader loader = proxyLoader; 204 proxy = AccessController.doPrivileged(new PrivilegedAction<>() { 205 public Object run() { 206 return Proxy.newProxyInstance( 207 loader, 208 new Class<?>[]{ intfc, WrapperInstance.class }, 209 ih); 210 } 211 }); 212 } else { 213 proxy = Proxy.newProxyInstance(proxyLoader, 214 new Class<?>[]{ intfc, WrapperInstance.class }, 215 ih); 216 } 217 return intfc.cast(proxy); 218 } 219 220 private static MethodHandle bindCaller(MethodHandle target, Class<?> hostClass) { 221 return MethodHandleImpl.bindCaller(target, hostClass).withVarargs(target.isVarargsCollector()); 222 } 223 224 /** 225 * Determines if the given object was produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. 226 * @param x any reference 227 * @return true if the reference is not null and points to an object produced by {@code asInterfaceInstance} 228 */ 229 public static 230 boolean isWrapperInstance(Object x) { 231 return x instanceof WrapperInstance; 232 } 233 234 private static WrapperInstance asWrapperInstance(Object x) { 235 try { 236 if (x != null) 237 return (WrapperInstance) x; 238 } catch (ClassCastException ex) { 239 } 240 throw newIllegalArgumentException("not a wrapper instance"); 241 } 242 243 /** 244 * Produces or recovers a target method handle which is behaviorally 245 * equivalent to the unique method of this wrapper instance. 246 * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. 247 * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. 248 * @param x any reference 249 * @return a method handle implementing the unique method 250 * @throws IllegalArgumentException if the reference x is not to a wrapper instance 251 */ 252 public static 253 MethodHandle wrapperInstanceTarget(Object x) { 254 return asWrapperInstance(x).getWrapperInstanceTarget(); 255 } 256 257 /** 258 * Recovers the unique single-method interface type for which this wrapper instance was created. 259 * The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}. 260 * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}. 261 * @param x any reference 262 * @return the single-method interface type for which the wrapper was created 263 * @throws IllegalArgumentException if the reference x is not to a wrapper instance 264 */ 265 public static 266 Class<?> wrapperInstanceType(Object x) { 267 return asWrapperInstance(x).getWrapperInstanceType(); 268 } 269 270 private static 271 boolean isObjectMethod(Method m) { 272 switch (m.getName()) { 273 case "toString": 274 return (m.getReturnType() == String.class 275 && m.getParameterTypes().length == 0); 276 case "hashCode": 277 return (m.getReturnType() == int.class 278 && m.getParameterTypes().length == 0); 279 case "equals": 280 return (m.getReturnType() == boolean.class 281 && m.getParameterTypes().length == 1 282 && m.getParameterTypes()[0] == Object.class); 283 } 284 return false; 285 } 286 287 private static 288 Object callObjectMethod(Object self, Method m, Object[] args) { 289 assert(isObjectMethod(m)) : m; 290 switch (m.getName()) { 291 case "toString": 292 return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode()); 293 case "hashCode": 294 return System.identityHashCode(self); 295 case "equals": 296 return (self == args[0]); 297 } 298 return null; 299 } 300 301 private static 302 Method[] getSingleNameMethods(Class<?> intfc) { 303 ArrayList<Method> methods = new ArrayList<>(); 304 String uniqueName = null; 305 for (Method m : intfc.getMethods()) { 306 if (isObjectMethod(m)) continue; 307 if (!Modifier.isAbstract(m.getModifiers())) continue; 308 String mname = m.getName(); 309 if (uniqueName == null) 310 uniqueName = mname; 311 else if (!uniqueName.equals(mname)) 312 return null; // too many abstract methods 313 methods.add(m); 314 } 315 if (uniqueName == null) return null; 316 return methods.toArray(new Method[methods.size()]); 317 } 318} 319