MethodHandleImpl.java 53.6 KB
Newer Older
1
/*
2
 * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
3 4 5 6
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * 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 12 13 14 15 16 17 18 19 20
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
21 22 23
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
24 25
 */

26
package java.lang.invoke;
27

28
import sun.invoke.util.VerifyType;
29 30 31 32 33
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
34 35 36
import sun.invoke.empty.Empty;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
37
import sun.misc.Unsafe;
38 39
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
40 41

/**
42
 * Trusted implementation code for MethodHandle.
43 44
 * @author jrose
 */
45
/*non-public*/ abstract class MethodHandleImpl {
46 47 48 49
    /// Factory methods to create method handles:

    private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;

50
    static void initStatics() {
51 52 53 54
        // Trigger preceding sequence.
    }

    /** Look up a given method.
55
     * Callable only from sun.invoke and related packages.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
     * <p>
     * The resulting method handle type will be of the given type,
     * with a receiver type {@code rcvc} prepended if the member is not static.
     * <p>
     * Access checks are made as of the given lookup class.
     * In particular, if the method is protected and {@code defc} is in a
     * different package from the lookup class, then {@code rcvc} must be
     * the lookup class or a subclass.
     * @param token Proof that the lookup class has access to this package.
     * @param member Resolved method or constructor to call.
     * @param name Name of the desired method.
     * @param rcvc Receiver type of desired non-static method (else null)
     * @param doDispatch whether the method handle will test the receiver type
     * @param lookupClass access-check relative to this class
     * @return a direct handle to the matching method
71
     * @throws IllegalAccessException if the given method cannot be accessed by the lookup class
72
     */
73 74
    static
    MethodHandle findMethod(MemberName method,
75
                            boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
76
        MethodType mtype = method.getMethodType();
77
        if (!method.isStatic()) {
78 79
            // adjust the advertised receiver type to be exactly the one requested
            // (in the case of invokespecial, this will be the calling class)
80
            Class<?> recvType = method.getDeclaringClass();
81
            mtype = mtype.insertParameterTypes(0, recvType);
82 83 84
        }
        DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
        if (!mh.isValid())
85
            throw method.makeAccessException("no access", lookupClass);
86
        assert(mh.type() == mtype);
87 88 89 90
        if (!method.isVarargs())
            return mh;
        else
            return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1));
91 92
    }

93 94
    static
    MethodHandle makeAllocator(MethodHandle rawConstructor) {
95 96 97
        MethodType rawConType = rawConstructor.type();
        // Wrap the raw (unsafe) constructor with the allocation of a suitable object.
        MethodHandle allocator
98
            = AllocateObject.make(rawConType.parameterType(0), rawConstructor);
99 100 101 102 103
        assert(allocator.type()
               .equals(rawConType.dropParameterTypes(0, 1).changeReturnType(rawConType.parameterType(0))));
        return allocator;
    }

104
    static final class AllocateObject<C> extends BoundMethodHandle {
105 106 107 108 109 110 111
        private static final Unsafe unsafe = Unsafe.getUnsafe();

        private final Class<C> allocateClass;
        private final MethodHandle rawConstructor;

        private AllocateObject(MethodHandle invoker,
                               Class<C> allocateClass, MethodHandle rawConstructor) {
112
            super(invoker);
113 114 115
            this.allocateClass = allocateClass;
            this.rawConstructor = rawConstructor;
        }
116
        static MethodHandle make(Class<?> allocateClass, MethodHandle rawConstructor) {
117 118 119 120 121 122 123
            MethodType rawConType = rawConstructor.type();
            assert(rawConType.parameterType(0) == allocateClass);
            MethodType newType = rawConType.dropParameterTypes(0, 1).changeReturnType(allocateClass);
            int nargs = rawConType.parameterCount() - 1;
            if (nargs < INVOKES.length) {
                MethodHandle invoke = INVOKES[nargs];
                MethodType conType = CON_TYPES[nargs];
124
                MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, null);
125 126 127
                if (gcon == null)  return null;
                MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
                assert(galloc.type() == newType.generic());
128
                return convertArguments(galloc, newType, galloc.type(), null);
129 130 131
            } else {
                MethodHandle invoke = VARARGS_INVOKE;
                MethodType conType = CON_TYPES[nargs];
132
                MethodHandle gcon = spreadArguments(rawConstructor, conType, 1);
133 134
                if (gcon == null)  return null;
                MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
135
                return collectArguments(galloc, newType, 1, null);
136 137 138 139
            }
        }
        @Override
        public String toString() {
140
            return addTypeString(allocateClass.getSimpleName(), this);
141 142 143 144 145 146 147
        }
        @SuppressWarnings("unchecked")
        private C allocate() throws InstantiationException {
            return (C) unsafe.allocateInstance(allocateClass);
        }
        private C invoke_V(Object... av) throws Throwable {
            C obj = allocate();
148
            rawConstructor.invokeExact((Object)obj, av);
149 150 151 152
            return obj;
        }
        private C invoke_L0() throws Throwable {
            C obj = allocate();
153
            rawConstructor.invokeExact((Object)obj);
154 155 156 157
            return obj;
        }
        private C invoke_L1(Object a0) throws Throwable {
            C obj = allocate();
158
            rawConstructor.invokeExact((Object)obj, a0);
159 160 161 162
            return obj;
        }
        private C invoke_L2(Object a0, Object a1) throws Throwable {
            C obj = allocate();
163
            rawConstructor.invokeExact((Object)obj, a0, a1);
164 165 166 167
            return obj;
        }
        private C invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
            C obj = allocate();
