~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/ikvm/openjdk/java/lang/invoke/MethodHandleImpl.java

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
/*
 
27
 * Extensively modified for IKVM.NET by Jeroen Frijters
 
28
 * Copyright (C) 2011 Jeroen Frijters
 
29
 */
 
30
 
 
31
package java.lang.invoke;
 
32
 
 
33
import sun.invoke.util.VerifyType;
 
34
import java.security.AccessController;
 
35
import java.security.PrivilegedAction;
 
36
import java.util.ArrayList;
 
37
import java.util.Arrays;
 
38
import java.util.Collections;
 
39
import java.util.HashMap;
 
40
import java.util.List;
 
41
import sun.invoke.empty.Empty;
 
42
import sun.invoke.util.ValueConversions;
 
43
import sun.invoke.util.Wrapper;
 
44
import sun.misc.Unsafe;
 
45
import static java.lang.invoke.MethodHandleStatics.*;
 
46
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 
47
 
 
48
/**
 
49
 * Trusted implementation code for MethodHandle.
 
50
 * @author jrose
 
51
 */
 
52
/*non-public*/ abstract class MethodHandleImpl {
 
53
    /// Factory methods to create method handles:
 
54
 
 
55
    private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
 
56
 
 
57
    static void initStatics() {
 
58
        // Trigger preceding sequence.
 
59
    }
 
60
 
 
61
    /** Look up a given method.
 
62
     * Callable only from sun.invoke and related packages.
 
63
     * <p>
 
64
     * The resulting method handle type will be of the given type,
 
65
     * with a receiver type {@code rcvc} prepended if the member is not static.
 
66
     * <p>
 
67
     * Access checks are made as of the given lookup class.
 
68
     * In particular, if the method is protected and {@code defc} is in a
 
69
     * different package from the lookup class, then {@code rcvc} must be
 
70
     * the lookup class or a subclass.
 
71
     * @param token Proof that the lookup class has access to this package.
 
72
     * @param member Resolved method or constructor to call.
 
73
     * @param name Name of the desired method.
 
74
     * @param rcvc Receiver type of desired non-static method (else null)
 
75
     * @param doDispatch whether the method handle will test the receiver type
 
76
     * @param lookupClass access-check relative to this class
 
77
     * @return a direct handle to the matching method
 
78
     * @throws IllegalAccessException if the given method cannot be accessed by the lookup class
 
79
     */
 
80
    static
 
81
    MethodHandle findMethod(MemberName method,
 
82
                            boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
 
83
        MethodType mtype = method.getMethodType();
 
84
        if (!method.isStatic()) {
 
85
            // adjust the advertised receiver type to be exactly the one requested
 
86
            // (in the case of invokespecial, this will be the calling class)
 
87
            Class<?> recvType = method.getDeclaringClass();
 
88
            mtype = mtype.insertParameterTypes(0, recvType);
 
89
        }
 
90
        DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
 
91
        if (!mh.isValid())
 
92
            throw method.makeAccessException("no direct method handle", lookupClass);
 
93
        assert(mh.type() == mtype);
 
94
        if (!method.isVarargs())
 
95
            return mh;
 
96
        int argc = mtype.parameterCount();
 
97
        if (argc != 0) {
 
98
            Class<?> arrayType = mtype.parameterType(argc-1);
 
99
            if (arrayType.isArray())
 
100
                return AdapterMethodHandle.makeVarargsCollector(mh, arrayType);
 
101
        }
 
102
        throw method.makeAccessException("cannot make variable arity", null);
 
103
    }
 
104
 
 
105
    static
 
106
    MethodHandle makeAllocator(MethodHandle rawConstructor) {
 
107
        MethodType rawConType = rawConstructor.type();
 
108
        Class<?> allocateClass = rawConType.parameterType(0);
 
109
        // Wrap the raw (unsafe) constructor with the allocation of a suitable object.
 
110
        // allocator(arg...)
 
111
        // [fold]=> cookedConstructor(obj=allocate(C), arg...)
 
112
        // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...))
 
113
        MethodHandle returner = MethodHandles.identity(allocateClass);
 
114
        MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass);
 
115
        MethodHandle  cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false);
 
