< prev index next >

test/java/lang/invoke/MethodHandlesTest.java

Print this page
rev 13106 : 8139885: implement JEP 274: enhanced method handles

*** 30,39 **** --- 30,40 ---- package test.java.lang.invoke; import test.java.lang.invoke.remote.RemoteExample; import java.lang.invoke.*; + import static java.lang.invoke.MethodType.methodType; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.*; import java.util.*; import org.junit.*; import static org.junit.Assert.*;
*** 446,455 **** --- 447,457 ---- private SubExample(int x) { called("<init>", this, x); } public SubExample() { super("SubExample#"+nextArg()); } } public static interface IntExample { public void v0(); + public default void vd() { called("vd", this); } public static class Impl implements IntExample { public void v0() { called("Int/v0", this); } final String name; public Impl() { name = "Impl#"+nextArg(); } @Override public String toString() { return name; }
*** 717,752 **** } public void testFindSpecial0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("findSpecial"); ! testFindSpecial(SubExample.class, Example.class, void.class, "v0"); ! testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0"); ! testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0"); // Do some negative testing: for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) { testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0"); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus"); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", Void.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0"); } } void testFindSpecial(Class<?> specialCaller, ! Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { if (specialCaller == RemoteExample.class) { testFindSpecial(false, EXAMPLE, specialCaller, defc, ret, name, params); testFindSpecial(false, PRIVATE, specialCaller, defc, ret, name, params); testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params); testFindSpecial(true, SUBCLASS, specialCaller, defc, ret, name, params); testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); return; } testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params); testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params); ! testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params); testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params); testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); } void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { --- 719,756 ---- } public void testFindSpecial0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("findSpecial"); ! testFindSpecial(SubExample.class, Example.class, void.class, false, "v0"); ! testFindSpecial(SubExample.class, Example.class, void.class, false, "pkg_v0"); ! testFindSpecial(RemoteExample.class, PubExample.class, void.class, false, "Pub/pro_v0"); ! testFindSpecial(Example.class, IntExample.class, void.class, true, "vd"); // Do some negative testing: for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) { testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0"); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus"); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", Void.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0"); + testFindSpecial(false, lookup, Example.class, IntExample.class, void.class, "v0"); } } void testFindSpecial(Class<?> specialCaller, ! Class<?> defc, Class<?> ret, boolean dflt, String name, Class<?>... params) throws Throwable { if (specialCaller == RemoteExample.class) { testFindSpecial(false, EXAMPLE, specialCaller, defc, ret, name, params); testFindSpecial(false, PRIVATE, specialCaller, defc, ret, name, params); testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params); testFindSpecial(true, SUBCLASS, specialCaller, defc, ret, name, params); testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); return; } testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params); testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params); ! testFindSpecial(false || dflt, PACKAGE, specialCaller, defc, ret, name, params); testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params); testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); } void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
*** 1832,1869 **** @Test // SLOW public void testSpreadArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0); } public void testSpreadArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("spreadArguments"); for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) { if (verbosity >= 3) System.out.println("spreadArguments "+argType); for (int nargs = 0; nargs < 50; nargs++) { if (CAN_TEST_LIGHTLY && nargs > 11) break; for (int pos = 0; pos <= nargs; pos++) { if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) continue; ! testSpreadArguments(argType, pos, nargs); } } } } ! public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable { countTest(); - Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); MethodHandle target2 = varargsArray(arrayType, nargs); MethodHandle target = target2.asType(target2.type().generic()); if (verbosity >= 3) System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); Object[] args = randomArgs(target2.type().parameterArray()); // make sure the target does what we think it does: if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { Object[] check = (Object[]) target.invokeWithArguments(args); assertArrayEquals(args, check); switch (nargs) { case 0: --- 1836,1945 ---- @Test // SLOW public void testSpreadArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0); + CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1); } public void testSpreadArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("spreadArguments"); for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) { if (verbosity >= 3) System.out.println("spreadArguments "+argType); + Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); for (int nargs = 0; nargs < 50; nargs++) { if (CAN_TEST_LIGHTLY && nargs > 11) break; for (int pos = 0; pos <= nargs; pos++) { if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) continue; ! testSpreadArguments(argType, arrayType, pos, nargs); } } } } ! public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, int nargs) throws Throwable { countTest(); MethodHandle target2 = varargsArray(arrayType, nargs); MethodHandle target = target2.asType(target2.type().generic()); if (verbosity >= 3) System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); Object[] args = randomArgs(target2.type().parameterArray()); // make sure the target does what we think it does: + checkTarget(argType, pos, nargs, target, args); + List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList()); + { // modify newParams in place + List<Class<?>> spreadParams = newParams.subList(pos, nargs); + spreadParams.clear(); spreadParams.add(arrayType); + } + MethodType newType = MethodType.methodType(arrayType, newParams); + MethodHandle result = target2.asSpreader(arrayType, nargs-pos); + assert(result.type() == newType) : Arrays.asList(result, newType); + result = result.asType(newType.generic()); + Object returnValue; + if (pos == 0) { + Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); + returnValue = result.invokeExact(args2); + } else { + Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); + args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); + returnValue = result.invokeWithArguments(args1); + } + checkReturnValue(argType, args, result, returnValue); + } + public void testSpreadArguments1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("spreadArguments/pos"); + for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) { + if (verbosity >= 3) + System.out.println("spreadArguments "+argType); + Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); + for (int nargs = 0; nargs < 50; nargs++) { + if (CAN_TEST_LIGHTLY && nargs > 11) break; + for (int pos = 0; pos <= nargs; pos++) { + if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; + if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) + continue; + for (int spr = 1; spr < nargs - pos; ++spr) { + if (spr > 4 && spr != 7 && spr != 11 && spr != 20 && spr < nargs - pos - 4) continue; + testSpreadArguments(argType, arrayType, pos, spr, nargs); + } + } + } + } + } + public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, int spread, int nargs) throws Throwable { + countTest(); + MethodHandle target2 = varargsArray(arrayType, nargs); + MethodHandle target = target2.asType(target2.type().generic()); + if (verbosity >= 3) + System.out.println("spread into " + target2 + " [" + pos + ".." + (pos + spread) + "["); + Object[] args = randomArgs(target2.type().parameterArray()); + // make sure the target does what we think it does: + checkTarget(argType, pos, nargs, target, args); + List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList()); + { // modify newParams in place + List<Class<?>> spreadParams = newParams.subList(pos, pos + spread); + spreadParams.clear(); + spreadParams.add(arrayType); + } + MethodType newType = MethodType.methodType(arrayType, newParams); + MethodHandle result = target2.asSpreader(pos, arrayType, spread); + assert (result.type() == newType) : Arrays.asList(result, newType); + result = result.asType(newType.generic()); + // args1 has nargs-spread entries, plus one for the to-be-spread array + int args1Length = nargs - (spread - 1); + Object[] args1 = new Object[args1Length]; + System.arraycopy(args, 0, args1, 0, pos); + args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, pos + spread)); + System.arraycopy(args, pos + spread, args1, pos + 1, nargs - spread - pos); + Object returnValue = result.invokeWithArguments(args1); + checkReturnValue(argType, args, result, returnValue); + } + private static void checkTarget(Class<?> argType, int pos, int nargs, MethodHandle target, Object[] args) throws Throwable { if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { Object[] check = (Object[]) target.invokeWithArguments(args); assertArrayEquals(args, check); switch (nargs) { case 0:
*** 1878,1905 **** check = (Object[]) (Object) target.invokeExact(args[0], args[1]); assertArrayEquals(args, check); break; } } - List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList()); - { // modify newParams in place - List<Class<?>> spreadParams = newParams.subList(pos, nargs); - spreadParams.clear(); spreadParams.add(arrayType); - } - MethodType newType = MethodType.methodType(arrayType, newParams); - MethodHandle result = target2.asSpreader(arrayType, nargs-pos); - assert(result.type() == newType) : Arrays.asList(result, newType); - result = result.asType(newType.generic()); - Object returnValue; - if (pos == 0) { - Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); - returnValue = result.invokeExact(args2); - } else { - Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); - args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); - returnValue = result.invokeWithArguments(args1); } String argstr = Arrays.toString(args); if (!argType.isPrimitive()) { Object[] rv = (Object[]) returnValue; String rvs = Arrays.toString(rv); if (!Arrays.equals(args, rv)) { --- 1954,1965 ---- check = (Object[]) (Object) target.invokeExact(args[0], args[1]); assertArrayEquals(args, check); break; } } } + private static void checkReturnValue(Class<?> argType, Object[] args, MethodHandle result, Object returnValue) { String argstr = Arrays.toString(args); if (!argType.isPrimitive()) { Object[] rv = (Object[]) returnValue; String rvs = Arrays.toString(rv); if (!Arrays.equals(args, rv)) {
*** 1930,1939 **** --- 1990,2000 ---- } @Test // SLOW public void testAsCollector() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testAsCollector0); + CodeCacheOverflowProcessor.runMHTest(this::testAsCollector1); } public void testAsCollector0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("asCollector");
*** 1972,1981 **** --- 2033,2087 ---- // assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]); // returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]); // collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]); assertArrayEquals(collectedArgs, returnValue); } + public void testAsCollector1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("asCollector/pos"); + for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) { + if (verbosity >= 3) + System.out.println("asCollector/pos "+argType); + for (int nargs = 0; nargs < 50; nargs++) { + if (CAN_TEST_LIGHTLY && nargs > 11) break; + for (int pos = 0; pos <= nargs; pos++) { + if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; + if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) + continue; + for (int coll = 1; coll < nargs - pos; ++coll) { + if (coll > 4 && coll != 7 && coll != 11 && coll != 20 && coll < nargs - pos - 4) continue; + testAsCollector(argType, pos, coll, nargs); + } + } + } + } + } + public void testAsCollector(Class<?> argType, int pos, int collect, int nargs) throws Throwable { + countTest(); + // fake up a MH with the same type as the desired adapter: + MethodHandle fake = varargsArray(nargs); + fake = changeArgTypes(fake, argType); + MethodType newType = fake.type(); + Object[] args = randomArgs(newType.parameterArray()); + // here is what should happen: + // new arg list has "collect" less arguments, but one extra for collected arguments array + int collectedLength = nargs-(collect-1); + Object[] collectedArgs = new Object[collectedLength]; + System.arraycopy(args, 0, collectedArgs, 0, pos); + collectedArgs[pos] = Arrays.copyOfRange(args, pos, pos+collect); + System.arraycopy(args, pos+collect, collectedArgs, pos+1, args.length-(pos+collect)); + // here is the MH which will witness the collected argument part (not tail!): + MethodHandle target = varargsArray(collectedLength); + target = changeArgTypes(target, 0, pos, argType); + target = changeArgTypes(target, pos, pos+1, Object[].class); + target = changeArgTypes(target, pos+1, collectedLength, argType); + if (verbosity >= 3) + System.out.println("collect "+collect+" from "+Arrays.asList(args)+" ["+pos+".."+(pos+collect)+"["); + MethodHandle result = target.asCollector(pos, Object[].class, collect).asType(newType); + Object[] returnValue = (Object[]) result.invokeWithArguments(args); + assertArrayEquals(collectedArgs, returnValue); + } @Test // SLOW public void testInsertArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testInsertArguments0); }
*** 2115,2139 **** } public void testCollectArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("collectArguments"); ! testFoldOrCollectArguments(true); } @Test public void testFoldArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments0); } public void testFoldArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("foldArguments"); ! testFoldOrCollectArguments(false); } ! void testFoldOrCollectArguments(boolean isCollect) throws Throwable { for (Class<?> lastType : new Class<?>[]{ Object.class, String.class, int.class }) { for (Class<?> collectType : new Class<?>[]{ Object.class, String.class, int.class, void.class }) { int maxArity = 10; if (collectType != String.class) maxArity = 5; if (lastType != Object.class) maxArity = 4; --- 2221,2253 ---- } public void testCollectArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("collectArguments"); ! testFoldOrCollectArguments(true, false); } @Test public void testFoldArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments0); + CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments1); } public void testFoldArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("foldArguments"); ! testFoldOrCollectArguments(false, false); } ! public void testFoldArguments1() throws Throwable { ! if (CAN_SKIP_WORKING) return; ! startTest("foldArguments/pos"); ! testFoldOrCollectArguments(false, true); ! } ! ! void testFoldOrCollectArguments(boolean isCollect, boolean withFoldPos) throws Throwable { ! assert !(isCollect && withFoldPos); // exclude illegal argument combination for (Class<?> lastType : new Class<?>[]{ Object.class, String.class, int.class }) { for (Class<?> collectType : new Class<?>[]{ Object.class, String.class, int.class, void.class }) { int maxArity = 10; if (collectType != String.class) maxArity = 5; if (lastType != Object.class) maxArity = 4;
*** 2144,2154 **** Map<Object,Integer> argTypesSeen = new HashMap<>(); for (int mix = 0; mix <= maxMix; mix++) { if (!mixArgs(argTypes, mix, argTypesSeen)) continue; for (int collect = 0; collect <= nargs; collect++) { for (int pos = 0; pos <= nargs - collect; pos++) { ! testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect); } } } } } --- 2258,2268 ---- Map<Object,Integer> argTypesSeen = new HashMap<>(); for (int mix = 0; mix <= maxMix; mix++) { if (!mixArgs(argTypes, mix, argTypesSeen)) continue; for (int collect = 0; collect <= nargs; collect++) { for (int pos = 0; pos <= nargs - collect; pos++) { ! testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect, withFoldPos); } } } } }
*** 2184,2200 **** void testFoldOrCollectArguments(List<Class<?>> argTypes, // argument types minus the inserted combineType int pos, int fold, // position and length of the folded arguments Class<?> combineType, // type returned from the combiner Class<?> lastType, // type returned from the target ! boolean isCollect) throws Throwable { int nargs = argTypes.size(); ! if (pos != 0 && !isCollect) return; // can fold only at pos=0 for now countTest(); List<Class<?>> combineArgTypes = argTypes.subList(pos, pos + fold); List<Class<?>> targetArgTypes = new ArrayList<>(argTypes); ! if (isCollect) // does targret see arg[pos..pos+cc-1]? targetArgTypes.subList(pos, pos + fold).clear(); if (combineType != void.class) targetArgTypes.add(pos, combineType); MethodHandle target = varargsList(targetArgTypes, lastType); MethodHandle combine = varargsList(combineArgTypes, combineType); --- 2298,2315 ---- void testFoldOrCollectArguments(List<Class<?>> argTypes, // argument types minus the inserted combineType int pos, int fold, // position and length of the folded arguments Class<?> combineType, // type returned from the combiner Class<?> lastType, // type returned from the target ! boolean isCollect, ! boolean withFoldPos) throws Throwable { int nargs = argTypes.size(); ! if (pos != 0 && !isCollect && !withFoldPos) return; // test MethodHandles.foldArguments(MH,MH) only for pos=0 countTest(); List<Class<?>> combineArgTypes = argTypes.subList(pos, pos + fold); List<Class<?>> targetArgTypes = new ArrayList<>(argTypes); ! if (isCollect) // does target see arg[pos..pos+cc-1]? targetArgTypes.subList(pos, pos + fold).clear(); if (combineType != void.class) targetArgTypes.add(pos, combineType); MethodHandle target = varargsList(targetArgTypes, lastType); MethodHandle combine = varargsList(combineArgTypes, combineType);
*** 2203,2213 **** System.out.println((isCollect ? "collect" : "fold")+" "+target+" with "+combine); MethodHandle target2; if (isCollect) target2 = MethodHandles.collectArguments(target, pos, combine); else ! target2 = MethodHandles.foldArguments(target, combine); // Simulate expected effect of combiner on arglist: List<Object> expectedList = new ArrayList<>(argsToPass); List<Object> argsToFold = expectedList.subList(pos, pos + fold); if (verbosity >= 3) System.out.println((isCollect ? "collect" : "fold")+": "+argsToFold+" into "+target2); --- 2318,2328 ---- System.out.println((isCollect ? "collect" : "fold")+" "+target+" with "+combine); MethodHandle target2; if (isCollect) target2 = MethodHandles.collectArguments(target, pos, combine); else ! target2 = withFoldPos ? MethodHandles.foldArguments(target, pos, combine) : MethodHandles.foldArguments(target, combine); // Simulate expected effect of combiner on arglist: List<Object> expectedList = new ArrayList<>(argsToPass); List<Object> argsToFold = expectedList.subList(pos, pos + fold); if (verbosity >= 3) System.out.println((isCollect ? "collect" : "fold")+": "+argsToFold+" into "+target2);
*** 2539,2548 **** --- 2654,2860 ---- assertCalled(willCall, argList); } } @Test + public void testGenericLoopCombinator() throws Throwable { + CodeCacheOverflowProcessor.runMHTest(this::testGenericLoopCombinator0); + } + public void testGenericLoopCombinator0() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("loop"); + // Test as follows: + // * Have an increasing number of loop-local state. Local state type diversity grows with the number. + // * Initializers set the starting value of loop-local state from the corresponding loop argument. + // * For each local state element, there is a predicate - for all state combinations, exercise all predicates. + // * Steps modify each local state element in each iteration. + // * Finalizers group all local state elements into a resulting array. Verify end values. + // * Exercise both pre- and post-checked loops. + // Local state types, start values, predicates, and steps: + // * int a, 0, a < 7, a = a + 1 + // * double b, 7.0, b > 0.5, b = b / 2.0 + // * String c, "start", c.length <= 9, c = c + a + final Class<?>[] argTypes = new Class<?>[] {int.class, double.class, String.class}; + final Object[][] args = new Object[][] { + new Object[]{0 }, + new Object[]{0, 7.0 }, + new Object[]{0, 7.0, "start"} + }; + // These are the expected final state tuples for argument type tuple / predicate combinations, for pre- and + // post-checked loops: + final Object[][] preCheckedResults = new Object[][] { + new Object[]{7 }, // (int) / int + new Object[]{7, 0.0546875 }, // (int,double) / int + new Object[]{5, 0.4375 }, // (int,double) / double + new Object[]{7, 0.0546875, "start1234567"}, // (int,double,String) / int + new Object[]{5, 0.4375, "start1234" }, // (int,double,String) / double + new Object[]{6, 0.109375, "start12345" } // (int,double,String) / String + }; + final Object[][] postCheckedResults = new Object[][] { + new Object[]{7 }, // (int) / int + new Object[]{7, 0.109375 }, // (int,double) / int + new Object[]{4, 0.4375 }, // (int,double) / double + new Object[]{7, 0.109375, "start123456"}, // (int,double,String) / int + new Object[]{4, 0.4375, "start123" }, // (int,double,String) / double + new Object[]{5, 0.21875, "start12345" } // (int,double,String) / String + }; + final Lookup l = MethodHandles.lookup(); + final Class<?> MHT = MethodHandlesTest.class; + final Class<?> B = boolean.class; + final Class<?> I = int.class; + final Class<?> D = double.class; + final Class<?> S = String.class; + final MethodHandle hip = l.findStatic(MHT, "loopIntPred", methodType(B, I)); + final MethodHandle hdp = l.findStatic(MHT, "loopDoublePred", methodType(B, I, D)); + final MethodHandle hsp = l.findStatic(MHT, "loopStringPred", methodType(B, I, D, S)); + final MethodHandle his = l.findStatic(MHT, "loopIntStep", methodType(I, I)); + final MethodHandle hds = l.findStatic(MHT, "loopDoubleStep", methodType(D, I, D)); + final MethodHandle hss = l.findStatic(MHT, "loopStringStep", methodType(S, I, D, S)); + final MethodHandle[] preds = new MethodHandle[] {hip, hdp, hsp}; + final MethodHandle[] steps = new MethodHandle[] {his, hds, hss}; + for (int nargs = 1, useResultsStart = 0; nargs <= argTypes.length; useResultsStart += nargs++) { + Class<?>[] useArgTypes = Arrays.copyOf(argTypes, nargs, Class[].class); + MethodHandle[] usePreds = Arrays.copyOf(preds, nargs, MethodHandle[].class); + MethodHandle[] useSteps = Arrays.copyOf(steps, nargs, MethodHandle[].class); + Object[] useArgs = args[nargs - 1]; + Object[][] usePreCheckedResults = new Object[nargs][]; + Object[][] usePostCheckedResults = new Object[nargs][]; + System.arraycopy(preCheckedResults, useResultsStart, usePreCheckedResults, 0, nargs); + System.arraycopy(postCheckedResults, useResultsStart, usePostCheckedResults, 0, nargs); + testGenericLoopCombinator(nargs, useArgTypes, usePreds, useSteps, useArgs, usePreCheckedResults, + usePostCheckedResults); + } + } + void testGenericLoopCombinator(int nargs, Class<?>[] argTypes, MethodHandle[] preds, MethodHandle[] steps, + Object[] args, Object[][] preCheckedResults, Object[][] postCheckedResults) + throws Throwable { + List<Class<?>> lArgTypes = Arrays.asList(argTypes); + // Predicate and step handles are passed in as arguments, initializer and finalizer handles are constructed here + // from the available information. + MethodHandle[] inits = new MethodHandle[nargs]; + for (int i = 0; i < nargs; ++i) { + MethodHandle h; + // Initializers are meant to return whatever they are passed at a given argument position. This means that + // additional arguments may have to be appended and prepended. + h = MethodHandles.identity(argTypes[i]); + if (i < nargs - 1) { + h = MethodHandles.dropArguments(h, 1, lArgTypes.subList(i + 1, nargs)); + } + if (i > 0) { + h = MethodHandles.dropArguments(h, 0, lArgTypes.subList(0, i)); + } + inits[i] = h; + } + // Finalizers are all meant to collect all of the loop-local state in a single array and return that. Local + // state is passed before the loop args. Construct such a finalizer by first taking a varargsArray collector for + // the number of local state arguments, and then appending the loop args as to-be-dropped arguments. + MethodHandle[] finis = new MethodHandle[nargs]; + MethodHandle genericFini = MethodHandles.dropArguments( + varargsArray(nargs).asType(methodType(Object[].class, lArgTypes)), nargs, lArgTypes); + Arrays.fill(finis, genericFini); + // The predicate and step handles' signatures need to be extended. They currently just accept local state args; + // append possibly missing local state args and loop args using dropArguments. + for (int i = 0; i < nargs; ++i) { + List<Class<?>> additionalLocalStateArgTypes = lArgTypes.subList(i + 1, nargs); + preds[i] = MethodHandles.dropArguments( + MethodHandles.dropArguments(preds[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes); + steps[i] = MethodHandles.dropArguments( + MethodHandles.dropArguments(steps[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes); + } + // Iterate over all of the predicates, using only one of them at a time. + for (int i = 0; i < nargs; ++i) { + MethodHandle[] usePreds; + if (nargs == 1) { + usePreds = preds; + } else { + // Create an all-null preds array, and only use one predicate in this iteration. The null entries will + // be substituted with true predicates by the loop combinator. + usePreds = new MethodHandle[nargs]; + usePreds[i] = preds[i]; + } + // Go for it. + if (verbosity >= 3) { + System.out.println("calling loop for argument types " + lArgTypes + " with predicate at index " + i); + if (verbosity >= 5) { + System.out.println("predicates: " + Arrays.asList(usePreds)); + } + } + MethodHandle[] preInits = new MethodHandle[nargs + 1]; + MethodHandle[] prePreds = new MethodHandle[nargs + 1]; + MethodHandle[] preSteps = new MethodHandle[nargs + 1]; + MethodHandle[] preFinis = new MethodHandle[nargs + 1]; + System.arraycopy(inits, 0, preInits, 1, nargs); + System.arraycopy(usePreds, 0, prePreds, 0, nargs); // preds are offset by 1 for pre-checked loops + System.arraycopy(steps, 0, preSteps, 1, nargs); + System.arraycopy(finis, 0, preFinis, 0, nargs); // finis are also offset by 1 for pre-checked loops + // Convert to clause-major form. + MethodHandle[][] preClauses = new MethodHandle[nargs+1][4]; + MethodHandle[][] postClauses = new MethodHandle[nargs][4]; + toClauseMajor(preClauses, preInits, preSteps, prePreds, preFinis); + toClauseMajor(postClauses, inits, steps, usePreds, finis); + MethodHandle pre = MethodHandles.loop(preClauses); + MethodHandle post = MethodHandles.loop(postClauses); + Object[] preResults = (Object[]) pre.invokeWithArguments(args); + if (verbosity >= 4) { + System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " + + Arrays.asList(preResults)); + } + Object[] postResults = (Object[]) post.invokeWithArguments(args); + if (verbosity >= 4) { + System.out.println("post-checked: expected " + Arrays.asList(postCheckedResults[i]) + ", actual " + + Arrays.asList(postResults)); + } + assertArrayEquals(preCheckedResults[i], preResults); + assertArrayEquals(postCheckedResults[i], postResults); + } + } + static void toClauseMajor(MethodHandle[][] clauses, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini) { + for (int i = 0; i < clauses.length; ++i) { + clauses[i][0] = init[i]; + clauses[i][1] = step[i]; + clauses[i][2] = pred[i]; + clauses[i][3] = fini[i]; + } + } + static boolean loopIntPred(int a) { + if (verbosity >= 5) { + System.out.println("int pred " + a + " -> " + (a < 7)); + } + return a < 7; + } + static boolean loopDoublePred(int a, double b) { + if (verbosity >= 5) { + System.out.println("double pred (a=" + a + ") " + b + " -> " + (b > 0.5)); + } + return b > 0.5; + } + static boolean loopStringPred(int a, double b, String c) { + if (verbosity >= 5) { + System.out.println("String pred (a=" + a + ",b=" + b + ") " + c + " -> " + (c.length() <= 9)); + } + return c.length() <= 9; + } + static int loopIntStep(int a) { + if (verbosity >= 5) { + System.out.println("int step " + a + " -> " + (a + 1)); + } + return a + 1; + } + static double loopDoubleStep(int a, double b) { + if (verbosity >= 5) { + System.out.println("double step (a=" + a + ") " + b + " -> " + (b / 2.0)); + } + return b / 2.0; + } + static String loopStringStep(int a, double b, String c) { + if (verbosity >= 5) { + System.out.println("String step (a=" + a + ",b=" + b + ") " + c + " -> " + (c + a)); + } + return c + a; + } + + @Test public void testThrowException() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testThrowException0); } public void testThrowException0() throws Throwable {
*** 2574,2589 **** } assertSame(thrown, caught); } @Test public void testInterfaceCast() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testInterfaceCast0); } public void testInterfaceCast0() throws Throwable { ! //if (CAN_SKIP_WORKING) return; startTest("interfaceCast"); assert( (((Object)"foo") instanceof CharSequence)); assert(!(((Object)"foo") instanceof Iterable)); for (MethodHandle mh : new MethodHandle[]{ MethodHandles.identity(String.class), --- 2886,2996 ---- } assertSame(thrown, caught); } @Test + public void testTryFinally() throws Throwable { + CodeCacheOverflowProcessor.runMHTest(this::testTryFinally0); + } + public void testTryFinally0() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("tryFinally"); + String inputMessage = "returned"; + String augmentedMessage = "augmented"; + String thrownMessage = "thrown"; + String rethrownMessage = "rethrown"; + // Test these cases: + // * target returns, cleanup passes through + // * target returns, cleanup augments + // * target throws, cleanup augments and returns + // * target throws, cleanup augments and rethrows + MethodHandle target = MethodHandles.identity(String.class); + MethodHandle targetThrow = MethodHandles.dropArguments( + MethodHandles.throwException(String.class, Exception.class).bindTo(new Exception(thrownMessage)), 0, String.class); + MethodHandle cleanupPassThrough = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, + Throwable.class, String.class); + MethodHandle cleanupAugment = MethodHandles.dropArguments(MethodHandles.constant(String.class, augmentedMessage), + 0, Throwable.class, String.class, String.class); + MethodHandle cleanupCatch = MethodHandles.dropArguments(MethodHandles.constant(String.class, thrownMessage), 0, + Throwable.class, String.class, String.class); + MethodHandle cleanupThrow = MethodHandles.dropArguments(MethodHandles.throwException(String.class, Exception.class). + bindTo(new Exception(rethrownMessage)), 0, Throwable.class, String.class, String.class); + testTryFinally(target, cleanupPassThrough, inputMessage, inputMessage, false); + testTryFinally(target, cleanupAugment, inputMessage, augmentedMessage, false); + testTryFinally(targetThrow, cleanupCatch, inputMessage, thrownMessage, true); + testTryFinally(targetThrow, cleanupThrow, inputMessage, rethrownMessage, true); + // Test the same cases as above for void targets and cleanups. + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class<?> C = this.getClass(); + MethodType targetType = methodType(void.class, String[].class); + MethodType cleanupType = methodType(void.class, Throwable.class, String[].class); + MethodHandle vtarget = lookup.findStatic(C, "vtarget", targetType); + MethodHandle vtargetThrow = lookup.findStatic(C, "vtargetThrow", targetType); + MethodHandle vcleanupPassThrough = lookup.findStatic(C, "vcleanupPassThrough", cleanupType); + MethodHandle vcleanupAugment = lookup.findStatic(C, "vcleanupAugment", cleanupType); + MethodHandle vcleanupCatch = lookup.findStatic(C, "vcleanupCatch", cleanupType); + MethodHandle vcleanupThrow = lookup.findStatic(C, "vcleanupThrow", cleanupType); + testTryFinally(vtarget, vcleanupPassThrough, inputMessage, inputMessage, false); + testTryFinally(vtarget, vcleanupAugment, inputMessage, augmentedMessage, false); + testTryFinally(vtargetThrow, vcleanupCatch, inputMessage, thrownMessage, true); + testTryFinally(vtargetThrow, vcleanupThrow, inputMessage, rethrownMessage, true); + } + void testTryFinally(MethodHandle target, MethodHandle cleanup, String input, String msg, boolean mustCatch) + throws Throwable { + countTest(); + MethodHandle tf = MethodHandles.tryFinally(target, cleanup); + String result = null; + boolean isVoid = target.type().returnType() == void.class; + String[] argArray = new String[]{input}; + try { + if (isVoid) { + tf.invoke(argArray); + } else { + result = (String) tf.invoke(input); + } + } catch (Throwable t) { + assertTrue(mustCatch); + assertEquals(msg, t.getMessage()); + return; + } + assertFalse(mustCatch); + if (isVoid) { + assertEquals(msg, argArray[0]); + } else { + assertEquals(msg, result); + } + } + static void vtarget(String[] a) { + // naught, akin to identity + } + static void vtargetThrow(String[] a) throws Exception { + throw new Exception("thrown"); + } + static void vcleanupPassThrough(Throwable t, String[] a) { + assertNull(t); + // naught, akin to identity + } + static void vcleanupAugment(Throwable t, String[] a) { + assertNull(t); + a[0] = "augmented"; + } + static void vcleanupCatch(Throwable t, String[] a) { + assertNotNull(t); + a[0] = "caught"; + } + static void vcleanupThrow(Throwable t, String[] a) throws Exception { + assertNotNull(t); + throw new Exception("rethrown"); + } + + @Test public void testInterfaceCast() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testInterfaceCast0); } public void testInterfaceCast0() throws Throwable { ! if (CAN_SKIP_WORKING) return; startTest("interfaceCast"); assert( (((Object)"foo") instanceof CharSequence)); assert(!(((Object)"foo") instanceof Iterable)); for (MethodHandle mh : new MethodHandle[]{ MethodHandles.identity(String.class),
< prev index next >