168
            rawConstructor.invokeExact((Object)obj, a0, a1, a2);
169 170 171 172
            return obj;
        }
        private C invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
            C obj = allocate();
173
            rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3);
174 175 176 177
            return obj;
        }
        private C invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
            C obj = allocate();
178
            rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4);
179 180 181 182
            return obj;
        }
        private C invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
            C obj = allocate();
183
            rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5);
184 185 186 187
            return obj;
        }
        private C invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
            C obj = allocate();
188
            rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6);
189 190 191 192
            return obj;
        }
        private C invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
            C obj = allocate();
193
            rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6, a7);
194 195 196 197 198 199 200 201 202 203 204
            return obj;
        }
        static MethodHandle[] makeInvokes() {
            ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>();
            MethodHandles.Lookup lookup = IMPL_LOOKUP;
            for (;;) {
                int nargs = invokes.size();
                String name = "invoke_L"+nargs;
                MethodHandle invoke = null;
                try {
                    invoke = lookup.findVirtual(AllocateObject.class, name, MethodType.genericMethodType(nargs));
205
                } catch (ReflectiveOperationException ex) {
206 207 208 209 210 211 212 213 214 215 216 217 218 219
                }
                if (invoke == null)  break;
                invokes.add(invoke);
            }
            assert(invokes.size() == 9);  // current number of methods
            return invokes.toArray(new MethodHandle[0]);
        };
        static final MethodHandle[] INVOKES = makeInvokes();
        // For testing use this:
        //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2);
        static final MethodHandle VARARGS_INVOKE;
        static {
            try {
                VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
220
            } catch (ReflectiveOperationException ex) {
221
                throw uncaughtException(ex);
222 223 224 225 226 227 228 229 230 231 232 233 234
            }
        }
        // Corresponding generic constructor types:
        static final MethodType[] CON_TYPES = new MethodType[INVOKES.length];
        static {
            for (int i = 0; i < INVOKES.length; i++)
                CON_TYPES[i] = makeConType(INVOKES[i]);
        }
        static final MethodType VARARGS_CON_TYPE = makeConType(VARARGS_INVOKE);
        static MethodType makeConType(MethodHandle invoke) {
            MethodType invType = invoke.type();
            return invType.changeParameterType(0, Object.class).changeReturnType(void.class);
        }
235 236
    }

237 238
    static
    MethodHandle accessField(MemberName member, boolean isSetter,
239 240
                             Class<?> lookupClass) {
        // Use sun. misc.Unsafe to dig up the dirt on the field.
241
        MethodHandle mh = new FieldAccessor(member, isSetter);
242
        return mh;
243 244
    }

245 246
    static
    MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) {
247 248
        if (!arrayClass.isArray())
            throw newIllegalArgumentException("not an array: "+arrayClass);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
        Class<?> elemClass = arrayClass.getComponentType();
        MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass);
        if (mhs == null) {
            if (!FieldAccessor.doCache(elemClass))
                return FieldAccessor.ahandle(arrayClass, isSetter);
            mhs = new MethodHandle[] {
                FieldAccessor.ahandle(arrayClass, false),
                FieldAccessor.ahandle(arrayClass, true)
            };
            if (mhs[0].type().parameterType(0) == Class.class) {
                mhs[0] = MethodHandles.insertArguments(mhs[0], 0, elemClass);
                mhs[1] = MethodHandles.insertArguments(mhs[1], 0, elemClass);
            }
            synchronized (FieldAccessor.ARRAY_CACHE) {}  // memory barrier
            FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
        }
        return mhs[isSetter ? 1 : 0];
    }

