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 }