116
        assert(cookedConstructor.type().equals(ctype));
 
117
        ctype = ctype.dropParameterTypes(0, 1);
 
118
        cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true);
 
119
        MethodHandle allocator = new AllocateObject(allocateClass);
 
120
        // allocate() => new C(void)
 
121
        assert(allocator.type().equals(MethodType.methodType(allocateClass)));
 
122
        ctype = ctype.dropParameterTypes(0, 1);
 
123
        MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator);
 
124
        return fold;
 
125
    }
 
126
 
 
127
    static final class AllocateObject<C> extends BoundMethodHandle {
 
128
        private static final Unsafe unsafe = Unsafe.getUnsafe();
 
129
 
 
130
        private final Class<C> allocateClass;
 
131
 
 
132
        // for allocation only:
 
133
        private AllocateObject(Class<C> allocateClass) {
 
134
            super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class)));
 
135
            this.allocateClass = allocateClass;
 
136
        }
 
137
        @SuppressWarnings("unchecked")
 
138
        private C allocate() throws InstantiationException {
 
139
            return (C) unsafe.allocateInstance(allocateClass);
 
140
        }
 
141
        static final MethodHandle ALLOCATE;
 
142
        static {
 
143
            try {
 
144
                ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0));
 
145
            } catch (ReflectiveOperationException ex) {
 
146
                throw uncaughtException(ex);
 
147
            }
 
148
        }
 
149
    }
 
150
 
 
151
    static
 
152
    MethodHandle accessField(MemberName member, boolean isSetter,
 
153
                             Class<?> lookupClass) {
 
154
        // Use sun. misc.Unsafe to dig up the dirt on the field.
 
155
        MethodHandle mh = new FieldAccessor(member, isSetter);
 
156
        return mh;
 
157
    }
 
158
 
 
159
    static
 
160
    MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) {
 
161
        if (!arrayClass.isArray())
 
162
            throw newIllegalArgumentException("not an array: "+arrayClass);
 
163
        Class<?> elemClass = arrayClass.getComponentType();
 
164
        MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass);
 
165
        if (mhs == null) {
 
166
            if (!FieldAccessor.doCache(elemClass))
 
167
                return FieldAccessor.ahandle(arrayClass, isSetter);
 
168
            mhs = new MethodHandle[] {
 
169
                FieldAccessor.ahandle(arrayClass, false),
 
170
                FieldAccessor.ahandle(arrayClass, true)
 
171
            };
 
172
            if (mhs[0].type().parameterType(0) == Class.class) {
 
173
                mhs[0] = mhs[0].bindTo(elemClass);
 
174
                mhs[1] = mhs[1].bindTo(elemClass);
 
175
            }
 
176
            synchronized (FieldAccessor.ARRAY_CACHE) {}  // memory barrier
 
177
            FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
 
178
        }
 
179
        return mhs[isSetter ? 1 : 0];
 
180
    }
 
181
 
 
182
    static final class FieldAccessor<C,V> extends BoundMethodHandle {
 
183
        private static final Unsafe unsafe = Unsafe.getUnsafe();
 
184
        final Object base;  // for static refs only
 
185
        final long offset;
 
186
        final String name;
 
187
 
 
188
        FieldAccessor(MemberName field, boolean isSetter) {
 
189
            super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
 
190
            this.offset = fieldOffset(field);
 
191
            this.name = field.getName();
 
192
            this.base = null;
 
193
        }
 
194
        @Override
 
195
        String debugString() { return addTypeString(name, this); }
 
196
 
 
197
        int getFieldI(C obj) { return unsafe.getInt(obj, offset); }
 
198
        void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); }
 
199
        long getFieldJ(C obj) { return unsafe.getLong(obj, offset); }
 
200
        void setFieldJ(C obj, long x) { unsafe.putLong(obj, offset, x); }
 
201
        float getFieldF(C obj) { return unsafe.getFloat(obj, offset); }
 
202
        void setFieldF(C obj, float x) { unsafe.putFloat(obj, offset, x); }
 
203
        double getFieldD(C obj) { return unsafe.getDouble(obj, offset); }
 
204
        void setFieldD(C obj, double x) { unsafe.putDouble(obj, offset, x); }
 
