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 }