Home » openjdk-7 » java.lang.invoke » [javadoc | source]

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

Home » openjdk-7 » java.lang.invoke » [javadoc | source]