jdk/src/share/classes/sun/dyn/BoundMethodHandle.java
changeset 4537 7c3c7f8d5195
parent 2707 5a17df307cbc
child 5722 4ada807383c8
child 5506 202f599c92aa
equal deleted inserted replaced
4536:0f6ec6364a69 4537:7c3c7f8d5195
    26 package sun.dyn;
    26 package sun.dyn;
    27 
    27 
    28 import sun.dyn.util.VerifyType;
    28 import sun.dyn.util.VerifyType;
    29 import sun.dyn.util.Wrapper;
    29 import sun.dyn.util.Wrapper;
    30 import java.dyn.*;
    30 import java.dyn.*;
       
    31 import java.util.List;
       
    32 import sun.dyn.MethodHandleNatives.Constants;
       
    33 import static sun.dyn.MethodHandleImpl.IMPL_LOOKUP;
       
    34 import static sun.dyn.MemberName.newIllegalArgumentException;
    31 
    35 
    32 /**
    36 /**
    33  * The flavor of method handle which emulates an invoke instruction
    37  * The flavor of method handle which emulates an invoke instruction
    34  * on a predetermined argument.  The JVM dispatches to the correct method
    38  * on a predetermined argument.  The JVM dispatches to the correct method
    35  * when the handle is created, not when it is invoked.
    39  * when the handle is created, not when it is invoked.
    36  * @author jrose
    40  * @author jrose
    37  */
    41  */
    38 public class BoundMethodHandle extends MethodHandle  {
    42 public class BoundMethodHandle extends MethodHandle {
    39     //MethodHandle vmtarget;           // next BMH or final DMH or methodOop
    43     //MethodHandle vmtarget;           // next BMH or final DMH or methodOop
    40     private final Object argument;     // argument to insert
    44     private final Object argument;     // argument to insert
    41     private final int    vmargslot;    // position at which it is inserted
    45     private final int    vmargslot;    // position at which it is inserted
    42 
    46 
       
    47     private static final Access IMPL_TOKEN = Access.getToken();
       
    48     private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
       
    49 
    43     // Constructors in this class *must* be package scoped or private.
    50     // Constructors in this class *must* be package scoped or private.
       
    51     // Exception:  JavaMethodHandle constructors are protected.
       
    52     // (The link between JMH and BMH is temporary.)
    44 
    53 
    45     /** Bind a direct MH to its receiver (or first ref. argument).
    54     /** Bind a direct MH to its receiver (or first ref. argument).
    46      *  The JVM will pre-dispatch the MH if it is not already static.
    55      *  The JVM will pre-dispatch the MH if it is not already static.
    47      */
    56      */
    48     BoundMethodHandle(DirectMethodHandle mh, Object argument) {
    57     BoundMethodHandle(DirectMethodHandle mh, Object argument) {
    49         super(Access.TOKEN, mh.type().dropParameterType(0));
    58         super(Access.TOKEN, mh.type().dropParameterTypes(0, 1));
    50         // check the type now, once for all:
    59         // check the type now, once for all:
    51         this.argument = checkReferenceArgument(argument, mh, 0);
    60         this.argument = checkReferenceArgument(argument, mh, 0);
    52         this.vmargslot = this.type().parameterSlotCount();
    61         this.vmargslot = this.type().parameterSlotCount();
    53         if (MethodHandleNatives.JVM_SUPPORT) {
    62         if (MethodHandleNatives.JVM_SUPPORT) {
    54             this.vmtarget = null;  // maybe updated by JVM
    63             this.vmtarget = null;  // maybe updated by JVM
    55             MethodHandleNatives.init(this, mh, 0);
    64             MethodHandleNatives.init(this, mh, 0);
    56         } else {
    65         } else {
    57             this.vmtarget = mh;
    66             this.vmtarget = mh;
    58         }
    67         }
    59      }
    68     }
    60 
       
    61     private static final int REF_ARG = 0, PRIM_ARG = 1, SELF_ARG = 2;
       
    62 
    69 
    63     /** Insert an argument into an arbitrary method handle.
    70     /** Insert an argument into an arbitrary method handle.
    64      *  If argnum is zero, inserts the first argument, etc.
    71      *  If argnum is zero, inserts the first argument, etc.
    65      *  The argument type must be a reference.
    72      *  The argument type must be a reference.
    66      */
    73      */
    67     BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
    74     BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
    68         this(mh, argument, argnum, mh.type().parameterType(argnum).isPrimitive() ? PRIM_ARG : REF_ARG);
    75         this(mh.type().dropParameterTypes(argnum, argnum+1),
       
    76              mh, argument, argnum);
    69     }
    77     }
    70 
    78 
    71     /** Insert an argument into an arbitrary method handle.
    79     /** Insert an argument into an arbitrary method handle.
    72      *  If argnum is zero, inserts the first argument, etc.
    80      *  If argnum is zero, inserts the first argument, etc.
    73      */
    81      */
    74     BoundMethodHandle(MethodHandle mh, Object argument, int argnum, int whichArg) {
    82     BoundMethodHandle(MethodType type, MethodHandle mh, Object argument, int argnum) {
    75         super(Access.TOKEN, mh.type().dropParameterType(argnum));
    83         super(Access.TOKEN, type);
    76         if (whichArg == PRIM_ARG)
    84         if (mh.type().parameterType(argnum).isPrimitive())
    77             this.argument = bindPrimitiveArgument(argument, mh, argnum);
    85             this.argument = bindPrimitiveArgument(argument, mh, argnum);
    78         else {
    86         else {
    79             if (whichArg == SELF_ARG)  argument = this;
       
    80             this.argument = checkReferenceArgument(argument, mh, argnum);
    87             this.argument = checkReferenceArgument(argument, mh, argnum);
    81         }
    88         }
    82         this.vmargslot = this.type().parameterSlotDepth(argnum);
    89         this.vmargslot = type.parameterSlotDepth(argnum);
       
    90         initTarget(mh, argnum);
       
    91     }
       
    92 
       
    93     private void initTarget(MethodHandle mh, int argnum) {
    83         if (MethodHandleNatives.JVM_SUPPORT) {
    94         if (MethodHandleNatives.JVM_SUPPORT) {
    84             this.vmtarget = null;  // maybe updated by JVM
    95             this.vmtarget = null; // maybe updated by JVM
    85             MethodHandleNatives.init(this, mh, argnum);
    96             MethodHandleNatives.init(this, mh, argnum);
    86         } else {
    97         } else {
    87             this.vmtarget = mh;
    98             this.vmtarget = mh;
    88         }
    99         }
    89     }
   100     }
    95         this.argument = argument;
   106         this.argument = argument;
    96         this.vmargslot = vmargslot;
   107         this.vmargslot = vmargslot;
    97         assert(this.getClass() == AdapterMethodHandle.class);
   108         assert(this.getClass() == AdapterMethodHandle.class);
    98     }
   109     }
    99 
   110 
   100     /** Initialize the current object as a method handle, binding it
   111     /** Initialize the current object as a Java method handle, binding it
   101      *  as the {@code argnum}th argument of the method handle {@code entryPoint}.
       
   102      *  The invocation type of the resulting method handle will be the
       
   103      *  same as {@code entryPoint},  except that the {@code argnum}th argument
       
   104      *  type will be dropped.
       
   105      */
       
   106     public BoundMethodHandle(MethodHandle entryPoint, int argnum) {
       
   107         this(entryPoint, null, argnum, SELF_ARG);
       
   108 
       
   109         // Note:  If the conversion fails, perhaps because of a bad entryPoint,
       
   110         // the MethodHandle.type field will not be filled in, and therefore
       
   111         // no MH.invoke call will ever succeed.  The caller may retain a pointer
       
   112         // to the broken method handle, but no harm can be done with it.
       
   113     }
       
   114 
       
   115     /** Initialize the current object as a method handle, binding it
       
   116      *  as the first argument of the method handle {@code entryPoint}.
   112      *  as the first argument of the method handle {@code entryPoint}.
   117      *  The invocation type of the resulting method handle will be the
   113      *  The invocation type of the resulting method handle will be the
   118      *  same as {@code entryPoint},  except that the first argument
   114      *  same as {@code entryPoint},  except that the first argument
   119      *  type will be dropped.
   115      *  type will be dropped.
   120      */
   116      */
   121     public BoundMethodHandle(MethodHandle entryPoint) {
   117     protected BoundMethodHandle(MethodHandle entryPoint) {
   122         this(entryPoint, null, 0, SELF_ARG);
   118         super(Access.TOKEN, entryPoint.type().dropParameterTypes(0, 1));
       
   119         this.argument = this; // kludge; get rid of
       
   120         this.vmargslot = this.type().parameterSlotDepth(0);
       
   121         initTarget(entryPoint, 0);
       
   122         assert(this instanceof JavaMethodHandle);
       
   123     }
       
   124 
       
   125     /** Initialize the current object as a Java method handle.
       
   126      */
       
   127     protected BoundMethodHandle(String entryPointName, MethodType type, boolean matchArity) {
       
   128         super(Access.TOKEN, null);
       
   129         MethodHandle entryPoint
       
   130                 = findJavaMethodHandleEntryPoint(this.getClass(),
       
   131                                         entryPointName, type, matchArity);
       
   132         MethodHandleImpl.initType(this, entryPoint.type().dropParameterTypes(0, 1));
       
   133         this.argument = this; // kludge; get rid of
       
   134         this.vmargslot = this.type().parameterSlotDepth(0);
       
   135         initTarget(entryPoint, 0);
       
   136         assert(this instanceof JavaMethodHandle);
       
   137     }
       
   138 
       
   139     private static
       
   140     MethodHandle findJavaMethodHandleEntryPoint(Class<?> caller,
       
   141                                                 String name,
       
   142                                                 MethodType type,
       
   143                                                 boolean matchArity) {
       
   144         if (matchArity)  type.getClass();  // elicit NPE
       
   145         List<MemberName> methods = IMPL_NAMES.getMethods(caller, true, name, null, caller);
       
   146         MethodType foundType = null;
       
   147         MemberName foundMethod = null;
       
   148         for (MemberName method : methods) {
       
   149             MethodType mtype = method.getMethodType();
       
   150             if (type != null && type.parameterCount() != mtype.parameterCount())
       
   151                 continue;
       
   152             else if (foundType == null)
       
   153                 foundType = mtype;
       
   154             else if (foundType != mtype)
       
   155                 throw newIllegalArgumentException("more than one method named "+name+" in "+caller.getName());
       
   156             // discard overrides
       
   157             if (foundMethod == null)
       
   158                 foundMethod = method;
       
   159             else if (foundMethod.getDeclaringClass().isAssignableFrom(method.getDeclaringClass()))
       
   160                 foundMethod = method;
       
   161         }
       
   162         if (foundMethod == null)
       
   163             throw newIllegalArgumentException("no method named "+name+" in "+caller.getName());
       
   164         MethodHandle entryPoint = MethodHandleImpl.findMethod(IMPL_TOKEN, foundMethod, true, caller);
       
   165         if (type != null) {
       
   166             MethodType epType = type.insertParameterTypes(0, entryPoint.type().parameterType(0));
       
   167             entryPoint = MethodHandles.convertArguments(entryPoint, epType);
       
   168         }
       
   169         return entryPoint;
   123     }
   170     }
   124 
   171 
   125     /** Make sure the given {@code argument} can be used as {@code argnum}-th
   172     /** Make sure the given {@code argument} can be used as {@code argnum}-th
   126      *  parameter of the given method handle {@code mh}, which must be a reference.
   173      *  parameter of the given method handle {@code mh}, which must be a reference.
   127      *  <p>
   174      *  <p>
   173         return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
   220         return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
   174     }
   221     }
   175 
   222 
   176     @Override
   223     @Override
   177     public String toString() {
   224     public String toString() {
   178         return "Bound[" + super.toString() + "]";
   225         MethodHandle mh = this;
       
   226         while (mh instanceof BoundMethodHandle) {
       
   227             Object info = MethodHandleNatives.getTargetInfo(mh);
       
   228             if (info instanceof MethodHandle) {
       
   229                 mh = (MethodHandle) info;
       
   230             } else {
       
   231                 String name = null;
       
   232                 if (info instanceof MemberName)
       
   233                     name = ((MemberName)info).getName();
       
   234                 if (name != null)
       
   235                     return name;
       
   236                 else
       
   237                     return super.toString(); // <unknown>, probably
       
   238             }
       
   239             assert(mh != this);
       
   240             if (mh instanceof JavaMethodHandle)
       
   241                 break;  // access JMH.toString(), not BMH.toString()
       
   242         }
       
   243         return mh.toString();
   179     }
   244     }
   180 }
   245 }