205
        boolean getFieldZ(C obj) { return unsafe.getBoolean(obj, offset); }
 
206
        void setFieldZ(C obj, boolean x) { unsafe.putBoolean(obj, offset, x); }
 
207
        byte getFieldB(C obj) { return unsafe.getByte(obj, offset); }
 
208
        void setFieldB(C obj, byte x) { unsafe.putByte(obj, offset, x); }
 
209
        short getFieldS(C obj) { return unsafe.getShort(obj, offset); }
 
210
        void setFieldS(C obj, short x) { unsafe.putShort(obj, offset, x); }
 
211
        char getFieldC(C obj) { return unsafe.getChar(obj, offset); }
 
212
        void setFieldC(C obj, char x) { unsafe.putChar(obj, offset, x); }
 
213
        @SuppressWarnings("unchecked")
 
214
        V getFieldL(C obj) { return (V) unsafe.getObject(obj, offset); }
 
215
        @SuppressWarnings("unchecked")
 
216
        void setFieldL(C obj, V x) { unsafe.putObject(obj, offset, x); }
 
217
        // cast (V) is OK here, since we wrap convertArguments around the MH.
 
218
 
 
219
        static Integer fieldOffset(final MemberName field) {
 
220
            return AccessController.doPrivileged(new PrivilegedAction<Integer>() {
 
221
                    public Integer run() {
 
222
                        try {
 
223
                            Class c = field.getDeclaringClass();
 
224
                            // FIXME:  Should not have to create 'f' to get this value.
 
225
                            java.lang.reflect.Field f = c.getDeclaredField(field.getName());
 
226
                            return unsafe.fieldOffset(f);
 
227
                        } catch (NoSuchFieldException ee) {
 
228
                            throw uncaughtException(ee);
 
229
                        }
 
230
                    }
 
231
                });
 
232
        }
 
233
 
 
234
        int getStaticI() { return unsafe.getInt(base, offset); }
 
235
        void setStaticI(int x) { unsafe.putInt(base, offset, x); }
 
236
        long getStaticJ() { return unsafe.getLong(base, offset); }
 
237
        void setStaticJ(long x) { unsafe.putLong(base, offset, x); }
 
238
        float getStaticF() { return unsafe.getFloat(base, offset); }
 
239
        void setStaticF(float x) { unsafe.putFloat(base, offset, x); }
 
240
        double getStaticD() { return unsafe.getDouble(base, offset); }
 
241
        void setStaticD(double x) { unsafe.putDouble(base, offset, x); }
 
242
        boolean getStaticZ() { return unsafe.getBoolean(base, offset); }
 
243
        void setStaticZ(boolean x) { unsafe.putBoolean(base, offset, x); }
 
244
        byte getStaticB() { return unsafe.getByte(base, offset); }
 
245
        void setStaticB(byte x) { unsafe.putByte(base, offset, x); }
 
246
        short getStaticS() { return unsafe.getShort(base, offset); }
 
247
        void setStaticS(short x) { unsafe.putShort(base, offset, x); }
 
248
        char getStaticC() { return unsafe.getChar(base, offset); }
 
249
        void setStaticC(char x) { unsafe.putChar(base, offset, x); }
 
250
        V getStaticL() { return (V) unsafe.getObject(base, offset); }
 
251
        void setStaticL(V x) { unsafe.putObject(base, offset, x); }
 
252
 
 
253
        static String fname(Class<?> vclass, boolean isSetter, boolean isStatic) {
 
254
            String stem;
 
255
            if (!isStatic)
 
256
                stem = (!isSetter ? "getField" : "setField");
 
257
            else
 
258
                stem = (!isSetter ? "getStatic" : "setStatic");
 
259
            return stem + Wrapper.basicTypeChar(vclass);
 
260
        }
 
261
        static MethodType ftype(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
 
262
            MethodType type;
 
263
            if (!isStatic) {
 
264
                if (!isSetter)
 
265
                    return MethodType.methodType(vclass, cclass);
 
266
                else
 
267
                    return MethodType.methodType(void.class, cclass, vclass);
 
268
            } else {
 
269
                if (!isSetter)
 
270
                    return MethodType.methodType(vclass);
 
271
                else
 
272
                    return MethodType.methodType(void.class, vclass);
 
273
            }
 
274
        }
 
