diff --git a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java index a77c904eb0e..b70bce26ed1 100644 --- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java +++ b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java @@ -141,7 +141,7 @@ class AdapterMethodHandle extends BoundMethodHandle { while (lastConv >= 0) { Class src = newType.parameterType(lastConv); // source type Class dst = oldType.parameterType(lastConv); // destination type - if (VerifyType.isNullConversion(src, dst)) { + if (isTrivialConversion(src, dst, level)) { --lastConv; } else { break; @@ -150,7 +150,7 @@ class AdapterMethodHandle extends BoundMethodHandle { Class needReturn = newType.returnType(); Class haveReturn = oldType.returnType(); - boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn); + boolean retConv = !isTrivialConversion(haveReturn, needReturn, level); // Now build a chain of one or more adapters. MethodHandle adapter = target, adapter2; @@ -158,7 +158,7 @@ class AdapterMethodHandle extends BoundMethodHandle { for (int i = 0; i <= lastConv; i++) { Class src = newType.parameterType(i); // source type Class dst = midType.parameterType(i); // destination type - if (VerifyType.isNullConversion(src, dst)) { + if (isTrivialConversion(src, dst, level)) { // do nothing: difference is trivial continue; } @@ -219,6 +219,22 @@ class AdapterMethodHandle extends BoundMethodHandle { return adapter; } + private static boolean isTrivialConversion(Class src, Class dst, int level) { + if (src == dst || dst == void.class) return true; + if (!VerifyType.isNullConversion(src, dst)) return false; + if (level > 1) return true; // explicitCastArguments + boolean sp = src.isPrimitive(); + boolean dp = dst.isPrimitive(); + if (sp != dp) return false; + if (sp) { + // in addition to being a null conversion, forbid boolean->int etc. + return Wrapper.forPrimitiveType(dst) + .isConvertibleFrom(Wrapper.forPrimitiveType(src)); + } else { + return dst.isAssignableFrom(src); + } + } + private static MethodHandle makeReturnConversion(MethodHandle target, Class haveReturn, Class needReturn) { MethodHandle adjustReturn; if (haveReturn == void.class) { @@ -596,7 +612,7 @@ class AdapterMethodHandle extends BoundMethodHandle { || !VerifyType.isNullConversion(castType, dst)) return false; int diff = diffTypes(newType, targetType, false); - return (diff == arg+1); // arg is sole non-trivial diff + return (diff == arg+1) || (diff == 0); // arg is sole non-trivial diff } /** Can an primitive conversion adapter validly convert src to dst? */ static boolean canCheckCast(Class src, Class dst) { @@ -1033,8 +1049,9 @@ class AdapterMethodHandle extends BoundMethodHandle { Class spreadArgType, int spreadArgPos, int spreadArgCount) { // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType MethodType targetType = target.type(); - if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount)) - return null; + assert(canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount)) + : "[newType, targetType, spreadArgType, spreadArgPos, spreadArgCount] = " + + Arrays.asList(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount); // dest is not significant; remove? int dest = T_VOID; for (int i = 0; i < spreadArgCount; i++) { diff --git a/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java b/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java index a747090b9a1..81f663bec4b 100644 --- a/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java +++ b/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java @@ -129,20 +129,17 @@ class InvokeGeneric { if (needType == erasedCallerType.returnType()) return false; // no conversions possible, since must be primitive or Object Class haveType = target.type().returnType(); - if (VerifyType.isNullConversion(haveType, needType)) + if (VerifyType.isNullConversion(haveType, needType) && !needType.isInterface()) return false; return true; } - private MethodHandle addReturnConversion(MethodHandle target, Class type) { - if (true) throw new RuntimeException("NYI"); + private MethodHandle addReturnConversion(MethodHandle finisher, Class type) { // FIXME: This is slow because it creates a closure node on every call that requires a return cast. - MethodType targetType = target.type(); + MethodType finisherType = finisher.type(); MethodHandle caster = ValueConversions.identity(type); - caster = caster.asType(MethodType.methodType(type, targetType.returnType())); - // Drop irrelevant arguments, because we only care about the return value: - caster = MethodHandles.dropArguments(caster, 1, targetType.parameterList()); - MethodHandle result = MethodHandles.foldArguments(caster, target); - return result.asType(target.type()); + caster = caster.asType(caster.type().changeParameterType(0, finisherType.returnType())); + finisher = MethodHandles.filterReturnValue(finisher, caster); + return finisher.asType(finisherType); } public String toString() { diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java index e6424f1516f..1ab8b344ed9 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java @@ -708,9 +708,9 @@ public abstract class MethodHandle { */ public MethodHandle asType(MethodType newType) { if (!type.isConvertibleTo(newType)) { - throw new WrongMethodTypeException("cannot convert "+type+" to "+newType); + throw new WrongMethodTypeException("cannot convert "+this+" to "+newType); } - return MethodHandles.convertArguments(this, newType); + return MethodHandleImpl.convertArguments(this, newType, 1); } /** diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java index b17251e4799..d8c435e86ff 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -93,9 +93,28 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; 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. + if (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; + } + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated MethodHandle allocator - = AllocateObject.make(rawConType.parameterType(0), rawConstructor); + = AllocateObject.make(allocateClass, rawConstructor); assert(allocator.type() .equals(rawConType.dropParameterTypes(0, 1).changeReturnType(rawConType.parameterType(0)))); return allocator; @@ -112,8 +131,16 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; super(invoker); this.allocateClass = allocateClass; this.rawConstructor = rawConstructor; + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated + } + // for allocation only: + private AllocateObject(Class allocateClass) { + super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class))); + this.allocateClass = allocateClass; + this.rawConstructor = null; } static MethodHandle make(Class allocateClass, MethodHandle rawConstructor) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated MethodType rawConType = rawConstructor.type(); assert(rawConType.parameterType(0) == allocateClass); MethodType newType = rawConType.dropParameterTypes(0, 1).changeReturnType(allocateClass); @@ -129,7 +156,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } else { MethodHandle invoke = VARARGS_INVOKE; MethodType conType = CON_TYPES[nargs]; - MethodHandle gcon = spreadArguments(rawConstructor, conType, 1); + MethodHandle gcon = spreadArgumentsFromPos(rawConstructor, conType, 1); if (gcon == null) return null; MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon); return collectArguments(galloc, newType, 1, null); @@ -214,9 +241,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // For testing use this: //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2); static final MethodHandle VARARGS_INVOKE; + static final MethodHandle ALLOCATE; static { try { VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true)); + ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0)); } catch (ReflectiveOperationException ex) { throw uncaughtException(ex); } @@ -747,7 +776,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; .insertParameterTypes(keepPosArgs, arrayType); return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength); } - static MethodHandle spreadArguments(MethodHandle target, MethodType newType, int spreadArgPos) { + static MethodHandle spreadArgumentsFromPos(MethodHandle target, MethodType newType, int spreadArgPos) { int arrayLength = target.type().parameterCount() - spreadArgPos; return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength); } @@ -761,9 +790,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // spread the last argument of newType to oldType assert(arrayLength == oldType.parameterCount() - spreadArgPos); assert(newType.parameterType(spreadArgPos) == arrayType); - MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); - if (res == null) throw new IllegalArgumentException("spread on "+target+" with "+arrayType.getSimpleName()); - return res; + return AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength); } static MethodHandle collectArguments(MethodHandle target, @@ -771,6 +798,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; 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 @@ -878,9 +906,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; this.test = test; this.target = target; this.fallback = fallback; + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated } // FIXME: Build the control flow out of foldArguments. static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) { + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated MethodType type = target.type(); int nargs = type.parameterCount(); if (nargs < INVOKES.length) { @@ -897,9 +927,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; MethodHandle invoke = VARARGS_INVOKE; MethodType gtype = MethodType.genericMethodType(1); assert(invoke.type().dropParameterTypes(0,1) == gtype); - MethodHandle gtest = spreadArguments(test, gtype.changeReturnType(boolean.class), 0); - MethodHandle gtarget = spreadArguments(target, gtype, 0); - MethodHandle gfallback = spreadArguments(fallback, gtype, 0); + MethodHandle gtest = spreadArgumentsFromPos(test, gtype.changeReturnType(boolean.class), 0); + MethodHandle gtarget = spreadArgumentsFromPos(target, gtype, 0); + MethodHandle gfallback = spreadArgumentsFromPos(fallback, gtype, 0); MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback); if (gtest == null || gtarget == null || gfallback == null) return null; return collectArguments(gguard, type, 0, null); @@ -989,10 +1019,49 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } } + 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 foldTargetType = target.type().insertParameterTypes(0, boolean.class); + if (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, fallback); + select = bindArgument(select, 1, 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; + } + assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated return GuardWithTest.make(test, target, fallback); } @@ -1144,11 +1213,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } else { MethodType gtype = MethodType.genericMethodType(0, true); MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); - MethodHandle gtarget = spreadArguments(target, gtype, 0); - MethodHandle gcatcher = spreadArguments(catcher, gcatchType, 1); + 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, null); + return collectArguments(gguard, type, 0, ValueConversions.varargsArray(nargs)).asType(type); } } diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java index 99985a09fee..a0a7b6ae51b 100644 --- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java +++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java @@ -1377,6 +1377,9 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type) */ public static MethodHandle convertArguments(MethodHandle target, MethodType newType) { + if (!target.type().isConvertibleTo(newType)) { + throw new WrongMethodTypeException("cannot convert "+target+" to "+newType); + } return MethodHandleImpl.convertArguments(target, newType, 1); } @@ -1567,7 +1570,7 @@ assert((int)twice.invokeExact(21) == 42); int numSpread = (outargs - spreadPos); MethodHandle res = null; if (spreadPos >= 0 && numSpread >= 0) { - res = MethodHandleImpl.spreadArguments(target, newType, spreadPos); + res = MethodHandleImpl.spreadArgumentsFromPos(target, newType, spreadPos); } if (res == null) { throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType); @@ -2135,7 +2138,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 int hpc = hargs.size(), tpc = targs.size(); if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs)) throw misMatchedTypes("target and handler types", ttype, htype); - handler = dropArguments(handler, hpc, hargs.subList(hpc, tpc)); + handler = dropArguments(handler, 1+hpc, targs.subList(hpc, tpc)); htype = handler.type(); } return MethodHandleImpl.makeGuardWithCatch(target, exType, handler); diff --git a/jdk/src/share/classes/sun/invoke/util/Wrapper.java b/jdk/src/share/classes/sun/invoke/util/Wrapper.java index 4b3179a3e88..7a05e1a0e9c 100644 --- a/jdk/src/share/classes/sun/invoke/util/Wrapper.java +++ b/jdk/src/share/classes/sun/invoke/util/Wrapper.java @@ -258,7 +258,7 @@ public enum Wrapper { } /** Return the wrapper that wraps values into the given wrapper type. - * If it is {@code Object} or an interface, return {@code OBJECT}. + * If it is {@code Object}, return {@code OBJECT}. * Otherwise, it must be a wrapper type. * The type must not be a primitive type. * @throws IllegalArgumentException for unexpected types @@ -277,8 +277,6 @@ public enum Wrapper { if (w != null && w.wrapperType == type) { return w; } - if (type.isInterface()) - return OBJECT; return null; } diff --git a/jdk/test/java/lang/invoke/6991596/Test6991596.java b/jdk/test/java/lang/invoke/6991596/Test6991596.java index b349a10c47b..a7083bd8114 100644 --- a/jdk/test/java/lang/invoke/6991596/Test6991596.java +++ b/jdk/test/java/lang/invoke/6991596/Test6991596.java @@ -51,10 +51,10 @@ public class Test6991596 { return MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(ret, arg)); } static MethodHandle getmh2(MethodHandle mh1, Class ret, Class arg) { - return MethodHandles.convertArguments(mh1, MethodType.methodType(ret, arg)); + return MethodHandles.explicitCastArguments(mh1, MethodType.methodType(ret, arg)); } static MethodHandle getmh3(MethodHandle mh1, Class ret, Class arg) { - return MethodHandles.convertArguments(mh1, MethodType.methodType(ret, arg)); + return MethodHandles.explicitCastArguments(mh1, MethodType.methodType(ret, arg)); } // test adapter_opt_i2i diff --git a/jdk/test/java/lang/invoke/InvokeGenericTest.java b/jdk/test/java/lang/invoke/InvokeGenericTest.java index 8ced6c66582..3d4933384db 100644 --- a/jdk/test/java/lang/invoke/InvokeGenericTest.java +++ b/jdk/test/java/lang/invoke/InvokeGenericTest.java @@ -53,9 +53,9 @@ public class InvokeGenericTest { if (vstr != null) verbosity = Integer.parseInt(vstr); } - public static void main(String... av) throws Throwable { - new InvokeGenericTest().testFirst(); - } +// public static void main(String... av) throws Throwable { +// new InvokeGenericTest().testFirst(); +// } @Test public void testFirst() throws Throwable { @@ -470,8 +470,6 @@ public class InvokeGenericTest { return allMethodTypes(argc, argc, types); } - interface RandomInterface { } - MethodHandle toString_MH; @Test @@ -480,33 +478,62 @@ public class InvokeGenericTest { toString_MH = LOOKUP. findVirtual(Object.class, "toString", MethodType.methodType(String.class)); Object[] args = { "one", "two" }; - for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) { + for (MethodType type : allMethodTypes(2, Object.class, String.class, CharSequence.class)) { testReferenceConversions(type, args); } } public void testReferenceConversions(MethodType type, Object... args) throws Throwable { countTest(); - if (verbosity > 3) System.out.println("target type: "+type); + int nargs = args.length; + List argList = Arrays.asList(args); + String expectString = argList.toString(); + if (verbosity > 3) System.out.println("target type: "+type+expectString); MethodHandle mh = callable(type.parameterList()); - MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList()); - mh = MethodHandles.foldArguments(tsdrop, mh); + mh = MethodHandles.filterReturnValue(mh, toString_MH); mh = mh.asType(type); - Object res = mh.invoke((String)args[0], (Object)args[1]); + Object res = null; + if (nargs == 2) { + res = mh.invoke((Object)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = mh.invoke((String)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = mh.invoke((Object)args[0], (String)args[1]); + assertEquals(expectString, res); + res = mh.invoke((String)args[0], (String)args[1]); + assertEquals(expectString, res); + res = mh.invoke((String)args[0], (CharSequence)args[1]); + assertEquals(expectString, res); + res = mh.invoke((CharSequence)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = (String) mh.invoke((Object)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = (String) mh.invoke((String)args[0], (Object)args[1]); + assertEquals(expectString, res); + res = (CharSequence) mh.invoke((String)args[0], (Object)args[1]); + assertEquals(expectString, res); + } else { + assert(false); // write this code + } //System.out.println(res); - assertEquals(Arrays.asList(args).toString(), res); } - @Test @Ignore("known failure pending 6939861") + @Test public void testBoxConversions() throws Throwable { startTest("testBoxConversions"); countTest(); Object[] args = { 1, 2 }; MethodHandle mh = callable(Object.class, int.class); - Object res; List resl; + Object res; List resl; int resi; res = resl = (List) mh.invoke((int)args[0], (Object)args[1]); //System.out.println(res); assertEquals(Arrays.asList(args), res); + mh = MethodHandles.identity(int.class); + mh = MethodHandles.dropArguments(mh, 1, int.class); + res = resi = (int) mh.invoke((Object) args[0], (Object) args[1]); + assertEquals(args[0], res); + res = resi = (int) mh.invoke((int) args[0], (Object) args[1]); + assertEquals(args[0], res); } }