# HG changeset patch # User jrose # Date 1262825451 28800 # Node ID 0f6ec6364a6958ed8983e0053c5f445429543ae1 # Parent 57311dc693a3b672edda165b583a77ee415cc9e5# Parent 50426a1436f0728ca84eed00e8e8d09c932ac44f Merge diff -r 57311dc693a3 -r 0f6ec6364a69 jdk/test/java/dyn/MethodHandlesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/dyn/MethodHandlesTest.java Wed Jan 06 16:50:51 2010 -0800 @@ -0,0 +1,881 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @summary unit tests for java.dyn.MethodHandles + * @compile -XDinvokedynamic MethodHandlesTest.java + * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic jdk.java.dyn.MethodHandlesTest + */ + +package jdk.java.dyn; + +import java.dyn.*; +import java.dyn.MethodHandles.Lookup; +import java.lang.reflect.*; +import java.util.*; +import org.junit.*; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; + + +/** + * + * @author jrose + */ +public class MethodHandlesTest { + // How much output? + static int verbosity = 1; + + // Set this true during development if you want to fast-forward to + // a particular new, non-working test. Tests which are known to + // work (or have recently worked) test this flag and return on true. + static boolean CAN_SKIP_WORKING = false; + //static { CAN_SKIP_WORKING = true; } + + // Set true to test more calls. If false, some tests are just + // lookups, without exercising the actual method handle. + static boolean DO_MORE_CALLS = false; + + + @Test + public void testFirst() throws Throwable { + verbosity += 9; try { + // left blank for debugging + } finally { verbosity -= 9; } + } + + static final int MAX_ARG_INCREASE = 3; + + public MethodHandlesTest() { + } + + @Before + public void checkImplementedPlatform() { + boolean platformOK = false; + Properties properties = System.getProperties(); + String vers = properties.getProperty("java.vm.version"); + String name = properties.getProperty("java.vm.name"); + String arch = properties.getProperty("os.arch"); + if (arch.equals("i386") && + (name.contains("Client") || name.contains("Server")) + ) { + platformOK = true; + } else { + System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch)); + } + assumeTrue(platformOK); + } + + String testName; + int posTests, negTests; + @After + public void printCounts() { + if (verbosity >= 1 && (posTests | negTests) != 0) { + System.out.println(); + if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run"); + if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run"); + } + } + void countTest(boolean positive) { + if (positive) ++posTests; + else ++negTests; + } + void countTest() { countTest(true); } + void startTest(String name) { + if (testName != null) printCounts(); + if (verbosity >= 0) + System.out.println(name); + posTests = negTests = 0; + testName = name; + } + + @BeforeClass + public static void setUpClass() throws Exception { + calledLog.clear(); + calledLog.add(null); + nextArg = 1000000; + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + static List calledLog = new ArrayList(); + static Object logEntry(String name, Object... args) { + return Arrays.asList(name, Arrays.asList(args)); + } + static Object called(String name, Object... args) { + Object entry = logEntry(name, args); + calledLog.add(entry); + return entry; + } + static void assertCalled(String name, Object... args) { + Object expected = logEntry(name, args); + Object actual = calledLog.get(calledLog.size() - 1); + if (expected.equals(actual)) return; + System.out.println("assertCalled "+name+":"); + System.out.println("expected: "+expected); + System.out.println("actual: "+actual); + System.out.println("ex. types: "+getClasses(expected)); + System.out.println("act. types: "+getClasses(actual)); + assertEquals("previous method call types", expected, actual); + assertEquals("previous method call", expected, actual); + } + static void printCalled(MethodHandle target, String name, Object... args) { + if (verbosity >= 2) + System.out.println("calling "+logEntry(name, args)+" on "+target); + } + + static Object castToWrapper(Object value, Class dst) { + Object wrap = null; + if (value instanceof Number) + wrap = castToWrapperOrNull(((Number)value).longValue(), dst); + if (value instanceof Character) + wrap = castToWrapperOrNull((char)(Character)value, dst); + if (wrap != null) return wrap; + return dst.cast(value); + } + + static Object castToWrapperOrNull(long value, Class dst) { + if (dst == int.class || dst == Integer.class) + return (int)(value); + if (dst == long.class || dst == Long.class) + return (long)(value); + if (dst == char.class || dst == Character.class) + return (char)(value); + if (dst == short.class || dst == Short.class) + return (short)(value); + if (dst == float.class || dst == Float.class) + return (float)(value); + if (dst == double.class || dst == Double.class) + return (double)(value); + return null; + } + + static int nextArg; + static Object randomArg(Class param) { + Object wrap = castToWrapperOrNull(nextArg, param); + if (wrap != null) { + nextArg++; + return wrap; + } +// import sun.dyn.util.Wrapper; +// Wrapper wrap = Wrapper.forBasicType(dst); +// if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst)) +// wrap = Wrapper.forWrapperType(dst); +// if (wrap != Wrapper.OBJECT) +// return wrap.wrap(nextArg++); + if (param.isInterface() || param.isAssignableFrom(String.class)) + return "#"+(nextArg++); + else + try { + return param.newInstance(); + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + return null; // random class not Object, String, Integer, etc. + } + static Object[] randomArgs(Class... params) { + Object[] args = new Object[params.length]; + for (int i = 0; i < args.length; i++) + args[i] = randomArg(params[i]); + return args; + } + static Object[] randomArgs(int nargs, Class param) { + Object[] args = new Object[nargs]; + for (int i = 0; i < args.length; i++) + args[i] = randomArg(param); + return args; + } + + static T[] array(Class atype, E... a) { + return Arrays.copyOf(a, a.length, atype); + } + static T[] cat(T[] a, T... b) { + int alen = a.length, blen = b.length; + if (blen == 0) return a; + T[] c = Arrays.copyOf(a, alen + blen); + System.arraycopy(b, 0, c, alen, blen); + return c; + } + static Integer[] boxAll(int... vx) { + Integer[] res = new Integer[vx.length]; + for (int i = 0; i < res.length; i++) { + res[i] = vx[i]; + } + return res; + } + static Object getClasses(Object x) { + if (x == null) return x; + if (x instanceof String) return x; // keep the name + if (x instanceof List) { + // recursively report classes of the list elements + Object[] xa = ((List)x).toArray(); + for (int i = 0; i < xa.length; i++) + xa[i] = getClasses(xa[i]); + return Arrays.asList(xa); + } + return x.getClass().getSimpleName(); + } + + static MethodHandle changeArgTypes(MethodHandle target, Class argType) { + return changeArgTypes(target, 0, 999, argType); + } + static MethodHandle changeArgTypes(MethodHandle target, + int beg, int end, Class argType) { + MethodType targetType = target.type(); + end = Math.min(end, targetType.parameterCount()); + ArrayList> argTypes = new ArrayList>(targetType.parameterList()); + Collections.fill(argTypes.subList(beg, end), argType); + MethodType ttype2 = MethodType.make(targetType.returnType(), argTypes); + return MethodHandles.convertArguments(target, ttype2); + } + + // This lookup is good for all members in and under MethodHandlesTest. + static final Lookup PRIVATE = MethodHandles.lookup(); + // This lookup is good for package-private members but not private ones. + static final Lookup PACKAGE = PackageSibling.lookup(); + // This lookup is good only for public members. + static final Lookup PUBLIC = MethodHandles.Lookup.PUBLIC_LOOKUP; + + // Subject methods... + static class Example implements IntExample { + final String name; + public Example() { name = "Example#"+(nextArg++); } + protected Example(String name) { this.name = name; } + protected Example(int x) { this(); called("protected ", this, x); } + @Override public String toString() { return name; } + + public void v0() { called("v0", this); } + void pkg_v0() { called("pkg_v0", this); } + private void pri_v0() { called("pri_v0", this); } + public static void s0() { called("s0"); } + static void pkg_s0() { called("pkg_s0"); } + private static void pri_s0() { called("pri_s0"); } + + public Object v1(Object x) { return called("v1", this, x); } + public Object v2(Object x, Object y) { return called("v2", this, x, y); } + public Object v2(Object x, int y) { return called("v2", this, x, y); } + public Object v2(int x, Object y) { return called("v2", this, x, y); } + public Object v2(int x, int y) { return called("v2", this, x, y); } + public static Object s1(Object x) { return called("s1", x); } + public static Object s2(int x) { return called("s2", x); } + public static Object s3(long x) { return called("s3", x); } + public static Object s4(int x, int y) { return called("s4", x, y); } + public static Object s5(long x, int y) { return called("s5", 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 class PubExample extends Example { + } + static class SubExample extends Example { + @Override public void v0() { called("Sub/v0", this); } + @Override void pkg_v0() { called("Sub/pkg_v0", this); } + private SubExample(int x) { called("", this, x); } + public SubExample() { super("SubExample#"+(nextArg++)); } + } + public static interface IntExample { + public void v0(); + static class Impl implements IntExample { + public void v0() { called("Int/v0", this); } + final String name; + public Impl() { name = "Example#"+(nextArg++); } + } + } + + static final Object[][][] ACCESS_CASES = { + { { true, PRIVATE } } // only one test case at present + }; + + static Object[][] accessCases(Class defc, String name) { + return ACCESS_CASES[0]; + } + + @Test + public void testFindStatic() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("findStatic"); + testFindStatic(PubExample.class, void.class, "s0"); + testFindStatic(Example.class, void.class, "s0"); + testFindStatic(Example.class, void.class, "pkg_s0"); + testFindStatic(Example.class, void.class, "pri_s0"); + + testFindStatic(Example.class, Object.class, "s1", Object.class); + testFindStatic(Example.class, Object.class, "s2", int.class); + testFindStatic(Example.class, Object.class, "s3", long.class); + testFindStatic(Example.class, Object.class, "s4", int.class, int.class); + testFindStatic(Example.class, Object.class, "s5", long.class, int.class); + testFindStatic(Example.class, Object.class, "s6", int.class, long.class); + testFindStatic(Example.class, Object.class, "s7", float.class, double.class); + + testFindStatic(false, PRIVATE, Example.class, void.class, "bogus"); + } + + void testFindStatic(Class defc, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testFindStatic((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params); + } + } + void testFindStatic(Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + testFindStatic(true, lookup, defc, ret, name, params); + } + void testFindStatic(boolean positive, Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.findStatic(defc, name, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("findStatic "+lookup+": "+defc+"."+name+"/"+type+" => "+target + +(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, target.type()); + assertTrue(target.toString().contains(name)); // rough check + if (!DO_MORE_CALLS && lookup != PRIVATE) return; + Object[] args = randomArgs(params); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + assertCalled(name, args); + System.out.print(':'); + } + + @Test + public void testFindVirtual() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("findVirtual"); + testFindVirtual(Example.class, void.class, "v0"); + testFindVirtual(Example.class, void.class, "pkg_v0"); + testFindVirtual(Example.class, void.class, "pri_v0"); + testFindVirtual(Example.class, Object.class, "v1", Object.class); + testFindVirtual(Example.class, Object.class, "v2", Object.class, Object.class); + testFindVirtual(Example.class, Object.class, "v2", Object.class, int.class); + testFindVirtual(Example.class, Object.class, "v2", int.class, Object.class); + testFindVirtual(Example.class, Object.class, "v2", int.class, int.class); + testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus"); + // test dispatch + testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/v0"); + testFindVirtual(SubExample.class, Example.class, void.class, "Sub/v0"); + testFindVirtual(SubExample.class, IntExample.class, void.class, "Sub/v0"); + testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/pkg_v0"); + testFindVirtual(SubExample.class, Example.class, void.class, "Sub/pkg_v0"); + testFindVirtual(Example.class, IntExample.class, void.class, "v0"); + testFindVirtual(IntExample.Impl.class, IntExample.class, void.class, "Int/v0"); + } + + void testFindVirtual(Class defc, Class ret, String name, Class... params) throws Throwable { + Class rcvc = defc; + testFindVirtual(rcvc, defc, ret, name, params); + } + void testFindVirtual(Class rcvc, Class defc, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testFindVirtual((Boolean)ac[0], (Lookup)ac[1], rcvc, defc, ret, name, params); + } + } + void testFindVirtual(Lookup lookup, Class rcvc, Class defc, Class ret, String name, Class... params) throws Throwable { + testFindVirtual(true, lookup, rcvc, defc, ret, name, params); + } + void testFindVirtual(boolean positive, Lookup lookup, Class rcvc, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.findVirtual(defc, methodName, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("findVirtual "+lookup+": "+defc+"."+name+"/"+type+" => "+target + +(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 + Class[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params); + MethodType typeWithSelf = MethodType.make(ret, paramsWithSelf); + MethodType ttype = target.type(); + ttype = ttype.changeParameterType(0, defc); // FIXME: test this + assertEquals(typeWithSelf, ttype); + assertTrue(target.toString().contains(methodName)); // rough check + if (!DO_MORE_CALLS && lookup != PRIVATE) return; + Object[] argsWithSelf = randomArgs(paramsWithSelf); + if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc); + printCalled(target, name, argsWithSelf); + MethodHandles.invoke(target, argsWithSelf); + assertCalled(name, argsWithSelf); + System.out.print(':'); + } + + @Test + public void testFindSpecial() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("findSpecial"); + testFindSpecial(Example.class, void.class, "v0"); + testFindSpecial(Example.class, void.class, "pkg_v0"); + testFindSpecial(false, PRIVATE, Example.class, void.class, "", int.class); + testFindSpecial(false, PRIVATE, Example.class, void.class, "bogus"); + } + + void testFindSpecial(Class defc, Class ret, String name, Class... params) throws Throwable { + testFindSpecial(true, PRIVATE, defc, ret, name, params); + testFindSpecial(false, PACKAGE, defc, ret, name, params); + testFindSpecial(false, PUBLIC, defc, ret, name, params); + } + void testFindSpecial(boolean positive, Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.findSpecial(defc, name, type, defc); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("findSpecial "+defc+"."+name+"/"+type+" => "+target + +(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 + Class[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params); + MethodType typeWithSelf = MethodType.make(ret, paramsWithSelf); + MethodType ttype = target.type(); + ttype = ttype.changeParameterType(0, defc); // FIXME: test this + assertEquals(typeWithSelf, ttype); + assertTrue(target.toString().contains(name)); // rough check + if (!DO_MORE_CALLS && lookup != PRIVATE) return; + Object[] args = randomArgs(paramsWithSelf); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + assertCalled(name, args); + System.out.print(':'); + } + + @Test + public void testBind() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("bind"); + testBind(Example.class, void.class, "v0"); + testBind(Example.class, void.class, "pkg_v0"); + testBind(Example.class, void.class, "pri_v0"); + testBind(Example.class, Object.class, "v1", Object.class); + testBind(Example.class, Object.class, "v2", Object.class, Object.class); + testBind(Example.class, Object.class, "v2", Object.class, int.class); + testBind(Example.class, Object.class, "v2", int.class, Object.class); + testBind(Example.class, Object.class, "v2", int.class, int.class); + testBind(false, PRIVATE, Example.class, void.class, "bogus"); + testBind(SubExample.class, void.class, "Sub/v0"); + testBind(SubExample.class, void.class, "Sub/pkg_v0"); + testBind(IntExample.Impl.class, void.class, "Int/v0"); + } + + void testBind(Class defc, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testBind((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params); + } + } + + void testBind(boolean positive, Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo + MethodType type = MethodType.make(ret, params); + Object receiver = randomArg(defc); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.bind(receiver, methodName, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target + +(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, target.type()); + Object[] args = randomArgs(params); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + Object[] argsWithReceiver = cat(array(Object[].class, receiver), args); + assertCalled(name, argsWithReceiver); + System.out.print(':'); + } + + @Test + public void testUnreflect() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("unreflect"); + testUnreflect(Example.class, true, void.class, "s0"); + testUnreflect(Example.class, true, void.class, "pkg_s0"); + testUnreflect(Example.class, true, void.class, "pri_s0"); + + testUnreflect(Example.class, true, Object.class, "s1", Object.class); + testUnreflect(Example.class, true, Object.class, "s2", int.class); + //testUnreflect(Example.class, true, Object.class, "s3", long.class); + //testUnreflect(Example.class, true, Object.class, "s4", int.class, int.class); + //testUnreflect(Example.class, true, Object.class, "s5", long.class, int.class); + //testUnreflect(Example.class, true, Object.class, "s6", int.class, long.class); + + testUnreflect(Example.class, false, void.class, "v0"); + testUnreflect(Example.class, false, void.class, "pkg_v0"); + testUnreflect(Example.class, false, void.class, "pri_v0"); + testUnreflect(Example.class, false, Object.class, "v1", Object.class); + testUnreflect(Example.class, false, Object.class, "v2", Object.class, Object.class); + testUnreflect(Example.class, false, Object.class, "v2", Object.class, int.class); + testUnreflect(Example.class, false, Object.class, "v2", int.class, Object.class); + testUnreflect(Example.class, false, Object.class, "v2", int.class, int.class); + } + + void testUnreflect(Class defc, boolean isStatic, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testUnreflect((Boolean)ac[0], (Lookup)ac[1], defc, isStatic, ret, name, params); + } + } + void testUnreflect(boolean positive, Lookup lookup, Class defc, boolean isStatic, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + MethodType type = MethodType.make(ret, params); + Method rmethod = null; + MethodHandle target = null; + RuntimeException noAccess = null; + try { + rmethod = defc.getDeclaredMethod(name, params); + } catch (NoSuchMethodException ex) { + throw new NoAccessException(ex); + } + assertEquals(isStatic, Modifier.isStatic(rmethod.getModifiers())); + try { + target = lookup.unreflect(rmethod); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("unreflect "+defc+"."+name+"/"+type+" => "+target + +(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 + Class[] paramsMaybeWithSelf = params; + if (!isStatic) { + paramsMaybeWithSelf = cat(array(Class[].class, (Class)defc), params); + } + MethodType typeMaybeWithSelf = MethodType.make(ret, paramsMaybeWithSelf); + MethodType ttype = target.type(); + if (!isStatic) + ttype = ttype.changeParameterType(0, defc); // FIXME: test this + assertEquals(typeMaybeWithSelf, ttype); + Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf); + printCalled(target, name, argsMaybeWithSelf); + MethodHandles.invoke(target, argsMaybeWithSelf); + assertCalled(name, argsMaybeWithSelf); + System.out.print(':'); + } + + @Test @Ignore("unimplemented") + public void testUnreflectSpecial() throws Throwable { + Lookup lookup = PRIVATE; // FIXME: test more lookups than this one + startTest("unreflectSpecial"); + Method m = null; + MethodHandle expResult = null; + MethodHandle result = lookup.unreflectSpecial(m, Example.class); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testUnreflectGetter() throws Throwable { + Lookup lookup = PRIVATE; // FIXME: test more lookups than this one + startTest("unreflectGetter"); + Field f = null; + MethodHandle expResult = null; + MethodHandle result = lookup.unreflectGetter(f); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testUnreflectSetter() throws Throwable { + Lookup lookup = PRIVATE; // FIXME: test more lookups than this one + startTest("unreflectSetter"); + Field f = null; + MethodHandle expResult = null; + MethodHandle result = lookup.unreflectSetter(f); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testArrayElementGetter() throws Throwable { + startTest("arrayElementGetter"); + Class arrayClass = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.arrayElementGetter(arrayClass); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testArrayElementSetter() throws Throwable { + startTest("arrayElementSetter"); + Class arrayClass = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.arrayElementSetter(arrayClass); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + static class Callee { + static Object id() { return called("id"); } + static Object id(Object x) { return called("id", x); } + static Object id(Object x, Object y) { return called("id", x, y); } + static Object id(Object x, Object y, Object z) { return called("id", x, y, z); } + static Object id(Object... vx) { return called("id", vx); } + static MethodHandle ofType(int n) { + return ofType(Object.class, n); + } + static MethodHandle ofType(Class rtype, int n) { + if (n == -1) + return ofType(MethodType.make(rtype, Object[].class)); + return ofType(MethodType.makeGeneric(n).changeReturnType(rtype)); + } + static MethodHandle ofType(Class rtype, Class... ptypes) { + return ofType(MethodType.make(rtype, ptypes)); + } + static MethodHandle ofType(MethodType type) { + Class rtype = type.returnType(); + String pfx = ""; + if (rtype != Object.class) + pfx = rtype.getSimpleName().substring(0, 1).toLowerCase(); + String name = pfx+"id"; + return PRIVATE.findStatic(Callee.class, name, type); + } + } + + @Test + public void testConvertArguments() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("convertArguments"); + testConvert(Callee.ofType(1), null, "id", int.class); + testConvert(Callee.ofType(1), null, "id", String.class); + testConvert(Callee.ofType(1), null, "id", Integer.class); + testConvert(Callee.ofType(1), null, "id", short.class); + } + + void testConvert(MethodHandle id, Class rtype, String name, Class... params) throws Throwable { + testConvert(true, id, rtype, name, params); + } + + void testConvert(boolean positive, MethodHandle id, Class rtype, String name, Class... params) throws Throwable { + countTest(positive); + MethodType idType = id.type(); + if (rtype == null) rtype = idType.returnType(); + for (int i = 0; i < params.length; i++) { + if (params[i] == null) params[i] = idType.parameterType(i); + } + // simulate the pairwise conversion + MethodType newType = MethodType.make(rtype, params); + Object[] args = randomArgs(newType.parameterArray()); + Object[] convArgs = args.clone(); + for (int i = 0; i < args.length; i++) { + Class src = newType.parameterType(i); + Class dst = idType.parameterType(i); + if (src != dst) + convArgs[i] = castToWrapper(convArgs[i], dst); + } + Object convResult = MethodHandles.invoke(id, convArgs); + { + Class dst = newType.returnType(); + Class src = idType.returnType(); + if (src != dst) + convResult = castToWrapper(convResult, dst); + } + MethodHandle target = null; + RuntimeException error = null; + try { + target = MethodHandles.convertArguments(id, newType); + } catch (RuntimeException ex) { + error = ex; + } + if (verbosity >= 2) + System.out.println("convert "+id+ " to "+newType+" => "+target + +(error == null ? "" : " !! "+error)); + if (positive && error != null) throw error; + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); + if (!positive) return; // negative test failed as expected + assertEquals(newType, target.type()); + printCalled(target, id.toString(), args); + Object result = MethodHandles.invoke(target, args); + assertCalled(name, convArgs); + assertEquals(convResult, result); + System.out.print(':'); + } + + @Test + public void testInsertArguments() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("insertArguments"); + for (int nargs = 0; nargs <= 4; nargs++) { + for (int ins = 0; ins <= 4; ins++) { + if (ins > MAX_ARG_INCREASE) continue; // FIXME Fail_6 + for (int pos = 0; pos <= nargs; pos++) { + testInsertArguments(nargs, pos, ins); + } + } + } + } + + void testInsertArguments(int nargs, int pos, int ins) throws Throwable { + if (pos != 0 || ins != 1) return; // temp. restriction until MHs.insertArguments + countTest(); + MethodHandle target = ValueConversions.varargsArray(nargs + ins); + Object[] args = randomArgs(target.type().parameterArray()); + List resList = Arrays.asList(args); + List argsToPass = new ArrayList(resList); + List argsToInsert = argsToPass.subList(pos, pos + ins); + if (verbosity >= 2) + System.out.println("insert: "+argsToInsert+" into "+target); + MethodHandle target2 = MethodHandles.insertArgument(target, pos, + argsToInsert.get(0)); + argsToInsert.clear(); // remove from argsToInsert + Object res2 = MethodHandles.invoke(target2, argsToPass.toArray()); + Object res2List = Arrays.asList((Object[])res2); + if (verbosity >= 2) + System.out.println("result: "+res2List); + //if (!resList.equals(res2List)) + // System.out.println("*** fail at n/p/i = "+nargs+"/"+pos+"/"+ins+": "+resList+" => "+res2List); + assertEquals(resList, res2List); + } + + private static final String MISSING_ARG = "missingArg"; + static Object targetIfEquals() { + return called("targetIfEquals"); + } + static Object fallbackIfNotEquals() { + return called("fallbackIfNotEquals"); + } + static Object targetIfEquals(Object x) { + assertEquals(x, MISSING_ARG); + return called("targetIfEquals", x); + } + static Object fallbackIfNotEquals(Object x) { + assertFalse(x.toString(), x.equals(MISSING_ARG)); + return called("fallbackIfNotEquals", x); + } + static Object targetIfEquals(Object x, Object y) { + assertEquals(x, y); + return called("targetIfEquals", x, y); + } + static Object fallbackIfNotEquals(Object x, Object y) { + assertFalse(x.toString(), x.equals(y)); + return called("fallbackIfNotEquals", x, y); + } + static Object targetIfEquals(Object x, Object y, Object z) { + assertEquals(x, y); + return called("targetIfEquals", x, y, z); + } + static Object fallbackIfNotEquals(Object x, Object y, Object z) { + assertFalse(x.toString(), x.equals(y)); + return called("fallbackIfNotEquals", x, y, z); + } + +} +// Local abbreviated copy of sun.dyn.util.ValueConversions +class ValueConversions { + private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); + private static final Object[] NO_ARGS_ARRAY = {}; + private static Object[] makeArray(Object... args) { return args; } + private static Object[] array() { return NO_ARGS_ARRAY; } + private static Object[] array(Object a0) + { return makeArray(a0); } + private static Object[] array(Object a0, Object a1) + { return makeArray(a0, a1); } + private static Object[] array(Object a0, Object a1, Object a2) + { return makeArray(a0, a1, a2); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3) + { return makeArray(a0, a1, a2, a3); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4) + { return makeArray(a0, a1, a2, a3, a4); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { return makeArray(a0, a1, a2, a3, a4, a5); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { return makeArray(a0, a1, a2, a3, a4, a5, a6); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + static MethodHandle[] makeArrays() { + ArrayList arrays = new ArrayList(); + MethodHandles.Lookup lookup = IMPL_LOOKUP; + for (;;) { + int nargs = arrays.size(); + MethodType type = MethodType.makeGeneric(nargs).changeReturnType(Object[].class); + String name = "array"; + MethodHandle array = null; + try { + array = lookup.findStatic(ValueConversions.class, name, type); + } catch (NoAccessException ex) { + } + if (array == null) break; + arrays.add(array); + } + assert(arrays.size() == 11); // current number of methods + return arrays.toArray(new MethodHandle[0]); + } + static final MethodHandle[] ARRAYS = makeArrays(); + + /** Return a method handle that takes the indicated number of Object + * arguments and returns an Object array of them, as if for varargs. + */ + public static MethodHandle varargsArray(int nargs) { + if (nargs < ARRAYS.length) + return ARRAYS[nargs]; + // else need to spin bytecode or do something else fancy + throw new UnsupportedOperationException("NYI"); + } +} +// This guy tests access from outside the same package member, but inside +// the package itself. +class PackageSibling { + static Lookup lookup() { + return MethodHandles.lookup(); + } +} +