PrivateInvokeTest.java revision 16177:89ef4b822745
120253Sjoerg/*
220302Sjoerg * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
320302Sjoerg * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
420253Sjoerg *
520253Sjoerg * This code is free software; you can redistribute it and/or modify it
620253Sjoerg * under the terms of the GNU General Public License version 2 only, as
720253Sjoerg * published by the Free Software Foundation.
820253Sjoerg *
920302Sjoerg * This code is distributed in the hope that it will be useful, but WITHOUT
1020253Sjoerg * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1120253Sjoerg * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1220253Sjoerg * version 2 for more details (a copy is included in the LICENSE file that
1320253Sjoerg * accompanied this code).
1420302Sjoerg *
1520253Sjoerg * You should have received a copy of the GNU General Public License version
1620253Sjoerg * 2 along with this work; if not, write to the Free Software Foundation,
1720302Sjoerg * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1820253Sjoerg *
1920253Sjoerg * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2020253Sjoerg * or visit www.oracle.com if you need additional information or have any
2120253Sjoerg * questions.
2220253Sjoerg */
2320253Sjoerg
2420253Sjoerg/* @test
2544229Sdavidn * @summary white-box testing of method handle sub-primitives
2620253Sjoerg * @modules java.base/java.lang.invoke:open
2720253Sjoerg * @run junit test.java.lang.invoke.PrivateInvokeTest
2830259Scharnier */
2930259Scharnier
3050479Speterpackage test.java.lang.invoke;
3130259Scharnier
3230259Scharnierimport java.lang.invoke.*;
3330259Scharnierimport static java.lang.invoke.MethodHandles.*;
3430259Scharnierimport static java.lang.invoke.MethodType.*;
3520253Sjoergimport java.lang.reflect.*;
3620253Sjoergimport java.util.ArrayList;
3720253Sjoergimport java.util.Arrays;
3830259Scharnierimport org.junit.*;
3920253Sjoergimport static org.junit.Assert.*;
4020555Sdavidn
4120555Sdavidnpublic class PrivateInvokeTest {
4220555Sdavidn    // Utility functions
4330259Scharnier    private static final Lookup LOOKUP = lookup();
4464918Sgreen    private static final Class<?> THIS_CLASS = PrivateInvokeTest.class;
4520253Sjoerg    private static final int
4620253Sjoerg            REF_NONE                    = 0,  // null value
4720253Sjoerg            REF_getField                = 1,
4823318Sache            REF_getStatic               = 2,
4922394Sdavidn            REF_putField                = 3,
5052512Sdavidn            REF_putStatic               = 4,
5124214Sache            REF_invokeVirtual           = 5,
5244386Sdavidn            REF_invokeStatic            = 6,
5320253Sjoerg            REF_invokeSpecial           = 7,
5420253Sjoerg            REF_newInvokeSpecial        = 8,
5520253Sjoerg            REF_invokeInterface         = 9,
5620253Sjoerg            REF_LIMIT                  = 10,
5720253Sjoerg            REF_MH_invokeBasic         = REF_NONE;;
5820253Sjoerg    private static final String[] REF_KIND_NAMES = {
5920253Sjoerg        "MH::invokeBasic",
6020253Sjoerg        "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
6120253Sjoerg        "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
6285145Sache        "REF_newInvokeSpecial", "REF_invokeInterface"
6320253Sjoerg    };
6420253Sjoerg    private int verbose;
6520253Sjoerg    //{ verbose = 99; }  // for debugging
6620253Sjoerg    {
6720253Sjoerg        String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbose");
6820253Sjoerg        if (vstr == null)
6920253Sjoerg            vstr = System.getProperty(THIS_CLASS.getName()+".verbose");
7020253Sjoerg        if (vstr == null)
7120253Sjoerg            vstr = System.getProperty("test.verbose");
7220253Sjoerg        if (vstr != null)  verbose = Integer.parseInt(vstr);
7320253Sjoerg    }
7420253Sjoerg    private static int referenceKind(Method m) {
7520253Sjoerg        if (Modifier.isStatic(m.getModifiers()))
7620253Sjoerg            return REF_invokeStatic;
7720253Sjoerg        else if (m.getDeclaringClass().isInterface())
7820253Sjoerg            return REF_invokeInterface;
7920253Sjoerg        else if (Modifier.isFinal(m.getModifiers()) ||
8020253Sjoerg            Modifier.isFinal(m.getDeclaringClass().getModifiers()))
81124382Siedowse            return REF_invokeSpecial;
8220253Sjoerg        else
8320253Sjoerg            return REF_invokeVirtual;
8420253Sjoerg    }
8520253Sjoerg    private static MethodType basicType(MethodType mtype) {
8620253Sjoerg        MethodType btype = mtype.erase();
8720253Sjoerg        if (btype.hasPrimitives()) {
8820253Sjoerg            for (int i = -1; i < mtype.parameterCount(); i++) {
8920253Sjoerg                Class<?> type = (i < 0 ? mtype.returnType() : mtype.parameterType(i));
9020253Sjoerg                if (type == boolean.class ||
9120253Sjoerg                    type == byte.class ||
9220253Sjoerg                    type == char.class ||
9320253Sjoerg                    type == short.class) {
9420253Sjoerg                    type = int.class;
9520253Sjoerg                    if (i < 0)
9620253Sjoerg                        btype = btype.changeReturnType(type);
9720253Sjoerg                    else
9820253Sjoerg                        btype = btype.changeParameterType(i, type);
9952527Sdavidn                }
10020253Sjoerg            }
10152512Sdavidn        }
10220253Sjoerg        return btype;
10320253Sjoerg    }
10420253Sjoerg    private static Method getMethod(Class<?> defc, String name, Class<?>... ptypes) {
10520253Sjoerg        try {
10620253Sjoerg            return defc.getDeclaredMethod(name, ptypes);
10720253Sjoerg        } catch (NoSuchMethodException ex) {
10820747Sdavidn        }
10982868Sdd        try {
110167919Sle            return defc.getMethod(name, ptypes);
111167919Sle        } catch (NoSuchMethodException ex) {
11220253Sjoerg            throw new IllegalArgumentException(ex);
11320253Sjoerg        }
11420253Sjoerg    }
11520253Sjoerg    private static MethodHandle unreflect(Method m) {
11620253Sjoerg        try {
11720253Sjoerg            MethodHandle mh = LOOKUP.unreflect(m);
11820253Sjoerg            if (Modifier.isTransient(m.getModifiers()))
11920253Sjoerg                mh = mh.asFixedArity();  // remove varargs wrapper
12020253Sjoerg            return mh;
12120253Sjoerg        } catch (IllegalAccessException ex) {
12256000Sdavidn            throw new IllegalArgumentException(ex);
12320253Sjoerg        }
12420253Sjoerg    }
12556000Sdavidn    private static final Lookup DIRECT_INVOKER_LOOKUP;
12656000Sdavidn    private static final Class<?> MEMBER_NAME_CLASS;
12756000Sdavidn    private static final MethodHandle MH_INTERNAL_MEMBER_NAME;
12820253Sjoerg    private static final MethodHandle MH_DEBUG_STRING;
12920253Sjoerg    static {
13052512Sdavidn        try {
13120253Sjoerg            // This is white box testing.  Use reflection to grab private implementation bits.
13220267Sjoerg            String magicName = "IMPL_LOOKUP";
13320267Sjoerg            Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
13420267Sjoerg            // This unit test will fail if a security manager is installed.
13520267Sjoerg            magicLookup.setAccessible(true);
13620267Sjoerg            // Forbidden fruit...
13720267Sjoerg            DIRECT_INVOKER_LOOKUP = (Lookup) magicLookup.get(null);
13820267Sjoerg            MEMBER_NAME_CLASS = Class.forName("java.lang.invoke.MemberName", false, MethodHandle.class.getClassLoader());
13920267Sjoerg            MH_INTERNAL_MEMBER_NAME = DIRECT_INVOKER_LOOKUP
14020267Sjoerg                    .findVirtual(MethodHandle.class, "internalMemberName", methodType(MEMBER_NAME_CLASS))
14120267Sjoerg                    .asType(methodType(Object.class, MethodHandle.class));
14220267Sjoerg            MH_DEBUG_STRING = DIRECT_INVOKER_LOOKUP
14320267Sjoerg                    .findVirtual(MethodHandle.class, "debugString", methodType(String.class));
14420267Sjoerg        } catch (ReflectiveOperationException ex) {
14520267Sjoerg            throw new Error(ex);
14620253Sjoerg        }
14720253Sjoerg    }
14820253Sjoerg    private Object internalMemberName(MethodHandle mh) {
14920253Sjoerg        try {
15020267Sjoerg            return MH_INTERNAL_MEMBER_NAME.invokeExact(mh);
15120253Sjoerg        } catch (Throwable ex) {
15221052Sdavidn            throw new Error(ex);
153167919Sle        }
154167919Sle    }
155167919Sle    private String debugString(MethodHandle mh) {
156167919Sle        try {
157167919Sle            return (String) MH_DEBUG_STRING.invokeExact(mh);
158219408Sjkim        } catch (Throwable ex) {
159167919Sle            throw new Error(ex);
160168044Sle        }
161167919Sle    }
16221052Sdavidn    private static MethodHandle directInvoker(int refKind, MethodType mtype) {
16321052Sdavidn        return directInvoker(REF_KIND_NAMES[refKind], mtype);
16421052Sdavidn    }
16521052Sdavidn    private static MethodHandle directInvoker(String name, MethodType mtype) {
166224535Sdelphij        boolean isStatic;
16721052Sdavidn        mtype = mtype.erase();
16821052Sdavidn        if (name.startsWith("MH::")) {
16921052Sdavidn            isStatic = false;
17021052Sdavidn            name = strip("MH::", name);
17121052Sdavidn        } else if (name.startsWith("REF_")) {
17221052Sdavidn            isStatic = true;
17330259Scharnier            name = strip("REF_", name);
17421052Sdavidn            if (name.startsWith("invoke"))
17521052Sdavidn                name = "linkTo"+strip("invoke", name);
17621052Sdavidn            mtype = mtype.appendParameterTypes(MEMBER_NAME_CLASS);
17721052Sdavidn        } else {
17821242Sdavidn            throw new AssertionError("name="+name);
17921242Sdavidn        }
18021242Sdavidn        //System.out.println("directInvoker = "+name+mtype);
18121242Sdavidn        try {
18221242Sdavidn            if (isStatic)
18321242Sdavidn                return DIRECT_INVOKER_LOOKUP
18421242Sdavidn                        .findStatic(MethodHandle.class, name, mtype);
18521242Sdavidn            else
18621242Sdavidn                return DIRECT_INVOKER_LOOKUP
187219408Sjkim                        .findVirtual(MethodHandle.class, name, mtype);
18821242Sdavidn        } catch (ReflectiveOperationException ex) {
189148584Spjd            throw new IllegalArgumentException(ex);
190148584Spjd        }
191148584Spjd    }
192148584Spjd    private Object invokeWithArguments(Method m, Object... args) {
193148584Spjd        Object recv = null;
19421242Sdavidn        if (!Modifier.isStatic(m.getModifiers())) {
19521242Sdavidn            recv = args[0];
19621242Sdavidn            args = pop(1, args);
197130633Srobert        }
198130633Srobert        try {
19921242Sdavidn            return m.invoke(recv, args);
20021242Sdavidn        } catch (IllegalAccessException|IllegalArgumentException|InvocationTargetException ex) {
20121242Sdavidn            throw new IllegalArgumentException(ex);
20221242Sdavidn        }
203219408Sjkim    }
20421242Sdavidn    private Object invokeWithArguments(MethodHandle mh, Object... args) {
20521242Sdavidn        try {
20621242Sdavidn            return mh.invokeWithArguments(args);
20730259Scharnier        } catch (Throwable ex) {
20821242Sdavidn            throw new IllegalArgumentException(ex);
20921242Sdavidn        }
21021052Sdavidn    }
21121242Sdavidn    private int counter;
212219408Sjkim    private Object makeArgument(Class<?> type) {
21330259Scharnier        final String cname = type.getSimpleName();
21421052Sdavidn        final int n = ++counter;
21521052Sdavidn        final int nn = (n << 10) + 13;
21621052Sdavidn        if (type.isAssignableFrom(String.class)) {
21721052Sdavidn            return "<"+cname+"#"+nn+">";
21830259Scharnier        }
21921052Sdavidn        if (type == THIS_CLASS)  return this.withCounter(nn);
22021052Sdavidn        if (type == Integer.class   || type == int.class)     return nn;
22120253Sjoerg        if (type == Character.class || type == char.class)    return (char)(n % 100+' ');
22220253Sjoerg        if (type == Byte.class      || type == byte.class)    return (byte)-(n % 100);
22320253Sjoerg        if (type == Long.class      || type == long.class)    return (long)nn;
22421330Sdavidn        throw new IllegalArgumentException("don't know how to make argument of type: "+type);
22521330Sdavidn    }
22621330Sdavidn    private Object[] makeArguments(Class<?>... ptypes) {
22720253Sjoerg        Object[] args = new Object[ptypes.length];
22820253Sjoerg        for (int i = 0; i < args.length; i++)
22920253Sjoerg            args[i] = makeArgument(ptypes[i]);
23020253Sjoerg        return args;
23163596Sdavidn    }
23263596Sdavidn    private Object[] makeArguments(MethodType mtype) {
23363596Sdavidn        return makeArguments(mtype.parameterArray());
23463596Sdavidn    }
23563596Sdavidn    private Object[] pop(int n, Object[] args) {
23663596Sdavidn        if (n >= 0)
23763596Sdavidn            return Arrays.copyOfRange(args, n, args.length);
23863596Sdavidn        else
23920253Sjoerg            return Arrays.copyOfRange(args, 0, args.length+n);
24020253Sjoerg    }
24120253Sjoerg    private Object[] pushAtFront(Object arg1, Object[] args) {
24220679Sdavidn        Object[] res = new Object[1+args.length];
24320253Sjoerg        res[0] = arg1;
24420253Sjoerg        System.arraycopy(args, 0, res, 1, args.length);
24552527Sdavidn        return res;
24620253Sjoerg    }
24720747Sdavidn    private Object[] pushAtBack(Object[] args, Object argN) {
24844229Sdavidn        Object[] res = new Object[1+args.length];
24961957Sache        System.arraycopy(args, 0, res, 0, args.length);
25030259Scharnier        res[args.length] = argN;
25120253Sjoerg        return res;
25220747Sdavidn    }
25320747Sdavidn    private static String strip(String prefix, String s) {
25420253Sjoerg        assert(s.startsWith(prefix));
25520747Sdavidn        return s.substring(prefix.length());
25620253Sjoerg    }
25720253Sjoerg
25852527Sdavidn    private final int[] refKindTestCounts = new int[REF_KIND_NAMES.length];
25920253Sjoerg    @After
26026088Sdavidn    public void printCounts() {
26130259Scharnier        ArrayList<String> zeroes = new ArrayList<>();
26220253Sjoerg        for (int i = 0; i < refKindTestCounts.length; i++) {
26352527Sdavidn            final int count = refKindTestCounts[i];
26420253Sjoerg            final String name = REF_KIND_NAMES[i];
26520253Sjoerg            if (count == 0) {
26620253Sjoerg                if (name != null)  zeroes.add(name);
26763600Sdavidn                continue;
26863600Sdavidn            }
26920253Sjoerg            if (verbose >= 0)
27020253Sjoerg                System.out.println("test count for "+name+" : "+count);
27130259Scharnier            else if (name != null)
27220253Sjoerg                zeroes.add(name);
27320253Sjoerg        }
27420253Sjoerg        if (verbose >= 0)
27520253Sjoerg            System.out.println("test counts zero for "+zeroes);
27620253Sjoerg    }
27720253Sjoerg
27820253Sjoerg    // Test subjects
27920253Sjoerg    public static String makeString(Object x) { return "makeString("+x+")"; }
28020253Sjoerg    public static String dupString(String x) { return "("+x+"+"+x+")"; }
28120253Sjoerg    public static String intString(int x) { return "intString("+x+")"; }
28220253Sjoerg    public static String byteString(byte x) { return "byteString("+x+")"; }
28320253Sjoerg    public static String longString(String x, long y, String z) { return "longString("+x+y+z+")"; }
28420253Sjoerg
28520253Sjoerg    public final String toString() {
28620253Sjoerg        return "<"+getClass().getSimpleName()+"#"+counter+">";
28720267Sjoerg    }
28830259Scharnier    public final String hello() { return "hello from "+this; }
28920267Sjoerg    private PrivateInvokeTest withCounter(int counter) {
29020253Sjoerg        PrivateInvokeTest res = new PrivateInvokeTest();
29152527Sdavidn        res.counter = counter;
29220253Sjoerg        return res;
29320267Sjoerg    }
29444386Sdavidn
29520253Sjoerg    public static void main(String... av) throws Throwable {
29644229Sdavidn        new PrivateInvokeTest().run();
29744229Sdavidn    }
29844386Sdavidn    public void run() throws Throwable {
29944229Sdavidn        testFirst();
30020267Sjoerg        testInvokeDirect();
30120253Sjoerg    }
30252527Sdavidn
30320253Sjoerg    @Test
30444229Sdavidn    public void testFirst() throws Throwable {
30520253Sjoerg        if (true)  return;  // nothing here
30620253Sjoerg        try {
30720253Sjoerg            System.out.println("start of testFirst");
30820253Sjoerg        } finally {
30930259Scharnier            System.out.println("end of testFirst");
31020253Sjoerg        }
31120253Sjoerg    }
31220253Sjoerg
31320253Sjoerg    @Test
31420253Sjoerg    public void testInvokeDirect() {
31520253Sjoerg        testInvokeDirect(getMethod(THIS_CLASS, "hello"));
31643780Sdes        testInvokeDirect(getMethod(Object.class, "toString"));
31743780Sdes        testInvokeDirect(getMethod(Comparable.class, "compareTo", Object.class));
31843780Sdes        testInvokeDirect(getMethod(THIS_CLASS, "makeString", Object.class));
31920253Sjoerg        testInvokeDirect(getMethod(THIS_CLASS, "dupString", String.class));
32020253Sjoerg        testInvokeDirect(getMethod(THIS_CLASS, "intString", int.class));
32120253Sjoerg        testInvokeDirect(getMethod(THIS_CLASS, "byteString", byte.class));
32220253Sjoerg        testInvokeDirect(getMethod(THIS_CLASS, "longString", String.class, long.class, String.class));
32352527Sdavidn    }
32420253Sjoerg
32520253Sjoerg    void testInvokeDirect(Method m) {
32620253Sjoerg        final int refKind = referenceKind(m);
32752512Sdavidn        testInvokeDirect(m, refKind);
32852512Sdavidn        testInvokeDirect(m, REF_MH_invokeBasic);
32952527Sdavidn    }
33020253Sjoerg    void testInvokeDirect(Method m, int refKind) {
33144229Sdavidn        if (verbose >= 1)
33220253Sjoerg            System.out.println("testInvoke m="+m+" : "+REF_KIND_NAMES[refKind]);
33320253Sjoerg        final MethodHandle mh = unreflect(m);
33420253Sjoerg        Object[] args = makeArguments(mh.type());
33520253Sjoerg        Object res1 = invokeWithArguments(m, args);
33620253Sjoerg        // res1 comes from java.lang.reflect.Method::invoke
33744386Sdavidn        if (verbose >= 1)
33844386Sdavidn            System.out.println("m"+Arrays.asList(args)+" => "+res1);
33944386Sdavidn        // res2 comes from java.lang.invoke.MethodHandle::invoke
34020253Sjoerg        Object res2 = invokeWithArguments(mh, args);
34120253Sjoerg        assertEquals(res1, res2);
34230259Scharnier        MethodType mtype = mh.type();
34330259Scharnier        testInvokeVia("DMH invoker", refKind, directInvoker(refKind, mtype), mh, res1, args);
34420253Sjoerg        MethodType etype = mtype.erase();
34552527Sdavidn        if (etype != mtype) {
34620253Sjoerg            // Try a detuned invoker.
34720253Sjoerg            testInvokeVia("erased DMH invoker", refKind, directInvoker(refKind, etype), mh, res1, args);
34820253Sjoerg        }
34920253Sjoerg        MethodType btype = basicType(mtype);
35052512Sdavidn        if (btype != mtype && btype != etype) {
35152512Sdavidn            // Try a detuned invoker.
35252512Sdavidn            testInvokeVia("basic DMH invoker", refKind, directInvoker(refKind, btype), mh, res1, args);
35352512Sdavidn        }
35452512Sdavidn        if (false) {
35552512Sdavidn            // this can crash the JVM
35652512Sdavidn            testInvokeVia("generic DMH invoker", refKind, directInvoker(refKind, mtype.generic()), mh, res1, args);
35752512Sdavidn        }
35852512Sdavidn        refKindTestCounts[refKind] += 1;
35952512Sdavidn    }
36052512Sdavidn
36152512Sdavidn    void testInvokeVia(String kind, int refKind, MethodHandle invoker, MethodHandle mh, Object res1, Object... args) {
36252512Sdavidn        Object[] args1;
36352512Sdavidn        if (refKind == REF_MH_invokeBasic)
36452512Sdavidn            args1 = pushAtFront(mh, args);
36552512Sdavidn        else
36652512Sdavidn            args1 = pushAtBack(args, internalMemberName(mh));
36752512Sdavidn        if (verbose >= 2) {
36852527Sdavidn            System.out.println(kind+" invoker="+invoker+" mh="+debugString(mh)+" args="+Arrays.asList(args1));
36952512Sdavidn        }
37052512Sdavidn        Object res3 = invokeWithArguments(invoker, args1);
37152512Sdavidn        assertEquals(res1, res3);
37252512Sdavidn    }
37352527Sdavidn}
37452527Sdavidn