268
    static final class FieldAccessor<C,V> extends BoundMethodHandle {
269 270 271 272 273
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        final Object base;  // for static refs only
        final long offset;
        final String name;

274 275 276
        FieldAccessor(MemberName field, boolean isSetter) {
            super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
            this.offset = (long) field.getVMIndex();
277 278 279
            this.name = field.getName();
            this.base = staticBase(field);
        }
280
        @Override
281
        public String toString() { return addTypeString(name, this); }
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313

        int getFieldI(C obj) { return unsafe.getInt(obj, offset); }
        void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); }
        long getFieldJ(C obj) { return unsafe.getLong(obj, offset); }
        void setFieldJ(C obj, long x) { unsafe.putLong(obj, offset, x); }
        float getFieldF(C obj) { return unsafe.getFloat(obj, offset); }
        void setFieldF(C obj, float x) { unsafe.putFloat(obj, offset, x); }
        double getFieldD(C obj) { return unsafe.getDouble(obj, offset); }
        void setFieldD(C obj, double x) { unsafe.putDouble(obj, offset, x); }
        boolean getFieldZ(C obj) { return unsafe.getBoolean(obj, offset); }
        void setFieldZ(C obj, boolean x) { unsafe.putBoolean(obj, offset, x); }
        byte getFieldB(C obj) { return unsafe.getByte(obj, offset); }
        void setFieldB(C obj, byte x) { unsafe.putByte(obj, offset, x); }
        short getFieldS(C obj) { return unsafe.getShort(obj, offset); }
        void setFieldS(C obj, short x) { unsafe.putShort(obj, offset, x); }
        char getFieldC(C obj) { return unsafe.getChar(obj, offset); }
        void setFieldC(C obj, char x) { unsafe.putChar(obj, offset, x); }
        @SuppressWarnings("unchecked")
        V getFieldL(C obj) { return (V) unsafe.getObject(obj, offset); }
        @SuppressWarnings("unchecked")
        void setFieldL(C obj, V x) { unsafe.putObject(obj, offset, x); }
        // cast (V) is OK here, since we wrap convertArguments around the MH.

        static Object staticBase(MemberName field) {
            if (!field.isStatic())  return null;
            Class c = field.getDeclaringClass();
            java.lang.reflect.Field f;
            try {
                // FIXME:  Should not have to create 'f' to get this value.
                f = c.getDeclaredField(field.getName());
                return unsafe.staticFieldBase(f);
            } catch (Exception ee) {
314
                throw uncaughtException(ee);
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
            }
        }

        int getStaticI() { return unsafe.getInt(base, offset); }
        void setStaticI(int x) { unsafe.putInt(base, offset, x); }
        long getStaticJ() { return unsafe.getLong(base, offset); }
        void setStaticJ(long x) { unsafe.putLong(base, offset, x); }
        float getStaticF() { return unsafe.getFloat(base, offset); }
        void setStaticF(float x) { unsafe.putFloat(base, offset, x); }
        double getStaticD() { return unsafe.getDouble(base, offset); }
        void setStaticD(double x) { unsafe.putDouble(base, offset, x); }
        boolean getStaticZ() { return unsafe.getBoolean(base, offset); }
        void setStaticZ(boolean x) { unsafe.putBoolean(base, offset, x); }
        byte getStaticB() { return unsafe.getByte(base, offset); }
        void setStaticB(byte x) { unsafe.putByte(base, offset, x); }
        short getStaticS() { return unsafe.getShort(base, offset); }
        void setStaticS(short x) { unsafe.putShort(base, offset, x); }
        char getStaticC() { return unsafe.getChar(base, offset); }
        void setStaticC(char x) { unsafe.putChar(base, offset, x); }
        V getStaticL() { return (V) unsafe.getObject(base, offset); }
        void setStaticL(V x) { unsafe.putObject(base, offset, x); }

        static String fname(Class<?> vclass, boolean isSetter, boolean isStatic) {
            String stem;
            if (!isStatic)
                stem = (!isSetter ? "getField" : "setField");
            else
                stem = (!isSetter ? "getStatic" : "setStatic");
            return stem + Wrapper.basicTypeChar(vclass);
        }
        static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
            MethodType type;
            if (!isStatic) {
                if (!isSetter)
                    return MethodType.methodType(vclass, cclass);
                else
                    return MethodType.methodType(void.class, cclass, vclass);
            } else {
                if (!isSetter)
                    return MethodType.methodType(vclass);
                else
                    return MethodType.methodType(void.class, vclass);
            }
        }
        static MethodHandle fhandle(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
            String name = FieldAccessor.fname(vclass, isSetter, isStatic);
            if (cclass.isPrimitive())  throw newIllegalArgumentException("primitive "+cclass);
            Class<?> ecclass = Object.class;  //erase this type
            Class<?> evclass = vclass;
            if (!evclass.isPrimitive())  evclass = Object.class;
            MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic);
            MethodHandle mh;
            try {
                mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
369
            } catch (ReflectiveOperationException ex) {
370
                throw uncaughtException(ex);
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
            }
            if (evclass != vclass || (!isStatic && ecclass != cclass)) {
                MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
                strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
                mh = MethodHandles.convertArguments(mh, strongType);
            }
            return mh;
        }

        /// Support for array element access
        static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE =
                new HashMap<Class<?>, MethodHandle[]>();
        // FIXME: Cache on the classes themselves, not here.
        static boolean doCache(Class<?> elemClass) {
            if (elemClass.isPrimitive())  return true;
            ClassLoader cl = elemClass.getClassLoader();
            return cl == null || cl == ClassLoader.getSystemClassLoader();
        }
        static int getElementI(int[] a, int i) { return a[i]; }
        static void setElementI(int[] a, int i, int x) { a[i] = x; }
        static long getElementJ(long[] a, int i) { return a[i]; }
        static void setElementJ(long[] a, int i, long x) { a[i] = x; }
        static float getElementF(float[] a, int i) { return a[i]; }
        static void setElementF(float[] a, int i, float x) { a[i] = x; }
        static double getElementD(double[] a, int i) { return a[i]; }
        static void setElementD(double[] a, int i, double x) { a[i] = x; }
        static boolean getElementZ(boolean[] a, int i) { return a[i]; }
        static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; }
        static byte getElementB(byte[] a, int i) { return a[i]; }
        static void setElementB(byte[] a, int i, byte x) { a[i] = x; }
        static short getElementS(short[] a, int i) { return a[i]; }
        static void setElementS(short[] a, int i, short x) { a[i] = x; }
        static char getElementC(char[] a, int i) { return a[i]; }
        static void setElementC(char[] a, int i, char x) { a[i] = x; }
        static Object getElementL(Object[] a, int i) { return a[i]; }
        static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
        static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; }
        static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; }

        static String aname(Class<?> aclass, boolean isSetter) {
            Class<?> vclass = aclass.getComponentType();
            if (vclass == null)  throw new IllegalArgumentException();
            return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass);
        }
        static MethodType atype(Class<?> aclass, boolean isSetter) {
            Class<?> vclass = aclass.getComponentType();
            if (!isSetter)
                return MethodType.methodType(vclass, aclass, int.class);
            else
                return MethodType.methodType(void.class, aclass, int.class, vclass);
        }
        static MethodHandle ahandle(Class<?> aclass, boolean isSetter) {
            Class<?> vclass = aclass.getComponentType();
            String name = FieldAccessor.aname(aclass, isSetter);
            Class<?> caclass = null;
            if (!vclass.isPrimitive() && vclass != Object.class) {
                caclass = aclass;
                aclass = Object[].class;
                vclass = Object.class;
            }
            MethodType type = FieldAccessor.atype(aclass, isSetter);
            if (caclass != null)
                type = type.insertParameterTypes(0, Class.class);
            MethodHandle mh;
            try {
                mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
437
            } catch (ReflectiveOperationException ex) {
438
                throw uncaughtException(ex);
439 440 441 442 443 444 445 446
            }
            if (caclass != null) {
                MethodType strongType = FieldAccessor.atype(caclass, isSetter);
                mh = MethodHandles.insertArguments(mh, 0, caclass);
                mh = MethodHandles.convertArguments(mh, strongType);
            }
            return mh;
        }
