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