提交 8cd530d6 编写于 作者: J jrose

7058651: JSR 292 unit tests need a refresh

Summary: Enhancements to unit tests.
Reviewed-by: never, twisti
上级 0059e8b2
...@@ -69,6 +69,7 @@ public class JavaDocExamplesTest { ...@@ -69,6 +69,7 @@ public class JavaDocExamplesTest {
testDropArguments(); testDropArguments();
testFilterArguments(); testFilterArguments();
testFoldArguments(); testFoldArguments();
testFoldArguments2();
testMethodHandlesSummary(); testMethodHandlesSummary();
testAsSpreader(); testAsSpreader();
testAsCollector(); testAsCollector();
...@@ -490,6 +491,47 @@ assertEquals("hodmet", (String) worker.invokeExact("met", "hod")); ...@@ -490,6 +491,47 @@ assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
}} }}
} }
@Test public void testFoldArguments2() throws Throwable {
{{
{} /// JAVADOC
// argument-based dispatch for methods of the form boolean x.___(y: String)
Lookup lookup = publicLookup();
// first, a tracing hack:
MethodHandle println = lookup.findVirtual(java.io.PrintStream.class, "println", methodType(void.class, String.class));
MethodHandle arrayToString = lookup.findStatic(Arrays.class, "toString", methodType(String.class, Object[].class));
MethodHandle concat = lookup.findVirtual(String.class, "concat", methodType(String.class, String.class));
MethodHandle arrayToString_DIS = filterReturnValue(arrayToString, concat.bindTo("DIS:"));
MethodHandle arrayToString_INV = filterReturnValue(arrayToString, concat.bindTo("INV:"));
MethodHandle printArgs_DIS = filterReturnValue(arrayToString_DIS, println.bindTo(System.out)).asVarargsCollector(Object[].class);
MethodHandle printArgs_INV = filterReturnValue(arrayToString_INV, println.bindTo(System.out)).asVarargsCollector(Object[].class);
// metaobject protocol:
MethodType mtype = methodType(boolean.class, String.class);
MethodHandle findVirtual = lookup.findVirtual(Lookup.class,
"findVirtual", methodType(MethodHandle.class, Class.class, String.class, MethodType.class));
MethodHandle getClass = lookup.findVirtual(Object.class,
"getClass", methodType(Class.class));
MethodHandle dispatch = findVirtual;
dispatch = filterArguments(dispatch, 1, getClass);
dispatch = insertArguments(dispatch, 3, mtype);
dispatch = dispatch.bindTo(lookup);
assertEquals(methodType(MethodHandle.class, Object.class, String.class), dispatch.type());
MethodHandle invoker = invoker(mtype.insertParameterTypes(0, Object.class));
// wrap tracing around the dispatch and invoke steps:
dispatch = foldArguments(dispatch, printArgs_DIS.asType(dispatch.type().changeReturnType(void.class)));
invoker = foldArguments(invoker, printArgs_INV.asType(invoker.type().changeReturnType(void.class)));
invoker = dropArguments(invoker, 2, String.class); // ignore selector
// compose the dispatcher and the invoker:
MethodHandle invokeDispatched = foldArguments(invoker, dispatch);
Object x = "football", y = new java.util.Scanner("bar");
assert( (boolean) invokeDispatched.invokeExact(x, "startsWith", "foo"));
assert(!(boolean) invokeDispatched.invokeExact(x, "startsWith", "#"));
assert( (boolean) invokeDispatched.invokeExact(x, "endsWith", "all"));
assert(!(boolean) invokeDispatched.invokeExact(x, "endsWith", "foo"));
assert( (boolean) invokeDispatched.invokeExact(y, "hasNext", "[abc]+[rst]"));
assert(!(boolean) invokeDispatched.invokeExact(y, "hasNext", "[123]+[789]"));
}}
}
/* ---- TEMPLATE ---- /* ---- TEMPLATE ----
@Test public void testFoo() throws Throwable { @Test public void testFoo() throws Throwable {
{{ {{
......
...@@ -37,7 +37,6 @@ import java.lang.reflect.*; ...@@ -37,7 +37,6 @@ import java.lang.reflect.*;
import java.util.*; import java.util.*;
import org.junit.*; import org.junit.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.junit.Assume.*;
/** /**
...@@ -45,10 +44,13 @@ import static org.junit.Assume.*; ...@@ -45,10 +44,13 @@ import static org.junit.Assume.*;
* @author jrose * @author jrose
*/ */
public class MethodHandlesTest { public class MethodHandlesTest {
static final Class<?> THIS_CLASS = MethodHandlesTest.class;
// How much output? // How much output?
static int verbosity = 0; static int verbosity = 0;
static { static {
String vstr = System.getProperty("test.java.lang.invoke.MethodHandlesTest.verbosity"); String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");
if (vstr == null)
vstr = System.getProperty(THIS_CLASS.getName()+".verbosity");
if (vstr != null) verbosity = Integer.parseInt(vstr); if (vstr != null) verbosity = Integer.parseInt(vstr);
} }
...@@ -58,9 +60,9 @@ public class MethodHandlesTest { ...@@ -58,9 +60,9 @@ public class MethodHandlesTest {
static boolean CAN_SKIP_WORKING = false; static boolean CAN_SKIP_WORKING = false;
//static { CAN_SKIP_WORKING = true; } //static { CAN_SKIP_WORKING = true; }
// Set true to test more calls. If false, some tests are just // Set 'true' to do about 15x fewer tests, especially those redundant with RicochetTest.
// lookups, without exercising the actual method handle. // This might be useful with -Xcomp stress tests that compile all method handles.
static boolean DO_MORE_CALLS = true; static boolean CAN_TEST_LIGHTLY = Boolean.getBoolean(THIS_CLASS.getName()+".CAN_TEST_LIGHTLY");
@Test @Test
public void testFirst() throws Throwable { public void testFirst() throws Throwable {
...@@ -70,37 +72,37 @@ public class MethodHandlesTest { ...@@ -70,37 +72,37 @@ public class MethodHandlesTest {
} }
// current failures // current failures
@Test @Ignore("failure in call to makeRawRetypeOnly in ToGeneric") @Test //@Ignore("failure in call to makeRawRetypeOnly in ToGeneric")
public void testFail_1() throws Throwable { public void testFail_1() throws Throwable {
// AMH.<init>: IllegalArgumentException: bad adapter (conversion=0xfffab300): adapter pushes too many parameters // AMH.<init>: IllegalArgumentException: bad adapter (conversion=0xfffab300): adapter pushes too many parameters
testSpreadArguments(int.class, 0, 6); testSpreadArguments(int.class, 0, 6);
} }
@Test @Ignore("failure in JVM when expanding the stack using asm stub for _adapter_spread_args") @Test //@Ignore("failure in JVM when expanding the stack using asm stub for _adapter_spread_args")
public void testFail_2() throws Throwable { public void testFail_2() throws Throwable {
// if CONV_OP_IMPLEMENTED_MASK includes OP_SPREAD_ARGS, this crashes: // if CONV_OP_IMPLEMENTED_MASK includes OP_SPREAD_ARGS, this crashes:
testSpreadArguments(Object.class, 0, 2); testSpreadArguments(Object.class, 0, 2);
} }
@Test @Ignore("IllArgEx failure in call to ToGeneric.make") @Test //@Ignore("IllArgEx failure in call to ToGeneric.make")
public void testFail_3() throws Throwable { public void testFail_3() throws Throwable {
// ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
testSpreadArguments(int.class, 1, 2); testSpreadArguments(int.class, 1, 2);
} }
@Test @Ignore("IllArgEx failure in call to ToGeneric.make") @Test //@Ignore("IllArgEx failure in call to ToGeneric.make")
public void testFail_4() throws Throwable { public void testFail_4() throws Throwable {
// ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
testCollectArguments(int.class, 1, 2); testCollectArguments(int.class, 1, 2);
} }
@Test @Ignore("cannot collect leading primitive types") @Test //@Ignore("cannot collect leading primitive types")
public void testFail_5() throws Throwable { public void testFail_5() throws Throwable {
// ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object
testInvokers(MethodType.genericMethodType(2).changeParameterType(0, int.class)); testInvokers(MethodType.genericMethodType(2).changeParameterType(0, int.class));
} }
@Test @Ignore("should not insert arguments beyond MethodHandlePushLimit") @Test //@Ignore("should not insert arguments beyond MethodHandlePushLimit")
public void testFail_6() throws Throwable { public void testFail_6() throws Throwable {
// ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13 // ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13
testInsertArguments(0, 0, MAX_ARG_INCREASE+10); testInsertArguments(0, 0, MAX_ARG_INCREASE+10);
} }
@Test @Ignore("permuteArguments has trouble with double slots") @Test //@Ignore("permuteArguments has trouble with double slots")
public void testFail_7() throws Throwable { public void testFail_7() throws Throwable {
testPermuteArguments(new Object[]{10, 200L}, testPermuteArguments(new Object[]{10, 200L},
new Class<?>[]{Integer.class, long.class}, new Class<?>[]{Integer.class, long.class},
...@@ -123,7 +125,7 @@ public class MethodHandlesTest { ...@@ -123,7 +125,7 @@ public class MethodHandlesTest {
testPermuteArguments(new Object[]{10, 200L, 5000L}, testPermuteArguments(new Object[]{10, 200L, 5000L},
new Class<?>[]{Integer.class, long.class, long.class}, new Class<?>[]{Integer.class, long.class, long.class},
new int[]{2,2,0,1}); new int[]{2,2,0,1});
testPermuteArguments(4, Integer.class, 2, long.class, 6); //testPermuteArguments(4, Integer.class, 2, long.class, 6);
} }
static final int MAX_ARG_INCREASE = 3; static final int MAX_ARG_INCREASE = 3;
...@@ -167,7 +169,7 @@ public class MethodHandlesTest { ...@@ -167,7 +169,7 @@ public class MethodHandlesTest {
@AfterClass @AfterClass
public static void tearDownClass() throws Exception { public static void tearDownClass() throws Exception {
int posTests = allPosTests, negTests = allNegTests; int posTests = allPosTests, negTests = allNegTests;
if (verbosity >= 2 && (posTests | negTests) != 0) { if (verbosity >= 0 && (posTests | negTests) != 0) {
System.out.println(); System.out.println();
if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases"); if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases");
if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases"); if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases");
...@@ -383,6 +385,13 @@ public class MethodHandlesTest { ...@@ -383,6 +385,13 @@ public class MethodHandlesTest {
MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes); MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
return target.asType(ttype2); return target.asType(ttype2);
} }
static MethodHandle addTrailingArgs(MethodHandle target, int nargs, Class<?> argClass) {
int targetLen = target.type().parameterCount();
int extra = (nargs - targetLen);
if (extra <= 0) return target;
List<Class<?>> fakeArgs = Collections.<Class<?>>nCopies(extra, argClass);
return MethodHandles.dropArguments(target, targetLen, fakeArgs);
}
// This lookup is good for all members in and under MethodHandlesTest. // This lookup is good for all members in and under MethodHandlesTest.
static final Lookup PRIVATE = MethodHandles.lookup(); static final Lookup PRIVATE = MethodHandles.lookup();
...@@ -419,6 +428,10 @@ public class MethodHandlesTest { ...@@ -419,6 +428,10 @@ public class MethodHandlesTest {
public static Object s6(int x, long y) { return called("s6", x, y); } public static Object s6(int x, long y) { return called("s6", x, y); }
public static Object s7(float x, double y) { return called("s7", x, y); } public static Object s7(float x, double y) { return called("s7", x, y); }
// for testing findConstructor:
public Example(String x, int y) { this.name = x+y; called("Example.<init>", x, y); }
public Example(int x, String y) { this.name = x+y; called("Example.<init>", x, y); }
static final Lookup EXAMPLE = MethodHandles.lookup(); // for testing findSpecial static final Lookup EXAMPLE = MethodHandles.lookup(); // for testing findSpecial
} }
static final Lookup EXAMPLE = Example.EXAMPLE; static final Lookup EXAMPLE = Example.EXAMPLE;
...@@ -521,7 +534,6 @@ public class MethodHandlesTest { ...@@ -521,7 +534,6 @@ public class MethodHandlesTest {
if (!positive) return; // negative test failed as expected if (!positive) return; // negative test failed as expected
assertEquals(type, target.type()); assertEquals(type, target.type());
assertNameStringContains(target, name); assertNameStringContains(target, name);
if (!DO_MORE_CALLS && lookup != PRIVATE) return;
Object[] args = randomArgs(params); Object[] args = randomArgs(params);
printCalled(target, name, args); printCalled(target, name, args);
target.invokeWithArguments(args); target.invokeWithArguments(args);
...@@ -604,7 +616,6 @@ public class MethodHandlesTest { ...@@ -604,7 +616,6 @@ public class MethodHandlesTest {
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
assertEquals(typeWithSelf, target.type()); assertEquals(typeWithSelf, target.type());
assertNameStringContains(target, methodName); assertNameStringContains(target, methodName);
if (!DO_MORE_CALLS && lookup != PRIVATE) return;
Object[] argsWithSelf = randomArgs(paramsWithSelf); Object[] argsWithSelf = randomArgs(paramsWithSelf);
if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc); if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc);
printCalled(target, name, argsWithSelf); printCalled(target, name, argsWithSelf);
...@@ -666,13 +677,49 @@ public class MethodHandlesTest { ...@@ -666,13 +677,49 @@ public class MethodHandlesTest {
Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params); Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params);
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
assertNameStringContains(target, name); assertNameStringContains(target, name);
if (!DO_MORE_CALLS && lookup != PRIVATE && lookup != EXAMPLE) return;
Object[] args = randomArgs(paramsWithSelf); Object[] args = randomArgs(paramsWithSelf);
printCalled(target, name, args); printCalled(target, name, args);
target.invokeWithArguments(args); target.invokeWithArguments(args);
assertCalled(name, args); assertCalled(name, args);
} }
@Test
public void testFindConstructor() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("findConstructor");
testFindConstructor(true, EXAMPLE, Example.class);
testFindConstructor(true, EXAMPLE, Example.class, int.class);
testFindConstructor(true, EXAMPLE, Example.class, String.class);
}
void testFindConstructor(boolean positive, Lookup lookup,
Class<?> defc, Class<?>... params) throws Throwable {
countTest(positive);
MethodType type = MethodType.methodType(void.class, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" <init>"+type);
target = lookup.findConstructor(defc, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
+(target == null ? "" : target.type())
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type.changeReturnType(defc), target.type());
Object[] args = randomArgs(params);
printCalled(target, defc.getSimpleName(), args);
Object obj = target.invokeWithArguments(args);
if (!(defc == Example.class && params.length < 2))
assertCalled(defc.getSimpleName()+".<init>", args);
assertTrue("instance of "+defc.getName(), defc.isInstance(obj));
}
@Test @Test
public void testBind() throws Throwable { public void testBind() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
...@@ -956,6 +1003,8 @@ public class MethodHandlesTest { ...@@ -956,6 +1003,8 @@ public class MethodHandlesTest {
public void testAccessor(boolean positive, MethodHandles.Lookup lookup, public void testAccessor(boolean positive, MethodHandles.Lookup lookup,
Object fieldRef, Object value, int testMode0) throws Throwable { Object fieldRef, Object value, int testMode0) throws Throwable {
if (verbosity >= 4)
System.out.println("testAccessor"+Arrays.asList(positive, lookup, fieldRef, value, testMode0));
boolean isGetter = ((testMode0 & TEST_SETTER) == 0); boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
int testMode = testMode0 & ~TEST_SETTER; int testMode = testMode0 & ~TEST_SETTER;
boolean isStatic; boolean isStatic;
...@@ -1150,7 +1199,7 @@ public class MethodHandlesTest { ...@@ -1150,7 +1199,7 @@ public class MethodHandlesTest {
public void testArrayElementGetterSetter(Object array, boolean testSetter) throws Throwable { public void testArrayElementGetterSetter(Object array, boolean testSetter) throws Throwable {
countTest(true); countTest(true);
if (verbosity >= 2) System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+Array.getLength(array)+"]"); if (verbosity > 2) System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+Array.getLength(array)+"]");
Class<?> arrayType = array.getClass(); Class<?> arrayType = array.getClass();
Class<?> elemType = arrayType.getComponentType(); Class<?> elemType = arrayType.getComponentType();
MethodType expType = !testSetter MethodType expType = !testSetter
...@@ -1326,9 +1375,10 @@ public class MethodHandlesTest { ...@@ -1326,9 +1375,10 @@ public class MethodHandlesTest {
public void testPermuteArguments() throws Throwable { public void testPermuteArguments() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
startTest("permuteArguments"); startTest("permuteArguments");
testPermuteArguments(4, Integer.class, 2, long.class, 6);
if (CAN_TEST_LIGHTLY) return;
testPermuteArguments(4, Integer.class, 2, String.class, 0); testPermuteArguments(4, Integer.class, 2, String.class, 0);
testPermuteArguments(6, Integer.class, 0, null, 30); testPermuteArguments(6, Integer.class, 0, null, 30);
//testPermuteArguments(4, Integer.class, 2, long.class, 6); // FIXME Fail_7
} }
public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable { public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable {
if (verbosity >= 2) if (verbosity >= 2)
...@@ -1354,7 +1404,9 @@ public class MethodHandlesTest { ...@@ -1354,7 +1404,9 @@ public class MethodHandlesTest {
casStep++; casStep++;
testPermuteArguments(args, types, outargs, numcases, casStep); testPermuteArguments(args, types, outargs, numcases, casStep);
numcases *= inargs; numcases *= inargs;
if (CAN_TEST_LIGHTLY && outargs < max-2) continue;
if (dilution > 10 && outargs >= 4) { if (dilution > 10 && outargs >= 4) {
if (CAN_TEST_LIGHTLY) continue;
int[] reorder = new int[outargs]; int[] reorder = new int[outargs];
// Do some special patterns, which we probably missed. // Do some special patterns, which we probably missed.
// Replication of a single argument or argument pair. // Replication of a single argument or argument pair.
...@@ -1392,6 +1444,7 @@ public class MethodHandlesTest { ...@@ -1392,6 +1444,7 @@ public class MethodHandlesTest {
reorder[i] = c % inargs; reorder[i] = c % inargs;
c /= inargs; c /= inargs;
} }
if (CAN_TEST_LIGHTLY && outargs >= 3 && (reorder[0] == reorder[1] || reorder[1] == reorder[2])) continue;
testPermuteArguments(args, types, reorder); testPermuteArguments(args, types, reorder);
} }
} }
...@@ -1464,12 +1517,13 @@ public class MethodHandlesTest { ...@@ -1464,12 +1517,13 @@ public class MethodHandlesTest {
for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) { for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) {
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("spreadArguments "+argType); System.out.println("spreadArguments "+argType);
// FIXME: enable _adapter_spread_args and fix Fail_2 for (int nargs = 0; nargs < 50; nargs++) {
for (int nargs = 0; nargs < 10; nargs++) { if (CAN_TEST_LIGHTLY && nargs > 7) break;
if (argType == int.class && nargs >= 6) continue; // FIXME Fail_1 for (int pos = 0; pos <= nargs; pos++) {
for (int pos = 0; pos < nargs; pos++) { if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue;
if (argType == int.class && pos > 0) continue; // FIXME Fail_3 if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
testSpreadArguments(argType, pos, nargs); continue;
testSpreadArguments(argType, pos, nargs);
} }
} }
} }
...@@ -1557,9 +1611,12 @@ public class MethodHandlesTest { ...@@ -1557,9 +1611,12 @@ public class MethodHandlesTest {
for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) { for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) {
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("collectArguments "+argType); System.out.println("collectArguments "+argType);
for (int nargs = 0; nargs < 10; nargs++) { for (int nargs = 0; nargs < 50; nargs++) {
for (int pos = 0; pos < nargs; pos++) { if (CAN_TEST_LIGHTLY && nargs > 7) break;
if (argType == int.class) continue; // FIXME Fail_4 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;
testCollectArguments(argType, pos, nargs); testCollectArguments(argType, pos, nargs);
} }
} }
...@@ -1593,10 +1650,15 @@ public class MethodHandlesTest { ...@@ -1593,10 +1650,15 @@ public class MethodHandlesTest {
public void testInsertArguments() throws Throwable { public void testInsertArguments() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
startTest("insertArguments"); startTest("insertArguments");
for (int nargs = 0; nargs <= 4; nargs++) { for (int nargs = 0; nargs < 50; nargs++) {
for (int ins = 0; ins <= 4; ins++) { if (CAN_TEST_LIGHTLY && nargs > 7) break;
if (ins > MAX_ARG_INCREASE) continue; // FIXME Fail_6 for (int ins = 0; ins <= nargs; ins++) {
if (nargs > 10 && ins > 4 && ins < nargs-4 && ins % 10 != 3)
continue;
for (int pos = 0; pos <= nargs; pos++) { for (int pos = 0; pos <= nargs; pos++) {
if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
continue;
if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue;
testInsertArguments(nargs, pos, ins); testInsertArguments(nargs, pos, ins);
} }
} }
...@@ -1634,8 +1696,8 @@ public class MethodHandlesTest { ...@@ -1634,8 +1696,8 @@ public class MethodHandlesTest {
for (Class<?> rtype : new Class[] { Object.class, for (Class<?> rtype : new Class[] { Object.class,
List.class, List.class,
int.class, int.class,
//byte.class, //FIXME: add this byte.class,
//long.class, //FIXME: add this long.class,
CharSequence.class, CharSequence.class,
String.class }) { String.class }) {
testFilterReturnValue(nargs, rtype); testFilterReturnValue(nargs, rtype);
...@@ -1780,6 +1842,7 @@ public class MethodHandlesTest { ...@@ -1780,6 +1842,7 @@ public class MethodHandlesTest {
// exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker // exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker
Set<MethodType> done = new HashSet<MethodType>(); Set<MethodType> done = new HashSet<MethodType>();
for (int i = 0; i <= 6; i++) { for (int i = 0; i <= 6; i++) {
if (CAN_TEST_LIGHTLY && i > 3) break;
MethodType gtype = MethodType.genericMethodType(i); MethodType gtype = MethodType.genericMethodType(i);
for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) { for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) {
for (int j = -1; j < i; j++) { for (int j = -1; j < i; j++) {
...@@ -1790,7 +1853,6 @@ public class MethodHandlesTest { ...@@ -1790,7 +1853,6 @@ public class MethodHandlesTest {
continue; continue;
else else
type = type.changeParameterType(j, argType); type = type.changeParameterType(j, argType);
if (argType.isPrimitive() && j != i-1) continue; // FIXME Fail_5
if (done.add(type)) if (done.add(type))
testInvokers(type); testInvokers(type);
MethodType vtype = type.changeReturnType(void.class); MethodType vtype = type.changeReturnType(void.class);
...@@ -1890,6 +1952,7 @@ public class MethodHandlesTest { ...@@ -1890,6 +1952,7 @@ public class MethodHandlesTest {
} }
for (int k = 0; k <= nargs; k++) { for (int k = 0; k <= nargs; k++) {
// varargs invoker #0..N // varargs invoker #0..N
if (CAN_TEST_LIGHTLY && (k > 1 || k < nargs - 1)) continue;
countTest(); countTest();
calledLog.clear(); calledLog.clear();
inv = MethodHandles.spreadInvoker(type, k); inv = MethodHandles.spreadInvoker(type, k);
...@@ -1933,6 +1996,7 @@ public class MethodHandlesTest { ...@@ -1933,6 +1996,7 @@ public class MethodHandlesTest {
} }
private static final String MISSING_ARG = "missingArg"; private static final String MISSING_ARG = "missingArg";
private static final String MISSING_ARG_2 = "missingArg#2";
static Object targetIfEquals() { static Object targetIfEquals() {
return called("targetIfEquals"); return called("targetIfEquals");
} }
...@@ -1968,28 +2032,39 @@ public class MethodHandlesTest { ...@@ -1968,28 +2032,39 @@ public class MethodHandlesTest {
public void testGuardWithTest() throws Throwable { public void testGuardWithTest() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
startTest("guardWithTest"); startTest("guardWithTest");
for (int nargs = 0; nargs <= 3; nargs++) { for (int nargs = 0; nargs <= 50; nargs++) {
if (nargs != 2) continue; // FIXME: test more later if (CAN_TEST_LIGHTLY && nargs > 7) break;
testGuardWithTest(nargs, Object.class); testGuardWithTest(nargs, Object.class);
testGuardWithTest(nargs, String.class); testGuardWithTest(nargs, String.class);
} }
} }
void testGuardWithTest(int nargs, Class<?> argClass) throws Throwable { void testGuardWithTest(int nargs, Class<?> argClass) throws Throwable {
testGuardWithTest(nargs, 0, argClass);
if (nargs <= 5 || nargs % 10 == 3) {
for (int testDrops = 1; testDrops <= nargs; testDrops++)
testGuardWithTest(nargs, testDrops, argClass);
}
}
void testGuardWithTest(int nargs, int testDrops, Class<?> argClass) throws Throwable {
countTest(); countTest();
int nargs1 = Math.min(3, nargs);
MethodHandle test = PRIVATE.findVirtual(Object.class, "equals", MethodType.methodType(boolean.class, Object.class)); MethodHandle test = PRIVATE.findVirtual(Object.class, "equals", MethodType.methodType(boolean.class, Object.class));
MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "targetIfEquals", MethodType.genericMethodType(nargs)); MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "targetIfEquals", MethodType.genericMethodType(nargs1));
MethodHandle fallback = PRIVATE.findStatic(MethodHandlesTest.class, "fallbackIfNotEquals", MethodType.genericMethodType(nargs)); MethodHandle fallback = PRIVATE.findStatic(MethodHandlesTest.class, "fallbackIfNotEquals", MethodType.genericMethodType(nargs1));
while (test.type().parameterCount() < nargs)
test = MethodHandles.dropArguments(test, test.type().parameterCount()-1, Object.class);
while (test.type().parameterCount() > nargs) while (test.type().parameterCount() > nargs)
// 0: test = constant(MISSING_ARG.equals(MISSING_ARG))
// 1: test = lambda (_) MISSING_ARG.equals(_)
test = MethodHandles.insertArguments(test, 0, MISSING_ARG); test = MethodHandles.insertArguments(test, 0, MISSING_ARG);
if (argClass != Object.class) { if (argClass != Object.class) {
test = changeArgTypes(test, argClass); test = changeArgTypes(test, argClass);
target = changeArgTypes(target, argClass); target = changeArgTypes(target, argClass);
fallback = changeArgTypes(fallback, argClass); fallback = changeArgTypes(fallback, argClass);
} }
MethodHandle mh = MethodHandles.guardWithTest(test, target, fallback); int testArgs = nargs - testDrops;
assertEquals(target.type(), mh.type()); assert(testArgs >= 0);
test = addTrailingArgs(test, Math.min(testArgs, nargs), argClass);
target = addTrailingArgs(target, nargs, argClass);
fallback = addTrailingArgs(fallback, nargs, argClass);
Object[][] argLists = { Object[][] argLists = {
{ }, { },
{ "foo" }, { MISSING_ARG }, { "foo" }, { MISSING_ARG },
...@@ -1997,7 +2072,19 @@ public class MethodHandlesTest { ...@@ -1997,7 +2072,19 @@ public class MethodHandlesTest {
{ "foo", "foo", "baz" }, { "foo", "bar", "baz" } { "foo", "foo", "baz" }, { "foo", "bar", "baz" }
}; };
for (Object[] argList : argLists) { for (Object[] argList : argLists) {
if (argList.length != nargs) continue; Object[] argList1 = argList;
if (argList.length != nargs) {
if (argList.length != nargs1) continue;
argList1 = Arrays.copyOf(argList, nargs);
Arrays.fill(argList1, nargs1, nargs, MISSING_ARG_2);
}
MethodHandle test1 = test;
if (test1.type().parameterCount() > testArgs) {
int pc = test1.type().parameterCount();
test1 = MethodHandles.insertArguments(test, testArgs, Arrays.copyOfRange(argList1, testArgs, pc));
}
MethodHandle mh = MethodHandles.guardWithTest(test1, target, fallback);
assertEquals(target.type(), mh.type());
boolean equals; boolean equals;
switch (nargs) { switch (nargs) {
case 0: equals = true; break; case 0: equals = true; break;
...@@ -2007,7 +2094,7 @@ public class MethodHandlesTest { ...@@ -2007,7 +2094,7 @@ public class MethodHandlesTest {
String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals"); String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals");
if (verbosity >= 3) if (verbosity >= 3)
System.out.println(logEntry(willCall, argList)); System.out.println(logEntry(willCall, argList));
Object result = mh.invokeWithArguments(argList); Object result = mh.invokeWithArguments(argList1);
assertCalled(willCall, argList); assertCalled(willCall, argList);
} }
} }
...@@ -2016,49 +2103,102 @@ public class MethodHandlesTest { ...@@ -2016,49 +2103,102 @@ public class MethodHandlesTest {
public void testCatchException() throws Throwable { public void testCatchException() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
startTest("catchException"); startTest("catchException");
for (int nargs = 2; nargs <= 6; nargs++) { for (int nargs = 0; nargs < 40; nargs++) {
for (int ti = 0; ti <= 1; ti++) { if (CAN_TEST_LIGHTLY && nargs > 7) break;
boolean throwIt = (ti != 0); for (int throwMode = 0; throwMode < THROW_MODE_LIMIT; throwMode++) {
testCatchException(int.class, new ClassCastException("testing"), throwIt, nargs); testCatchException(int.class, new ClassCastException("testing"), throwMode, nargs);
testCatchException(void.class, new java.io.IOException("testing"), throwIt, nargs); if (CAN_TEST_LIGHTLY && nargs > 3) continue;
testCatchException(String.class, new LinkageError("testing"), throwIt, nargs); testCatchException(void.class, new java.io.IOException("testing"), throwMode, nargs);
testCatchException(String.class, new LinkageError("testing"), throwMode, nargs);
} }
} }
} }
static final int THROW_NOTHING = 0, THROW_CAUGHT = 1, THROW_UNCAUGHT = 2, THROW_THROUGH_ADAPTER = 3, THROW_MODE_LIMIT = 4;
void testCatchException(Class<?> returnType, Throwable thrown, int throwMode, int nargs) throws Throwable {
testCatchException(returnType, thrown, throwMode, nargs, 0);
if (nargs <= 5 || nargs % 10 == 3) {
for (int catchDrops = 1; catchDrops <= nargs; catchDrops++)
testCatchException(returnType, thrown, throwMode, nargs, catchDrops);
}
}
private static <T extends Throwable> private static <T extends Throwable>
Object throwOrReturn(Object normal, T exception) throws T { Object throwOrReturn(Object normal, T exception) throws T {
if (exception != null) throw exception; if (exception != null) {
called("throwOrReturn/throw", normal, exception);
throw exception;
}
called("throwOrReturn/normal", normal, exception);
return normal; return normal;
} }
private int fakeIdentityCount;
private Object fakeIdentity(Object x) {
System.out.println("should throw through this!");
fakeIdentityCount++;
return x;
}
void testCatchException(Class<?> returnType, Throwable thrown, boolean throwIt, int nargs) throws Throwable { void testCatchException(Class<?> returnType, Throwable thrown, int throwMode, int nargs, int catchDrops) throws Throwable {
countTest(); countTest();
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("catchException rt="+returnType+" throw="+throwIt+" nargs="+nargs); System.out.println("catchException rt="+returnType+" throw="+throwMode+" nargs="+nargs+" drops="+catchDrops);
Class<? extends Throwable> exType = thrown.getClass(); Class<? extends Throwable> exType = thrown.getClass();
if (throwMode > THROW_CAUGHT) thrown = new UnsupportedOperationException("do not catch this");
MethodHandle throwOrReturn MethodHandle throwOrReturn
= PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn", = PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn",
MethodType.methodType(Object.class, Object.class, Throwable.class)); MethodType.methodType(Object.class, Object.class, Throwable.class));
if (throwMode == THROW_THROUGH_ADAPTER) {
MethodHandle fakeIdentity
= PRIVATE.findVirtual(MethodHandlesTest.class, "fakeIdentity",
MethodType.methodType(Object.class, Object.class)).bindTo(this);
for (int i = 0; i < 10; i++)
throwOrReturn = MethodHandles.filterReturnValue(throwOrReturn, fakeIdentity);
}
int nargs1 = Math.max(2, nargs);
MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2)); MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
while (thrower.type().parameterCount() < nargs) thrower = addTrailingArgs(thrower, nargs, Object.class);
thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class); int catchArgc = 1 + nargs - catchDrops;
MethodHandle catcher = varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs)); MethodHandle catcher = varargsList(catchArgc).asType(MethodType.genericMethodType(catchArgc));
MethodHandle target = MethodHandles.catchException(thrower, Object[] args = randomArgs(nargs, Object.class);
thrown.getClass(), catcher); Object arg0 = MISSING_ARG;
Object arg1 = (throwMode == THROW_NOTHING) ? (Throwable) null : thrown;
if (nargs > 0) arg0 = args[0];
if (nargs > 1) args[1] = arg1;
assertEquals(nargs1, thrower.type().parameterCount());
if (nargs < nargs1) {
Object[] appendArgs = { arg0, arg1 };
appendArgs = Arrays.copyOfRange(appendArgs, nargs, nargs1);
thrower = MethodHandles.insertArguments(thrower, nargs, appendArgs);
}
assertEquals(nargs, thrower.type().parameterCount());
MethodHandle target = MethodHandles.catchException(thrower, exType, catcher);
assertEquals(thrower.type(), target.type()); assertEquals(thrower.type(), target.type());
assertEquals(nargs, target.type().parameterCount());
//System.out.println("catching with "+target+" : "+throwOrReturn); //System.out.println("catching with "+target+" : "+throwOrReturn);
Object[] args = randomArgs(nargs, Object.class); Object returned;
args[1] = (throwIt ? thrown : null); try {
Object returned = target.invokeWithArguments(args); returned = target.invokeWithArguments(args);
} catch (Throwable ex) {
assertSame("must get the out-of-band exception", thrown, ex);
if (throwMode <= THROW_CAUGHT)
assertEquals(THROW_UNCAUGHT, throwMode);
returned = ex;
}
assertCalled("throwOrReturn/"+(throwMode == THROW_NOTHING ? "normal" : "throw"), arg0, arg1);
//System.out.println("return from "+target+" : "+returned); //System.out.println("return from "+target+" : "+returned);
if (!throwIt) { if (throwMode == THROW_NOTHING) {
assertSame(args[0], returned); assertSame(arg0, returned);
} else { } else if (throwMode == THROW_CAUGHT) {
List<Object> catchArgs = new ArrayList<Object>(Arrays.asList(args)); List<Object> catchArgs = new ArrayList<Object>(Arrays.asList(args));
// catcher receives an initial subsequence of target arguments:
catchArgs.subList(nargs - catchDrops, nargs).clear();
// catcher also receives the exception, prepended:
catchArgs.add(0, thrown); catchArgs.add(0, thrown);
assertEquals(catchArgs, returned); assertEquals(catchArgs, returned);
} }
assertEquals(0, fakeIdentityCount);
} }
@Test @Test
...@@ -2092,6 +2232,48 @@ public class MethodHandlesTest { ...@@ -2092,6 +2232,48 @@ public class MethodHandlesTest {
assertSame(thrown, caught); assertSame(thrown, caught);
} }
@Test
public void testInterfaceCast() throws Throwable {
for (Class<?> ctype : new Class<?>[]{ Object.class, String.class, CharSequence.class, Number.class, Iterable.class}) {
testInterfaceCast(ctype, false, false);
testInterfaceCast(ctype, true, false);
testInterfaceCast(ctype, false, true);
testInterfaceCast(ctype, true, true);
}
}
public void testInterfaceCast(Class<?> ctype, boolean doret, boolean docast) throws Throwable {
String str = "normal return value";
MethodHandle mh = MethodHandles.identity(String.class);
MethodType mt = mh.type();
if (doret) mt = mt.changeReturnType(ctype);
else mt = mt.changeParameterType(0, ctype);
if (docast) mh = MethodHandles.explicitCastArguments(mh, mt);
else mh = mh.asType(mt);
// this bit is needed to make the interface types disappear for invokeWithArguments:
mh = MethodHandles.explicitCastArguments(mh, mt.generic());
boolean expectFail = !ctype.isInstance(str);
if (ctype.isInterface()) {
// special rules: interfaces slide by more frequently
if (docast || !doret) expectFail = false;
}
Object res;
try {
res = mh.invokeWithArguments(str);
} catch (Exception ex) {
res = ex;
}
boolean sawFail = !(res instanceof String);
if (sawFail != expectFail) {
System.out.println("*** testInterfaceCast: "+mh+" was "+mt+" => "+res+(docast ? " (explicitCastArguments)" : ""));
}
if (!sawFail) {
assertFalse(res.toString(), expectFail);
assertEquals(str, res);
} else {
assertTrue(res.toString(), expectFail);
}
}
@Test @Test
public void testCastFailure() throws Throwable { public void testCastFailure() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
...@@ -2235,46 +2417,105 @@ public class MethodHandlesTest { ...@@ -2235,46 +2417,105 @@ public class MethodHandlesTest {
called("runForRunnable"); called("runForRunnable");
} }
public interface Fooable { public interface Fooable {
Object foo(Fooable x, Object y); // overloads:
// this is for randomArg: Object foo(Object x, String y);
public class Impl implements Fooable { List foo(String x, int y);
public Object foo(Fooable x, Object y) { Object foo(String x);
throw new RuntimeException("do not call");
}
final String name;
public Impl() { name = "Fooable#"+nextArg(); }
@Override public String toString() { return name; }
}
} }
static Object fooForFooable(Fooable x, Object y) { static Object fooForFooable(String x, Object... y) {
return called("fooForFooable", x, y); return called("fooForFooable/"+x, y);
} }
public static class MyCheckedException extends Exception { public static class MyCheckedException extends Exception {
} }
public interface WillThrow { public interface WillThrow {
void willThrow() throws MyCheckedException; void willThrow() throws MyCheckedException;
} }
/*non-public*/ interface PrivateRunnable {
public void run();
}
@Test @Test
public void testAsInstance() throws Throwable { public void testAsInterfaceInstance() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
startTest("testAsInterfaceInstance");
Lookup lookup = MethodHandles.lookup(); Lookup lookup = MethodHandles.lookup();
// test typical case: Runnable.run
{ {
countTest();
if (verbosity >= 2) System.out.println("Runnable");
MethodType mt = MethodType.methodType(void.class); MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt); MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt);
Runnable proxy = MethodHandleProxies.asInterfaceInstance(Runnable.class, mh); Runnable proxy = MethodHandleProxies.asInterfaceInstance(Runnable.class, mh);
proxy.run(); proxy.run();
assertCalled("runForRunnable"); assertCalled("runForRunnable");
} }
// well known single-name overloaded interface: Appendable.append
{
countTest();
if (verbosity >= 2) System.out.println("Appendable");
ArrayList<List> appendResults = new ArrayList<List>();
MethodHandle append = lookup.bind(appendResults, "add", MethodType.methodType(boolean.class, Object.class));
append = append.asType(MethodType.methodType(void.class, List.class)); // specialize the type
MethodHandle asList = lookup.findStatic(Arrays.class, "asList", MethodType.methodType(List.class, Object[].class));
MethodHandle mh = MethodHandles.filterReturnValue(asList, append).asVarargsCollector(Object[].class);
Appendable proxy = MethodHandleProxies.asInterfaceInstance(Appendable.class, mh);
proxy.append("one");
proxy.append("two", 3, 4);
proxy.append('5');
assertEquals(Arrays.asList(Arrays.asList("one"),
Arrays.asList("two", 3, 4),
Arrays.asList('5')),
appendResults);
if (verbosity >= 3) System.out.println("appendResults="+appendResults);
appendResults.clear();
Formatter formatter = new Formatter(proxy);
String fmt = "foo str=%s char='%c' num=%d";
Object[] fmtArgs = { "str!", 'C', 42 };
String expect = String.format(fmt, fmtArgs);
formatter.format(fmt, fmtArgs);
String actual = "";
if (verbosity >= 3) System.out.println("appendResults="+appendResults);
for (List l : appendResults) {
Object x = l.get(0);
switch (l.size()) {
case 1: actual += x; continue;
case 3: actual += ((String)x).substring((int)l.get(1), (int)l.get(2)); continue;
}
actual += l;
}
if (verbosity >= 3) System.out.println("expect="+expect);
if (verbosity >= 3) System.out.println("actual="+actual);
assertEquals(expect, actual);
}
// test case of an single name which is overloaded: Fooable.foo(...)
{ {
MethodType mt = MethodType.methodType(Object.class, Fooable.class, Object.class); if (verbosity >= 2) System.out.println("Fooable");
MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable", mt); MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable",
MethodType.methodType(Object.class, String.class, Object[].class));
Fooable proxy = MethodHandleProxies.asInterfaceInstance(Fooable.class, mh); Fooable proxy = MethodHandleProxies.asInterfaceInstance(Fooable.class, mh);
Object[] args = randomArgs(mt.parameterArray()); for (Method m : Fooable.class.getDeclaredMethods()) {
Object result = proxy.foo((Fooable) args[0], args[1]); countTest();
assertCalled("fooForFooable", args); assertSame("foo", m.getName());
assertEquals(result, logEntry("fooForFooable", args)); if (verbosity > 3)
System.out.println("calling "+m);
MethodHandle invoker = lookup.unreflect(m);
MethodType mt = invoker.type();
Class<?>[] types = mt.parameterArray();
types[0] = int.class; // placeholder
Object[] args = randomArgs(types);
args[0] = proxy;
if (verbosity > 3)
System.out.println("calling "+m+" on "+Arrays.asList(args));
Object result = invoker.invokeWithArguments(args);
if (verbosity > 4)
System.out.println("result = "+result);
String name = "fooForFooable/"+args[1];
Object[] argTail = Arrays.copyOfRange(args, 2, args.length);
assertCalled(name, argTail);
assertEquals(result, logEntry(name, argTail));
}
} }
// test processing of thrown exceptions:
for (Throwable ex : new Throwable[] { new NullPointerException("ok"), for (Throwable ex : new Throwable[] { new NullPointerException("ok"),
new InternalError("ok"), new InternalError("ok"),
new Throwable("fail"), new Throwable("fail"),
...@@ -2285,11 +2526,12 @@ public class MethodHandlesTest { ...@@ -2285,11 +2526,12 @@ public class MethodHandlesTest {
mh = MethodHandles.insertArguments(mh, 0, ex); mh = MethodHandles.insertArguments(mh, 0, ex);
WillThrow proxy = MethodHandleProxies.asInterfaceInstance(WillThrow.class, mh); WillThrow proxy = MethodHandleProxies.asInterfaceInstance(WillThrow.class, mh);
try { try {
countTest();
proxy.willThrow(); proxy.willThrow();
System.out.println("Failed to throw: "+ex); System.out.println("Failed to throw: "+ex);
assertTrue(false); assertTrue(false);
} catch (Throwable ex1) { } catch (Throwable ex1) {
if (verbosity > 2) { if (verbosity > 3) {
System.out.println("throw "+ex); System.out.println("throw "+ex);
System.out.println("catch "+(ex == ex1 ? "UNWRAPPED" : ex1)); System.out.println("catch "+(ex == ex1 ? "UNWRAPPED" : ex1));
} }
...@@ -2308,16 +2550,49 @@ public class MethodHandlesTest { ...@@ -2308,16 +2550,49 @@ public class MethodHandlesTest {
} }
} }
} }
// Test error checking: // Test error checking on bad interfaces:
for (Class<?> nonSAM : new Class[] { Object.class, for (Class<?> nonSMI : new Class[] { Object.class,
String.class, String.class,
CharSequence.class, CharSequence.class,
java.io.Serializable.class,
PrivateRunnable.class,
Example.class }) { Example.class }) {
if (verbosity > 2) System.out.println(nonSMI.getName());
try { try {
MethodHandleProxies.asInterfaceInstance(nonSAM, varargsArray(0)); countTest(false);
System.out.println("Failed to throw"); MethodHandleProxies.asInterfaceInstance(nonSMI, varargsArray(0));
assertTrue(false); assertTrue("Failed to throw on "+nonSMI.getName(), false);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
if (verbosity > 2) System.out.println(nonSMI.getSimpleName()+": "+ex);
// Object: java.lang.IllegalArgumentException:
// not a public interface: java.lang.Object
// String: java.lang.IllegalArgumentException:
// not a public interface: java.lang.String
// CharSequence: java.lang.IllegalArgumentException:
// not a single-method interface: java.lang.CharSequence
// Serializable: java.lang.IllegalArgumentException:
// not a single-method interface: java.io.Serializable
// PrivateRunnable: java.lang.IllegalArgumentException:
// not a public interface: test.java.lang.invoke.MethodHandlesTest$PrivateRunnable
// Example: java.lang.IllegalArgumentException:
// not a public interface: test.java.lang.invoke.MethodHandlesTest$Example
}
}
// Test error checking on interfaces with the wrong method type:
for (Class<?> intfc : new Class[] { Runnable.class /*arity 0*/,
Fooable.class /*arity 1 & 2*/ }) {
int badArity = 1; // known to be incompatible
if (verbosity > 2) System.out.println(intfc.getName());
try {
countTest(false);
MethodHandleProxies.asInterfaceInstance(intfc, varargsArray(badArity));
assertTrue("Failed to throw on "+intfc.getName(), false);
} catch (WrongMethodTypeException ex) {
if (verbosity > 2) System.out.println(intfc.getSimpleName()+": "+ex);
// Runnable: java.lang.invoke.WrongMethodTypeException:
// cannot convert MethodHandle(Object)Object[] to ()void
// Fooable: java.lang.invoke.WrongMethodTypeException:
// cannot convert MethodHandle(Object)Object[] to (Object,String)Object
} }
} }
} }
......
...@@ -82,6 +82,7 @@ public class RicochetTest { ...@@ -82,6 +82,7 @@ public class RicochetTest {
testLongSpreads(); testLongSpreads();
testIntCollects(); testIntCollects();
testReturns(); testReturns();
testRecursion();
} }
@Test @Test
...@@ -371,6 +372,61 @@ public class RicochetTest { ...@@ -371,6 +372,61 @@ public class RicochetTest {
//System.out.println("faultCount="+faultCount); //System.out.println("faultCount="+faultCount);
} }
@Test
public void testRecursion() throws Throwable {
if (!startTest("testRecursion")) return;
final int LIMIT = 10;
for (int i = 0; i < LIMIT; i++) {
RFCB rfcb = new RFCB(i);
Object x = "x", y = "y";
Object result = rfcb.recursiveFunction(x, y);
verbose(1, result);
}
}
/** Recursive Function Control Block */
private static class RFCB {
java.util.Random random;
final MethodHandle[] fns;
int depth;
RFCB(int seed) throws Throwable {
this.random = new java.util.Random(seed);
this.fns = new MethodHandle[Math.max(29, (1 << MAX_DEPTH-2)/3)];
java.util.Arrays.fill(fns, lookup().bind(this, "recursiveFunction", genericMethodType(2)));
for (int i = 5; i < fns.length; i++) {
switch (i % 4) {
case 0: fns[i] = filterArguments(fns[i - 5], 0, insertArguments(fns[i - 4], 1, ".")); break;
case 1: fns[i] = filterArguments(fns[i - 5], 1, insertArguments(fns[i - 3], 1, ".")); break;
case 2: fns[i] = filterReturnValue(fns[i - 5], insertArguments(fns[i - 2], 1, ".")); break;
}
}
}
Object recursiveFunction(Object x, Object y) throws Throwable {
depth++;
try {
final int ACTION_COUNT = 11;
switch (random.nextInt(ACTION_COUNT)) {
case 1:
Throwable ex = new RuntimeException();
ex.fillInStackTrace();
if (VERBOSITY >= 2) ex.printStackTrace();
x = "ST; " + x;
break;
case 2:
System.gc();
x = "GC; " + x;
break;
}
boolean isLeaf = (depth >= MAX_DEPTH);
if (isLeaf) {
return Arrays.asList(x, y).toString();
}
return fns[random.nextInt(fns.length)].invokeExact(x, y);
} finally {
depth--;
}
}
}
private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) { private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) {
MethodHandle res = mh1; MethodHandle res = mh1;
for (MethodHandle mh2 : mhs) for (MethodHandle mh2 : mhs)
...@@ -536,6 +592,7 @@ public class RicochetTest { ...@@ -536,6 +592,7 @@ public class RicochetTest {
} }
// stress modes: // stress modes:
private static final int MAX_DEPTH = getProperty("MAX_DEPTH", 5);
private static final int REPEAT = getProperty("REPEAT", 0); private static final int REPEAT = getProperty("REPEAT", 0);
private static final int STRESS = getProperty("STRESS", 0); private static final int STRESS = getProperty("STRESS", 0);
private static /*v*/ int STRESS_COUNT; private static /*v*/ int STRESS_COUNT;
......
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary unit tests for method handles which permute their arguments
* @run junit test.java.lang.invoke.ThrowExceptionsTest
*/
package test.java.lang.invoke;
import org.junit.*;
import java.util.*;
import java.lang.reflect.*;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
public class ThrowExceptionsTest {
private static final Class CLASS = ThrowExceptionsTest.class;
private static final Lookup LOOKUP = lookup();
public static void main(String argv[]) throws Throwable {
new ThrowExceptionsTest().testAll((argv.length == 0 ? null : Arrays.asList(argv).toString()));
}
@Test
public void testWMT() throws Throwable {
// mostly call testWMTCallee, but sometimes call its void-returning variant
MethodHandle mh = testWMTCallee();
MethodHandle mh1 = mh.asType(mh.type().changeReturnType(void.class));
assert(mh1 != mh);
testWMT(mh, mh1, 1000);
}
@Test
public void testBoundWMT() throws Throwable {
// mostly call exactInvoker.bindTo(testWMTCallee), but sometimes call its void-returning variant
MethodHandle callee = testWMTCallee();
MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class));
MethodHandle invoker = exactInvoker(callee.type());
MethodHandle mh = invoker.bindTo(callee);
MethodHandle mh1 = invoker.bindTo(callee1);
testWMT(mh, mh1, 1000);
}
@Test
public void testFoldWMT() throws Throwable {
// mostly call exactInvoker.fold(constant(testWMTCallee)), but sometimes call its void-returning variant
MethodHandle callee = testWMTCallee();
MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class));
MethodHandle invoker = exactInvoker(callee.type());
MethodHandle mh = foldArguments(invoker, constant(MethodHandle.class, callee));
MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
testWMT(mh, mh1, 1000);
}
@Test
public void testFoldCCE() throws Throwable {
MethodHandle callee = testWMTCallee();
MethodHandle callee1 = callee.asType(callee.type().changeParameterType(1, Number.class)).asType(callee.type());
MethodHandle invoker = exactInvoker(callee.type());
MethodHandle mh = foldArguments(invoker, constant(MethodHandle.class, callee));
MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
testWMT(mh, mh1, 1000);
}
@Test
public void testStackOverflow() throws Throwable {
MethodHandle callee = testWMTCallee();
MethodHandle callee1 = makeStackOverflow().asType(callee.type());
MethodHandle invoker = exactInvoker(callee.type());
MethodHandle mh = foldArguments(invoker, constant(MethodHandle.class, callee));
MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
for (int i = 0; i < REPEAT; i++) {
try {
testWMT(mh, mh1, 1000);
} catch (StackOverflowError ex) {
// OK, try again
}
}
}
private static MethodHandle makeStackOverflow() {
MethodType cellType = methodType(void.class);
MethodHandle[] cell = { null }; // recursion point
MethodHandle getCell = insertArguments(arrayElementGetter(cell.getClass()), 0, cell, 0);
MethodHandle invokeCell = foldArguments(exactInvoker(cellType), getCell);
assert(invokeCell.type() == cellType);
cell[0] = invokeCell;
// make it conformable to any type:
invokeCell = dropArguments(invokeCell, 0, Object[].class).asVarargsCollector(Object[].class);
return invokeCell;
}
static int testCases;
private void testAll(String match) throws Throwable {
testCases = 0;
Lookup lookup = lookup();
for (Method m : CLASS.getDeclaredMethods()) {
String name = m.getName();
if (name.startsWith("test") &&
(match == null || match.contains(name.substring("test".length()))) &&
m.getParameterTypes().length == 0 &&
Modifier.isPublic(m.getModifiers()) &&
!Modifier.isStatic(m.getModifiers())) {
System.out.println("["+name+"]");
int tc = testCases;
try {
m.invoke(this);
} catch (Throwable ex) {
System.out.println("*** "+ex);
ex.printStackTrace();
}
if (testCases == tc) testCases++;
}
}
if (testCases == 0) throw new RuntimeException("no test cases found");
System.out.println("ran a total of "+testCases+" test cases");
}
private static MethodHandle findStatic(String name) {
return findMethod(name, true);
}
private static MethodHandle findVirtual(String name) {
return findMethod(name, false);
}
private static MethodHandle findMethod(String name, boolean isStatic) {
MethodHandle mh = null;
for (Method m : CLASS.getDeclaredMethods()) {
if (m.getName().equals(name) &&
Modifier.isStatic(m.getModifiers()) == isStatic) {
if (mh != null)
throw new RuntimeException("duplicate methods: "+name);
try {
mh = LOOKUP.unreflect(m);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
}
if (mh == null)
throw new RuntimeException("no method: "+name);
return mh;
}
int testWMTCallee;
private int testWMTCallee(String x) {
return testWMTCallee++;
}
private static MethodHandle testWMTCallee() {
MethodHandle callee = findVirtual("testWMTCallee");
// FIXME: should not have to retype callee
callee = callee.asType(callee.type().changeParameterType(0, Object.class));
return callee;
}
private Exception testWMT(MethodHandle[] mhs, int reps) throws Throwable {
testCases += 1;
testWMTCallee = 0;
int catches = 0;
Exception savedEx = null;
for (int i = 0; i < reps; i++) {
MethodHandle mh = mhs[i % mhs.length];
int n;
try {
// FIXME: should not have to retype this
n = (int) mh.invokeExact((Object)this, "x");
assertEquals(n, i - catches);
// Using the exact type for this causes endless deopt due to
// 'non_cached_result' in SystemDictionary::find_method_handle_invoke.
// The problem is that the compiler thread needs to access a cached
// invoke method, but invoke methods are not cached if one of the
// component types is not on the BCP.
} catch (Exception ex) {
savedEx = ex;
catches++;
}
}
//VERBOSE: System.out.println("reps="+reps+" catches="+catches);
return savedEx;
}
private static final int REPEAT = Integer.getInteger(CLASS.getSimpleName()+".REPEAT", 10);
private Exception testWMT(MethodHandle mh, MethodHandle mh1, int reps) throws Throwable {
//VERBOSE: System.out.println("mh="+mh+" mh1="+mh1);
MethodHandle[] mhs = new MethodHandle[100];
Arrays.fill(mhs, mh);
int patch = mhs.length-1;
Exception savedEx = null;
for (int i = 0; i < REPEAT; i++) {
mhs[patch] = mh;
testWMT(mhs, 10000);
mhs[patch] = mh1;
savedEx = testWMT(mhs, reps);
}
return savedEx;
}
private static void assertEquals(Object x, Object y) {
if (x == y || x != null && x.equals(y)) return;
throw new RuntimeException(x+" != "+y);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册