275
        static MethodHandle fhandle(Class<?> cclass, Class<?> vclass, boolean isSetter, boolean isStatic) {
 
276
            String name = FieldAccessor.fname(vclass, isSetter, isStatic);
 
277
            if (cclass.isPrimitive())  throw newIllegalArgumentException("primitive "+cclass);
 
278
            Class<?> ecclass = Object.class;  //erase this type
 
279
            Class<?> evclass = vclass;
 
280
            if (!evclass.isPrimitive())  evclass = Object.class;
 
281
            MethodType type = FieldAccessor.ftype(ecclass, evclass, isSetter, isStatic);
 
282
            MethodHandle mh;
 
283
            try {
 
284
                mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
 
285
            } catch (ReflectiveOperationException ex) {
 
286
                throw uncaughtException(ex);
 
287
            }
 
288
            if (evclass != vclass || (!isStatic && ecclass != cclass)) {
 
289
                MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
 
290
                strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
 
291
                mh = convertArguments(mh, strongType, 0);
 
292
            }
 
293
            return mh;
 
294
        }
 
295
 
 
296
        /// Support for array element access
 
297
        static final HashMap<Class<?>, MethodHandle[]> ARRAY_CACHE =
 
298
                new HashMap<Class<?>, MethodHandle[]>();
 
299
        // FIXME: Cache on the classes themselves, not here.
 
300
        static boolean doCache(Class<?> elemClass) {
 
301
            if (elemClass.isPrimitive())  return true;
 
302
            ClassLoader cl = elemClass.getClassLoader();
 
303
            return cl == null || cl == ClassLoader.getSystemClassLoader();
 
304
        }
 
305
        static int getElementI(int[] a, int i) { return a[i]; }
 
306
        static void setElementI(int[] a, int i, int x) { a[i] = x; }
 
307
        static long getElementJ(long[] a, int i) { return a[i]; }
 
308
        static void setElementJ(long[] a, int i, long x) { a[i] = x; }
 
309
        static float getElementF(float[] a, int i) { return a[i]; }
 
310
        static void setElementF(float[] a, int i, float x) { a[i] = x; }
 
311
        static double getElementD(double[] a, int i) { return a[i]; }
 
312
        static void setElementD(double[] a, int i, double x) { a[i] = x; }
 
313
        static boolean getElementZ(boolean[] a, int i) { return a[i]; }
 
314
        static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; }
 
315
        static byte getElementB(byte[] a, int i) { return a[i]; }
 
316
        static void setElementB(byte[] a, int i, byte x) { a[i] = x; }
 
317
        static short getElementS(short[] a, int i) { return a[i]; }
 
318
        static void setElementS(short[] a, int i, short x) { a[i] = x; }
 
319
        static char getElementC(char[] a, int i) { return a[i]; }
 
320
        static void setElementC(char[] a, int i, char x) { a[i] = x; }
 
321
        static Object getElementL(Object[] a, int i) { return a[i]; }
 
322
        static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
 
323
        static <V> V getElementL(Class<V[]> aclass, V[] a, int i) { return aclass.cast(a)[i]; }
 
324
        static <V> void setElementL(Class<V[]> aclass, V[] a, int i, V x) { aclass.cast(a)[i] = x; }
 
325
 
 
326
        static String aname(Class<?> aclass, boolean isSetter) {
 
327
            Class<?> vclass = aclass.getComponentType();
 
328
            if (vclass == null)  throw new IllegalArgumentException();
 
329
            return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(vclass);
 
330
        }
 
331
        static MethodType atype(Class<?> aclass, boolean isSetter) {
 
332
            Class<?> vclass = aclass.getComponentType();
 
333
            if (!isSetter)
 
334
                return MethodType.methodType(vclass, aclass, int.class);
 
335
            else
 
336
                return MethodType.methodType(void.class, aclass, int.class, vclass);
 
337
        }
 
