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 sun.invoke.util.VerifyType; 29 import java.security.AccessController; 30 import java.security.PrivilegedAction; 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.List; 36 import sun.invoke.empty.Empty; 37 import sun.invoke.util.ValueConversions; 38 import sun.invoke.util.Wrapper; 39 import sun.misc.Unsafe; 40 import static java.lang.invoke.MethodHandleStatics.*; 41 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; 42 43 /** 44 * Trusted implementation code for MethodHandle. 45 * @author jrose 46 */ 47 /*non-public*/ abstract class MethodHandleImpl { 48 /// Factory methods to create method handles: 49 50 private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE; 51 52 static void initStatics() { 53 // Trigger preceding sequence. 54 } 55 56 /** Look up a given method. 57 * Callable only from sun.invoke and related packages. 58 * <p> 59 * The resulting method handle type will be of the given type, 60 * with a receiver type {@code rcvc} prepended if the member is not static. 61 * <p> 62 * Access checks are made as of the given lookup class. 63 * In particular, if the method is protected and {@code defc} is in a 64 * different package from the lookup class, then {@code rcvc} must be 65 * the lookup class or a subclass. 66 * @param token Proof that the lookup class has access to this package. 67 * @param member Resolved method or constructor to call. 68 * @param name Name of the desired method. 69 * @param rcvc Receiver type of desired non-static method (else null) 70 * @param doDispatch whether the method handle will test the receiver type 71 * @param lookupClass access-check relative to this class 72 * @return a direct handle to the matching method 73 * @throws IllegalAccessException if the given method cannot be accessed by the lookup class 74 */ 75 static 76 MethodHandle findMethod(MemberName method, 77 boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException { 78 MethodType mtype = method.getMethodType(); 79 if (!method.isStatic()) { 80 // adjust the advertised receiver type to be exactly the one requested 81 // (in the case of invokespecial, this will be the calling class) 82 Class<?> recvType = method.getDeclaringClass(); 83 mtype = mtype.insertParameterTypes(0, recvType); 84 } 85 DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass); 86 if (!mh.isValid()) 87 throw method.makeAccessException("no direct method handle", lookupClass); 88 assert(mh.type() == mtype); 89 if (!method.isVarargs()) 90 return mh; 91 int argc = mtype.parameterCount(); 92 if (argc != 0) { 93 Class<?> arrayType = mtype.parameterType(argc-1); 94 if (arrayType.isArray()) 95 return AdapterMethodHandle.makeVarargsCollector(mh, arrayType); 96 } 97 throw method.makeAccessException("cannot make variable arity", null); 98 } 99 100 static 101 MethodHandle makeAllocator(MethodHandle rawConstructor) { 102 MethodType rawConType = rawConstructor.type(); 103 Class<?> allocateClass = rawConType.parameterType(0); 104 // Wrap the raw (unsafe) constructor with the allocation of a suitable object. 105 assert(AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true)); 106 // allocator(arg...) 107 // [fold]=> cookedConstructor(obj=allocate(C), arg...) 108 // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...)) 109 MethodHandle returner = MethodHandles.identity(allocateClass); 110 MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass); 111 MethodHandle cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false); 112 assert(cookedConstructor.type().equals(ctype)); 113 ctype = ctype.dropParameterTypes(0, 1); 114 cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true); 115 MethodHandle allocator = new AllocateObject(allocateClass); 116 // allocate() => new C(void) 117 assert(allocator.type().equals(MethodType.methodType(allocateClass))); 118 ctype = ctype.dropParameterTypes(0, 1); 119 MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator); 120 return fold; 121 } 122 123 static final class AllocateObject<C> extends BoundMethodHandle { 124 private static final Unsafe unsafe = Unsafe.getUnsafe(); 125 126 private final Class<C> allocateClass; 127 128 // for allocation only: 129 private AllocateObject(Class<C> allocateClass) { 130 super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class))); 131 this.allocateClass = allocateClass; 132 } 133 @SuppressWarnings("unchecked") 134 private C allocate() throws InstantiationException { 135 return (C) unsafe.allocateInstance(allocateClass); 136 } 137 static final MethodHandle ALLOCATE; 138 static { 139 try { 140 ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0)); 141 } catch (ReflectiveOperationException ex) { 142 throw uncaughtException(ex); 143 } 144 } 145 } 146 147 static 148 MethodHandle accessField(MemberName member, boolean isSetter, 149 Class<?> lookupClass) { 150 // Use sun. misc.Unsafe to dig up the dirt on the field. 151 MethodHandle mh = new FieldAccessor(member, isSetter); 152 return mh; 153 } 154 155 static 156 MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) { 157 if (!arrayClass.isArray()) 158 throw newIllegalArgumentException("not an array: "+arrayClass); 159 Class<?> elemClass = arrayClass.getComponentType(); 160 MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass); 161 if (mhs == null) { 162 if (!FieldAccessor.doCache(elemClass)) 163 return FieldAccessor.ahandle(arrayClass, isSetter); 164 mhs = new MethodHandle[] { 165 FieldAccessor.ahandle(arrayClass, false), 166 FieldAccessor.ahandle(arrayClass, true) 167 }; 168 if (mhs[0].type().parameterType(0) == Class.class) { 169 mhs[0] = mhs[0].bindTo(elemClass); 170 mhs[1] = mhs[1].bindTo(elemClass); 171 } 172 synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier 173 FieldAccessor.ARRAY_CACHE.put(elemClass, mhs); 174 } 175 return mhs[isSetter ? 1 : 0]; 176 } 177 178 static final class FieldAccessor<C,V> extends BoundMethodHandle { 179 private static final Unsafe unsafe = Unsafe.getUnsafe(); 180 final Object base; // for static refs only 181 final long offset; 182 final String name; 183 184 FieldAccessor(MemberName field, boolean isSetter) { 185 super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic())); 186 this.offset = (long) field.getVMIndex(); 187 this.name = field.getName(); 188 this.base = staticBase(field); 189 } 190 @Override 191 String debugString() { return addTypeString(name, this); } 192 193 int getFieldI(C obj) { return unsafe.getInt(obj, offset); } 194 void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); } 195 long getFieldJ(C obj) { return unsafe.getLong(obj, offset); } 196 void setFieldJ(C obj, long x) { unsafe.putLong(obj, offset, x); } 197 float getFieldF(C obj) { return unsafe.getFloat(obj, offset); } 198 void setFieldF(C obj, float x) { unsafe.putFloat(obj, offset, x); } 199 double getFieldD(C obj) { return unsafe.getDouble(obj, offset); } 200 void setFieldD(C obj, double x) { unsafe.putDouble(obj, offset, x); } 201 boolean getFieldZ(C obj) { return unsafe.getBoolean(obj, offset); } 202 void setFieldZ(C obj, boolean x) { unsafe.putBoolean(obj, offset, x); } 203 byte getFieldB(C obj) { return unsafe.getByte(obj, offset); } 204 void setFieldB(C obj, byte x) { unsafe.putByte(obj, offset, x); } 205 short getFieldS(C obj) { return unsafe.getShort(obj, offset); } 206 void setFieldS(C obj, short x) { unsafe.putShort(obj, offset, x); } 207 char getFieldC(C obj) { return unsafe.getChar(obj, offset); } 208 void setFieldC(C obj, char x) { unsafe.putChar(obj, offset, x); } 209 @SuppressWarnings("unchecked") 210 V getFieldL(C obj) { return (V) unsafe.getObject(obj, offset); } 211 @SuppressWarnings("unchecked") 212 void setFieldL(C obj, V x) { unsafe.putObject(obj, offset, x); } 213 // cast (V) is OK here, since we wrap convertArguments around the MH. 214 215 static Object staticBase(final MemberName field) { 216 if (!field.isStatic()) return null; 217 return AccessController.doPrivileged(new PrivilegedAction<Object>() { 218 public Object run() { 219 try { 220 Class c = field.getDeclaringClass(); 221 // FIXME: Should not have to create 'f' to get this value. 222 java.lang.reflect.Field f = c.getDeclaredField(field.getName()); 223 return unsafe.staticFieldBase(f); 224 } catch (NoSuchFieldException ee) { 225 throw uncaughtException(ee); 226 } 227 } 228 }); 229 } 230 231 int getStaticI() { return unsafe.getInt(base, offset); } 232 void setStaticI(int x) { unsafe.putInt(base, offset, x); } 233 long getStaticJ() { return unsafe.getLong(base, offset); } 234 void setStaticJ(long x) { unsafe.putLong(base, offset, x); } 235 float getStaticF() { return unsafe.getFloat(base, offset); } 236 void setStaticF(float x) { unsafe.putFloat(base, offset, x); } 237 double getStaticD() { return unsafe.getDouble(base, offset); } 238 void setStaticD(double x) { unsafe.putDouble(base, offset, x); } 239 boolean getStaticZ() { return unsafe.getBoolean(base, offset); } 240 void setStaticZ(boolean x) { unsafe.putBoolean(base, offset, x); } 241 byte getStaticB() { return unsafe.getByte(base, offset); } 242 void setStaticB(byte x) { unsafe.putByte(base, offset, x); } 243 short getStaticS() { return unsafe.getShort(base, offset); } 244 void setStaticS(short x) { unsafe.putShort(base, offset, x); } 245 char getStaticC() { return unsafe.getChar(base, offset); } 246 void setStaticC(char x) { unsafe.putChar(base, offset, x); } 247 V getStaticL() { return (V) unsafe.getObject(base, offset); } 248 void setStaticL(V x) { unsafe.putObject(base, offset, x); } 249 250 static String fname(Class<?> vclass, boolean isSetter, boolean isStatic) { 251 String stem; 252 if (!isStatic) 253 stem = (!isSetter ? "getField" : "setField"); 254 else 255 stem = (!isSetter ? "getStatic" : "setStatic"); 256 return stem + Wrapper.basicTypeChar(vclass); 257 } 258 static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) { 259 MethodType type; 260 if (!isStatic) { 261 if (!isSetter) 262 return MethodType.methodType(vclass, cclass); 263 else 264 return MethodType.methodType(void.class, cclass, vclass); 265 } else { 266 if (!isSetter) 267 return MethodType.methodType(vclass); 268 else 269 return MethodType.methodType(void.class, vclass); 270 } 271 } 272 static MethodHandle fhandle(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) { 273 String name = FieldAccessor.fname(vclass, isSetter, isStatic); 274 if (cclass.isPrimitive()) throw newIllegalArgumentException("primitive "+cclass); 275 Class<?> ecclass = Object.class; //erase this type 276 Class<?> evclass = vclass; 277 if (!evclass.isPrimitive()) evclass = Object.class; 278 MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic); 279 MethodHandle mh; 280 try { 281 mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type); 282 } catch (ReflectiveOperationException ex) { 283 throw uncaughtException(ex); 284 } 285 if (evclass != vclass || (!isStatic && ecclass != cclass)) { 286 MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); 287 strongType = strongType.insertParameterTypes(0, FieldAccessor.class); 288 mh = convertArguments(mh, strongType, 0); 289 } 290 return mh; 291 } 292 293 /// Support for array element access 294 static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE = 295 new HashMap<Class<?>, MethodHandle[]>(); 296 // FIXME: Cache on the classes themselves, not here. 297 static boolean doCache(Class<?> elemClass) { 298 if (elemClass.isPrimitive()) return true; 299 ClassLoader cl = elemClass.getClassLoader(); 300 return cl == null || cl == ClassLoader.getSystemClassLoader(); 301 } 302 static int getElementI(int[] a, int i) { return a[i]; } 303 static void setElementI(int[] a, int i, int x) { a[i] = x; } 304 static long getElementJ(long[] a, int i) { return a[i]; } 305 static void setElementJ(long[] a, int i, long x) { a[i] = x; } 306 static float getElementF(float[] a, int i) { return a[i]; } 307 static void setElementF(float[] a, int i, float x) { a[i] = x; } 308 static double getElementD(double[] a, int i) { return a[i]; } 309 static void setElementD(double[] a, int i, double x) { a[i] = x; } 310 static boolean getElementZ(boolean[] a, int i) { return a[i]; } 311 static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; } 312 static byte getElementB(byte[] a, int i) { return a[i]; } 313 static void setElementB(byte[] a, int i, byte x) { a[i] = x; } 314 static short getElementS(short[] a, int i) { return a[i]; } 315 static void setElementS(short[] a, int i, short x) { a[i] = x; } 316 static char getElementC(char[] a, int i) { return a[i]; } 317 static void setElementC(char[] a, int i, char x) { a[i] = x; } 318 static Object getElementL(Object[] a, int i) { return a[i]; } 319 static void setElementL(Object[] a, int i, Object x) { a[i] = x; } 320 static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; } 321 static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; } 322 323 static String aname(Class<?> aclass, boolean isSetter) { 324 Class<?> vclass = aclass.getComponentType(); 325 if (vclass == null) throw new IllegalArgumentException(); 326 return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass); 327 } 328 static MethodType atype(Class<?> aclass, boolean isSetter) { 329 Class<?> vclass = aclass.getComponentType(); 330 if (!isSetter) 331 return MethodType.methodType(vclass, aclass, int.class); 332 else 333 return MethodType.methodType(void.class, aclass, int.class, vclass); 334 } 335 static MethodHandle ahandle(Class<?> aclass, boolean isSetter) { 336 Class<?> vclass = aclass.getComponentType(); 337 String name = FieldAccessor.aname(aclass, isSetter); 338 Class<?> caclass = null; 339 if (!vclass.isPrimitive() && vclass != Object.class) { 340 caclass = aclass; 341 aclass = Object[].class; 342 vclass = Object.class; 343 } 344 MethodType type = FieldAccessor.atype(aclass, isSetter); 345 if (caclass != null) 346 type = type.insertParameterTypes(0, Class.class); 347 MethodHandle mh; 348 try { 349 mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type); 350 } catch (ReflectiveOperationException ex) { 351 throw uncaughtException(ex); 352 } 353 if (caclass != null) { 354 MethodType strongType = FieldAccessor.atype(caclass, isSetter); 355 mh = mh.bindTo(caclass); 356 mh = convertArguments(mh, strongType, 0); 357 } 358 return mh; 359 } 360 } 361 362 /** Bind a predetermined first argument to the given direct method handle. 363 * Callable only from MethodHandles. 364 * @param token Proof that the caller has access to this package. 365 * @param target Any direct method handle. 366 * @param receiver Receiver (or first static method argument) to pre-bind. 367 * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist 368 */ 369 static 370 MethodHandle bindReceiver(MethodHandle target, Object receiver) { 371 if (receiver == null) return null; 372 if (target instanceof AdapterMethodHandle && 373 ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY 374 ) { 375 Object info = MethodHandleNatives.getTargetInfo(target); 376 if (info instanceof DirectMethodHandle) { 377 DirectMethodHandle dmh = (DirectMethodHandle) info; 378 if (dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) { 379 MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0); 380 MethodType newType = target.type().dropParameterTypes(0, 1); 381 return convertArguments(bmh, newType, bmh.type(), 0); 382 } 383 } 384 } 385 if (target instanceof DirectMethodHandle) 386 return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0); 387 return null; // let caller try something else 388 } 389 390 /** Bind a predetermined argument to the given arbitrary method handle. 391 * Callable only from MethodHandles. 392 * @param token Proof that the caller has access to this package. 393 * @param target Any method handle. 394 * @param receiver Argument (which can be a boxed primitive) to pre-bind. 395 * @return a suitable BoundMethodHandle 396 */ 397 static 398 MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) { 399 return new BoundMethodHandle(target, receiver, argnum); 400 } 401 402 static MethodHandle permuteArguments(MethodHandle target, 403 MethodType newType, 404 MethodType oldType, 405 int[] permutationOrNull) { 406 assert(oldType.parameterCount() == target.type().parameterCount()); 407 int outargs = oldType.parameterCount(), inargs = newType.parameterCount(); 408 if (permutationOrNull.length != outargs) 409 throw newIllegalArgumentException("wrong number of arguments in permutation"); 410 // Make the individual outgoing argument types match up first. 411 Class<?>[] callTypeArgs = new Class<?>[outargs]; 412 for (int i = 0; i < outargs; i++) 413 callTypeArgs[i] = newType.parameterType(permutationOrNull[i]); 414 MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs); 415 target = convertArguments(target, callType, oldType, 0); 416 assert(target != null); 417 oldType = target.type(); 418 List<Integer> goal = new ArrayList<Integer>(); // i*TOKEN 419 List<Integer> state = new ArrayList<Integer>(); // i*TOKEN 420 List<Integer> drops = new ArrayList<Integer>(); // not tokens 421 List<Integer> dups = new ArrayList<Integer>(); // not tokens 422 final int TOKEN = 10; // to mark items which are symbolic only 423 // state represents the argument values coming into target 424 for (int i = 0; i < outargs; i++) { 425 state.add(permutationOrNull[i] * TOKEN); 426 } 427 // goal represents the desired state 428 for (int i = 0; i < inargs; i++) { 429 if (state.contains(i * TOKEN)) { 430 goal.add(i * TOKEN); 431 } else { 432 // adapter must initially drop all unused arguments 433 drops.add(i); 434 } 435 } 436 // detect duplications 437 while (state.size() > goal.size()) { 438 for (int i2 = 0; i2 < state.size(); i2++) { 439 int arg1 = state.get(i2); 440 int i1 = state.indexOf(arg1); 441 if (i1 != i2) { 442 // found duplicate occurrence at i2 443 int arg2 = (inargs++) * TOKEN; 444 state.set(i2, arg2); 445 dups.add(goal.indexOf(arg1)); 446 goal.add(arg2); 447 } 448 } 449 } 450 assert(state.size() == goal.size()); 451 int size = goal.size(); 452 while (!state.equals(goal)) { 453 // Look for a maximal sequence of adjacent misplaced arguments, 454 // and try to rotate them into place. 455 int bestRotArg = -10 * TOKEN, bestRotLen = 0; 456 int thisRotArg = -10 * TOKEN, thisRotLen = 0; 457 for (int i = 0; i < size; i++) { 458 int arg = state.get(i); 459 // Does this argument match the current run? 460 if (arg == thisRotArg + TOKEN) { 461 thisRotArg = arg; 462 thisRotLen += 1; 463 if (bestRotLen < thisRotLen) { 464 bestRotLen = thisRotLen; 465 bestRotArg = thisRotArg; 466 } 467 } else { 468 // The old sequence (if any) stops here. 469 thisRotLen = 0; 470 thisRotArg = -10 * TOKEN; 471 // But maybe a new one starts here also. 472 int wantArg = goal.get(i); 473 final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION; 474 if (arg != wantArg && 475 arg >= wantArg - TOKEN * MAX_ARG_ROTATION && 476 arg <= wantArg + TOKEN * MAX_ARG_ROTATION) { 477 thisRotArg = arg; 478 thisRotLen = 1; 479 } 480 } 481 } 482 if (bestRotLen >= 2) { 483 // Do a rotation if it can improve argument positioning 484 // by at least 2 arguments. This is not always optimal, 485 // but it seems to catch common cases. 486 int dstEnd = state.indexOf(bestRotArg); 487 int srcEnd = goal.indexOf(bestRotArg); 488 int rotBy = dstEnd - srcEnd; 489 int dstBeg = dstEnd - (bestRotLen - 1); 490 int srcBeg = srcEnd - (bestRotLen - 1); 491 assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs 492 // Make a span which covers both source and destination. 493 int rotBeg = Math.min(dstBeg, srcBeg); 494 int rotEnd = Math.max(dstEnd, srcEnd); 495 int score = 0; 496 for (int i = rotBeg; i <= rotEnd; i++) { 497 if ((int)state.get(i) != (int)goal.get(i)) 498 score += 1; 499 } 500 List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1); 501 Collections.rotate(rotSpan, -rotBy); // reverse direction 502 for (int i = rotBeg; i <= rotEnd; i++) { 503 if ((int)state.get(i) != (int)goal.get(i)) 504 score -= 1; 505 } 506 if (score >= 2) { 507 // Improved at least two argument positions. Do it. 508 List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray()); 509 Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy); 510 MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes); 511 MethodHandle nextTarget 512 = AdapterMethodHandle.makeRotateArguments(rotType, target, 513 rotBeg, rotSpan.size(), rotBy); 514 if (nextTarget != null) { 515 //System.out.println("Rot: "+rotSpan+" by "+rotBy); 516 target = nextTarget; 517 oldType = rotType; 518 continue; 519 } 520 } 521 // Else de-rotate, and drop through to the swap-fest. 522 Collections.rotate(rotSpan, rotBy); 523 } 524 525 // Now swap like the wind! 526 List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray()); 527 for (int i = 0; i < size; i++) { 528 // What argument do I want here? 529 int arg = goal.get(i); 530 if (arg != state.get(i)) { 531 // Where is it now? 532 int j = state.indexOf(arg); 533 Collections.swap(ptypes, i, j); 534 MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes); 535 target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j); 536 if (target == null) throw newIllegalArgumentException("cannot swap"); 537 assert(target.type() == swapType); 538 oldType = swapType; 539 Collections.swap(state, i, j); 540 } 541 } 542 // One pass of swapping must finish the job. 543 assert(state.equals(goal)); 544 } 545 while (!dups.isEmpty()) { 546 // Grab a contiguous trailing sequence of dups. 547 int grab = dups.size() - 1; 548 int dupArgPos = dups.get(grab), dupArgCount = 1; 549 while (grab - 1 >= 0) { 550 int dup0 = dups.get(grab - 1); 551 if (dup0 != dupArgPos - 1) break; 552 dupArgPos -= 1; 553 dupArgCount += 1; 554 grab -= 1; 555 } 556 //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size())); 557 dups.subList(grab, dups.size()).clear(); 558 // In the new target type drop that many args from the tail: 559 List<Class<?>> ptypes = oldType.parameterList(); 560 ptypes = ptypes.subList(0, ptypes.size() - dupArgCount); 561 MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes); 562 target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount); 563 if (target == null) 564 throw newIllegalArgumentException("cannot dup"); 565 oldType = target.type(); 566 } 567 while (!drops.isEmpty()) { 568 // Grab a contiguous initial sequence of drops. 569 int dropArgPos = drops.get(0), dropArgCount = 1; 570 while (dropArgCount < drops.size()) { 571 int drop1 = drops.get(dropArgCount); 572 if (drop1 != dropArgPos + dropArgCount) break; 573 dropArgCount += 1; 574 } 575 //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount)); 576 drops.subList(0, dropArgCount).clear(); 577 List<Class<?>> dropTypes = newType.parameterList() 578 .subList(dropArgPos, dropArgPos + dropArgCount); 579 MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes); 580 target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount); 581 if (target == null) throw newIllegalArgumentException("cannot drop"); 582 oldType = target.type(); 583 } 584 target = convertArguments(target, newType, oldType, 0); 585 assert(target != null); 586 return target; 587 } 588 589 /*non-public*/ static 590 MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) { 591 MethodType oldType = target.type(); 592 if (oldType.equals(newType)) 593 return target; 594 assert(level > 1 || oldType.isConvertibleTo(newType)); 595 MethodHandle retFilter = null; 596 Class<?> oldRT = oldType.returnType(); 597 Class<?> newRT = newType.returnType(); 598 if (!VerifyType.isNullConversion(oldRT, newRT)) { 599 if (oldRT == void.class) { 600 Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT; 601 retFilter = ValueConversions.zeroConstantFunction(wrap); 602 } else { 603 retFilter = MethodHandles.identity(newRT); 604 retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level); 605 } 606 newType = newType.changeReturnType(oldRT); 607 } 608 MethodHandle res = null; 609 Exception ex = null; 610 try { 611 res = convertArguments(target, newType, oldType, level); 612 } catch (IllegalArgumentException ex1) { 613 ex = ex1; 614 } 615 if (res == null) { 616 WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target); 617 wmt.initCause(ex); 618 throw wmt; 619 } 620 if (retFilter != null) 621 res = MethodHandles.filterReturnValue(res, retFilter); 622 return res; 623 } 624 625 static MethodHandle convertArguments(MethodHandle target, 626 MethodType newType, 627 MethodType oldType, 628 int level) { 629 assert(oldType.parameterCount() == target.type().parameterCount()); 630 if (newType == oldType) 631 return target; 632 if (oldType.parameterCount() != newType.parameterCount()) 633 throw newIllegalArgumentException("mismatched parameter count", oldType, newType); 634 return AdapterMethodHandle.makePairwiseConvert(newType, target, level); 635 } 636 637 static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) { 638 MethodType oldType = target.type(); 639 int nargs = oldType.parameterCount(); 640 int keepPosArgs = nargs - arrayLength; 641 MethodType newType = oldType 642 .dropParameterTypes(keepPosArgs, nargs) 643 .insertParameterTypes(keepPosArgs, arrayType); 644 return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength); 645 } 646 // called internally only 647 static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) { 648 int arrayLength = target.type().parameterCount() - spreadArgPos; 649 return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength); 650 } 651 static MethodHandle spreadArguments(MethodHandle target, 652 MethodType newType, 653 int spreadArgPos, 654 Class<?> arrayType, 655 int arrayLength) { 656 // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[] 657 MethodType oldType = target.type(); 658 // spread the last argument of newType to oldType 659 assert(arrayLength == oldType.parameterCount() - spreadArgPos); 660 assert(newType.parameterType(spreadArgPos) == arrayType); 661 return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); 662 } 663 664 static MethodHandle collectArguments(MethodHandle target, 665 int collectArg, 666 MethodHandle collector) { 667 MethodType type = target.type(); 668 Class<?> collectType = collector.type().returnType(); 669 assert(collectType != void.class); // else use foldArguments 670 if (collectType != type.parameterType(collectArg)) 671 target = target.asType(type.changeParameterType(collectArg, collectType)); 672 MethodType newType = type 673 .dropParameterTypes(collectArg, collectArg+1) 674 .insertParameterTypes(collectArg, collector.type().parameterArray()); 675 return collectArguments(target, newType, collectArg, collector); 676 } 677 static MethodHandle collectArguments(MethodHandle target, 678 MethodType newType, 679 int collectArg, 680 MethodHandle collector) { 681 MethodType oldType = target.type(); // (a...,c)=>r 682 // newType // (a..., b...)=>r 683 MethodType colType = collector.type(); // (b...)=>c 684 // oldType // (a..., b...)=>r 685 assert(newType.parameterCount() == collectArg + colType.parameterCount()); 686 assert(oldType.parameterCount() == collectArg + 1); 687 assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)); 688 return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false); 689 } 690 691 static MethodHandle filterArgument(MethodHandle target, 692 int pos, 693 MethodHandle filter) { 694 MethodType ttype = target.type(); 695 MethodType ftype = filter.type(); 696 assert(ftype.parameterCount() == 1); 697 return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false); 698 } 699 700 static MethodHandle foldArguments(MethodHandle target, 701 MethodType newType, 702 int foldPos, 703 MethodHandle combiner) { 704 MethodType oldType = target.type(); 705 MethodType ctype = combiner.type(); 706 assert(AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)); 707 return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true); 708 } 709 710 static 711 MethodHandle dropArguments(MethodHandle target, 712 MethodType newType, int argnum) { 713 int drops = newType.parameterCount() - target.type().parameterCount(); 714 return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops); 715 } 716 717 static 718 MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) { 719 return testResult ? target : fallback; 720 } 721 722 static MethodHandle SELECT_ALTERNATIVE; 723 static MethodHandle selectAlternative() { 724 if (SELECT_ALTERNATIVE != null) return SELECT_ALTERNATIVE; 725 try { 726 SELECT_ALTERNATIVE 727 = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative", 728 MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)); 729 } catch (ReflectiveOperationException ex) { 730 throw new RuntimeException(ex); 731 } 732 return SELECT_ALTERNATIVE; 733 } 734 735 static 736 MethodHandle makeGuardWithTest(MethodHandle test, 737 MethodHandle target, 738 MethodHandle fallback) { 739 // gwt(arg...) 740 // [fold]=> continueAfterTest(z=test(arg...), arg...) 741 // [filter]=> (tf=select(z))(arg...) 742 // where select(z) = select(z, t, f).bindTo(t, f) => z ? t f 743 // [tailcall]=> tf(arg...) 744 assert(test.type().returnType() == boolean.class); 745 MethodType targetType = target.type(); 746 MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class); 747 assert(AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true)); 748 // working backwards, as usual: 749 assert(target.type().equals(fallback.type())); 750 MethodHandle tailcall = MethodHandles.exactInvoker(target.type()); 751 MethodHandle select = selectAlternative(); 752 select = bindArgument(select, 2, CountingMethodHandle.wrap(fallback)); 753 select = bindArgument(select, 1, CountingMethodHandle.wrap(target)); 754 // select(z: boolean) => (z ? target : fallback) 755 MethodHandle filter = filterArgument(tailcall, 0, select); 756 assert(filter.type().parameterType(0) == boolean.class); 757 MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test); 758 return fold; 759 } 760 761 private static class GuardWithCatch extends BoundMethodHandle { 762 private final MethodHandle target; 763 private final Class<? extends Throwable> exType; 764 private final MethodHandle catcher; 765 GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { 766 this(INVOKES[target.type().parameterCount()], target, exType, catcher); 767 } 768 // FIXME: Build the control flow out of foldArguments. 769 GuardWithCatch(MethodHandle invoker, 770 MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { 771 super(invoker); 772 this.target = target; 773 this.exType = exType; 774 this.catcher = catcher; 775 } 776 @Override 777 String debugString() { 778 return addTypeString(target, this); 779 } 780 private Object invoke_V(Object... av) throws Throwable { 781 try { 782 return target.invokeExact(av); 783 } catch (Throwable t) { 784 if (!exType.isInstance(t)) throw t; 785 return catcher.invokeExact(t, av); 786 } 787 } 788 private Object invoke_L0() throws Throwable { 789 try { 790 return target.invokeExact(); 791 } catch (Throwable t) { 792 if (!exType.isInstance(t)) throw t; 793 return catcher.invokeExact(t); 794 } 795 } 796 private Object invoke_L1(Object a0) throws Throwable { 797 try { 798 return target.invokeExact(a0); 799 } catch (Throwable t) { 800 if (!exType.isInstance(t)) throw t; 801 return catcher.invokeExact(t, a0); 802 } 803 } 804 private Object invoke_L2(Object a0, Object a1) throws Throwable { 805 try { 806 return target.invokeExact(a0, a1); 807 } catch (Throwable t) { 808 if (!exType.isInstance(t)) throw t; 809 return catcher.invokeExact(t, a0, a1); 810 } 811 } 812 private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { 813 try { 814 return target.invokeExact(a0, a1, a2); 815 } catch (Throwable t) { 816 if (!exType.isInstance(t)) throw t; 817 return catcher.invokeExact(t, a0, a1, a2); 818 } 819 } 820 private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { 821 try { 822 return target.invokeExact(a0, a1, a2, a3); 823 } catch (Throwable t) { 824 if (!exType.isInstance(t)) throw t; 825 return catcher.invokeExact(t, a0, a1, a2, a3); 826 } 827 } 828 private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { 829 try { 830 return target.invokeExact(a0, a1, a2, a3, a4); 831 } catch (Throwable t) { 832 if (!exType.isInstance(t)) throw t; 833 return catcher.invokeExact(t, a0, a1, a2, a3, a4); 834 } 835 } 836 private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { 837 try { 838 return target.invokeExact(a0, a1, a2, a3, a4, a5); 839 } catch (Throwable t) { 840 if (!exType.isInstance(t)) throw t; 841 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5); 842 } 843 } 844 private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { 845 try { 846 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); 847 } catch (Throwable t) { 848 if (!exType.isInstance(t)) throw t; 849 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6); 850 } 851 } 852 private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { 853 try { 854 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); 855 } catch (Throwable t) { 856 if (!exType.isInstance(t)) throw t; 857 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); 858 } 859 } 860 static MethodHandle[] makeInvokes() { 861 ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>(); 862 MethodHandles.Lookup lookup = IMPL_LOOKUP; 863 for (;;) { 864 int nargs = invokes.size(); 865 String name = "invoke_L"+nargs; 866 MethodHandle invoke = null; 867 try { 868 invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs)); 869 } catch (ReflectiveOperationException ex) { 870 } 871 if (invoke == null) break; 872 invokes.add(invoke); 873 } 874 assert(invokes.size() == 9); // current number of methods 875 return invokes.toArray(new MethodHandle[0]); 876 }; 877 static final MethodHandle[] INVOKES = makeInvokes(); 878 // For testing use this: 879 //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2); 880 static final MethodHandle VARARGS_INVOKE; 881 static { 882 try { 883 VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true)); 884 } catch (ReflectiveOperationException ex) { 885 throw uncaughtException(ex); 886 } 887 } 888 } 889 890 891 static 892 MethodHandle makeGuardWithCatch(MethodHandle target, 893 Class<? extends Throwable> exType, 894 MethodHandle catcher) { 895 MethodType type = target.type(); 896 MethodType ctype = catcher.type(); 897 int nargs = type.parameterCount(); 898 if (nargs < GuardWithCatch.INVOKES.length) { 899 MethodType gtype = type.generic(); 900 MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); 901 // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0) 902 MethodHandle gtarget = convertArguments(target, gtype, type, 2); 903 MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2); 904 MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher); 905 if (gtarget == null || gcatcher == null || gguard == null) return null; 906 return convertArguments(gguard, type, gtype, 2); 907 } else { 908 MethodType gtype = MethodType.genericMethodType(0, true); 909 MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); 910 MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0); 911 catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class)); 912 MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1); 913 MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher); 914 if (gtarget == null || gcatcher == null || gguard == null) return null; 915 return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type); 916 } 917 } 918 919 static 920 MethodHandle throwException(MethodType type) { 921 return AdapterMethodHandle.makeRetypeRaw(type, throwException()); 922 } 923 924 static MethodHandle THROW_EXCEPTION; 925 static MethodHandle throwException() { 926 if (THROW_EXCEPTION != null) return THROW_EXCEPTION; 927 try { 928 THROW_EXCEPTION 929 = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", 930 MethodType.methodType(Empty.class, Throwable.class)); 931 } catch (ReflectiveOperationException ex) { 932 throw new RuntimeException(ex); 933 } 934 return THROW_EXCEPTION; 935 } 936 static <T extends Throwable> Empty throwException(T t) throws T { throw t; } 937 938 // Linkage support: 939 static void registerBootstrap(Class<?> callerClass, MethodHandle bootstrapMethod) { 940 MethodHandleNatives.registerBootstrap(callerClass, bootstrapMethod); 941 } 942 static MethodHandle getBootstrap(Class<?> callerClass) { 943 return MethodHandleNatives.getBootstrap(callerClass); 944 } 945 }