jdk/src/share/classes/sun/dyn/BoundMethodHandle.java
changeset 8823 7cd28219a1e4
parent 8717 f75a1efb1412
parent 8822 8145ab9f5f86
child 8824 0762fa26f813
child 9033 a88f5656f05d
equal deleted inserted replaced
8717:f75a1efb1412 8823:7cd28219a1e4
     1 /*
       
     2  * Copyright (c) 2008, 2010, 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 sun.dyn;
       
    27 
       
    28 import sun.dyn.util.VerifyType;
       
    29 import sun.dyn.util.Wrapper;
       
    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;
       
    35 
       
    36 /**
       
    37  * The flavor of method handle which emulates an invoke instruction
       
    38  * on a predetermined argument.  The JVM dispatches to the correct method
       
    39  * when the handle is created, not when it is invoked.
       
    40  * @author jrose
       
    41  */
       
    42 public class BoundMethodHandle extends MethodHandle {
       
    43     //MethodHandle vmtarget;           // next BMH or final DMH or methodOop
       
    44     private final Object argument;     // argument to insert
       
    45     private final int    vmargslot;    // position at which it is inserted
       
    46 
       
    47     private static final Access IMPL_TOKEN = Access.getToken();
       
    48     private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
       
    49 
       
    50     // Constructors in this class *must* be package scoped or private.
       
    51 
       
    52     /** Bind a direct MH to its receiver (or first ref. argument).
       
    53      *  The JVM will pre-dispatch the MH if it is not already static.
       
    54      */
       
    55     BoundMethodHandle(DirectMethodHandle mh, Object argument) {
       
    56         super(Access.TOKEN, mh.type().dropParameterTypes(0, 1));
       
    57         // check the type now, once for all:
       
    58         this.argument = checkReferenceArgument(argument, mh, 0);
       
    59         this.vmargslot = this.type().parameterSlotCount();
       
    60         if (MethodHandleNatives.JVM_SUPPORT) {
       
    61             this.vmtarget = null;  // maybe updated by JVM
       
    62             MethodHandleNatives.init(this, mh, 0);
       
    63         } else {
       
    64             this.vmtarget = mh;
       
    65         }
       
    66     }
       
    67 
       
    68     /** Insert an argument into an arbitrary method handle.
       
    69      *  If argnum is zero, inserts the first argument, etc.
       
    70      *  The argument type must be a reference.
       
    71      */
       
    72     BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
       
    73         this(mh.type().dropParameterTypes(argnum, argnum+1),
       
    74              mh, argument, argnum);
       
    75     }
       
    76 
       
    77     /** Insert an argument into an arbitrary method handle.
       
    78      *  If argnum is zero, inserts the first argument, etc.
       
    79      */
       
    80     BoundMethodHandle(MethodType type, MethodHandle mh, Object argument, int argnum) {
       
    81         super(Access.TOKEN, type);
       
    82         if (mh.type().parameterType(argnum).isPrimitive())
       
    83             this.argument = bindPrimitiveArgument(argument, mh, argnum);
       
    84         else {
       
    85             this.argument = checkReferenceArgument(argument, mh, argnum);
       
    86         }
       
    87         this.vmargslot = type.parameterSlotDepth(argnum);
       
    88         initTarget(mh, argnum);
       
    89     }
       
    90 
       
    91     private void initTarget(MethodHandle mh, int argnum) {
       
    92         if (MethodHandleNatives.JVM_SUPPORT) {
       
    93             this.vmtarget = null; // maybe updated by JVM
       
    94             MethodHandleNatives.init(this, mh, argnum);
       
    95         } else {
       
    96             this.vmtarget = mh;
       
    97         }
       
    98     }
       
    99 
       
   100     /** For the AdapterMethodHandle subclass.
       
   101      */
       
   102     BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
       
   103         super(Access.TOKEN, type);
       
   104         this.argument = argument;
       
   105         this.vmargslot = vmargslot;
       
   106         assert(this instanceof AdapterMethodHandle);
       
   107     }
       
   108 
       
   109     /** Initialize the current object as a self-bound method handle, binding it
       
   110      *  as the first argument of the method handle {@code entryPoint}.
       
   111      *  The invocation type of the resulting method handle will be the
       
   112      *  same as {@code entryPoint},  except that the first argument
       
   113      *  type will be dropped.
       
   114      */
       
   115     protected BoundMethodHandle(Access token, MethodHandle entryPoint) {
       
   116         super(token, entryPoint.type().dropParameterTypes(0, 1));
       
   117         this.argument = this; // kludge; get rid of
       
   118         this.vmargslot = this.type().parameterSlotDepth(0);
       
   119         initTarget(entryPoint, 0);
       
   120     }
       
   121 
       
   122     /** Make sure the given {@code argument} can be used as {@code argnum}-th
       
   123      *  parameter of the given method handle {@code mh}, which must be a reference.
       
   124      *  <p>
       
   125      *  If this fails, throw a suitable {@code WrongMethodTypeException},
       
   126      *  which will prevent the creation of an illegally typed bound
       
   127      *  method handle.
       
   128      */
       
   129     final static Object checkReferenceArgument(Object argument, MethodHandle mh, int argnum) {
       
   130         Class<?> ptype = mh.type().parameterType(argnum);
       
   131         if (ptype.isPrimitive()) {
       
   132             // fail
       
   133         } else if (argument == null) {
       
   134             return null;
       
   135         } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) {
       
   136             return argument;
       
   137         }
       
   138         throw badBoundArgumentException(argument, mh, argnum);
       
   139     }
       
   140 
       
   141     /** Make sure the given {@code argument} can be used as {@code argnum}-th
       
   142      *  parameter of the given method handle {@code mh}, which must be a primitive.
       
   143      *  <p>
       
   144      *  If this fails, throw a suitable {@code WrongMethodTypeException},
       
   145      *  which will prevent the creation of an illegally typed bound
       
   146      *  method handle.
       
   147      */
       
   148     final static Object bindPrimitiveArgument(Object argument, MethodHandle mh, int argnum) {
       
   149         Class<?> ptype = mh.type().parameterType(argnum);
       
   150         Wrapper  wrap = Wrapper.forPrimitiveType(ptype);
       
   151         Object   zero  = wrap.zero();
       
   152         if (zero == null) {
       
   153             // fail
       
   154         } else if (argument == null) {
       
   155             if (ptype != int.class && wrap.isSubwordOrInt())
       
   156                 return Integer.valueOf(0);
       
   157             else
       
   158                 return zero;
       
   159         } else if (VerifyType.isNullReferenceConversion(argument.getClass(), zero.getClass())) {
       
   160             if (ptype != int.class && wrap.isSubwordOrInt())
       
   161                 return Wrapper.INT.wrap(argument);
       
   162             else
       
   163                 return argument;
       
   164         }
       
   165         throw badBoundArgumentException(argument, mh, argnum);
       
   166     }
       
   167 
       
   168     final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
       
   169         String atype = (argument == null) ? "null" : argument.getClass().toString();
       
   170         return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
       
   171     }
       
   172 
       
   173     @Override
       
   174     public String toString() {
       
   175         return MethodHandleImpl.addTypeString(baseName(), this);
       
   176     }
       
   177 
       
   178     /** Component of toString() before the type string. */
       
   179     protected String baseName() {
       
   180         MethodHandle mh = this;
       
   181         while (mh instanceof BoundMethodHandle) {
       
   182             Object info = MethodHandleNatives.getTargetInfo(mh);
       
   183             if (info instanceof MethodHandle) {
       
   184                 mh = (MethodHandle) info;
       
   185             } else {
       
   186                 String name = null;
       
   187                 if (info instanceof MemberName)
       
   188                     name = ((MemberName)info).getName();
       
   189                 if (name != null)
       
   190                     return name;
       
   191                 else
       
   192                     return noParens(super.toString()); // "invoke", probably
       
   193             }
       
   194             assert(mh != this);
       
   195         }
       
   196         return noParens(mh.toString());
       
   197     }
       
   198 
       
   199     private static String noParens(String str) {
       
   200         int paren = str.indexOf('(');
       
   201         if (paren >= 0) str = str.substring(0, paren);
       
   202         return str;
       
   203     }
       
   204 }