Mercurial > hg > icedtea9-forest > jdk
view src/share/classes/java/lang/invoke/MethodHandleImpl.java @ 5874:beeb1d5ecd9e
7129034: VM crash with a field setter method with a filterArguments
Summary: add null checks before unsafe calls that take a variable base reference; update unit tests
Reviewed-by: kvn, twisti
author | jrose |
---|---|
date | Thu, 12 Jul 2012 00:11:35 -0700 |
parents | db189e2f3cdb |
children | 050116960e99 |
line wrap: on
line source
/* * 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); AllocateObject 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 Object /*C*/ allocate() throws InstantiationException { return 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. FieldAccessor accessor = new FieldAccessor(member, isSetter); return accessor; } 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); } private static Object nullCheck(Object obj) { obj.getClass(); // NPE return obj; } int getFieldI(Object /*C*/ obj) { return unsafe.getInt(nullCheck(obj), offset); } void setFieldI(Object /*C*/ obj, int x) { unsafe.putInt(nullCheck(obj), offset, x); } long getFieldJ(Object /*C*/ obj) { return unsafe.getLong(nullCheck(obj), offset); } void setFieldJ(Object /*C*/ obj, long x) { unsafe.putLong(nullCheck(obj), offset, x); } float getFieldF(Object /*C*/ obj) { return unsafe.getFloat(nullCheck(obj), offset); } void setFieldF(Object /*C*/ obj, float x) { unsafe.putFloat(nullCheck(obj), offset, x); } double getFieldD(Object /*C*/ obj) { return unsafe.getDouble(nullCheck(obj), offset); } void setFieldD(Object /*C*/ obj, double x) { unsafe.putDouble(nullCheck(obj), offset, x); } boolean getFieldZ(Object /*C*/ obj) { return unsafe.getBoolean(nullCheck(obj), offset); } void setFieldZ(Object /*C*/ obj, boolean x) { unsafe.putBoolean(nullCheck(obj), offset, x); } byte getFieldB(Object /*C*/ obj) { return unsafe.getByte(nullCheck(obj), offset); } void setFieldB(Object /*C*/ obj, byte x) { unsafe.putByte(nullCheck(obj), offset, x); } short getFieldS(Object /*C*/ obj) { return unsafe.getShort(nullCheck(obj), offset); } void setFieldS(Object /*C*/ obj, short x) { unsafe.putShort(nullCheck(obj), offset, x); } char getFieldC(Object /*C*/ obj) { return unsafe.getChar(nullCheck(obj), offset); } void setFieldC(Object /*C*/ obj, char x) { unsafe.putChar(nullCheck(obj), offset, x); } Object /*V*/ getFieldL(Object /*C*/ obj) { return unsafe.getObject(nullCheck(obj), offset); } void setFieldL(Object /*C*/ obj, Object /*V*/ x) { unsafe.putObject(nullCheck(obj), offset, x); } 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); } Object /*V*/ getStaticL() { return unsafe.getObject(base, offset); } void setStaticL(Object /*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; } }