447 448 449 450 451 452 453 454 455
    }

    /** Bind a predetermined first argument to the given direct method handle.
     * Callable only from MethodHandles.
     * @param token Proof that the caller has access to this package.
     * @param target Any direct method handle.
     * @param receiver Receiver (or first static method argument) to pre-bind.
     * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
     */
456 457
    static
    MethodHandle bindReceiver(MethodHandle target, Object receiver) {
458 459 460
        if (target instanceof AdapterMethodHandle &&
            ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY
            ) {
461 462 463 464
            Object info = MethodHandleNatives.getTargetInfo(target);
            if (info instanceof DirectMethodHandle) {
                DirectMethodHandle dmh = (DirectMethodHandle) info;
                if (receiver == null ||
465 466 467
                    dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
                    MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
                    MethodType newType = target.type().dropParameterTypes(0, 1);
468
                    return convertArguments(bmh, newType, bmh.type(), null);
469
                }
470 471
            }
        }
472 473 474 475 476 477 478 479 480 481 482 483
        if (target instanceof DirectMethodHandle)
            return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
        return null;   // let caller try something else
    }

    /** Bind a predetermined argument to the given arbitrary method handle.
     * Callable only from MethodHandles.
     * @param token Proof that the caller has access to this package.
     * @param target Any method handle.
     * @param receiver Argument (which can be a boxed primitive) to pre-bind.
     * @return a suitable BoundMethodHandle
     */
484 485
    static
    MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) {
486
        return new BoundMethodHandle(target, receiver, argnum);
487 488
    }

