1 /*
   2  * Copyright (c) 2008, 2009, 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 }