338
        static MethodHandle ahandle(Class<?> aclass, boolean isSetter) {
 
339
            Class<?> vclass = aclass.getComponentType();
 
340
            String name = FieldAccessor.aname(aclass, isSetter);
 
341
            Class<?> caclass = null;
 
342
            if (!vclass.isPrimitive() && vclass != Object.class) {
 
343
                caclass = aclass;
 
344
                aclass = Object[].class;
 
345
                vclass = Object.class;
 
346
            }
 
347
            MethodType type = FieldAccessor.atype(aclass, isSetter);
 
348
            if (caclass != null)
 
349
                type = type.insertParameterTypes(0, Class.class);
 
350
            MethodHandle mh;
 
351
            try {
 
352
                mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
 
353
            } catch (ReflectiveOperationException ex) {
 
354
                throw uncaughtException(ex);
 
355
            }
 
356
            if (caclass != null) {
 
357
                MethodType strongType = FieldAccessor.atype(caclass, isSetter);
 
358
                mh = mh.bindTo(caclass);
 
359
                mh = convertArguments(mh, strongType, 0);
 
360
            }
 
361
            return mh;
 
362
        }
 
363
    }
 
364
 
 
365
    /** Bind a predetermined first argument to the given direct method handle.
 
366
     * Callable only from MethodHandles.
 
367
     * @param token Proof that the caller has access to this package.
 
368
     * @param target Any direct method handle.
 
369
     * @param receiver Receiver (or first static method argument) to pre-bind.
 
370
     * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
 
371
     */
 
372
    static
 
373
    MethodHandle bindReceiver(MethodHandle target, Object receiver) {
 
374
        if (receiver == null)  return null;
 
375
        return new BoundMethodHandle(target, receiver, 0);
 
376
    }
 
377
 
 
378
    /** Bind a predetermined argument to the given arbitrary method handle.
 
379
     * Callable only from MethodHandles.
 
380
     * @param token Proof that the caller has access to this package.
 
381
     * @param target Any method handle.
 
382
     * @param receiver Argument (which can be a boxed primitive) to pre-bind.
 
383
     * @return a suitable BoundMethodHandle
 
384
     */
 
385
    static
 
386
    MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) {
 
387
        return new BoundMethodHandle(target, receiver, argnum);
 
388
    }
 
389
 
 
390
    static native MethodHandle permuteArguments(MethodHandle target,
 
391
                                                MethodType newType,
 
392
                                                MethodType oldType,
 
393
                                                int[] permutationOrNull);
 
394
 
 
395
    /*non-public*/ static
 
396
    MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) {
 
397
        MethodType oldType = target.type();
 
398
        if (oldType.equals(newType))
 
399
            return target;
 
400
        assert(level > 1 || oldType.isConvertibleTo(newType));
 
401
        MethodHandle retFilter = null;
 
402
        Class<?> oldRT = oldType.returnType();
 
403
        Class<?> newRT = newType.returnType();
 
404
        if (!VerifyType.isNullConversion(oldRT, newRT)) {
 
405
            if (oldRT == void.class) {
 
406
                Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
 
407
                retFilter = ValueConversions.zeroConstantFunction(wrap);
 
408
            } else {
 
409
                retFilter = MethodHandles.identity(newRT);
 
410
                retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
 
411
            }
 
412
            newType = newType.changeReturnType(oldRT);
 
413
        }
 
414
        MethodHandle res = null;
 
415
        Exception ex = null;
 
416
        try {
 
417
            res = convertArguments(target, newType, oldType, level);
 
418
        } catch (IllegalArgumentException ex1) {
 
419
            ex = ex1;
 
420
        }
 
421
        if (res == null) {
 
422
            WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
 
423
            wmt.initCause(ex);
 
424
            throw wmt;
 
425
        }
 
426
        if (retFilter != null)
 
427
            res = MethodHandles.filterReturnValue(res, retFilter);
 
428
        return res;
 
429
    }
 
430
 
 
431
    static MethodHandle convertArguments(MethodHandle target,
 
432
                                                MethodType newType,
 
433
                                                MethodType oldType,
 
434
                                                int level) {
 
435
        assert(oldType.parameterCount() == target.type().parameterCount());
 
436
        if (newType == oldType)
 
437
            return target;
 
438
        if (oldType.parameterCount() != newType.parameterCount())
 
439
            throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
 
440
        return AdapterMethodHandle.makePairwiseConvert(newType, target, level);
 
441
    }
 