489
    static MethodHandle convertArguments(MethodHandle target,
490 491 492
                                                MethodType newType,
                                                MethodType oldType,
                                                int[] permutationOrNull) {
493
        assert(oldType.parameterCount() == target.type().parameterCount());
494 495 496 497 498 499 500 501 502
        if (permutationOrNull != null) {
            int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
            if (permutationOrNull.length != outargs)
                throw newIllegalArgumentException("wrong number of arguments in permutation");
            // Make the individual outgoing argument types match up first.
            Class<?>[] callTypeArgs = new Class<?>[outargs];
            for (int i = 0; i < outargs; i++)
                callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
            MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
503
            target = convertArguments(target, callType, oldType, null);
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
            assert(target != null);
            oldType = target.type();
            List<Integer> goal = new ArrayList<Integer>();  // i*TOKEN
            List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
            List<Integer> drops = new ArrayList<Integer>(); // not tokens
            List<Integer> dups = new ArrayList<Integer>();  // not tokens
            final int TOKEN = 10; // to mark items which are symbolic only
            // state represents the argument values coming into target
            for (int i = 0; i < outargs; i++) {
                state.add(permutationOrNull[i] * TOKEN);
            }
            // goal represents the desired state
            for (int i = 0; i < inargs; i++) {
                if (state.contains(i * TOKEN)) {
                    goal.add(i * TOKEN);
                } else {
                    // adapter must initially drop all unused arguments
                    drops.add(i);
                }
            }
            // detect duplications
            while (state.size() > goal.size()) {
                for (int i2 = 0; i2 < state.size(); i2++) {
                    int arg1 = state.get(i2);
                    int i1 = state.indexOf(arg1);
                    if (i1 != i2) {
                        // found duplicate occurrence at i2
                        int arg2 = (inargs++) * TOKEN;
                        state.set(i2, arg2);
                        dups.add(goal.indexOf(arg1));
                        goal.add(arg2);
                    }
                }
            }
            assert(state.size() == goal.size());
            int size = goal.size();
            while (!state.equals(goal)) {
                // Look for a maximal sequence of adjacent misplaced arguments,
                // and try to rotate them into place.
                int bestRotArg = -10 * TOKEN, bestRotLen = 0;
                int thisRotArg = -10 * TOKEN, thisRotLen = 0;
                for (int i = 0; i < size; i++) {
                    int arg = state.get(i);
                    // Does this argument match the current run?
                    if (arg == thisRotArg + TOKEN) {
                        thisRotArg = arg;
                        thisRotLen += 1;
                        if (bestRotLen < thisRotLen) {
                            bestRotLen = thisRotLen;
                            bestRotArg = thisRotArg;
                        }
                    } else {
                        // The old sequence (if any) stops here.
                        thisRotLen = 0;
                        thisRotArg = -10 * TOKEN;
                        // But maybe a new one starts here also.
                        int wantArg = goal.get(i);
                        final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
                        if (arg != wantArg &&
                            arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
                            arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
                            thisRotArg = arg;
                            thisRotLen = 1;
                        }
                    }
                }
                if (bestRotLen >= 2) {
                    // Do a rotation if it can improve argument positioning
                    // by at least 2 arguments.  This is not always optimal,
                    // but it seems to catch common cases.
                    int dstEnd = state.indexOf(bestRotArg);
                    int srcEnd = goal.indexOf(bestRotArg);
                    int rotBy = dstEnd - srcEnd;
                    int dstBeg = dstEnd - (bestRotLen - 1);
                    int srcBeg = srcEnd - (bestRotLen - 1);
                    assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
                    // Make a span which covers both source and destination.
                    int rotBeg = Math.min(dstBeg, srcBeg);
                    int rotEnd = Math.max(dstEnd, srcEnd);
                    int score = 0;
                    for (int i = rotBeg; i <= rotEnd; i++) {
                        if ((int)state.get(i) != (int)goal.get(i))
                            score += 1;
                    }
                    List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
                    Collections.rotate(rotSpan, -rotBy);  // reverse direction
                    for (int i = rotBeg; i <= rotEnd; i++) {
                        if ((int)state.get(i) != (int)goal.get(i))
                            score -= 1;
                    }
                    if (score >= 2) {
                        // Improved at least two argument positions.  Do it.
                        List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
                        Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
                        MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
                        MethodHandle nextTarget
600
                                = AdapterMethodHandle.makeRotateArguments(rotType, target,
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
                                        rotBeg, rotSpan.size(), rotBy);
                        if (nextTarget != null) {
                            //System.out.println("Rot: "+rotSpan+" by "+rotBy);
                            target = nextTarget;
                            oldType = rotType;
                            continue;
                        }
                    }
                    // Else de-rotate, and drop through to the swap-fest.
                    Collections.rotate(rotSpan, rotBy);
                }

                // Now swap like the wind!
                List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
                for (int i = 0; i < size; i++) {
                    // What argument do I want here?
                    int arg = goal.get(i);
                    if (arg != state.get(i)) {
                        // Where is it now?
                        int j = state.indexOf(arg);
                        Collections.swap(ptypes, i, j);
                        MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
623
                        target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
                        if (target == null)  throw newIllegalArgumentException("cannot swap");
                        assert(target.type() == swapType);
                        oldType = swapType;
                        Collections.swap(state, i, j);
                    }
                }
                // One pass of swapping must finish the job.
                assert(state.equals(goal));
            }
            while (!dups.isEmpty()) {
                // Grab a contiguous trailing sequence of dups.
                int grab = dups.size() - 1;
                int dupArgPos = dups.get(grab), dupArgCount = 1;
                while (grab - 1 >= 0) {
                    int dup0 = dups.get(grab - 1);
                    if (dup0 != dupArgPos - 1)  break;
                    dupArgPos -= 1;
                    dupArgCount += 1;
                    grab -= 1;
                }
                //if (dupArgCount > 1)  System.out.println("Dup: "+dups.subList(grab, dups.size()));
                dups.subList(grab, dups.size()).clear();
                // In the new target type drop that many args from the tail:
                List<Class<?>> ptypes = oldType.parameterList();
                ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
                MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
650
                target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
                if (target == null)
                    throw newIllegalArgumentException("cannot dup");
                oldType = target.type();
            }
            while (!drops.isEmpty()) {
                // Grab a contiguous initial sequence of drops.
                int dropArgPos = drops.get(0), dropArgCount = 1;
                while (dropArgCount < drops.size()) {
                    int drop1 = drops.get(dropArgCount);
                    if (drop1 != dropArgPos + dropArgCount)  break;
                    dropArgCount += 1;
                }
                //if (dropArgCount > 1)  System.out.println("Drop: "+drops.subList(0, dropArgCount));
                drops.subList(0, dropArgCount).clear();
                List<Class<?>> dropTypes = newType.parameterList()
                        .subList(dropArgPos, dropArgPos + dropArgCount);
                MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
668
                target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
669 670 671 672 673 674 675 676
                if (target == null)  throw newIllegalArgumentException("cannot drop");
                oldType = target.type();
            }
        }
        if (newType == oldType)
            return target;
        if (oldType.parameterCount() != newType.parameterCount())
            throw newIllegalArgumentException("mismatched parameter count");
677
        MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target);
678 679 680 681 682 683 684 685
        if (res != null)
            return res;
        int argc = oldType.parameterCount();
        // The JVM can't do it directly, so fill in the gap with a Java adapter.
        // TO DO: figure out what to put here from case-by-case experience
        // Use a heavier method:  Convert all the arguments to Object,
        // then back to the desired types.  We might have to use Java-based
        // method handles to do this.
