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 }