442
 
 
443
    static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
 
444
        MethodType oldType = target.type();
 
445
        int nargs = oldType.parameterCount();
 
446
        int keepPosArgs = nargs - arrayLength;
 
447
        MethodType newType = oldType
 
448
                .dropParameterTypes(keepPosArgs, nargs)
 
449
                .insertParameterTypes(keepPosArgs, arrayType);
 
450
        return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
 
451
    }
 
452
    // called internally only
 
453
    static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) {
 
454
        int arrayLength = target.type().parameterCount() - spreadArgPos;
 
455
        return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
 
456
    }
 
457
    static MethodHandle spreadArguments(MethodHandle target,
 
458
                                               MethodType newType,
 
459
                                               int spreadArgPos,
 
460
                                               Class<?> arrayType,
 
461
                                               int arrayLength) {
 
462
        // TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
 
463
        MethodType oldType = target.type();
 
464
        // spread the last argument of newType to oldType
 
465
        assert(arrayLength == oldType.parameterCount() - spreadArgPos);
 
466
        assert(newType.parameterType(spreadArgPos) == arrayType);
 
467
        return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
 
468
    }
 
469
 
 
470
    static MethodHandle collectArguments(MethodHandle target,
 
471
                                                int collectArg,
 
472
                                                MethodHandle collector) {
 
473
        MethodType type = target.type();
 
474
        Class<?> collectType = collector.type().returnType();
 
475
        assert(collectType != void.class);  // else use foldArguments
 
476
        if (collectType != type.parameterType(collectArg))
 
477
            target = target.asType(type.changeParameterType(collectArg, collectType));
 
478
        MethodType newType = type
 
479
                .dropParameterTypes(collectArg, collectArg+1)
 
480
                .insertParameterTypes(collectArg, collector.type().parameterArray());
 
481
        return collectArguments(target, newType, collectArg, collector);
 
482
    }
 
483
    static MethodHandle collectArguments(MethodHandle target,
 
484
                                                MethodType newType,
 
485
                                                int collectArg,
 
486
                                                MethodHandle collector) {
 
487
        MethodType oldType = target.type();     // (a...,c)=>r
 
488
        //         newType                      // (a..., b...)=>r
 
489
        MethodType colType = collector.type();  // (b...)=>c
 
490
        //         oldType                      // (a..., b...)=>r
 
491
        assert(newType.parameterCount() == collectArg + colType.parameterCount());
 
492
        assert(oldType.parameterCount() == collectArg + 1);
 
493
        return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
 
494
    }
 
495
 
 
496
    static MethodHandle filterArgument(MethodHandle target,
 
497
                                       int pos,
 
498
                                       MethodHandle filter) {
 
499
        MethodType ttype = target.type();
 
500
        MethodType ftype = filter.type();
 
501
        assert(ftype.parameterCount() == 1);
 
502
        return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
 
503
    }
 
504
 
 
505
    static MethodHandle foldArguments(MethodHandle target,
 
506
                                      MethodType newType,
 
507
                                      int foldPos,
 
508
                                      MethodHandle combiner) {
 
509
        MethodType oldType = target.type();
 
510
        MethodType ctype = combiner.type();
 
511
        return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
 
512
    }
 
513
 
 
514
    static
 
515
    MethodHandle dropArguments(MethodHandle target,
 
516
                               MethodType newType, int argnum) {
 
517
        int drops = newType.parameterCount() - target.type().parameterCount();
 
518
        return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops);
 
519
    }
 
520
 
 
521
    static
 
522
    MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) {
 
523
        return testResult ? target : fallback;
 
524
    }
 
525
 
 
526
    static MethodHandle SELECT_ALTERNATIVE;
 
527
    static MethodHandle selectAlternative() {
 
528
        if (SELECT_ALTERNATIVE != null)  return SELECT_ALTERNATIVE;
 
529
        try {
 
530
            SELECT_ALTERNATIVE
 
531
            = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
 
532
                    MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class));
 