686
        MethodType objType = MethodType.genericMethodType(argc);
687
        MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target);
688 689
        if (objTarget == null)
            objTarget = FromGeneric.make(target);
690
        res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget);
691 692 693 694 695
        if (res != null)
            return res;
        return ToGeneric.make(newType, objTarget);
    }

696
    static MethodHandle spreadArguments(MethodHandle target,
697 698 699 700 701 702 703
                                               MethodType newType,
                                               int spreadArg) {
        // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
        MethodType oldType = target.type();
        // spread the last argument of newType to oldType
        int spreadCount = oldType.parameterCount() - spreadArg;
        Class<Object[]> spreadArgType = Object[].class;
704
        MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, spreadArgType, spreadArg, spreadCount);
705 706 707 708 709 710 711 712 713 714
        if (res != null)
            return res;
        // try an intermediate adapter
        Class<?> spreadType = null;
        if (spreadArg < 0 || spreadArg >= newType.parameterCount()
            || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg)))
            throw newIllegalArgumentException("no restarg in "+newType);
        Class<?>[] ptypes = oldType.parameterArray();
        for (int i = 0; i < spreadCount; i++)
            ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i);
715
        MethodType midType = MethodType.methodType(newType.returnType(), ptypes);
716
        // after spreading, some arguments may need further conversion
717
        MethodHandle target2 = convertArguments(target, midType, oldType, null);
718
        if (target2 == null)
719
            throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType);
720
        res = AdapterMethodHandle.makeSpreadArguments(newType, target2, spreadArgType, spreadArg, spreadCount);
721 722 723 724
        if (res != null)
            return res;
        res = SpreadGeneric.make(target2, spreadCount);
        if (res != null)
725
            res = convertArguments(res, newType, res.type(), null);
726 727 728
        return res;
    }

729
    static MethodHandle collectArguments(MethodHandle target,
730
                                                MethodType newType,
731 732 733 734 735 736 737 738 739 740 741 742
                                                int collectArg,
                                                MethodHandle collector) {
        MethodType oldType = target.type();     // (a...,c)=>r
        if (collector == null) {
            int numCollect = newType.parameterCount() - oldType.parameterCount() + 1;
            collector = ValueConversions.varargsArray(numCollect);
        }
        //         newType                      // (a..., b...)=>r
        MethodType colType = collector.type();  // (b...)=>c
        //         oldType                      // (a..., b...)=>r
        assert(newType.parameterCount() == collectArg + colType.parameterCount());
        assert(oldType.parameterCount() == collectArg + 1);
743 744
        MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
        MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, null);
745 746
        if (gtarget == null || gcollector == null)  return null;
        MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget);
747
        MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
748
        return result;
749
    }
750

751
    static MethodHandle filterArgument(MethodHandle target,
752 753 754 755
                                              int pos,
                                              MethodHandle filter) {
        MethodType ttype = target.type(), gttype = ttype.generic();
        if (ttype != gttype) {
756
            target = convertArguments(target, gttype, ttype, null);
757 758 759 760 761 762
            ttype = gttype;
        }
        MethodType ftype = filter.type(), gftype = ftype.generic();
        if (ftype.parameterCount() != 1)
            throw new InternalError();
        if (ftype != gftype) {
763
            filter = convertArguments(filter, gftype, ftype, null);
764 765 766 767 768 769 770 771 772
            ftype = gftype;
        }
        if (ftype == ttype) {
            // simple unary case
            return FilterOneArgument.make(filter, target);
        }
        return FilterGeneric.makeArgumentFilter(pos, filter, target);
    }

773
    static MethodHandle foldArguments(MethodHandle target,
774 775 776 777
                                             MethodType newType,
                                             MethodHandle combiner) {
        MethodType oldType = target.type();
        MethodType ctype = combiner.type();
778 779
        MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
        MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, null);
780 781
        if (gtarget == null || gcombiner == null)  return null;
        MethodHandle gresult = FilterGeneric.makeArgumentFolder(gcombiner, gtarget);
782
        MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
783 784 785
        return result;
    }

786 787
    static
    MethodHandle dropArguments(MethodHandle target,
788
                               MethodType newType, int argnum) {
789
        int drops = newType.parameterCount() - target.type().parameterCount();
790
        MethodHandle res = AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops);
791 792
        if (res != null)
            return res;
793 794 795
        throw new UnsupportedOperationException("NYI");
    }

796
    private static class GuardWithTest extends BoundMethodHandle {
797
        private final MethodHandle test, target, fallback;
798 799
        private GuardWithTest(MethodHandle invoker,
                              MethodHandle test, MethodHandle target, MethodHandle fallback) {
800
            super(invoker);
801 802 803 804
            this.test = test;
            this.target = target;
            this.fallback = fallback;
        }
805
        static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) {
806 807 808 809 810 811
            MethodType type = target.type();
            int nargs = type.parameterCount();
            if (nargs < INVOKES.length) {
                MethodHandle invoke = INVOKES[nargs];
                MethodType gtype = type.generic();
                assert(invoke.type().dropParameterTypes(0,1) == gtype);
812 813 814
                MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), null);
                MethodHandle gtarget = convertArguments(target, gtype, type, null);
                MethodHandle gfallback = convertArguments(fallback, gtype, type, null);
