| /* |
| * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. |
| * 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 |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * 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. |
| * |
| * 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. |
| */ |
| |
| package java.lang.invoke; |
| |
| import sun.invoke.util.VerifyType; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import sun.invoke.empty.Empty; |
| import sun.invoke.util.ValueConversions; |
| import sun.invoke.util.Wrapper; |
| import sun.misc.Unsafe; |
| import static java.lang.invoke.MethodHandleStatics.*; |
| import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; |
| |
| /** |
| * Trusted implementation code for MethodHandle. |
| * @author jrose |
| */ |
| /*non-public*/ abstract class MethodHandleImpl { |
| /// Factory methods to create method handles: |
| |
| private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE; |
| |
| static void initStatics() { |
| // Trigger preceding sequence. |
| } |
| |
| /** Look up a given method. |
| * Callable only from sun.invoke and related packages. |
| * <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 |
| * @throws IllegalAccessException if the given method cannot be accessed by the lookup class |
| */ |
| static |
| MethodHandle findMethod(MemberName method, |
| boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException { |
| MethodType mtype = method.getMethodType(); |
| if (!method.isStatic()) { |
| // adjust the advertised receiver type to be exactly the one requested |
| // (in the case of invokespecial, this will be the calling class) |
| Class<?> recvType = method.getDeclaringClass(); |
| mtype = mtype.insertParameterTypes(0, recvType); |
| } |
| DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass); |
| if (!mh.isValid()) |
| throw method.makeAccessException("no direct method handle", lookupClass); |
| assert(mh.type() == mtype); |
| if (!method.isVarargs()) |
| return mh; |
| int argc = mtype.parameterCount(); |
| if (argc != 0) { |
| Class<?> arrayType = mtype.parameterType(argc-1); |
| if (arrayType.isArray()) |
| return AdapterMethodHandle.makeVarargsCollector(mh, arrayType); |
| } |
| throw method.makeAccessException("cannot make variable arity", null); |
| } |
| |
| static |
| MethodHandle makeAllocator(MethodHandle rawConstructor) { |
| MethodType rawConType = rawConstructor.type(); |
| Class<?> allocateClass = rawConType.parameterType(0); |
| // Wrap the raw (unsafe) constructor with the allocation of a suitable object. |
| assert(AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true)); |
| // allocator(arg...) |
| // [fold]=> cookedConstructor(obj=allocate(C), arg...) |
| // [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...)) |
| MethodHandle returner = MethodHandles.identity(allocateClass); |
| MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass); |
| MethodHandle cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false); |
| assert(cookedConstructor.type().equals(ctype)); |
| ctype = ctype.dropParameterTypes(0, 1); |
| cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true); |
| MethodHandle allocator = new AllocateObject(allocateClass); |
| // allocate() => new C(void) |
| assert(allocator.type().equals(MethodType.methodType(allocateClass))); |
| ctype = ctype.dropParameterTypes(0, 1); |
| MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator); |
| return fold; |
| } |
| |
| static final class AllocateObject<C> extends BoundMethodHandle { |
| private static final Unsafe unsafe = Unsafe.getUnsafe(); |
| |
| private final Class<C> allocateClass; |
| |
| // for allocation only: |
| private AllocateObject(Class<C> allocateClass) { |
| super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class))); |
| this.allocateClass = allocateClass; |
| } |
| @SuppressWarnings("unchecked") |
| private C allocate() throws InstantiationException { |
| return (C) unsafe.allocateInstance(allocateClass); |
| } |
| static final MethodHandle ALLOCATE; |
| static { |
| try { |
| ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0)); |
| } catch (ReflectiveOperationException ex) { |
| throw uncaughtException(ex); |
| } |
| } |
| } |
| |
| static |
| MethodHandle accessField(MemberName member, boolean isSetter, |
| Class<?> lookupClass) { |
| // Use sun. misc.Unsafe to dig up the dirt on the field. |
| MethodHandle mh = new FieldAccessor(member, isSetter); |
| return mh; |
| } |
| |
| static |
| MethodHandle accessArrayElement(Class<?> arrayClass, boolean isSetter) { |
| if (!arrayClass.isArray()) |
| throw newIllegalArgumentException("not an array: "+arrayClass); |
| 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] = mhs[0].bindTo(elemClass); |
| mhs[1] = mhs[1].bindTo(elemClass); |
| } |
| synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier |
| FieldAccessor.ARRAY_CACHE.put(elemClass, mhs); |
| } |
| return mhs[isSetter ? 1 : 0]; |
| } |
| |
| static final class FieldAccessor<C,V> extends BoundMethodHandle { |
| private static final Unsafe unsafe = Unsafe.getUnsafe(); |
| final Object base; // for static refs only |
| final long offset; |
| final String name; |
| |
| FieldAccessor(MemberName field, boolean isSetter) { |
| super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic())); |
| this.offset = (long) field.getVMIndex(); |
| this.name = field.getName(); |
| this.base = staticBase(field); |
| } |
| @Override |
| String debugString() { return addTypeString(name, this); } |
| |
| 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(final MemberName field) { |
| if (!field.isStatic()) return null; |
| return AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| try { |
| Class c = field.getDeclaringClass(); |
| // FIXME: Should not have to create 'f' to get this value. |
| java.lang.reflect.Field f = c.getDeclaredField(field.getName()); |
| return unsafe.staticFieldBase(f); |
| } catch (NoSuchFieldException ee) { |
| throw uncaughtException(ee); |
| } |
| } |
| }); |
| } |
| |
| 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); |
| } catch (ReflectiveOperationException ex) { |
| throw uncaughtException(ex); |
| } |
| if (evclass != vclass || (!isStatic && ecclass != cclass)) { |
| MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic); |
| strongType = strongType.insertParameterTypes(0, FieldAccessor.class); |
| mh = convertArguments(mh, strongType, 0); |
| } |
| 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); |
| } catch (ReflectiveOperationException ex) { |
| throw uncaughtException(ex); |
| } |
| if (caclass != null) { |
| MethodType strongType = FieldAccessor.atype(caclass, isSetter); |
| mh = mh.bindTo(caclass); |
| mh = convertArguments(mh, strongType, 0); |
| } |
| return mh; |
| } |
| } |
| |
| /** 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 |
| */ |
| static |
| MethodHandle bindReceiver(MethodHandle target, Object receiver) { |
| if (receiver == null) return null; |
| if (target instanceof AdapterMethodHandle && |
| ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY |
| ) { |
| Object info = MethodHandleNatives.getTargetInfo(target); |
| if (info instanceof DirectMethodHandle) { |
| DirectMethodHandle dmh = (DirectMethodHandle) info; |
| if (dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) { |
| MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0); |
| MethodType newType = target.type().dropParameterTypes(0, 1); |
| return convertArguments(bmh, newType, bmh.type(), 0); |
| } |
| } |
| } |
| 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 |
| */ |
| static |
| MethodHandle bindArgument(MethodHandle target, int argnum, Object receiver) { |
| return new BoundMethodHandle(target, receiver, argnum); |
| } |
| |
| static MethodHandle permuteArguments(MethodHandle target, |
| MethodType newType, |
| MethodType oldType, |
| int[] permutationOrNull) { |
| assert(oldType.parameterCount() == target.type().parameterCount()); |
| 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); |
| target = convertArguments(target, callType, oldType, 0); |
| 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 |
| = AdapterMethodHandle.makeRotateArguments(rotType, target, |
| 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); |
| target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j); |
| 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); |
| target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount); |
| 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); |
| target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount); |
| if (target == null) throw newIllegalArgumentException("cannot drop"); |
| oldType = target.type(); |
| } |
| target = convertArguments(target, newType, oldType, 0); |
| assert(target != null); |
| return target; |
| } |
| |
| /*non-public*/ static |
| MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) { |
| MethodType oldType = target.type(); |
| if (oldType.equals(newType)) |
| return target; |
| assert(level > 1 || oldType.isConvertibleTo(newType)); |
| MethodHandle retFilter = null; |
| Class<?> oldRT = oldType.returnType(); |
| Class<?> newRT = newType.returnType(); |
| if (!VerifyType.isNullConversion(oldRT, newRT)) { |
| if (oldRT == void.class) { |
| Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT; |
| retFilter = ValueConversions.zeroConstantFunction(wrap); |
| } else { |
| retFilter = MethodHandles.identity(newRT); |
| retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level); |
| } |
| newType = newType.changeReturnType(oldRT); |
| } |
| MethodHandle res = null; |
| Exception ex = null; |
| try { |
| res = convertArguments(target, newType, oldType, level); |
| } catch (IllegalArgumentException ex1) { |
| ex = ex1; |
| } |
| if (res == null) { |
| WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target); |
| wmt.initCause(ex); |
| throw wmt; |
| } |
| if (retFilter != null) |
| res = MethodHandles.filterReturnValue(res, retFilter); |
| return res; |
| } |
| |
| static MethodHandle convertArguments(MethodHandle target, |
| MethodType newType, |
| MethodType oldType, |
| int level) { |
| assert(oldType.parameterCount() == target.type().parameterCount()); |
| if (newType == oldType) |
| return target; |
| if (oldType.parameterCount() != newType.parameterCount()) |
| throw newIllegalArgumentException("mismatched parameter count", oldType, newType); |
| return AdapterMethodHandle.makePairwiseConvert(newType, target, level); |
| } |
| |
| static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) { |
| MethodType oldType = target.type(); |
| int nargs = oldType.parameterCount(); |
| int keepPosArgs = nargs - arrayLength; |
| MethodType newType = oldType |
| .dropParameterTypes(keepPosArgs, nargs) |
| .insertParameterTypes(keepPosArgs, arrayType); |
| return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength); |
| } |
| // called internally only |
| static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) { |
| int arrayLength = target.type().parameterCount() - spreadArgPos; |
| return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength); |
| } |
| static MethodHandle spreadArguments(MethodHandle target, |
| MethodType newType, |
| int spreadArgPos, |
| Class<?> arrayType, |
| int arrayLength) { |
| // 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 |
| assert(arrayLength == oldType.parameterCount() - spreadArgPos); |
| assert(newType.parameterType(spreadArgPos) == arrayType); |
| return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); |
| } |
| |
| static MethodHandle collectArguments(MethodHandle target, |
| int collectArg, |
| MethodHandle collector) { |
| MethodType type = target.type(); |
| Class<?> collectType = collector.type().returnType(); |
| assert(collectType != void.class); // else use foldArguments |
| if (collectType != type.parameterType(collectArg)) |
| target = target.asType(type.changeParameterType(collectArg, collectType)); |
| MethodType newType = type |
| .dropParameterTypes(collectArg, collectArg+1) |
| .insertParameterTypes(collectArg, collector.type().parameterArray()); |
| return collectArguments(target, newType, collectArg, collector); |
| } |
| static MethodHandle collectArguments(MethodHandle target, |
| MethodType newType, |
| int collectArg, |
| MethodHandle collector) { |
| MethodType oldType = target.type(); // (a...,c)=>r |
| // 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); |
| assert(AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)); |
| return AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false); |
| } |
| |
| static MethodHandle filterArgument(MethodHandle target, |
| int pos, |
| MethodHandle filter) { |
| MethodType ttype = target.type(); |
| MethodType ftype = filter.type(); |
| assert(ftype.parameterCount() == 1); |
| return AdapterMethodHandle.makeCollectArguments(target, filter, pos, false); |
| } |
| |
| static MethodHandle foldArguments(MethodHandle target, |
| MethodType newType, |
| int foldPos, |
| MethodHandle combiner) { |
| MethodType oldType = target.type(); |
| MethodType ctype = combiner.type(); |
| assert(AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)); |
| return AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true); |
| } |
| |
| static |
| MethodHandle dropArguments(MethodHandle target, |
| MethodType newType, int argnum) { |
| int drops = newType.parameterCount() - target.type().parameterCount(); |
| return AdapterMethodHandle.makeDropArguments(newType, target, argnum, drops); |
| } |
| |
| static |
| MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) { |
| return testResult ? target : fallback; |
| } |
| |
| static MethodHandle SELECT_ALTERNATIVE; |
| static MethodHandle selectAlternative() { |
| if (SELECT_ALTERNATIVE != null) return SELECT_ALTERNATIVE; |
| try { |
| SELECT_ALTERNATIVE |
| = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative", |
| MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)); |
| } catch (ReflectiveOperationException ex) { |
| throw new RuntimeException(ex); |
| } |
| return SELECT_ALTERNATIVE; |
| } |
| |
| static |
| MethodHandle makeGuardWithTest(MethodHandle test, |
| MethodHandle target, |
| MethodHandle fallback) { |
| // gwt(arg...) |
| // [fold]=> continueAfterTest(z=test(arg...), arg...) |
| // [filter]=> (tf=select(z))(arg...) |
| // where select(z) = select(z, t, f).bindTo(t, f) => z ? t f |
| // [tailcall]=> tf(arg...) |
| assert(test.type().returnType() == boolean.class); |
| MethodType targetType = target.type(); |
| MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class); |
| assert(AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true)); |
| // working backwards, as usual: |
| assert(target.type().equals(fallback.type())); |
| MethodHandle tailcall = MethodHandles.exactInvoker(target.type()); |
| MethodHandle select = selectAlternative(); |
| select = bindArgument(select, 2, CountingMethodHandle.wrap(fallback)); |
| select = bindArgument(select, 1, CountingMethodHandle.wrap(target)); |
| // select(z: boolean) => (z ? target : fallback) |
| MethodHandle filter = filterArgument(tailcall, 0, select); |
| assert(filter.type().parameterType(0) == boolean.class); |
| MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test); |
| return fold; |
| } |
| |
| private static class GuardWithCatch extends BoundMethodHandle { |
| private final MethodHandle target; |
| private final Class<? extends Throwable> exType; |
| private final MethodHandle catcher; |
| GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { |
| this(INVOKES[target.type().parameterCount()], target, exType, catcher); |
| } |
| // FIXME: Build the control flow out of foldArguments. |
| GuardWithCatch(MethodHandle invoker, |
| MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { |
| super(invoker); |
| this.target = target; |
| this.exType = exType; |
| this.catcher = catcher; |
| } |
| @Override |
| String debugString() { |
| return addTypeString(target, this); |
| } |
| private Object invoke_V(Object... av) throws Throwable { |
| try { |
| return target.invokeExact(av); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, av); |
| } |
| } |
| private Object invoke_L0() throws Throwable { |
| try { |
| return target.invokeExact(); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t); |
| } |
| } |
| private Object invoke_L1(Object a0) throws Throwable { |
| try { |
| return target.invokeExact(a0); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0); |
| } |
| } |
| private Object invoke_L2(Object a0, Object a1) throws Throwable { |
| try { |
| return target.invokeExact(a0, a1); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0, a1); |
| } |
| } |
| private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { |
| try { |
| return target.invokeExact(a0, a1, a2); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0, a1, a2); |
| } |
| } |
| private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { |
| try { |
| return target.invokeExact(a0, a1, a2, a3); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0, a1, a2, a3); |
| } |
| } |
| private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { |
| try { |
| return target.invokeExact(a0, a1, a2, a3, a4); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0, a1, a2, a3, a4); |
| } |
| } |
| private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { |
| try { |
| return target.invokeExact(a0, a1, a2, a3, a4, a5); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5); |
| } |
| } |
| private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { |
| try { |
| return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6); |
| } |
| } |
| private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { |
| try { |
| return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); |
| } catch (Throwable t) { |
| if (!exType.isInstance(t)) throw t; |
| return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); |
| } |
| } |
| 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)); |
| } catch (ReflectiveOperationException ex) { |
| } |
| 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)); |
| } catch (ReflectiveOperationException ex) { |
| throw uncaughtException(ex); |
| } |
| } |
| } |
| |
| |
| static |
| MethodHandle makeGuardWithCatch(MethodHandle target, |
| 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); |
| // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0) |
| MethodHandle gtarget = convertArguments(target, gtype, type, 2); |
| MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 2); |
| MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher); |
| if (gtarget == null || gcatcher == null || gguard == null) return null; |
| return convertArguments(gguard, type, gtype, 2); |
| } else { |
| MethodType gtype = MethodType.genericMethodType(0, true); |
| MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); |
| MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0); |
| catcher = catcher.asType(ctype.changeParameterType(0, Throwable.class)); |
| MethodHandle gcatcher = spreadArgumentsFromPos(catcher, gcatchType, 1); |
| MethodHandle gguard = new GuardWithCatch(GuardWithCatch.VARARGS_INVOKE, gtarget, exType, gcatcher); |
| if (gtarget == null || gcatcher == null || gguard == null) return null; |
| return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type); |
| } |
| } |
| |
| static |
| MethodHandle throwException(MethodType type) { |
| return AdapterMethodHandle.makeRetypeRaw(type, throwException()); |
| } |
| |
| static MethodHandle THROW_EXCEPTION; |
| static MethodHandle throwException() { |
| if (THROW_EXCEPTION != null) return THROW_EXCEPTION; |
| try { |
| THROW_EXCEPTION |
| = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", |
| MethodType.methodType(Empty.class, Throwable.class)); |
| } catch (ReflectiveOperationException ex) { |
| throw new RuntimeException(ex); |
| } |
| return THROW_EXCEPTION; |
| } |
| static <T extends Throwable> Empty throwException(T t) throws T { throw t; } |
| } |