533
        } catch (ReflectiveOperationException ex) {
 
534
            throw new RuntimeException(ex);
 
535
        }
 
536
        return SELECT_ALTERNATIVE;
 
537
    }
 
538
 
 
539
    static
 
540
    MethodHandle makeGuardWithTest(MethodHandle test,
 
541
                                   MethodHandle target,
 
542
                                   MethodHandle fallback) {
 
543
        // gwt(arg...)
 
544
        // [fold]=> continueAfterTest(z=test(arg...), arg...)
 
545
        // [filter]=> (tf=select(z))(arg...)
 
546
        //    where select(z) = select(z, t, f).bindTo(t, f) => z ? t f
 
547
        // [tailcall]=> tf(arg...)
 
548
        assert(test.type().returnType() == boolean.class);
 
549
        MethodType targetType = target.type();
 
550
        MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class);
 
551
        // working backwards, as usual:
 
552
        assert(target.type().equals(fallback.type()));
 
553
        MethodHandle tailcall = MethodHandles.exactInvoker(target.type());
 
554
        MethodHandle select = selectAlternative();
 
555
        select = bindArgument(select, 2, fallback);
 
556
        select = bindArgument(select, 1, target);
 
557
        // select(z: boolean) => (z ? target : fallback)
 
558
        MethodHandle filter = filterArgument(tailcall, 0, select);
 
559
        assert(filter.type().parameterType(0) == boolean.class);
 
560
        MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test);
 
561
        return fold;
 
562
    }
 
563
 
 
564
    private static class GuardWithCatch extends BoundMethodHandle {
 
565
        private final MethodHandle target;
 
566
        private final Class<? extends Throwable> exType;
 
567
        private final MethodHandle catcher;
 
568
        GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
 
569
            this(INVOKES[target.type().parameterCount()], target, exType, catcher);
 
570
        }
 
571
        // FIXME: Build the control flow out of foldArguments.
 
572
        GuardWithCatch(MethodHandle invoker,
 
573
                       MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
 
574
            super(invoker);
 
575
            this.target = target;
 
576
            this.exType = exType;
 
577
            this.catcher = catcher;
 
578
        }
 
579
        @Override
 
580
        String debugString() {
 
581
            return addTypeString(target, this);
 
582
        }
 
583
        private Object invoke_V(Object... av) throws Throwable {
 
584
            try {
 
585
                return target.invokeExact(av);
 
586
            } catch (Throwable t) {
 
587
                if (!exType.isInstance(t))  throw t;
 
588
                return catcher.invokeExact(t, av);
 
589
            }
 
590
        }
 
591
        private Object invoke_L0() throws Throwable {
 
592
            try {
 
593
                return target.invokeExact();
 
594
            } catch (Throwable t) {
 
595
                if (!exType.isInstance(t))  throw t;
 
596
                return catcher.invokeExact(t);
 
597
            }
 
598
        }
 
599
        private Object invoke_L1(Object a0) throws Throwable {
 
600
            try {
 
601
                return target.invokeExact(a0);
 
602
            } catch (Throwable t) {
 
603
                if (!exType.isInstance(t))  throw t;
 
604
                return catcher.invokeExact(t, a0);
 
605
            }
 
606
        }
 
607
        private Object invoke_L2(Object a0, Object a1) throws Throwable {
 
608
            try {
 
609
                return target.invokeExact(a0, a1);
 
610
            } catch (Throwable t) {
 
611
                if (!exType.isInstance(t))  throw t;
 
612
                return catcher.invokeExact(t, a0, a1);
 
613
            }
 
614
        }
 
615
        private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
 
616
            try {
 
617
                return target.invokeExact(a0, a1, a2);
 
618
            } catch (Throwable t) {
 
619
                if (!exType.isInstance(t))  throw t;
 
620
                return catcher.invokeExact(t, a0, a1, a2);
 
621
            }
 
622
        }
 
623
        private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
 
624
            try {
 
625
                return target.invokeExact(a0, a1, a2, a3);
 
626
            } catch (Throwable t) {
 
627
                if (!exType.isInstance(t))  throw t;
 
628
                return catcher.invokeExact(t, a0, a1, a2, a3);
 
629
            }
 
630
        }
 
