jaroslav@1646: /* jaroslav@1646: * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. jaroslav@1646: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@1646: * jaroslav@1646: * This code is free software; you can redistribute it and/or modify it jaroslav@1646: * under the terms of the GNU General Public License version 2 only, as jaroslav@1646: * published by the Free Software Foundation. Oracle designates this jaroslav@1646: * particular file as subject to the "Classpath" exception as provided jaroslav@1646: * by Oracle in the LICENSE file that accompanied this code. jaroslav@1646: * jaroslav@1646: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@1646: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@1646: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@1646: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@1646: * accompanied this code). jaroslav@1646: * jaroslav@1646: * You should have received a copy of the GNU General Public License version jaroslav@1646: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@1646: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@1646: * jaroslav@1646: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@1646: * or visit www.oracle.com if you need additional information or have any jaroslav@1646: * questions. jaroslav@1646: */ jaroslav@1646: jaroslav@1646: package java.lang.invoke; jaroslav@1646: jaroslav@1646: import java.security.AccessController; jaroslav@1646: import java.security.PrivilegedAction; jaroslav@1646: import java.util.ArrayList; jaroslav@1646: import java.util.Arrays; jaroslav@1646: import java.util.HashMap; jaroslav@1646: import sun.invoke.empty.Empty; jaroslav@1646: import sun.invoke.util.ValueConversions; jaroslav@1646: import sun.invoke.util.VerifyType; jaroslav@1646: import sun.invoke.util.Wrapper; jaroslav@1646: import sun.reflect.CallerSensitive; jaroslav@1646: import sun.reflect.Reflection; jaroslav@1646: import static java.lang.invoke.LambdaForm.*; jaroslav@1646: import static java.lang.invoke.MethodHandleStatics.*; jaroslav@1646: import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Trusted implementation code for MethodHandle. jaroslav@1646: * @author jrose jaroslav@1646: */ jaroslav@1646: /*non-public*/ abstract class MethodHandleImpl { jaroslav@1646: /// Factory methods to create method handles: jaroslav@1646: jaroslav@1646: static void initStatics() { jaroslav@1646: // Trigger selected static initializations. jaroslav@1646: MemberName.Factory.INSTANCE.getClass(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static MethodHandle makeArrayElementAccessor(Class arrayClass, boolean isSetter) { jaroslav@1646: if (!arrayClass.isArray()) jaroslav@1646: throw newIllegalArgumentException("not an array: "+arrayClass); jaroslav@1646: MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter); jaroslav@1646: MethodType srcType = accessor.type().erase(); jaroslav@1646: MethodType lambdaType = srcType.invokerType(); jaroslav@1646: Name[] names = arguments(1, lambdaType); jaroslav@1646: Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount()); jaroslav@1646: names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args); jaroslav@1646: LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names); jaroslav@1646: MethodHandle mh = SimpleMethodHandle.make(srcType, form); jaroslav@1646: if (ArrayAccessor.needCast(arrayClass)) { jaroslav@1646: mh = mh.bindTo(arrayClass); jaroslav@1646: } jaroslav@1646: mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter)); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static final class ArrayAccessor { jaroslav@1646: /// Support for array element access jaroslav@1646: static final HashMap, MethodHandle> GETTER_CACHE = new HashMap<>(); // TODO use it jaroslav@1646: static final HashMap, MethodHandle> SETTER_CACHE = new HashMap<>(); // TODO use it jaroslav@1646: jaroslav@1646: static int getElementI(int[] a, int i) { return a[i]; } jaroslav@1646: static long getElementJ(long[] a, int i) { return a[i]; } jaroslav@1646: static float getElementF(float[] a, int i) { return a[i]; } jaroslav@1646: static double getElementD(double[] a, int i) { return a[i]; } jaroslav@1646: static boolean getElementZ(boolean[] a, int i) { return a[i]; } jaroslav@1646: static byte getElementB(byte[] a, int i) { return a[i]; } jaroslav@1646: static short getElementS(short[] a, int i) { return a[i]; } jaroslav@1646: static char getElementC(char[] a, int i) { return a[i]; } jaroslav@1646: static Object getElementL(Object[] a, int i) { return a[i]; } jaroslav@1646: jaroslav@1646: static void setElementI(int[] a, int i, int x) { a[i] = x; } jaroslav@1646: static void setElementJ(long[] a, int i, long x) { a[i] = x; } jaroslav@1646: static void setElementF(float[] a, int i, float x) { a[i] = x; } jaroslav@1646: static void setElementD(double[] a, int i, double x) { a[i] = x; } jaroslav@1646: static void setElementZ(boolean[] a, int i, boolean x) { a[i] = x; } jaroslav@1646: static void setElementB(byte[] a, int i, byte x) { a[i] = x; } jaroslav@1646: static void setElementS(short[] a, int i, short x) { a[i] = x; } jaroslav@1646: static void setElementC(char[] a, int i, char x) { a[i] = x; } jaroslav@1646: static void setElementL(Object[] a, int i, Object x) { a[i] = x; } jaroslav@1646: jaroslav@1646: static Object getElementL(Class arrayClass, Object[] a, int i) { arrayClass.cast(a); return a[i]; } jaroslav@1646: static void setElementL(Class arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; } jaroslav@1646: jaroslav@1646: // Weakly typed wrappers of Object[] accessors: jaroslav@1646: static Object getElementL(Object a, int i) { return getElementL((Object[])a, i); } jaroslav@1646: static void setElementL(Object a, int i, Object x) { setElementL((Object[]) a, i, x); } jaroslav@1646: static Object getElementL(Object arrayClass, Object a, int i) { return getElementL((Class) arrayClass, (Object[])a, i); } jaroslav@1646: static void setElementL(Object arrayClass, Object a, int i, Object x) { setElementL((Class) arrayClass, (Object[])a, i, x); } jaroslav@1646: jaroslav@1646: static boolean needCast(Class arrayClass) { jaroslav@1646: Class elemClass = arrayClass.getComponentType(); jaroslav@1646: return !elemClass.isPrimitive() && elemClass != Object.class; jaroslav@1646: } jaroslav@1646: static String name(Class arrayClass, boolean isSetter) { jaroslav@1646: Class elemClass = arrayClass.getComponentType(); jaroslav@1646: if (elemClass == null) throw new IllegalArgumentException(); jaroslav@1646: return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass); jaroslav@1646: } jaroslav@1646: static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false; // FIXME: decide jaroslav@1646: static MethodType type(Class arrayClass, boolean isSetter) { jaroslav@1646: Class elemClass = arrayClass.getComponentType(); jaroslav@1646: Class arrayArgClass = arrayClass; jaroslav@1646: if (!elemClass.isPrimitive()) { jaroslav@1646: arrayArgClass = Object[].class; jaroslav@1646: if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS) jaroslav@1646: arrayArgClass = Object.class; jaroslav@1646: } jaroslav@1646: if (!needCast(arrayClass)) { jaroslav@1646: return !isSetter ? jaroslav@1646: MethodType.methodType(elemClass, arrayArgClass, int.class) : jaroslav@1646: MethodType.methodType(void.class, arrayArgClass, int.class, elemClass); jaroslav@1646: } else { jaroslav@1646: Class classArgClass = Class.class; jaroslav@1646: if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS) jaroslav@1646: classArgClass = Object.class; jaroslav@1646: return !isSetter ? jaroslav@1646: MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) : jaroslav@1646: MethodType.methodType(void.class, classArgClass, arrayArgClass, int.class, Object.class); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: static MethodType correctType(Class arrayClass, boolean isSetter) { jaroslav@1646: Class elemClass = arrayClass.getComponentType(); jaroslav@1646: return !isSetter ? jaroslav@1646: MethodType.methodType(elemClass, arrayClass, int.class) : jaroslav@1646: MethodType.methodType(void.class, arrayClass, int.class, elemClass); jaroslav@1646: } jaroslav@1646: static MethodHandle getAccessor(Class arrayClass, boolean isSetter) { jaroslav@1646: String name = name(arrayClass, isSetter); jaroslav@1646: MethodType type = type(arrayClass, isSetter); jaroslav@1646: try { jaroslav@1646: return IMPL_LOOKUP.findStatic(ArrayAccessor.class, name, type); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: throw uncaughtException(ex); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Create a JVM-level adapter method handle to conform the given method jaroslav@1646: * handle to the similar newType, using only pairwise argument conversions. jaroslav@1646: * For each argument, convert incoming argument to the exact type needed. jaroslav@1646: * The argument conversions allowed are casting, boxing and unboxing, jaroslav@1646: * integral widening or narrowing, and floating point widening or narrowing. jaroslav@1646: * @param srcType required call type jaroslav@1646: * @param target original method handle jaroslav@1646: * @param level which strength of conversion is allowed jaroslav@1646: * @return an adapter to the original handle with the desired new type, jaroslav@1646: * or the original target if the types are already identical jaroslav@1646: * or null if the adaptation cannot be made jaroslav@1646: */ jaroslav@1646: static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) { jaroslav@1646: assert(level >= 0 && level <= 2); jaroslav@1646: MethodType dstType = target.type(); jaroslav@1646: assert(dstType.parameterCount() == target.type().parameterCount()); jaroslav@1646: if (srcType == dstType) jaroslav@1646: return target; jaroslav@1646: jaroslav@1646: // Calculate extra arguments (temporaries) required in the names array. jaroslav@1646: // FIXME: Use an ArrayList. Some arguments require more than one conversion step. jaroslav@1646: final int INARG_COUNT = srcType.parameterCount(); jaroslav@1646: int conversions = 0; jaroslav@1646: boolean[] needConv = new boolean[1+INARG_COUNT]; jaroslav@1646: for (int i = 0; i <= INARG_COUNT; i++) { jaroslav@1646: Class src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i); jaroslav@1646: Class dst = (i == INARG_COUNT) ? srcType.returnType() : dstType.parameterType(i); jaroslav@1646: if (!VerifyType.isNullConversion(src, dst) || jaroslav@1646: level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) { jaroslav@1646: needConv[i] = true; jaroslav@1646: conversions++; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: boolean retConv = needConv[INARG_COUNT]; jaroslav@1646: jaroslav@1646: final int IN_MH = 0; jaroslav@1646: final int INARG_BASE = 1; jaroslav@1646: final int INARG_LIMIT = INARG_BASE + INARG_COUNT; jaroslav@1646: final int NAME_LIMIT = INARG_LIMIT + conversions + 1; jaroslav@1646: final int RETURN_CONV = (!retConv ? -1 : NAME_LIMIT - 1); jaroslav@1646: final int OUT_CALL = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1; jaroslav@1646: jaroslav@1646: // Now build a LambdaForm. jaroslav@1646: MethodType lambdaType = srcType.basicType().invokerType(); jaroslav@1646: Name[] names = arguments(NAME_LIMIT - INARG_LIMIT, lambdaType); jaroslav@1646: jaroslav@1646: // Collect the arguments to the outgoing call, maybe with conversions: jaroslav@1646: final int OUTARG_BASE = 0; // target MH is Name.function, name Name.arguments[0] jaroslav@1646: Object[] outArgs = new Object[OUTARG_BASE + INARG_COUNT]; jaroslav@1646: jaroslav@1646: int nameCursor = INARG_LIMIT; jaroslav@1646: for (int i = 0; i < INARG_COUNT; i++) { jaroslav@1646: Class src = srcType.parameterType(i); jaroslav@1646: Class dst = dstType.parameterType(i); jaroslav@1646: jaroslav@1646: if (!needConv[i]) { jaroslav@1646: // do nothing: difference is trivial jaroslav@1646: outArgs[OUTARG_BASE + i] = names[INARG_BASE + i]; jaroslav@1646: continue; jaroslav@1646: } jaroslav@1646: jaroslav@1646: // Tricky case analysis follows. jaroslav@1646: MethodHandle fn = null; jaroslav@1646: if (src.isPrimitive()) { jaroslav@1646: if (dst.isPrimitive()) { jaroslav@1646: fn = ValueConversions.convertPrimitive(src, dst); jaroslav@1646: } else { jaroslav@1646: Wrapper w = Wrapper.forPrimitiveType(src); jaroslav@1646: MethodHandle boxMethod = ValueConversions.box(w); jaroslav@1646: if (dst == w.wrapperType()) jaroslav@1646: fn = boxMethod; jaroslav@1646: else jaroslav@1646: fn = boxMethod.asType(MethodType.methodType(dst, src)); jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: if (dst.isPrimitive()) { jaroslav@1646: // Caller has boxed a primitive. Unbox it for the target. jaroslav@1646: Wrapper w = Wrapper.forPrimitiveType(dst); jaroslav@1646: if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) { jaroslav@1646: fn = ValueConversions.unbox(dst); jaroslav@1646: } else if (src == Object.class || !Wrapper.isWrapperType(src)) { jaroslav@1646: // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int jaroslav@1646: // must include additional conversions jaroslav@1646: // src must be examined at runtime, to detect Byte, Character, etc. jaroslav@1646: MethodHandle unboxMethod = (level == 1 jaroslav@1646: ? ValueConversions.unbox(dst) jaroslav@1646: : ValueConversions.unboxCast(dst)); jaroslav@1646: fn = unboxMethod; jaroslav@1646: } else { jaroslav@1646: // Example: Byte->int jaroslav@1646: // Do this by reformulating the problem to Byte->byte. jaroslav@1646: Class srcPrim = Wrapper.forWrapperType(src).primitiveType(); jaroslav@1646: MethodHandle unbox = ValueConversions.unbox(srcPrim); jaroslav@1646: // Compose the two conversions. FIXME: should make two Names for this job jaroslav@1646: fn = unbox.asType(MethodType.methodType(dst, src)); jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: // Simple reference conversion. jaroslav@1646: // Note: Do not check for a class hierarchy relation jaroslav@1646: // between src and dst. In all cases a 'null' argument jaroslav@1646: // will pass the cast conversion. jaroslav@1646: fn = ValueConversions.cast(dst); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: Name conv = new Name(fn, names[INARG_BASE + i]); jaroslav@1646: assert(names[nameCursor] == null); jaroslav@1646: names[nameCursor++] = conv; jaroslav@1646: assert(outArgs[OUTARG_BASE + i] == null); jaroslav@1646: outArgs[OUTARG_BASE + i] = conv; jaroslav@1646: } jaroslav@1646: jaroslav@1646: // Build argument array for the call. jaroslav@1646: assert(nameCursor == OUT_CALL); jaroslav@1646: names[OUT_CALL] = new Name(target, outArgs); jaroslav@1646: jaroslav@1646: if (RETURN_CONV < 0) { jaroslav@1646: assert(OUT_CALL == names.length-1); jaroslav@1646: } else { jaroslav@1646: Class needReturn = srcType.returnType(); jaroslav@1646: Class haveReturn = dstType.returnType(); jaroslav@1646: MethodHandle fn; jaroslav@1646: Object[] arg = { names[OUT_CALL] }; jaroslav@1646: if (haveReturn == void.class) { jaroslav@1646: // synthesize a zero value for the given void jaroslav@1646: Object zero = Wrapper.forBasicType(needReturn).zero(); jaroslav@1646: fn = MethodHandles.constant(needReturn, zero); jaroslav@1646: arg = new Object[0]; // don't pass names[OUT_CALL] to conversion jaroslav@1646: } else { jaroslav@1646: MethodHandle identity = MethodHandles.identity(needReturn); jaroslav@1646: MethodType needConversion = identity.type().changeParameterType(0, haveReturn); jaroslav@1646: fn = makePairwiseConvert(identity, needConversion, level); jaroslav@1646: } jaroslav@1646: assert(names[RETURN_CONV] == null); jaroslav@1646: names[RETURN_CONV] = new Name(fn, arg); jaroslav@1646: assert(RETURN_CONV == names.length-1); jaroslav@1646: } jaroslav@1646: jaroslav@1646: LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names); jaroslav@1646: return SimpleMethodHandle.make(srcType, form); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static MethodHandle makeReferenceIdentity(Class refType) { jaroslav@1646: MethodType lambdaType = MethodType.genericMethodType(1).invokerType(); jaroslav@1646: Name[] names = arguments(1, lambdaType); jaroslav@1646: names[names.length - 1] = new Name(ValueConversions.identity(), names[1]); jaroslav@1646: LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names); jaroslav@1646: return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static MethodHandle makeVarargsCollector(MethodHandle target, Class arrayType) { jaroslav@1646: MethodType type = target.type(); jaroslav@1646: int last = type.parameterCount() - 1; jaroslav@1646: if (type.parameterType(last) != arrayType) jaroslav@1646: target = target.asType(type.changeParameterType(last, arrayType)); jaroslav@1646: target = target.asFixedArity(); // make sure this attribute is turned off jaroslav@1646: return new AsVarargsCollector(target, target.type(), arrayType); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static class AsVarargsCollector extends MethodHandle { jaroslav@1646: private final MethodHandle target; jaroslav@1646: private final Class arrayType; jaroslav@1646: private /*@Stable*/ MethodHandle asCollectorCache; jaroslav@1646: jaroslav@1646: AsVarargsCollector(MethodHandle target, MethodType type, Class arrayType) { jaroslav@1646: super(type, reinvokerForm(target)); jaroslav@1646: this.target = target; jaroslav@1646: this.arrayType = arrayType; jaroslav@1646: this.asCollectorCache = target.asCollector(arrayType, 0); jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override MethodHandle reinvokerTarget() { return target; } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: public boolean isVarargsCollector() { jaroslav@1646: return true; jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: public MethodHandle asFixedArity() { jaroslav@1646: return target; jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: public MethodHandle asTypeUncached(MethodType newType) { jaroslav@1646: MethodType type = this.type(); jaroslav@1646: int collectArg = type.parameterCount() - 1; jaroslav@1646: int newArity = newType.parameterCount(); jaroslav@1646: if (newArity == collectArg+1 && jaroslav@1646: type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) { jaroslav@1646: // if arity and trailing parameter are compatible, do normal thing jaroslav@1646: return asTypeCache = asFixedArity().asType(newType); jaroslav@1646: } jaroslav@1646: // check cache jaroslav@1646: MethodHandle acc = asCollectorCache; jaroslav@1646: if (acc != null && acc.type().parameterCount() == newArity) jaroslav@1646: return asTypeCache = acc.asType(newType); jaroslav@1646: // build and cache a collector jaroslav@1646: int arrayLength = newArity - collectArg; jaroslav@1646: MethodHandle collector; jaroslav@1646: try { jaroslav@1646: collector = asFixedArity().asCollector(arrayType, arrayLength); jaroslav@1646: assert(collector.type().parameterCount() == newArity) : "newArity="+newArity+" but collector="+collector; jaroslav@1646: } catch (IllegalArgumentException ex) { jaroslav@1646: throw new WrongMethodTypeException("cannot build collector", ex); jaroslav@1646: } jaroslav@1646: asCollectorCache = collector; jaroslav@1646: return asTypeCache = collector.asType(newType); jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MethodHandle setVarargs(MemberName member) { jaroslav@1646: if (member.isVarargs()) return this; jaroslav@1646: return asFixedArity(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MethodHandle viewAsType(MethodType newType) { jaroslav@1646: if (newType.lastParameterType() != type().lastParameterType()) jaroslav@1646: throw new InternalError(); jaroslav@1646: MethodHandle newTarget = asFixedArity().viewAsType(newType); jaroslav@1646: // put back the varargs bit: jaroslav@1646: return new AsVarargsCollector(newTarget, newType, arrayType); jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MemberName internalMemberName() { jaroslav@1646: return asFixedArity().internalMemberName(); jaroslav@1646: } jaroslav@1646: @Override jaroslav@1646: Class internalCallerClass() { jaroslav@1646: return asFixedArity().internalCallerClass(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /*non-public*/ jaroslav@1646: @Override jaroslav@1646: boolean isInvokeSpecial() { jaroslav@1646: return asFixedArity().isInvokeSpecial(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MethodHandle bindArgument(int pos, char basicType, Object value) { jaroslav@1646: return asFixedArity().bindArgument(pos, basicType, value); jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MethodHandle bindReceiver(Object receiver) { jaroslav@1646: return asFixedArity().bindReceiver(receiver); jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MethodHandle dropArguments(MethodType srcType, int pos, int drops) { jaroslav@1646: return asFixedArity().dropArguments(srcType, pos, drops); jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MethodHandle permuteArguments(MethodType newType, int[] reorder) { jaroslav@1646: return asFixedArity().permuteArguments(newType, reorder); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** Factory method: Spread selected argument. */ jaroslav@1646: static MethodHandle makeSpreadArguments(MethodHandle target, jaroslav@1646: Class spreadArgType, int spreadArgPos, int spreadArgCount) { jaroslav@1646: MethodType targetType = target.type(); jaroslav@1646: jaroslav@1646: for (int i = 0; i < spreadArgCount; i++) { jaroslav@1646: Class arg = VerifyType.spreadArgElementType(spreadArgType, i); jaroslav@1646: if (arg == null) arg = Object.class; jaroslav@1646: targetType = targetType.changeParameterType(spreadArgPos + i, arg); jaroslav@1646: } jaroslav@1646: target = target.asType(targetType); jaroslav@1646: jaroslav@1646: MethodType srcType = targetType jaroslav@1646: .replaceParameterTypes(spreadArgPos, spreadArgPos + spreadArgCount, spreadArgType); jaroslav@1646: // Now build a LambdaForm. jaroslav@1646: MethodType lambdaType = srcType.invokerType(); jaroslav@1646: Name[] names = arguments(spreadArgCount + 2, lambdaType); jaroslav@1646: int nameCursor = lambdaType.parameterCount(); jaroslav@1646: int[] indexes = new int[targetType.parameterCount()]; jaroslav@1646: jaroslav@1646: for (int i = 0, argIndex = 1; i < targetType.parameterCount() + 1; i++, argIndex++) { jaroslav@1646: Class src = lambdaType.parameterType(i); jaroslav@1646: if (i == spreadArgPos) { jaroslav@1646: // Spread the array. jaroslav@1646: MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType); jaroslav@1646: Name array = names[argIndex]; jaroslav@1646: names[nameCursor++] = new Name(Lazy.NF_checkSpreadArgument, array, spreadArgCount); jaroslav@1646: for (int j = 0; j < spreadArgCount; i++, j++) { jaroslav@1646: indexes[i] = nameCursor; jaroslav@1646: names[nameCursor++] = new Name(aload, array, j); jaroslav@1646: } jaroslav@1646: } else if (i < indexes.length) { jaroslav@1646: indexes[i] = argIndex; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: assert(nameCursor == names.length-1); // leave room for the final call jaroslav@1646: jaroslav@1646: // Build argument array for the call. jaroslav@1646: Name[] targetArgs = new Name[targetType.parameterCount()]; jaroslav@1646: for (int i = 0; i < targetType.parameterCount(); i++) { jaroslav@1646: int idx = indexes[i]; jaroslav@1646: targetArgs[i] = names[idx]; jaroslav@1646: } jaroslav@1646: names[names.length - 1] = new Name(target, (Object[]) targetArgs); jaroslav@1646: jaroslav@1646: LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names); jaroslav@1646: return SimpleMethodHandle.make(srcType, form); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static void checkSpreadArgument(Object av, int n) { jaroslav@1646: if (av == null) { jaroslav@1646: if (n == 0) return; jaroslav@1646: } else if (av instanceof Object[]) { jaroslav@1646: int len = ((Object[])av).length; jaroslav@1646: if (len == n) return; jaroslav@1646: } else { jaroslav@1646: int len = java.lang.reflect.Array.getLength(av); jaroslav@1646: if (len == n) return; jaroslav@1646: } jaroslav@1646: // fall through to error: jaroslav@1646: throw newIllegalArgumentException("array is not of length "+n); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Pre-initialized NamedFunctions for bootstrapping purposes. jaroslav@1646: * Factored in an inner class to delay initialization until first usage. jaroslav@1646: */ jaroslav@1646: private static class Lazy { jaroslav@1646: static final NamedFunction NF_checkSpreadArgument; jaroslav@1646: static { jaroslav@1646: try { jaroslav@1646: NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class jaroslav@1646: .getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); jaroslav@1646: NF_checkSpreadArgument.resolve(); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: throw newInternalError(ex); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** Factory method: Collect or filter selected argument(s). */ jaroslav@1646: static MethodHandle makeCollectArguments(MethodHandle target, jaroslav@1646: MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) { jaroslav@1646: MethodType targetType = target.type(); // (a..., c, [b...])=>r jaroslav@1646: MethodType collectorType = collector.type(); // (b...)=>c jaroslav@1646: int collectArgCount = collectorType.parameterCount(); jaroslav@1646: Class collectValType = collectorType.returnType(); jaroslav@1646: int collectValCount = (collectValType == void.class ? 0 : 1); jaroslav@1646: MethodType srcType = targetType // (a..., [b...])=>r jaroslav@1646: .dropParameterTypes(collectArgPos, collectArgPos+collectValCount); jaroslav@1646: if (!retainOriginalArgs) { // (a..., b...)=>r jaroslav@1646: srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterList()); jaroslav@1646: } jaroslav@1646: // in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ] jaroslav@1646: // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ] jaroslav@1646: // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ] jaroslav@1646: jaroslav@1646: // Now build a LambdaForm. jaroslav@1646: MethodType lambdaType = srcType.invokerType(); jaroslav@1646: Name[] names = arguments(2, lambdaType); jaroslav@1646: final int collectNamePos = names.length - 2; jaroslav@1646: final int targetNamePos = names.length - 1; jaroslav@1646: jaroslav@1646: Name[] collectorArgs = Arrays.copyOfRange(names, 1 + collectArgPos, 1 + collectArgPos + collectArgCount); jaroslav@1646: names[collectNamePos] = new Name(collector, (Object[]) collectorArgs); jaroslav@1646: jaroslav@1646: // Build argument array for the target. jaroslav@1646: // Incoming LF args to copy are: [ (mh) headArgs collectArgs tailArgs ]. jaroslav@1646: // Output argument array is [ headArgs (collectVal)? (collectArgs)? tailArgs ]. jaroslav@1646: Name[] targetArgs = new Name[targetType.parameterCount()]; jaroslav@1646: int inputArgPos = 1; // incoming LF args to copy to target jaroslav@1646: int targetArgPos = 0; // fill pointer for targetArgs jaroslav@1646: int chunk = collectArgPos; // |headArgs| jaroslav@1646: System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk); jaroslav@1646: inputArgPos += chunk; jaroslav@1646: targetArgPos += chunk; jaroslav@1646: if (collectValType != void.class) { jaroslav@1646: targetArgs[targetArgPos++] = names[collectNamePos]; jaroslav@1646: } jaroslav@1646: chunk = collectArgCount; jaroslav@1646: if (retainOriginalArgs) { jaroslav@1646: System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk); jaroslav@1646: targetArgPos += chunk; // optionally pass on the collected chunk jaroslav@1646: } jaroslav@1646: inputArgPos += chunk; jaroslav@1646: chunk = targetArgs.length - targetArgPos; // all the rest jaroslav@1646: System.arraycopy(names, inputArgPos, targetArgs, targetArgPos, chunk); jaroslav@1646: assert(inputArgPos + chunk == collectNamePos); // use of rest of input args also jaroslav@1646: names[targetNamePos] = new Name(target, (Object[]) targetArgs); jaroslav@1646: jaroslav@1646: LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names); jaroslav@1646: return SimpleMethodHandle.make(srcType, form); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static jaroslav@1646: MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) { jaroslav@1646: return testResult ? target : fallback; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static MethodHandle SELECT_ALTERNATIVE; jaroslav@1646: static MethodHandle selectAlternative() { jaroslav@1646: if (SELECT_ALTERNATIVE != null) return SELECT_ALTERNATIVE; jaroslav@1646: try { jaroslav@1646: SELECT_ALTERNATIVE jaroslav@1646: = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative", jaroslav@1646: MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: throw new RuntimeException(ex); jaroslav@1646: } jaroslav@1646: return SELECT_ALTERNATIVE; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static jaroslav@1646: MethodHandle makeGuardWithTest(MethodHandle test, jaroslav@1646: MethodHandle target, jaroslav@1646: MethodHandle fallback) { jaroslav@1646: MethodType basicType = target.type().basicType(); jaroslav@1646: MethodHandle invokeBasic = MethodHandles.basicInvoker(basicType); jaroslav@1646: int arity = basicType.parameterCount(); jaroslav@1646: int extraNames = 3; jaroslav@1646: MethodType lambdaType = basicType.invokerType(); jaroslav@1646: Name[] names = arguments(extraNames, lambdaType); jaroslav@1646: jaroslav@1646: Object[] testArgs = Arrays.copyOfRange(names, 1, 1 + arity, Object[].class); jaroslav@1646: Object[] targetArgs = Arrays.copyOfRange(names, 0, 1 + arity, Object[].class); jaroslav@1646: jaroslav@1646: // call test jaroslav@1646: names[arity + 1] = new Name(test, testArgs); jaroslav@1646: jaroslav@1646: // call selectAlternative jaroslav@1646: Object[] selectArgs = { names[arity + 1], target, fallback }; jaroslav@1646: names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs); jaroslav@1646: targetArgs[0] = names[arity + 2]; jaroslav@1646: jaroslav@1646: // call target or fallback jaroslav@1646: names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs); jaroslav@1646: jaroslav@1646: LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names); jaroslav@1646: return SimpleMethodHandle.make(target.type(), form); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static class GuardWithCatch { jaroslav@1646: private final MethodHandle target; jaroslav@1646: private final Class exType; jaroslav@1646: private final MethodHandle catcher; jaroslav@1646: // FIXME: Build the control flow out of foldArguments. jaroslav@1646: GuardWithCatch(MethodHandle target, Class exType, MethodHandle catcher) { jaroslav@1646: this.target = target; jaroslav@1646: this.exType = exType; jaroslav@1646: this.catcher = catcher; jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_V(Object... av) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(av); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, av); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L0() throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L1(Object a0) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L2(Object a0, Object a1) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0, a1); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0, a1); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0, a1, a2); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0, a1, a2); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0, a1, a2, a3); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0, a1, a2, a3); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0, a1, a2, a3, a4); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0, a1, a2, a3, a4); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0, a1, a2, a3, a4, a5); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: @LambdaForm.Hidden jaroslav@1646: private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { jaroslav@1646: try { jaroslav@1646: return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); jaroslav@1646: } catch (Throwable t) { jaroslav@1646: if (!exType.isInstance(t)) throw t; jaroslav@1646: return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: static MethodHandle[] makeInvokes() { jaroslav@1646: ArrayList invokes = new ArrayList<>(); jaroslav@1646: MethodHandles.Lookup lookup = IMPL_LOOKUP; jaroslav@1646: for (;;) { jaroslav@1646: int nargs = invokes.size(); jaroslav@1646: String name = "invoke_L"+nargs; jaroslav@1646: MethodHandle invoke = null; jaroslav@1646: try { jaroslav@1646: invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs)); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: } jaroslav@1646: if (invoke == null) break; jaroslav@1646: invokes.add(invoke); jaroslav@1646: } jaroslav@1646: assert(invokes.size() == 9); // current number of methods jaroslav@1646: return invokes.toArray(new MethodHandle[0]); jaroslav@1646: }; jaroslav@1646: static final MethodHandle[] INVOKES = makeInvokes(); jaroslav@1646: // For testing use this: jaroslav@1646: //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2); jaroslav@1646: static final MethodHandle VARARGS_INVOKE; jaroslav@1646: static { jaroslav@1646: try { jaroslav@1646: VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true)); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: throw uncaughtException(ex); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: jaroslav@1646: static jaroslav@1646: MethodHandle makeGuardWithCatch(MethodHandle target, jaroslav@1646: Class exType, jaroslav@1646: MethodHandle catcher) { jaroslav@1646: MethodType type = target.type(); jaroslav@1646: MethodType ctype = catcher.type(); jaroslav@1646: int nargs = type.parameterCount(); jaroslav@1646: if (nargs < GuardWithCatch.INVOKES.length) { jaroslav@1646: MethodType gtype = type.generic(); jaroslav@1646: MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); jaroslav@1646: // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0) jaroslav@1646: MethodHandle gtarget = makePairwiseConvert(target, gtype, 2); jaroslav@1646: MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2); jaroslav@1646: GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); jaroslav@1646: if (gtarget == null || gcatcher == null) throw new InternalError(); jaroslav@1646: MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard); jaroslav@1646: return makePairwiseConvert(ginvoker, type, 2); jaroslav@1646: } else { jaroslav@1646: target = target.asType(type.changeReturnType(Object.class)); jaroslav@1646: MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs); jaroslav@1646: MethodType catcherType = ctype.changeParameterType(0, Throwable.class) jaroslav@1646: .changeReturnType(Object.class); jaroslav@1646: catcher = catcher.asType(catcherType); jaroslav@1646: MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs); jaroslav@1646: GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); jaroslav@1646: if (gtarget == null || gcatcher == null) throw new InternalError(); jaroslav@1646: MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard); jaroslav@1646: MethodHandle gcollect = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false); jaroslav@1646: return makePairwiseConvert(gcollect, type, 2); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: static jaroslav@1646: MethodHandle throwException(MethodType type) { jaroslav@1646: assert(Throwable.class.isAssignableFrom(type.parameterType(0))); jaroslav@1646: int arity = type.parameterCount(); jaroslav@1646: if (arity > 1) { jaroslav@1646: return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1); jaroslav@1646: } jaroslav@1646: return makePairwiseConvert(throwException(), type, 2); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static MethodHandle THROW_EXCEPTION; jaroslav@1646: static MethodHandle throwException() { jaroslav@1646: MethodHandle mh = THROW_EXCEPTION; jaroslav@1646: if (mh != null) return mh; jaroslav@1646: try { jaroslav@1646: mh jaroslav@1646: = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", jaroslav@1646: MethodType.methodType(Empty.class, Throwable.class)); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: throw new RuntimeException(ex); jaroslav@1646: } jaroslav@1646: THROW_EXCEPTION = mh; jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: static Empty throwException(T t) throws T { throw t; } jaroslav@1646: jaroslav@1646: static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2]; jaroslav@1646: static MethodHandle fakeMethodHandleInvoke(MemberName method) { jaroslav@1646: int idx; jaroslav@1646: assert(method.isMethodHandleInvoke()); jaroslav@1646: switch (method.getName()) { jaroslav@1646: case "invoke": idx = 0; break; jaroslav@1646: case "invokeExact": idx = 1; break; jaroslav@1646: default: throw new InternalError(method.getName()); jaroslav@1646: } jaroslav@1646: MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE[idx]; jaroslav@1646: if (mh != null) return mh; jaroslav@1646: MethodType type = MethodType.methodType(Object.class, UnsupportedOperationException.class, jaroslav@1646: MethodHandle.class, Object[].class); jaroslav@1646: mh = throwException(type); jaroslav@1646: mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle")); jaroslav@1646: if (!method.getInvocationType().equals(mh.type())) jaroslav@1646: throw new InternalError(method.toString()); jaroslav@1646: mh = mh.withInternalMemberName(method); jaroslav@1646: mh = mh.asVarargsCollector(Object[].class); jaroslav@1646: assert(method.isVarargs()); jaroslav@1646: FAKE_METHOD_HANDLE_INVOKE[idx] = mh; jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Create an alias for the method handle which, when called, jaroslav@1646: * appears to be called from the same class loader and protection domain jaroslav@1646: * as hostClass. jaroslav@1646: * This is an expensive no-op unless the method which is called jaroslav@1646: * is sensitive to its caller. A small number of system methods jaroslav@1646: * are in this category, including Class.forName and Method.invoke. jaroslav@1646: */ jaroslav@1646: static jaroslav@1646: MethodHandle bindCaller(MethodHandle mh, Class hostClass) { jaroslav@1646: return BindCaller.bindCaller(mh, hostClass); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // Put the whole mess into its own nested class. jaroslav@1646: // That way we can lazily load the code and set up the constants. jaroslav@1646: private static class BindCaller { jaroslav@1646: static jaroslav@1646: MethodHandle bindCaller(MethodHandle mh, Class hostClass) { jaroslav@1646: // Do not use this function to inject calls into system classes. jaroslav@1646: if (hostClass == null jaroslav@1646: || (hostClass.isArray() || jaroslav@1646: hostClass.isPrimitive() || jaroslav@1646: hostClass.getName().startsWith("java.") || jaroslav@1646: hostClass.getName().startsWith("sun."))) { jaroslav@1646: throw new InternalError(); // does not happen, and should not anyway jaroslav@1646: } jaroslav@1646: // For simplicity, convert mh to a varargs-like method. jaroslav@1646: MethodHandle vamh = prepareForInvoker(mh); jaroslav@1646: // Cache the result of makeInjectedInvoker once per argument class. jaroslav@1646: MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass); jaroslav@1646: return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName(), hostClass); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static MethodHandle makeInjectedInvoker(Class hostClass) { jaroslav@1646: Class bcc = UNSAFE.defineAnonymousClass(hostClass, T_BYTES, null); jaroslav@1646: if (hostClass.getClassLoader() != bcc.getClassLoader()) jaroslav@1646: throw new InternalError(hostClass.getName()+" (CL)"); jaroslav@1646: try { jaroslav@1646: if (hostClass.getProtectionDomain() != bcc.getProtectionDomain()) jaroslav@1646: throw new InternalError(hostClass.getName()+" (PD)"); jaroslav@1646: } catch (SecurityException ex) { jaroslav@1646: // Self-check was blocked by security manager. This is OK. jaroslav@1646: // In fact the whole try body could be turned into an assertion. jaroslav@1646: } jaroslav@1646: try { jaroslav@1646: MethodHandle init = IMPL_LOOKUP.findStatic(bcc, "init", MethodType.methodType(void.class)); jaroslav@1646: init.invokeExact(); // force initialization of the class jaroslav@1646: } catch (Throwable ex) { jaroslav@1646: throw uncaughtException(ex); jaroslav@1646: } jaroslav@1646: MethodHandle bccInvoker; jaroslav@1646: try { jaroslav@1646: MethodType invokerMT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class); jaroslav@1646: bccInvoker = IMPL_LOOKUP.findStatic(bcc, "invoke_V", invokerMT); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: throw uncaughtException(ex); jaroslav@1646: } jaroslav@1646: // Test the invoker, to ensure that it really injects into the right place. jaroslav@1646: try { jaroslav@1646: MethodHandle vamh = prepareForInvoker(MH_checkCallerClass); jaroslav@1646: Object ok = bccInvoker.invokeExact(vamh, new Object[]{hostClass, bcc}); jaroslav@1646: } catch (Throwable ex) { jaroslav@1646: throw new InternalError(ex); jaroslav@1646: } jaroslav@1646: return bccInvoker; jaroslav@1646: } jaroslav@1646: private static ClassValue CV_makeInjectedInvoker = new ClassValue() { jaroslav@1646: @Override protected MethodHandle computeValue(Class hostClass) { jaroslav@1646: return makeInjectedInvoker(hostClass); jaroslav@1646: } jaroslav@1646: }; jaroslav@1646: jaroslav@1646: // Adapt mh so that it can be called directly from an injected invoker: jaroslav@1646: private static MethodHandle prepareForInvoker(MethodHandle mh) { jaroslav@1646: mh = mh.asFixedArity(); jaroslav@1646: MethodType mt = mh.type(); jaroslav@1646: int arity = mt.parameterCount(); jaroslav@1646: MethodHandle vamh = mh.asType(mt.generic()); jaroslav@1646: vamh.internalForm().compileToBytecode(); // eliminate LFI stack frames jaroslav@1646: vamh = vamh.asSpreader(Object[].class, arity); jaroslav@1646: vamh.internalForm().compileToBytecode(); // eliminate LFI stack frames jaroslav@1646: return vamh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: // Undo the adapter effect of prepareForInvoker: jaroslav@1646: private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, jaroslav@1646: MemberName member, jaroslav@1646: Class hostClass) { jaroslav@1646: MethodHandle mh = vamh.asCollector(Object[].class, type.parameterCount()); jaroslav@1646: mh = mh.asType(type); jaroslav@1646: mh = new WrappedMember(mh, type, member, hostClass); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final MethodHandle MH_checkCallerClass; jaroslav@1646: static { jaroslav@1646: final Class THIS_CLASS = BindCaller.class; jaroslav@1646: assert(checkCallerClass(THIS_CLASS, THIS_CLASS)); jaroslav@1646: try { jaroslav@1646: MH_checkCallerClass = IMPL_LOOKUP jaroslav@1646: .findStatic(THIS_CLASS, "checkCallerClass", jaroslav@1646: MethodType.methodType(boolean.class, Class.class, Class.class)); jaroslav@1646: assert((boolean) MH_checkCallerClass.invokeExact(THIS_CLASS, THIS_CLASS)); jaroslav@1646: } catch (Throwable ex) { jaroslav@1646: throw new InternalError(ex); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: @CallerSensitive jaroslav@1646: private static boolean checkCallerClass(Class expected, Class expected2) { jaroslav@1646: // This method is called via MH_checkCallerClass and so it's jaroslav@1646: // correct to ask for the immediate caller here. jaroslav@1646: Class actual = Reflection.getCallerClass(); jaroslav@1646: if (actual != expected && actual != expected2) jaroslav@1646: throw new InternalError("found "+actual.getName()+", expected "+expected.getName() jaroslav@1646: +(expected == expected2 ? "" : ", or else "+expected2.getName())); jaroslav@1646: return true; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final byte[] T_BYTES; jaroslav@1646: static { jaroslav@1646: final Object[] values = {null}; jaroslav@1646: AccessController.doPrivileged(new PrivilegedAction() { jaroslav@1646: public Void run() { jaroslav@1646: try { jaroslav@1646: Class tClass = T.class; jaroslav@1646: String tName = tClass.getName(); jaroslav@1646: String tResource = tName.substring(tName.lastIndexOf('.')+1)+".class"; jaroslav@1646: java.net.URLConnection uconn = tClass.getResource(tResource).openConnection(); jaroslav@1646: int len = uconn.getContentLength(); jaroslav@1646: byte[] bytes = new byte[len]; jaroslav@1646: try (java.io.InputStream str = uconn.getInputStream()) { jaroslav@1646: int nr = str.read(bytes); jaroslav@1646: if (nr != len) throw new java.io.IOException(tResource); jaroslav@1646: } jaroslav@1646: values[0] = bytes; jaroslav@1646: } catch (java.io.IOException ex) { jaroslav@1646: throw new InternalError(ex); jaroslav@1646: } jaroslav@1646: return null; jaroslav@1646: } jaroslav@1646: }); jaroslav@1646: T_BYTES = (byte[]) values[0]; jaroslav@1646: } jaroslav@1646: jaroslav@1646: // The following class is used as a template for Unsafe.defineAnonymousClass: jaroslav@1646: private static class T { jaroslav@1646: static void init() { } // side effect: initializes this class jaroslav@1646: static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable { jaroslav@1646: return vamh.invokeExact(args); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: jaroslav@1646: /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */ jaroslav@1646: static class WrappedMember extends MethodHandle { jaroslav@1646: private final MethodHandle target; jaroslav@1646: private final MemberName member; jaroslav@1646: private final Class callerClass; jaroslav@1646: jaroslav@1646: private WrappedMember(MethodHandle target, MethodType type, MemberName member, Class callerClass) { jaroslav@1646: super(type, reinvokerForm(target)); jaroslav@1646: this.target = target; jaroslav@1646: this.member = member; jaroslav@1646: this.callerClass = callerClass; jaroslav@1646: } jaroslav@1646: jaroslav@1646: @Override jaroslav@1646: MethodHandle reinvokerTarget() { jaroslav@1646: return target; jaroslav@1646: } jaroslav@1646: @Override jaroslav@1646: public MethodHandle asTypeUncached(MethodType newType) { jaroslav@1646: // This MH is an alias for target, except for the MemberName jaroslav@1646: // Drop the MemberName if there is any conversion. jaroslav@1646: return asTypeCache = target.asType(newType); jaroslav@1646: } jaroslav@1646: @Override jaroslav@1646: MemberName internalMemberName() { jaroslav@1646: return member; jaroslav@1646: } jaroslav@1646: @Override jaroslav@1646: Class internalCallerClass() { jaroslav@1646: return callerClass; jaroslav@1646: } jaroslav@1646: @Override jaroslav@1646: boolean isInvokeSpecial() { jaroslav@1646: return target.isInvokeSpecial(); jaroslav@1646: } jaroslav@1646: @Override jaroslav@1646: MethodHandle viewAsType(MethodType newType) { jaroslav@1646: return new WrappedMember(target, newType, member, callerClass); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: static MethodHandle makeWrappedMember(MethodHandle target, MemberName member) { jaroslav@1646: if (member.equals(target.internalMemberName())) jaroslav@1646: return target; jaroslav@1646: return new WrappedMember(target, target.type(), member, null); jaroslav@1646: } jaroslav@1646: jaroslav@1646: }