815 816
                if (gtest == null || gtarget == null || gfallback == null)  return null;
                MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback);
817
                return convertArguments(gguard, type, gtype, null);
818 819 820 821
            } else {
                MethodHandle invoke = VARARGS_INVOKE;
                MethodType gtype = MethodType.genericMethodType(1);
                assert(invoke.type().dropParameterTypes(0,1) == gtype);
822 823 824
                MethodHandle gtest = spreadArguments(test, gtype.changeReturnType(boolean.class), 0);
                MethodHandle gtarget = spreadArguments(target, gtype, 0);
                MethodHandle gfallback = spreadArguments(fallback, gtype, 0);
825 826
                MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback);
                if (gtest == null || gtarget == null || gfallback == null)  return null;
827
                return collectArguments(gguard, type, 0, null);
828 829
            }
        }
830 831
        @Override
        public String toString() {
832
            return addTypeString(target, this);
833 834
        }
        private Object invoke_V(Object... av) throws Throwable {
835 836 837
            if ((boolean) test.invokeExact(av))
                return target.invokeExact(av);
            return fallback.invokeExact(av);
838 839
        }
        private Object invoke_L0() throws Throwable {
840 841 842
            if ((boolean) test.invokeExact())
                return target.invokeExact();
            return fallback.invokeExact();
843 844
        }
        private Object invoke_L1(Object a0) throws Throwable {
845 846 847
            if ((boolean) test.invokeExact(a0))
                return target.invokeExact(a0);
            return fallback.invokeExact(a0);
848 849
        }
        private Object invoke_L2(Object a0, Object a1) throws Throwable {
850 851 852
            if ((boolean) test.invokeExact(a0, a1))
                return target.invokeExact(a0, a1);
            return fallback.invokeExact(a0, a1);
853 854
        }
        private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
855 856 857
            if ((boolean) test.invokeExact(a0, a1, a2))
                return target.invokeExact(a0, a1, a2);
            return fallback.invokeExact(a0, a1, a2);
858 859
        }
        private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
860 861 862
            if ((boolean) test.invokeExact(a0, a1, a2, a3))
                return target.invokeExact(a0, a1, a2, a3);
            return fallback.invokeExact(a0, a1, a2, a3);
863 864
        }
        private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
865 866 867
            if ((boolean) test.invokeExact(a0, a1, a2, a3, a4))
                return target.invokeExact(a0, a1, a2, a3, a4);
            return fallback.invokeExact(a0, a1, a2, a3, a4);
868 869
        }
        private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
870 871 872
            if ((boolean) test.invokeExact(a0, a1, a2, a3, a4, a5))
                return target.invokeExact(a0, a1, a2, a3, a4, a5);
            return fallback.invokeExact(a0, a1, a2, a3, a4, a5);
873 874
        }
        private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
875 876 877
            if ((boolean) test.invokeExact(a0, a1, a2, a3, a4, a5, a6))
                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
            return fallback.invokeExact(a0, a1, a2, a3, a4, a5, a6);
878 879
        }
        private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
880 881 882
            if ((boolean) test.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7))
                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
            return fallback.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
883 884 885 886 887 888 889 890 891 892
        }
        static MethodHandle[] makeInvokes() {
            ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>();
            MethodHandles.Lookup lookup = IMPL_LOOKUP;
            for (;;) {
                int nargs = invokes.size();
                String name = "invoke_L"+nargs;
                MethodHandle invoke = null;
                try {
                    invoke = lookup.findVirtual(GuardWithTest.class, name, MethodType.genericMethodType(nargs));
893
                } catch (ReflectiveOperationException ex) {
894 895 896 897 898 899 900 901 902 903 904 905 906 907
                }
                if (invoke == null)  break;
                invokes.add(invoke);
            }
            assert(invokes.size() == 9);  // current number of methods
            return invokes.toArray(new MethodHandle[0]);
        };
        static final MethodHandle[] INVOKES = makeInvokes();
        // For testing use this:
        //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2);
        static final MethodHandle VARARGS_INVOKE;
        static {
            try {
                VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true));
908
            } catch (ReflectiveOperationException ex) {
909
                throw uncaughtException(ex);
910 911 912 913
            }
        }
    }

914 915
    static
    MethodHandle makeGuardWithTest(MethodHandle test,
916 917
                                   MethodHandle target,
                                   MethodHandle fallback) {
918
        return GuardWithTest.make(test, target, fallback);
919 920
    }

921
    private static class GuardWithCatch extends BoundMethodHandle {
922 923 924
        private final MethodHandle target;
        private final Class<? extends Throwable> exType;
        private final MethodHandle catcher;
925
        GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
926 927
            this(INVOKES[target.type().parameterCount()], target, exType, catcher);
        }