631
        private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
 
632
            try {
 
633
                return target.invokeExact(a0, a1, a2, a3, a4);
 
634
            } catch (Throwable t) {
 
635
                if (!exType.isInstance(t))  throw t;
 
636
                return catcher.invokeExact(t, a0, a1, a2, a3, a4);
 
637
            }
 
638
        }
 
639
        private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
 
640
            try {
 
641
                return target.invokeExact(a0, a1, a2, a3, a4, a5);
 
642
            } catch (Throwable t) {
 
643
                if (!exType.isInstance(t))  throw t;
 
644
                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5);
 
645
            }
 
646
        }
 
647
        private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
 
648
            try {
 
649
                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6);
 
650
            } catch (Throwable t) {
 
651
                if (!exType.isInstance(t))  throw t;
 
652
                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6);
 
653
            }
 
654
        }
 
655
        private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
 
656
            try {
 
657
                return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7);
 
658
            } catch (Throwable t) {
 
659
                if (!exType.isInstance(t))  throw t;
 
660
                return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7);
 
661
            }
 
662
        }
 
663
        static MethodHandle[] makeInvokes() {
 
664
            ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>();
 
665
            MethodHandles.Lookup lookup = IMPL_LOOKUP;
 
666
            for (;;) {
 
667
                int nargs = invokes.size();
 
668
                String name = "invoke_L"+nargs;
 
669
                MethodHandle invoke = null;
 
670
                try {
 
671
                    invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs));
 
672
                } catch (ReflectiveOperationException ex) {
 
673
                }
 
674
                if (invoke == null)  break;
 
675
                invokes.add(invoke);
 
676
            }
 
677
            assert(invokes.size() == 9);  // current number of methods
 
678
            return invokes.toArray(new MethodHandle[0]);
 
679
        };
 
680
        static final MethodHandle[] INVOKES = makeInvokes();
 
681
        // For testing use this:
 
682
        //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2);
 
683
        static final MethodHandle VARARGS_INVOKE;
 
684
        static {
 
685
            try {
 
686
                VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
 
687
            } catch (ReflectiveOperationException ex) {
 
688
                throw uncaughtException(ex);
 
689
            }
 
690
        }
 
691
    }
 
692
 
 
693
 
 
694
    static
 
695
    MethodHandle makeGuardWithCatch(MethodHandle target,
 
696
                                    Class<? extends Throwable> exType,
 
697
                                    MethodHandle catcher) {
 
698
        MethodType type = target.type();
 
699
        MethodType ctype = catcher.type();
 
700
        int nargs = type.parameterCount();
 
701
        if (nargs < GuardWithCatch.INVOKES.length) {
 
702
            MethodType gtype = type.generic();
 
703
            MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
 
704
            // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0)
 
705
            MethodHandle gtarget = convertArguments(target, gtype, type, 2);
 
706
            MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2);
 
707
            MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
 
708
            if (gtarget == null || gcatcher == null || gguard == null)  return null;
 
709
            return convertArguments(gguard, type, gtype, 2);
 
710
        } else {
 
711
            MethodType gtype = MethodType.genericMethodType(0, true);
 
712
            MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
 
713
            MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0);
 
714
            catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class));
 
715
            MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1);
 
716
            MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher);
 
717
            if (gtarget == null || gcatcher == null || gguard == null)  return null;
 
718
            return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type);
 
719
        }
 
720
    }
 
721
 
 
722
    static
 
723
    MethodHandle throwException(MethodType type) {
 
724
        return AdapterMethodHandle.makeRetypeRaw(type, throwException());
 
725
    }
 
726
 
 
727
    static MethodHandle THROW_EXCEPTION;
 
728
    static MethodHandle throwException() {
 
729
        if (THROW_EXCEPTION != null)  return THROW_EXCEPTION;
 
730
        try {
 
731
            THROW_EXCEPTION
 
732
            = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
 
733
                    MethodType.methodType(Empty.class, Throwable.class));
 
734
        } catch (ReflectiveOperationException ex) {
 
735
            throw new RuntimeException(ex);
 
736
        }
 
737
        return THROW_EXCEPTION;
 
738
    }
 
739
    static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
 
740
}