928 929 930
       GuardWithCatch(MethodHandle invoker,
                      MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
            super(invoker);
931 932 933 934 935 936
            this.target = target;
            this.exType = exType;
            this.catcher = catcher;
        }
        @Override
        public String toString() {
937
            return addTypeString(target, this);
938 939 940
        }
        private Object invoke_V(Object... av) throws Throwable {
            try {
941
                return target.invokeExact(av);
942 943
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
944
                return catcher.invokeExact(t, av);
945
            }
946 947 948
        }
        private Object invoke_L0() throws Throwable {
            try {
949
                return target.invokeExact();
950 951
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
952
                return catcher.invokeExact(t);
953 954 955 956
            }
        }
        private Object invoke_L1(Object a0) throws Throwable {
            try {
957
                return target.invokeExact(a0);
958 959
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
960
                return catcher.invokeExact(t, a0);
961 962
            }
        }
963 964
        private Object invoke_L2(Object a0, Object a1) throws Throwable {
            try {
965
                return target.invokeExact(a0, a1);
966 967
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
968
                return catcher.invokeExact(t, a0, a1);
969 970 971 972
            }
        }
        private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
            try {
973
                return target.invokeExact(a0, a1, a2);
974 975
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
976
                return catcher.invokeExact(t, a0, a1, a2);
977 978 979 980
            }
        }
        private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
            try {
981
                return target.invokeExact(a0, a1, a2, a3);
982 983
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
984
                return catcher.invokeExact(t, a0, a1, a2, a3);
985 986 987 988
            }
        }
        private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
            try {
989
                return target.invokeExact(a0, a1, a2, a3, a4);
990 991
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
992
                return catcher.invokeExact(t, a0, a1, a2, a3, a4);
993 994 995 996
            }
        }
        private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
            try {
997
                return target.invokeExact(a0, a1, a2, a3, a4, a5);
998 999
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
1000
                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
1001 1002 1003 1004
            }
        }
        private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
            try {
1005
                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
1006 1007
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
1008
                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
1009 1010 1011 1012
            }
        }
        private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
            try {
1013
                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
1014 1015
            } catch (Throwable t) {
                if (!exType.isInstance(t))  throw t;
1016
                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7);
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
            }
        }
        static MethodHandle[] makeInvokes() {
            ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>();
            MethodHandles.Lookup lookup = IMPL_LOOKUP;
            for (;;) {
                int nargs = invokes.size();
                String name = "invoke_L"+nargs;
                MethodHandle invoke = null;
                try {
                    invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs));
1028
                } catch (ReflectiveOperationException ex) {
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
                }
                if (invoke == null)  break;
                invokes.add(invoke);
            }
            assert(invokes.size() == 9);  // current number of methods
            return invokes.toArray(new MethodHandle[0]);
        };
        static final MethodHandle[] INVOKES = makeInvokes();
        // For testing use this:
        //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2);
        static final MethodHandle VARARGS_INVOKE;
        static {
            try {
                VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
1043
            } catch (ReflectiveOperationException ex) {
1044
                throw uncaughtException(ex);
1045 1046 1047 1048 1049
            }
        }
    }


1050 1051
    static
    MethodHandle makeGuardWithCatch(MethodHandle target,
1052 1053 1054 1055 1056 1057 1058 1059
                                    Class<? extends Throwable> exType,
                                    MethodHandle catcher) {
        MethodType type = target.type();
        MethodType ctype = catcher.type();
        int nargs = type.parameterCount();
        if (nargs < GuardWithCatch.INVOKES.length) {
            MethodType gtype = type.generic();
            MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
1060 1061
            MethodHandle gtarget = convertArguments(target, gtype, type, null);
            MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, null);
1062 1063
            MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
            if (gtarget == null || gcatcher == null || gguard == null)  return null;
1064
            return convertArguments(gguard, type, gtype, null);
1065 1066 1067
        } else {
            MethodType gtype = MethodType.genericMethodType(0, true);
            MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
1068 1069
            MethodHandle gtarget = spreadArguments(target, gtype, 0);
            MethodHandle gcatcher = spreadArguments(catcher, gcatchType, 1);
1070 1071
            MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher);
            if (gtarget == null || gcatcher == null || gguard == null)  return null;
1072
            return collectArguments(gguard, type, 0, null);
1073
        }
1074 1075
    }

1076 1077 1078
    static
    MethodHandle throwException(MethodType type) {
        return AdapterMethodHandle.makeRetypeRaw(type, throwException());
1079 1080
    }

1081 1082 1083
    static MethodHandle THROW_EXCEPTION;
    static MethodHandle throwException() {
        if (THROW_EXCEPTION != null)  return THROW_EXCEPTION;
1084 1085
        try {
            THROW_EXCEPTION
1086 1087
            = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
                    MethodType.methodType(Empty.class, Throwable.class));
1088
        } catch (ReflectiveOperationException ex) {
1089 1090
            throw new RuntimeException(ex);
        }
1091
        return THROW_EXCEPTION;
1092
    }
1093 1094
    static <T extends Throwable> Empty throwException(T t) throws T { throw t; }

1095
    // Linkage support:
1096
    static void registerBootstrap(Class<?> callerClass, MethodHandle bootstrapMethod) {
1097 1098
        MethodHandleNatives.registerBootstrap(callerClass, bootstrapMethod);
    }
1099
    static MethodHandle getBootstrap(Class<?> callerClass) {
1100 1101
        return MethodHandleNatives.getBootstrap(callerClass);
    }
1102

1103 1104
    static MethodHandle asVarargsCollector(MethodHandle target, Class<?> arrayType) {
        return AdapterMethodHandle.makeVarargsCollector(target, arrayType);
1105
    }
1106
}